Greasy Fork

Greasy Fork is available in English.

PikPak 增强大师

桌面级PikPak网盘管家!包含Aria2/Motrix带目录结构推送、文件查重(哈希/时长/名称)、文件夹查重(名称/相似度/包含率)、批量重命名(正则替换/连续编号/文本格式化/FC2名称清洗/前缀去广告/后缀智能修复)、清理空文件夹、内置解压密码库的批量解压、夹杂无关文字或“去头”的污染磁链智能识别、自定义资源黑白名单:清理垃圾文件/文件夹、多账号数据迁移、分享提取次数限制、导出目录树等。沉浸式媒体播放引擎:以图搜图、高级字幕加载、跳过片头尾及进度条缩略图预览。叫“增强大师”是有原因的,何不进来看看?

当前为 2026-04-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name               PikPak 增强大师
// @name:zh-CN         PikPak 增强大师
// @name:zh-TW         PikPak 增強大師
// @name:en            PikPak Enhancement Master
// @name:ko            PikPak Enhancement Master
// @name:ja            PikPak Enhancement Master
// @name:id            PikPak Enhancement Master
// @name:ms            PikPak Enhancement Master
// @namespace          https://github.com/digbug82/
// @version            2.2.2
// @author             digbug82
// @license            CC-BY-NC-SA-4.0
// @description        桌面级PikPak网盘管家!包含Aria2/Motrix带目录结构推送、文件查重(哈希/时长/名称)、文件夹查重(名称/相似度/包含率)、批量重命名(正则替换/连续编号/文本格式化/FC2名称清洗/前缀去广告/后缀智能修复)、清理空文件夹、内置解压密码库的批量解压、夹杂无关文字或“去头”的污染磁链智能识别、自定义资源黑白名单:清理垃圾文件/文件夹、多账号数据迁移、分享提取次数限制、导出目录树等。沉浸式媒体播放引擎:以图搜图、高级字幕加载、跳过片头尾及进度条缩略图预览。叫“增强大师”是有原因的,何不进来看看?
// @description:zh-CN  桌面级PikPak网盘管家!包含Aria2/Motrix带目录结构推送、文件查重(哈希/时长/名称)、文件夹查重(名称/相似度/包含率)、批量重命名(正则替换/连续编号/文本格式化/FC2名称清洗/前缀去广告/后缀智能修复)、清理空文件夹、内置解压密码库的批量解压、夹杂无关文字或“去头”的污染磁链智能识别、自定义资源黑白名单:清理垃圾文件/文件夹、多账号数据迁移、分享提取次数限制、导出目录树等。沉浸式媒体播放引擎:以图搜图、高级字幕加载、跳过片头尾及进度条缩略图预览。叫“增强大师”是有原因的,何不进来看看?
// @description:zh-TW  桌面級PikPak網盤管家!包含Aria2/Motrix帶目錄結構推送、檔案查重(雜湊/時長/名稱)、資料夾查重(名稱/相似度/包含率)、批次重新命名(正規替換/連續編號/文字格式化/FC2名稱清洗/前綴去廣告/後綴智慧修復)、清理空資料夾、內建解壓縮密碼庫的批次解壓縮、夾雜無關文字或「去頭」的污染磁鏈智慧識別、自訂資源黑白名單:清理垃圾檔案/資料夾、多帳號資料遷移、分享提取次數限制、匯出目錄樹等。沉浸式媒體播放引擎:以圖搜圖、進階字幕載入、跳過片頭尾及進度條縮圖預覽。叫「增強大師」是有原因的,何不進來看看?
// @description:en     Desktop-grade PikPak manager! Features Aria2/Motrix push with folders, file dedupe (hash/time/name), folder dedupe (name/similarity/inclusion), bulk rename (regex/sequential/format/FC2/ad-free/ext-fix), empty folder pruning, batch extract via password vault, corrupted magnet recognition, custom trash black/whitelists, multi-account migration, share limit restrictions, tree export. Immersive player: image search, advanced subs, intro/outro skip, thumbnails. Why not take a look?
// @description:ko     데스크톱급 PikPak 관리자! 구조 유지 Aria2/Motrix 푸시, 파일(해시/시간/이름)·폴더(이름/유사도/포함율) 중복 검사, 일괄 이름 변경(정규식/순번/포맷/FC2/광고제거/확장자복구), 빈 폴더 정리, 내장 암호 일괄 압축 해제, 오염 마그넷 인식, 지정 흑백명단 쓰레기 정리, 다중 계정 마이그레이션, 공유 횟수 제한, 트리 내보내기. 몰입형 플레이어: 이미지 검색, 고급 자막, 오프/엔딩 스킵, 썸네일. "인핸서 마스터"인 이유를 확인해보세요!
// @description:ja     デスクトップ級PikPakマネージャー!階層保持Aria2/Motrix転送、ファイル(ハッシュ/時間/名前)・フォルダ(名前/類似度/包含率)重複チェック、一括リネーム(正規表現/連番/書式/FC2/広告削除/拡張子修復)、空フォルダ削除、内蔵パス庫の一括解凍、破損マグネット認識、白/黒リストによる不要ファイル整理、複数アカウント移行、共有回数制限、ツリー出力。没入型プレーヤー:画像検索、高度な字幕、OP/EDスキップ、サムネイル。なぜ「拡張マスター」なのか、ぜひお試しを!
// @description:id     Pengelola PikPak kelas desktop! Mendukung push Aria2/Motrix dengan struktur folder, duplikat file/folder, ganti nama massal, bersih folder kosong, ekstraksi massal dengan vault kata sandi, identifikasi magnet tercemar, daftar hitam/putih kustom, migrasi multi-akun, batas ekstraksi berbagi, ekspor pohon direktori, dan lainnya. Mesin media imersif: cari gambar, subtitle lanjutan, lewati intro/outro, dan pratinjau thumbnail progres.
// @description:ms     Pengurus PikPak bertaraf desktop! Menyokong push Aria2/Motrix dengan struktur folder, semakan pendua fail/folder, penamaan semula pukal, pembersihan folder kosong, nyahmampat pukal dengan peti kata laluan, pengecaman magnet tercemar, senarai hitam/putih tersuai, migrasi berbilang akaun, had pengekstrakan perkongsian, eksport pepohon direktori dan banyak lagi. Enjin media imersif: carian imej, sari kata lanjutan, langkau intro/outro, dan pratonton lakaran kecil kemajuan.
// @match              https://mypikpak.com/drive/*
// @match              https://app.mypikpak.com/*
// @match              https://drive.mypikpak.com/*
// @icon               https://raw.githubusercontent.com/digbug82/PikPak_Enhancement_Master/main/img/logo.svg
// @homepage           https://github.com/digbug82/PikPak_Enhancement_Master
// @supportURL         https://github.com/digbug82/PikPak_Enhancement_Master/issues
// @compatible         chrome
// @compatible         edge
// @grant              GM_setClipboard
// @grant              GM_setValue
// @grant              GM_getValue
// @grant              GM_deleteValue
// @grant              GM_listValues
// @grant              GM_xmlhttpRequest
// @connect            catbox.moe
// @connect            litterbox.catbox.moe
// @connect            uguu.se
// @connect            mypikpak.com
// @connect            whatslink.info
// @connect            localhost
// @run-at             document-start
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/hls.min.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js

// ==/UserScript==

/*
 * ============================================================================
 * COPYRIGHT & LICENSE NOTICE
 * ============================================================================
 * This project (PikPak Enhancement Master) is a derivative work created by digbug82.
 *
 * [1] NEW CONTRIBUTIONS & ENHANCEMENTS:
 * All new features, extensive refactoring, UI overhaul, and advanced management
 * suites (e.g., Image Search, Blacklist, Smart Deduplication, Aria2 integration)
 * are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike
 * 4.0 International License (CC-BY-NC-SA-4.0).
 * Copyright (c) 2025-2026 digbug82.
 * You may NOT use this material for commercial purposes.
 *
 * ----------------------------------------------------------------------------
 * [2] ORIGINAL PROJECT ACKNOWLEDGEMENT (MIT License):
 * The base framework and original API logics are derived from
 * "PikPak File Manager v1.2.0" (Original Repository: https://github.com/poihoii/PikPak_FileManager).
 *
 * MIT License
 * Copyright (c) 2025 브랜뉴
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * ============================================================================
 */

(() => {
"use strict";

if (window.self !== window.top) return;

const parseCloudLinks = (rawString, isSmartFix) => {
    const formattedVal = String(rawString || '').replace(/(https?:\/\/|ftp:\/\/|sftp:\/\/|magnet:\?|ed2k:\/\/|thunder:\/\/)/gi, '\n$1');
    const lines = formattedVal.split('\n').map(l => l.trim()).filter(l => l);
    const uniqueTasks = new Map();
    const linkRegex = /^(https?:\/\/|ftp:\/\/|sftp:\/\/|magnet:\?|ed2k:\/\/|thunder:\/\/)/i;
    const base32ToHex = (b32) => {
        const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
        let bits = "";
        const input = String(b32 || '').toUpperCase();
        for (let i = 0; i < input.length; i++) {
            const val = alphabet.indexOf(input[i]);
            if (val === -1) return null;
            bits += val.toString(2).padStart(5, '0');
        }
        let hex = "";
        for (let i = 0; i + 4 <= bits.length; i += 4) {
            const chunk = bits.substring(i, i + 4);
            const num = parseInt(chunk, 2);
            if (!isNaN(num)) hex += num.toString(16);
        }
        return hex.toUpperCase();
    };
    const extractHexHash = (url) => {
        const match = String(url || '').match(/urn:btih:([^&]+)/i);
        if (!match) return null;
        const hash = match[1].toUpperCase();
        if (hash.length === 40) return hash;
        if (hash.length === 32) return base32ToHex(hash);
        return null;
    };
    const addResult = (url) => {
        const hash = extractHexHash(url);
        if (hash) {
            if (!uniqueTasks.has(hash)) uniqueTasks.set(hash, url);
        } else {
            uniqueTasks.set(url, url);
        }
    };
    for (let i = 0; i < lines.length; i++) {
        let line = lines[i];
        if (linkRegex.test(line)) {
            addResult(line);
            continue;
        }
        if (isSmartFix) {
            let cleanedStr = line.replace(/[^a-zA-Z0-9]/g, '');
            const hexMatches = cleanedStr.matchAll(/([a-fA-F0-9]{40})/g);
            for (const match of hexMatches) {
                addResult(`magnet:?xt=urn:btih:${match[1].toUpperCase()}`);
                cleanedStr = cleanedStr.replace(match[1], ' '.repeat(40));
            }
            const b32Matches = cleanedStr.matchAll(/([a-zA-Z2-7]{32})/g);
            for (const match of b32Matches) {
                const hex = base32ToHex(match[1].toUpperCase());
                if (hex) addResult(`magnet:?xt=urn:btih:${hex}`);
            }
        }
    }
    return Array.from(uniqueTasks.values());
};

const NativeTokenSniffer = {
    init: () => {
        const inject = () => {
            const s = document.createElement('script');
            s.textContent = `(function(){
                const _f = window.fetch;
                window.fetch = async function(...args) {
                    const url = args[0] ? args[0].toString() : '';

                    if (url.includes('area_accessible')) {
                        return new Promise((_, reject) => {
                            reject(new Error("Bypassed area_accessible"));
                        });
                    }

                    const isTurbo = localStorage.getItem('pk_turbo_mode') === 'true';

                    if (isTurbo) {
                        if (location.href.includes('/login') || location.pathname.includes('login')) {
                            return _f.apply(this, args);
                        }

                        if (url.includes(':incremental_sync') || url.includes(':sync')) {
                            return new Response(JSON.stringify({
                                error_code: 0,
                                data: [],
                                files: [],
                                tasks: [],
                                next_page_token: ''
                            }), {status: 200, headers: {'Content-Type': 'application/json'}});
                        }

                        try {
                            const opts = args[1] || {};
                            let cap = null;
                            let auth = null;
                            if (opts.headers) {
                                if (opts.headers instanceof Headers) {
                                    cap = opts.headers.get('x-captcha-token') || opts.headers.get('X-Captcha-Token');
                                    auth = opts.headers.get('authorization') || opts.headers.get('Authorization');
                                }
                                else if (typeof opts.headers === 'object') {
                                    const capKey = Object.keys(opts.headers).find(k => k.toLowerCase() === 'x-captcha-token');
                                    if (capKey) cap = opts.headers[capKey];
                                    const authKey = Object.keys(opts.headers).find(k => k.toLowerCase() === 'authorization');
                                    if (authKey) auth = opts.headers[authKey];
                                }
                            }
                            if (cap && cap.length > 20) localStorage.setItem('pk_captured_captcha', cap);

                            if (auth && auth.length > 20) document.dispatchEvent(new CustomEvent('pk-token-captured', { detail: auth }));
                        } catch (e) {}
                    }

                    return _f.apply(this, args);
                };

                const _W = window.Worker;
                window.Worker = function(url, opts) {
                    const isTurbo = localStorage.getItem('pk_turbo_mode') === 'true';
                    if (!isTurbo) return new _W(url, opts);

                    if (location.href.includes('/login') || location.pathname.includes('login')) {
                        return new _W(url, opts);
                    }

                    const u = url ? url.toString() : '';
                    const blockList = ['query_db', 'sync', 'database', 'index', 'calc_sha1', 'query_docs'];

                    if (blockList.some(k => u.includes(k))) {
                        console.log('🚫 [PikPak Master] OOM-Guard blocked worker:', u);
                        return new _W('data:application/javascript,self.onmessage=()=>{}');
                    }
                    return new _W(url, opts);
                };

            })()`;
            (document.head || document.documentElement).appendChild(s).remove();
        };
        inject();
    }
};

NativeTokenSniffer.init();

window.addEventListener('beforeunload', (e) => {
    const activeStatus = ['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'];
    const tasks = pkState?.uploadTasks || [];
    if (tasks.some(t => activeStatus.includes(t.status))) {
        e.preventDefault();
        return e.returnValue;
    }
});

window.pkAddGhostFile = function(id) {
    try {
        const ghosts = JSON.parse(localStorage.getItem('pk_ghost_files') || '[]');
        if (!ghosts.includes(id)) {
            ghosts.push(id);
            localStorage.setItem('pk_ghost_files', JSON.stringify(ghosts));
        }
    } catch(e){}
};
window.pkRemoveGhostFile = function(id) {
    try {
        const ghosts = JSON.parse(localStorage.getItem('pk_ghost_files') || '[]');
        const updated = ghosts.filter(x => x !== id);
        localStorage.setItem('pk_ghost_files', JSON.stringify(updated));
    } catch(e){}
};
window.pkCleanupGhostFiles = function() {
    try {
        const ghosts = JSON.parse(localStorage.getItem('pk_ghost_files') || '[]');
        if (ghosts.length > 0) {
            console.log(`[GhostCleanup] Found ${ghosts.length} interrupted uploads. Cleaning up...`);
            const BATCH_SIZE = 100;
            for (let i = 0; i < ghosts.length; i += BATCH_SIZE) {
                const chunk = ghosts.slice(i, i + BATCH_SIZE);
                fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                    method: 'POST',
                    headers: getHeaders(),
                    body: JSON.stringify({ ids: chunk })
                }).catch(()=>{});
            }
            localStorage.setItem('pk_ghost_files', '[]');
        }
    } catch(e){}

    try {
        const phases = "PHASE_TYPE_UNKNOW,PHASE_TYPE_PENDING,PHASE_TYPE_RUNNING,PHASE_TYPE_PAUSED,PHASE_TYPE_ERROR";
        const filters = encodeURIComponent(JSON.stringify({ "phase": { "in": phases } }));
        const url = `https://api-drive.mypikpak.com/drive/v1/tasks?type=upload&limit=100&filters=${filters}&_t=${Date.now()}`;
        fetch(url, { headers: getHeaders() })
            .then(res => res.json())
            .then(cloudData => {
                const cloudTasks = cloudData.tasks || [];
                const ghostIds = [];
                cloudTasks.forEach(ct => {
                    if (ct.file_id) {
                        const ref = ct.reference_resource || {};
                        const taskName = ref.name || ct.name || ct.file_name || 'Ghost Task';
                        if (taskName === 'upload' || taskName === 'Ghost Task') {
                            ghostIds.push(ct.file_id);
                        }
                    }
                });
                if (ghostIds.length > 0) {
                    console.log(`[Reconcile] Auto-cleaning orphaned cloud ghost files on startup: ${ghostIds.length} files`);
                    fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                        method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: ghostIds })
                    }).catch(()=>{});
                }
            }).catch(()=>{});
    } catch (e) {}
};

window.addEventListener('beforeunload', () => {
    const tasks = pkState?.uploadTasks || [];
    const filesToDelete = tasks.filter(t => t.status !== 'DONE' && t.file_id && !t._deleted).map(t => t.file_id);
    if (filesToDelete.length > 0) {
        const BATCH_SIZE = 100;
        for (let i = 0; i < filesToDelete.length; i += BATCH_SIZE) {
            const chunk = filesToDelete.slice(i, i + BATCH_SIZE);
            try {
                fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                    method: 'POST',
                    headers: getHeaders(),
                    body: JSON.stringify({ ids: chunk }),
                    keepalive: true
                }).catch(()=>{});
            } catch(e) {}
        }
        if (typeof window.pkRemoveGhostFile === 'function') {
            filesToDelete.forEach(id => window.pkRemoveGhostFile(id));
        }
    }
});

;
const CONF = {
    rowHeight: 40,
    buffer: 20,
    dupGridHeaderHeight: { normal: 40, max: 60 },
    dupGridSectionGap: { normal: 14, max: 18 },
    dupGridBodyGapY: { normal: 16, max: 20 },
    SYSTEM_FOLDER_NAME: 'My Pack',
    browserDownloadConfirmFileCount: 10,
    browserDownloadConfirmTotalBytes: 10 * 1024 * 1024 * 1024,
    mouseSideNavHistoryMax: 50,
    mouseSideNavDebug: false,
    clipboardMagnetPaste: true,
    clipboardMagnetFocusCooldown: 2500,
    clipboardMagnetDenyCooldown: 60000,
    clipboardMagnetPromptGap: 10 * 60 * 1000,
    clipboardMagnetIgnoreTTL: 10 * 60 * 1000,
    clipboardMagnetMaxChars: 200000,
    magnetPreviewApi: 'https://whatslink.info/api/v1/link',
    magnetPreviewTimeout: 9000,
    magnetPreviewCacheTTL: 60 * 60 * 1000,
    magnetPreviewErrorCacheTTL: 10 * 60 * 1000,
    magnetPreviewCircuitTTL: 5 * 60 * 1000,
    magnetPreviewMaxShots: 5,
    logoSVG: `<svg viewBox="0 0 238 200" style="width:24px;height:24px;border-radius:4px;flex-shrink:0;"><path d="M0 0 C1.82724609 0.01353516 1.82724609 0.01353516 3.69140625 0.02734375 C4.59761719 0.03894531 5.50382812 0.05054688 6.4375 0.0625 C5.95097979 7.11704304 4.33696858 12.90149479 1.6875 19.4375 C1.35234375 20.32566406 1.0171875 21.21382812 0.671875 22.12890625 C0.3315625 22.98097656 -0.00875 23.83304688 -0.359375 24.7109375 C-0.66198242 25.47583496 -0.96458984 26.24073242 -1.27636719 27.02880859 C-3.01571023 29.77913653 -4.60880008 30.70366989 -7.5625 32.0625 C-10.93383789 32.72265625 -10.93383789 32.72265625 -14.78515625 33.125 C-15.47874237 33.20142731 -16.17232849 33.27785461 -16.88693237 33.3565979 C-18.36660067 33.51855298 -19.84685768 33.67520381 -21.3276062 33.82696533 C-25.19232303 34.22318595 -29.05286739 34.65697538 -32.9140625 35.0859375 C-33.67180466 35.16903168 -34.42954681 35.25212585 -35.21025085 35.33773804 C-40.99791882 35.97875931 -46.74864414 36.77615252 -52.5 37.6875 C-61.81496788 39.10080547 -71.19269316 40.07620454 -80.5625 41.0625 C-19.8425 41.0625 40.8775 41.0625 103.4375 41.0625 C91.8875 39.7425 80.3375 38.4225 68.4375 37.0625 C63.8175 36.4025 59.1975 35.7425 54.4375 35.0625 C49.17221542 34.42736314 43.90722683 33.79696512 38.63671875 33.20703125 C37.62996094 33.08714844 36.62320313 32.96726563 35.5859375 32.84375 C34.69052246 32.74126953 33.79510742 32.63878906 32.87255859 32.53320312 C30.35601376 32.0467485 28.59527547 31.44037784 26.4375 30.0625 C23.38532266 24.97553776 21.3341425 19.45473677 19.1875 13.9375 C18.91695801 13.25671387 18.64641602 12.57592773 18.36767578 11.87451172 C16.82394482 7.78804812 16.13851057 4.42502757 16.4375 0.0625 C33.20320897 -0.76054389 50.04132 2.04640823 66.578125 4.53515625 C70.96365446 5.13439358 75.35589707 5.627565 79.75488281 6.11669922 C97.85972043 8.13836316 97.85972043 8.13836316 106.6875 9.4375 C107.39487305 9.52700928 108.10224609 9.61651855 108.83105469 9.70874023 C113.96714941 10.51808328 116.87598017 12.31623275 120.4375 16.0625 C121.69830294 18.53927732 122.67025259 20.7202309 123.5625 23.3125 C124.02136126 24.56846882 124.48232815 25.8236702 124.9453125 27.078125 C125.27250149 28.00288179 125.27250149 28.00288179 125.60630035 28.94632053 C126.38750394 31.05750635 126.38750394 31.05750635 127.44002533 32.93062496 C131.07482517 39.83448151 131.00351579 46.31795394 130.95507812 53.99243164 C130.96050802 55.37978344 130.96763552 56.76712947 130.97631836 58.15446472 C130.99445028 61.89829685 130.98752708 65.6416848 130.97480202 69.38552403 C130.96462344 73.31622656 130.97408092 77.24689291 130.98034668 81.17759705 C130.98760817 87.77544941 130.97807403 94.37312221 130.95898438 100.97094727 C130.93720936 108.58452515 130.94427739 116.19767461 130.96629 123.81124216 C130.98447611 130.36524706 130.98698696 136.91912344 130.97653532 143.47314543 C130.97031913 147.38014362 130.96941296 151.2869408 130.98268127 155.19392586 C130.99428653 158.8672447 130.9861299 162.54001414 130.96310425 166.213274 C130.95534421 168.19404482 130.96713242 170.17486244 130.97961426 172.15560913 C130.90049754 180.52230774 129.95755225 186.09535704 124.25390625 192.5234375 C123.51011719 193.15507812 122.76632813 193.78671875 122 194.4375 C121.25878906 195.08460938 120.51757812 195.73171875 119.75390625 196.3984375 C114.7661098 199.98157627 110.22842399 200.35421576 104.22135925 200.32992554 C103.39785408 200.33445665 102.5743489 200.33898776 101.72588903 200.34365618 C98.968488 200.35630894 96.21128426 200.35467924 93.45385742 200.35302734 C91.475975 200.35901206 89.49809491 200.36581748 87.5202179 200.37338257 C82.14823484 200.39105594 76.77631549 200.39573853 71.40430617 200.39701414 C66.91878502 200.39891354 62.4332787 200.40627158 57.94776326 200.41335833 C47.36384951 200.42964512 36.77996977 200.43452703 26.19604492 200.43310547 C15.28118177 200.43190408 4.36651636 200.45300486 -6.54829675 200.4845928 C-15.92170288 200.51075235 -25.29504442 200.52147289 -34.66848677 200.52019465 C-40.26569836 200.51968491 -45.86273424 200.52537507 -51.45990944 200.54655075 C-56.725388 200.56592749 -61.99052314 200.5660613 -67.25601387 200.55151749 C-69.1861191 200.54942757 -71.11624579 200.55414114 -73.04631424 200.5662384 C-75.68641426 200.58171127 -78.32533312 200.57236959 -80.96540833 200.55697632 C-81.72466655 200.56726344 -82.48392478 200.57755057 -83.26619083 200.58814943 C-90.327556 200.49750269 -96.39704041 197.82485418 -101.375 192.75 C-102.18904297 191.95142578 -102.18904297 191.95142578 -103.01953125 191.13671875 C-108.29053612 184.05088689 -108.01804154 177.09915158 -108.0300293 168.55004883 C-108.04229625 167.18245883 -108.05575106 165.81487905 -108.07029724 164.4473114 C-108.10523797 160.74401042 -108.12059214 157.04088761 -108.13013434 153.33744264 C-108.13673436 151.01403475 -108.14708893 148.69067299 -108.15863991 146.36728477 C-108.19836069 138.23287671 -108.22038571 130.09860956 -108.22827148 121.96411133 C-108.23610728 114.43116961 -108.28516577 106.89925647 -108.35333699 99.36664182 C-108.41007964 92.86514961 -108.43519788 86.36399446 -108.43721896 79.86225718 C-108.43904166 75.9947118 -108.45309089 72.1282487 -108.50003624 68.26096535 C-108.72797687 48.29049317 -107.52961567 30.83210742 -95.5625 14.0625 C-92.23797604 10.732487 -88.44904231 10.20048941 -83.953125 9.5 C-83.20613342 9.37633057 -82.45914185 9.25266113 -81.68951416 9.12524414 C-74.04584045 7.901492 -66.3645662 7.06662299 -58.66394043 6.29776001 C-54.62860447 5.8940274 -50.59547976 5.46951727 -46.5625 5.04296875 C-45.77776306 4.96008102 -44.99302612 4.8771933 -44.18450928 4.79179382 C-36.33754684 3.9513441 -28.53467892 2.87051571 -20.734375 1.67578125 C-13.79617508 0.63078847 -7.03103815 -0.06826251 0 0 Z M-47 131 L-15 106 L-47 81 L-47 91 L-27 106 L-47 121 Z M45.4375 89.0625 C43.16309531 93.61130937 44.11732026 99.81887268 44.0625 104.8125 C44.02511719 106.08867188 43.98773438 107.36484375 43.94921875 108.6796875 C43.6563417 116.25277258 43.6563417 116.25277258 46.7109375 122.91015625 C50.0632924 125.55649945 51.41007501 125.90713502 55.50390625 125.58984375 C58.83921214 124.68021487 60.4149221 122.75927054 62.4375 120.0625 C64.03299443 115.26404894 63.62174204 110.1852134 63.625 105.1875 C63.64336914 103.71603516 63.64336914 103.71603516 63.66210938 102.21484375 C63.77173933 93.57358621 63.77173933 93.57358621 59.75 86.1875 C54.01325068 83.39664894 49.78182352 84.71817648 45.4375 89.0625 Z M-18.5625 155.0625 C-20.89546251 157.88967213 -20.89546251 157.88967213 -20.3125 161.125 C-19.8031756 164.161959 -19.8031756 164.161959 -17.5625 166.0625 C-15.5023267 166.81656896 -13.41368556 167.49416461 -11.3125 168.125 C-10.19359375 168.46660156 -9.0746875 168.80820313 -7.921875 169.16015625 C-1.62436639 170.85169635 4.26860909 171.24487637 10.75 171.25 C11.9555957 171.26836914 11.9555957 171.26836914 13.18554688 171.28710938 C21.14907742 171.30632948 28.31945463 169.57146397 35.875 167.125 C36.88433594 166.80660156 37.89367187 166.48820313 38.93359375 166.16015625 C41.73511224 165.200361 41.73511224 165.200361 43.4375 162.0625 C43.1133631 158.74009676 42.82973697 157.45473697 40.4375 155.0625 C35.63637087 154.61062902 31.50016124 155.74460874 26.9375 157.0625 C14.69655136 160.31686985 0.09246916 160.8899845 -11.5625 155.0625 C-15.0625 154.72916667 -15.0625 154.72916667 -18.5625 155.0625 Z " fill="currentColor" transform="translate(107.5625,-0.0625)"/></svg>`,
    emptySVG: `<svg viewBox="-2 -2 28 28" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 10L7 5H17L19 10H5Z" fill="#E2E8F0" stroke="#94A3B8" stroke-width="1.2" stroke-linejoin="round"/><path d="M4 10V18C4 19.1 4.9 20 6 20H18C19.1 20 20 19.1 20 18V10" stroke="#334155" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M4 10L1 6.5" stroke="#334155" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 10L23 6.5" stroke="#334155" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><g stroke="#64748B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 13L10 14L9 15"/><path d="M15 13L14 14L15 15"/><path d="M11 17.5H13"/></g>`,
    dupHashSVG: `<svg style="width:24px;height:24px;margin-right:8px;flex-shrink:0;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M798 322.42A308.78 308.78 0 0 0 676.73 211.1a17.5 17.5 0 1 0-15.94 31.16 272.73 272.73 0 0 1 148.71 243v63.83c0 25.58-3.14 134.1-8.62 159.68a17.5 17.5 0 0 0 13.44 20.78 17.94 17.94 0 0 0 3.69 0.39 17.5 17.5 0 0 0 17.09-13.83c6.81-31.76 9.4-148.75 9.4-167v-63.88A307 307 0 0 0 798 322.42zM365.68 272.82a273.38 273.38 0 0 1 231.18-53.68 17.5 17.5 0 1 0 7.68-34.14 307.93 307.93 0 0 0-367.72 231.18 17.5 17.5 0 1 0 34.11 7.82 273.89 273.89 0 0 1 94.75-151.18zM246.54 467.73a17.49 17.49 0 0 0-17.5 17.5v69c0 50.29-14.45 87.61-44.18 114.11a17.5 17.5 0 0 0 23.28 26.13c22.56-20.11 38.52-45.63 47.43-75.85 5.7-19.34 8.47-40.4 8.47-64.39v-69a17.5 17.5 0 0 0-17.5-17.5zM743.42 636.35v-0.17l-0.5-52.83a17.5 17.5 0 1 0-35 0.34l0.5 52.74c0 4.2 0 8.79 0.05 13.68 0.21 34.94 0.53 87.74-9.16 116.81a17.5 17.5 0 1 0 33.2 11.08c11.52-34.56 11.2-88.62 11-128.09-0.07-4.85-0.09-9.4-0.09-13.56z" fill="currentColor"></path><path d="M707.92 527.26a17.5 17.5 0 0 0 35 0v-45c0-114.17-92.89-207-207.06-207a207.35 207.35 0 0 0-58.49 8.38 17.5 17.5 0 0 0 9.87 33.58 172.24 172.24 0 0 1 48.62-7c94.87 0 172.06 77.18 172.06 172.05zM363.81 482.22A172.4 172.4 0 0 1 437 341.4a17.5 17.5 0 1 0-20.14-28.62 207.45 207.45 0 0 0-88 169.44v108.39a203 203 0 0 1-6.86 55.17 162.05 162.05 0 0 1-47.22 77.75 17.5 17.5 0 1 0 23.65 25.8c27.84-25.53 47.13-57.24 57.32-94.26a236.32 236.32 0 0 0 8.09-64.46zM440.83 566a17.5 17.5 0 0 0-17.5 17.47l-0.11 56.86c0 12.5-2.7 77.59-56 131.85a17.5 17.5 0 1 0 25 24.53 229.06 229.06 0 0 0 56.17-94.59c8.93-29.25 9.89-53 9.89-61.75l0.11-56.84A17.5 17.5 0 0 0 440.83 566z" fill="currentColor"></path><path d="M604.17 419.76a17.5 17.5 0 0 0-4.71-24.3 113 113 0 0 0-176.16 93.68v38.12a17.5 17.5 0 0 0 35 0v-38.12a78 78 0 0 1 121.57-64.68 17.49 17.49 0 0 0 24.3-4.7zM618.85 438.05a17.51 17.51 0 0 0-9.92 22.68 77.55 77.55 0 0 1 5.33 28.41v206.29c0 33.49-6.45 66.07-19.71 99.61a17.5 17.5 0 1 0 32.55 12.87c14.9-37.71 22.16-74.51 22.16-112.48V489.14a112.38 112.38 0 0 0-7.74-41.14 17.5 17.5 0 0 0-22.67-9.95z" fill="currentColor"></path><path d="M549.91 488a17.5 17.5 0 0 0-35 0v174.37c0 0.51 0 1 0.06 1.52 0.08 0.88 7 89.15-51.16 152.8a17.5 17.5 0 0 0 25.83 23.62c66-72.15 61-168 60.27-178.62z" fill="currentColor"></path></svg>`,
    dupSimSVG: `<svg style="width:18px;height:18px;margin-right:8px;flex-shrink:0;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M956.416 348.864a328.512 328.512 0 0 1-245.12 363.2 328.576 328.576 0 0 1-643.712-36.928 328.512 328.512 0 0 1 245.12-363.2 328.576 328.576 0 0 1 643.712 36.928zM534.336 639.808a263.04 263.04 0 0 0 121.92 16.96c1.28-12.736 1.664-25.728 1.024-38.848l-122.88 21.888z m-75.136-45.12l189.056-33.664a263.488 263.488 0 0 0-14.272-39.808l-211.072 35.648c10.88 13.824 23.04 26.432 36.288 37.76zM390.528 503.936l211.456-35.712a265.28 265.28 0 0 0-34.176-36.288l-192.256 30.336c3.84 14.464 8.96 28.416 14.976 41.6z m-23.808-98.56l126.08-19.84a263.04 263.04 0 0 0-125.056-18.304 263.68 263.68 0 0 0-1.024 38.144z m351.744 180.48c2.56 18.944 3.52 37.76 2.88 56.32a264.576 264.576 0 0 0-126.336-510.72A264.448 264.448 0 0 0 382.72 302.144a328.512 328.512 0 0 1 335.744 283.712zM305.536 438.144a330.624 330.624 0 0 1-2.88-56.32 264.576 264.576 0 0 0 126.336 510.72 264.448 264.448 0 0 0 212.288-170.688 328.512 328.512 0 0 1-335.744-283.712z" fill="currentColor" fill-opacity=".9"></path></svg>`,
    dupNameSVG: `<svg style="width:18px;height:18px;margin-right:8px;flex-shrink:0;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M478.144 947.328c-120.448 0-176.96 0.128-297.408-0.064-104.384-0.128-146.432-42.56-146.496-147.264 0-195.008-0.128-389.952 0-584.96 0.064-92.224 44.48-137.792 137.152-137.984 247.04-0.576 430.144-0.64 677.184 0.064 95.104 0.256 140.992 47.36 141.056 141.696 0.128 196.032 0.128 392 0 588.032-0.064 95.872-44.16 140.096-140.992 140.416-123.456 0.384-246.976 0.064-370.496 0.064z m375.296-51.52c62.016-0.128 84.224-22.72 84.288-85.248v-596.992c0-62.528-22.208-85.312-84.16-85.312-248.96-0.128-433.984-0.128-682.944 0-62.016 0.064-84.224 22.72-84.288 85.248v596.992c0 62.592 22.208 85.248 84.16 85.312 123.456 0.192 182.976 0.064 306.432 0.064 125.44 0 251.008 0.128 376.512-0.064z" fill="currentColor"></path><path d="M594.176 358.912c0.576-26.24-2.432-50.88 8.768-74.048 27.264-56.576 77.952-56.896 130.624-57.024 49.024-0.128 94.272 7.232 114.304 57.6 19.776 49.664 20.864 103.296-0.576 152.256-25.984 59.456-82.368 58.048-136.64 57.28-46.912-0.704-86.464-13.632-108.224-59.584-11.968-25.216-6.912-52.032-8.256-76.48z m51.84 1.024c-0.384 76.544 6.144 82.944 83.328 82.944 7.104 0 14.208 0.128 21.312-0.064 32.832-1.024 53.44-14.656 56.128-44.096 5.12-56.512 6.208-99.84-26.752-114.048a58.112 58.112 0 0 0-20.736-3.84 1113.856 1113.856 0 0 0-67.008 0c-29.184 1.152-44.16 16.32-46.144 45.632-0.768 11.136-0.128 22.336-0.128 33.472z m-319.232 416.768c-58.176 0-52.288 0.064-110.464-0.064-9.152 0-18.496 0.256-27.456-1.28-13.12-2.24-23.488-9.6-23.872-23.808-0.384-15.744 10.24-25.6 24.896-25.664 135.68-0.576 207.296-0.576 342.976 0 13.312 0.064 23.104 8.768 23.424 23.488 0.384 16.32-10.112 24.832-24.576 25.92-23.36 1.664-46.848 1.28-70.336 1.344-44.8 0.192-89.728 0.064-134.592 0.064z m-121.792-109.376c-19.2 0-40.256-1.216-39.552-26.496 0.576-22.976 21.376-24.512 38.976-24.576 125.248-0.32 186.56-0.32 311.808-0.128 18.88 0.064 39.872 1.728 39.232 27.328-0.64 24.064-20.928 23.808-38.656 23.808-63.168 0.128-126.272 0.064-189.44 0.064h-122.368z" fill="currentColor"></path></svg>`,
    dupContainSVG: `<svg style="width:18px;height:18px;margin-right:8px;flex-shrink:0;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M784 64a144 144 0 0 1 144 144v96A144 144 0 0 1 784 448H320v200.32c0 44.16 35.84 80 80 80H448v-8.32A144 144 0 0 1 592 576h192a144 144 0 0 1 144 144v96A144 144 0 0 1 784 960h-192a144 144 0 0 1-143.744-135.68H400a176 176 0 0 1-176-176V447.104a144 144 0 0 1-128-143.104v-96A144 144 0 0 1 240 64h544z m0 608h-192a48 48 0 0 0-48 48v96a48 48 0 0 0 48 48h192a48 48 0 0 0 48-48v-96a48 48 0 0 0-48-48z m0-512h-544a48 48 0 0 0-48 48v96c0 26.496 21.504 48 48 48h544a48 48 0 0 0 48-48v-96a48 48 0 0 0-48-48z" fill="currentColor"></path></svg>`,
    crumbIcons: {
    right: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`,
    down: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
    sortAZ: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(21,843) scale(0.09,-0.09)"><path d="M2566 6494 c-32 -10 -63 -31 -95 -62 -40 -41 -74 -107 -283 -552 -131 -278 -281 -597 -334 -710 -113 -237 -118 -265 -68 -348 69 -113 229 -123 308 -20 13 18 49 87 79 153 30 66 65 141 78 168 l23 47 355 -2 355 -3 76 -168 c42 -93 89 -184 104 -203 69 -87 220 -83 289 7 31 42 50 109 41 146 -6 24 -425 976 -582 1323 -65 144 -110 197 -187 223 -58 20 -94 20 -159 1z m117 -639 c20 -44 58 -131 86 -194 28 -62 51 -115 51 -117 0 -2 -83 -4 -185 -4 -102 0 -185 2 -185 5 0 11 190 398 194 394 2 -2 19 -40 39 -84z"/><path d="M4965 6436 c-37 -16 -92 -77 -102 -114 -5 -15 -8 -696 -8 -1515 0 -818 -1 -1487 -1 -1487 -1 0 -120 127 -265 283 -145 155 -284 303 -309 328 -77 77 -173 89 -254 32 -75 -53 -100 -157 -57 -240 18 -35 243 -280 723 -788 115 -121 216 -220 240 -232 54 -30 136 -31 193 -3 25 12 71 53 111 99 38 42 129 142 203 222 654 702 660 710 661 789 0 20 -10 56 -22 80 -48 93 -140 131 -233 95 -45 -17 -49 -20 -567 -580 l-37 -40 -1 1451 c0 1585 2 1519 -57 1576 -56 54 -151 73 -218 44z"/><path d="M1865 4166 c-78 -34 -119 -103 -113 -187 5 -74 40 -129 100 -158 43 -20 56 -21 507 -21 l463 0 -169 -123 c-92 -68 -276 -204 -408 -303 -132 -99 -276 -206 -320 -238 -105 -77 -133 -103 -156 -148 -37 -72 -18 -190 37 -239 59 -51 27 -50 832 -47 l749 3 37 29 c111 84 95 258 -29 324 -26 14 -87 16 -467 16 -241 1 -438 3 -438 6 1 3 135 105 298 228 655 491 684 514 709 562 35 66 29 163 -12 221 -17 23 -50 53 -74 66 l-44 23 -736 0 c-590 -1 -742 -3 -766 -14z"/></g></svg>`,
    sortZA: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(35,808) scale(0.09,-0.09)"><path d="M2433 6180 c-51 -12 -122 -59 -152 -101 -16 -21 -56 -103 -89 -181 -33 -79 -141 -336 -240 -573 -99 -236 -202 -479 -228 -540 -70 -163 -70 -236 0 -313 33 -37 71 -52 131 -52 118 0 156 39 239 243 32 78 65 152 72 165 l14 22 325 0 c179 0 325 -1 325 -3 0 -13 126 -311 146 -345 39 -67 120 -100 201 -82 59 14 107 50 133 100 34 66 27 105 -56 309 -41 102 -165 411 -276 686 -110 275 -213 520 -229 545 -63 99 -194 149 -316 120z m163 -745 l80 -205 -172 0 -173 0 16 38 c8 20 25 60 37 87 12 28 45 103 72 168 27 64 51 117 54 117 3 -1 41 -93 86 -205z"/><path d="M4828 6116 c-28 -10 -139 -116 -426 -407 -491 -499 -574 -586 -588 -612 -34 -66 -23 -144 28 -203 54 -61 123 -84 199 -64 39 10 76 43 351 323 l308 312 0 -1481 c0 -1106 3 -1490 12 -1515 29 -86 91 -129 184 -129 70 0 131 32 166 87 l23 38 5 1498 5 1498 310 -311 c335 -336 339 -339 431 -325 122 18 202 167 146 272 -26 49 -950 990 -992 1010 -47 22 -117 26 -162 9z"/><path d="M1745 3866 c-68 -29 -124 -131 -111 -199 10 -51 38 -97 74 -125 l35 -27 510 -5 509 -5 -523 -394 c-288 -217 -537 -406 -552 -420 -65 -59 -90 -169 -54 -236 20 -40 75 -91 110 -104 19 -8 272 -11 775 -11 708 0 749 1 788 19 138 63 143 256 9 333 l-40 23 -459 -4 c-298 -2 -457 0 -455 7 2 5 234 182 514 392 281 210 527 398 548 418 105 101 70 280 -65 337 -33 13 -135 15 -810 15 -616 -1 -779 -3 -803 -14z"/></g></svg>`,
    sortNew: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(9,813) scale(0.1,-0.1)"><path d="M3545 6019 c-792 -100 -1452 -629 -1696 -1360 -67 -199 -89 -333 -96 -564 -9 -360 47 -638 192 -942 164 -343 395 -611 705 -818 334 -222 670 -329 1076 -342 315 -10 579 36 860 152 736 303 1230 1022 1238 1804 1 124 0 128 -27 170 -70 106 -224 106 -293 1 -24 -37 -27 -51 -35 -193 -13 -250 -56 -427 -154 -634 -222 -468 -651 -800 -1185 -914 -91 -20 -135 -23 -325 -24 -253 0 -343 13 -538 76 -350 114 -649 333 -859 629 -77 108 -172 293 -217 424 -94 266 -114 598 -55 882 86 420 374 823 754 1057 236 145 575 247 825 247 97 0 174 41 211 111 18 36 18 112 0 147 -19 36 -65 78 -99 91 -33 13 -181 12 -282 0z"/><path d="M4942 5844 c-43 -22 -79 -66 -92 -112 -6 -22 -10 -180 -10 -397 l0 -360 -164 165 c-98 99 -180 172 -203 182 -150 68 -300 -93 -224 -240 25 -47 634 -654 681 -678 45 -23 125 -24 170 -1 20 9 185 164 367 345 298 294 333 331 343 371 21 81 -11 156 -85 198 -47 26 -101 29 -152 8 -21 -9 -104 -83 -202 -180 -92 -91 -169 -165 -172 -165 -2 0 -4 170 -4 378 0 421 1 413 -71 468 -46 35 -132 44 -182 18z"/><path d="M3693 5021 c-28 -13 -49 -33 -67 -64 l-27 -45 3 -564 c3 -544 4 -564 23 -594 32 -50 557 -579 592 -598 41 -21 115 -21 160 0 74 36 111 133 84 217 -9 25 -85 110 -261 289 l-249 253 -1 500 0 501 -27 42 c-50 76 -144 102 -230 63z"/></g></svg>`,
    sortOld: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(55,832) scale(0.1,-0.1)"><path d="M3165 6093 c-239 -30 -413 -70 -574 -130 -484 -180 -885 -542 -1106 -998 -99 -204 -158 -410 -186 -650 -18 -156 -6 -441 26 -594 79 -380 252 -716 508 -988 308 -326 736 -548 1175 -608 147 -20 418 -20 563 1 215 30 420 94 621 195 294 148 567 392 750 670 199 302 301 631 315 1014 5 149 4 163 -15 200 -79 155 -271 155 -343 0 -14 -31 -19 -64 -19 -145 -1 -565 -289 -1067 -777 -1356 -332 -196 -747 -265 -1130 -188 -575 115 -1043 533 -1227 1096 -60 183 -71 257 -71 493 0 182 3 224 23 319 97 453 364 820 772 1064 195 116 485 207 725 227 102 8 157 28 194 70 51 59 63 133 32 200 -43 93 -120 126 -256 108z"/><path d="M4382 6058 c-53 -29 -650 -641 -669 -685 -33 -81 -4 -180 69 -233 34 -25 51 -30 95 -30 30 0 70 7 87 15 18 9 95 79 171 155 76 77 140 140 142 140 2 0 3 -150 3 -332 0 -194 4 -348 10 -369 12 -42 85 -115 125 -125 55 -14 133 1 173 33 72 58 72 56 72 451 0 193 2 352 5 352 2 0 73 -66 156 -147 83 -82 163 -154 177 -162 14 -8 50 -14 80 -14 128 0 217 139 167 261 -19 46 -632 664 -687 693 -51 26 -124 25 -176 -3z"/><path d="M3177 5075 c-50 -17 -85 -50 -108 -100 -17 -37 -19 -76 -19 -553 0 -439 2 -518 15 -550 20 -47 583 -614 639 -644 85 -44 203 -2 246 86 23 49 26 113 6 159 -7 18 -129 147 -270 287 l-256 255 0 463 c0 442 -1 465 -20 502 -24 47 -71 84 -125 99 -50 14 -61 13 -108 -4z"/></g></svg>`,
    sortLarge: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(11.142563,862.746312) scale(0.102729,-0.102729)"><path d="M4815 6123 c-34 -8 -81 -50 -99 -86 -15 -32 -16 -148 -16 -1303 l0 -1268 -262 261 c-231 230 -268 263 -308 273 -77 21 -152 -15 -190 -91 -24 -46 -25 -87 -5 -137 21 -51 814 -847 870 -873 46 -22 65 -23 120 -8 34 9 103 74 453 422 227 227 422 429 433 449 66 127 -53 275 -192 238 -39 -10 -77 -45 -311 -278 l-268 -266 -2 1280 -3 1281 -29 37 c-45 59 -120 86 -191 69z"/><path d="M1912 5899 c-61 -30 -101 -71 -138 -139 -18 -33 -19 -68 -19 -755 l0 -720 27 -51 c33 -63 91 -116 154 -140 46 -18 87 -19 779 -19 681 0 733 1 768 18 58 27 127 96 153 152 l24 50 0 701 c0 802 4 761 -84 850 -89 88 -45 84 -876 84 l-727 0 -61 -31z m1414 -899 l-1 -590 -617 0 -618 0 0 590 0 590 619 0 619 0 -2 -590z"/><path d="M2060 3806 c-154 -34 -277 -159 -311 -317 -6 -29 -10 -101 -7 -164 4 -98 8 -118 35 -178 90 -191 284 -279 519 -236 134 25 240 105 297 227 32 66 32 68 32 222 0 154 0 156 -32 223 -88 186 -301 275 -533 223z m199 -332 l32 -7 -3 -111 -3 -111 -97 -3 -98 -3 0 121 0 120 69 0 c38 0 83 -3 100 -6z"/><path d="M3100 3806 c-119 -26 -228 -113 -283 -225 -32 -66 -32 -67 -32 -221 0 -150 1 -157 28 -212 89 -182 267 -269 491 -241 152 18 267 100 330 232 27 59 31 78 34 176 2 61 -1 136 -7 167 -31 168 -164 302 -329 329 -75 12 -164 10 -232 -5z m220 -448 l0 -118 -84 0 c-46 0 -91 3 -100 6 -13 5 -16 25 -16 113 0 59 3 110 6 114 3 3 48 5 100 4 l94 -1 0 -118z"/></g></svg>`,
    sortSmall: `<svg viewBox="0 0 800 800" fill="currentColor" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(-76.715811,775.317778) scale(0.099953,-0.099953)"><path d="M5860 5450 c-103 -21 -86 -5 -588 -562 -308 -342 -331 -371 -337 -413 -12 -73 1 -115 50 -162 48 -45 103 -61 170 -49 48 9 76 36 259 241 204 230 301 335 309 335 4 0 7 -597 7 -1328 0 -1273 1 -1329 19 -1362 10 -19 37 -48 60 -65 35 -25 52 -30 99 -30 71 0 122 29 157 89 l25 43 0 1323 0 1324 53 -59 c210 -238 412 -458 440 -478 143 -108 342 42 273 206 -7 18 -118 149 -247 292 -129 143 -303 338 -387 432 -125 140 -163 177 -205 197 -61 28 -108 36 -157 26z"/><path d="M2964 5196 c-107 -39 -175 -104 -228 -215 l-31 -66 -3 -634 c-2 -433 1 -651 8 -687 26 -126 114 -239 228 -292 l57 -27 677 -3 c646 -2 679 -2 732 17 111 39 191 116 238 231 l23 55 0 670 0 670 -23 56 c-32 81 -117 170 -200 211 l-67 33 -675 2 -675 2 -61 -23z m1317 -345 l24 -19 3 -568 c2 -400 -1 -577 -9 -595 -6 -15 -23 -32 -38 -38 -18 -8 -193 -11 -588 -9 l-562 3 -23 23 -23 23 0 580 0 581 24 19 c22 18 47 19 596 19 549 0 574 -1 596 -19z"/><path d="M3023 3015 c-317 -86 -456 -468 -272 -744 165 -247 524 -283 736 -73 99 99 143 204 143 347 0 142 -43 248 -140 346 -91 92 -209 140 -345 138 -38 0 -94 -7 -122 -14z m182 -351 c124 -59 76 -251 -62 -248 -40 1 -51 6 -80 37 -29 32 -33 43 -33 91 0 61 23 100 74 122 41 18 61 17 101 -2z"/><path d="M4080 3011 c-158 -51 -273 -165 -320 -319 -24 -78 -26 -194 -5 -277 64 -245 315 -397 567 -344 278 59 449 359 358 629 -52 155 -178 275 -334 316 -71 19 -199 17 -266 -5z m199 -349 c97 -52 89 -185 -14 -236 -35 -17 -42 -18 -79 -5 -54 19 -86 67 -86 128 1 58 18 89 61 112 44 23 76 23 118 1z"/></g></svg>`,
    sortStarNew: `<svg viewBox="0 0 1024 1024" fill="none" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(128,194) scale(0.75,0.62)"><path d="M512 0c-42.304125 297.428121-76.596255 331.720251-374.024376 374.024376 369.043946 37.402438 335.362067 221.914568 374.024376 649.975624C554.382867 597.198816 524.460917 418.907301 886.024376 374.024376c-297.428121-42.304125-331.720251-76.596255-374.024376-374.024376" fill="currentColor"></path></g></svg>`,
    sortStarOld: `<svg viewBox="0 0 1024 1024" fill="none" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(128,830) scale(0.75,-0.62)"><path d="M512 0c-42.304125 297.428121-76.596255 331.720251-374.024376 374.024376 369.043946 37.402438 335.362067 221.914568 374.024376 649.975624C554.382867 597.198816 524.460917 418.907301 886.024376 374.024376c-297.428121-42.304125-331.720251-76.596255-374.024376-374.024376" fill="currentColor"></path></g></svg>`,
    sortTypeDurAsc: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(3.7,4.2) scale(0.70)"><path d="M11.3 6H20"></path><path d="M11.3 10H17.5"></path><path d="M11.3 14H15.4"></path><path d="M5.9 4.8v12.8"></path><path d="M3.6 15.3l2.3 2.3 2.3-2.3"></path></g></svg>`,
    sortTypeDurDesc: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(3.7,4.2) scale(0.70)"><path d="M11.3 6H20"></path><path d="M11.3 10H17.5"></path><path d="M11.3 14H15.4"></path><path d="M5.9 17.6V4.8"></path><path d="M3.6 7.1l2.3-2.3 2.3 2.3"></path></g></svg>`,
    sortPathAsc: `<svg viewBox="0 0 1024 1024" fill="none" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(112.64,112.64) scale(0.78)"><path d="M622.85169 212.271137V497.604128C622.85169 544.925739 584.632066 583.145673 537.310455 583.145673H309.614593C262.292982 583.145673 224.435609 621.080508 224.435609 668.402119V753.777232" stroke="currentColor" stroke-width="56.916291" stroke-linecap="round" stroke-linejoin="round"></path><path d="M82.14486 753.777232L224.435609 895.902183L366.726358 753.777232" stroke="currentColor" stroke-width="56.916291" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`,
    sortPathDesc: `<svg viewBox="0 0 1024 1024" fill="none" style="width:35px;height:35px;flex-shrink:0;"><g transform="translate(112.64,112.64) scale(0.78)"><path d="M309.614593 554.687218c-62.760392 0-113.63713 51.125401-113.63713 113.714901l0 227.78489 56.916291 0 0-227.677443c0-31.428803 25.52842-56.906058 57.08309-56.906058l227.695862 0c62.760392 0 113.63713-51.216475 113.63713-113.833605L651.309836 355.479177l142.29175 0-170.749896-227.666186L452.101795 355.479177l142.29175 0 0 142.124951c0 31.526017-25.52842 57.08309-57.08309 57.08309L309.614593 554.687218z M452.101795 355.479177L622.85169 127.812991L793.601586 355.479177Z" fill="currentColor" fill-rule="evenodd"></path><path d="M480.560941 327.604128L622.85169 185.479177L765.14244 327.604128" stroke="currentColor" stroke-width="56.916291" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>`
    },
    icons: {
        offline: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;"><path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"></path><path d="M12 12v9"></path><path d="m8 17 4 4 4-4"></path></svg>`,
        navShare: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>`,
        unshare: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M392.2 517.2c0-13.8-2.4-26.9-5.9-39.6l280-168c27.6 34.6 69.6 57.1 117.2 57.1 83.1 0 150.4-67.4 150.4-150.4S866.5 65.8 783.5 65.8s-150.4 67.4-150.4 150.4c0 13.8 2.4 26.9 5.9 39.6l-280 168c-27.6-34.6-69.6-57.1-117.2-57.1-83.1 0-150.4 67.4-150.4 150.4s67.4 150.4 150.4 150.4c47.6 0 89.6-22.6 117.2-57.1l160.2 96.1 30.9-51.6-163.8-98.2c3.5-12.7 5.9-25.8 5.9-39.5z m391.3-391.3c49.8 0 90.3 40.5 90.3 90.3s-40.5 90.3-90.3 90.3-90.3-40.5-90.3-90.3 40.5-90.3 90.3-90.3zM241.8 607.4c-49.8 0-90.3-40.5-90.3-90.3s40.5-90.3 90.3-90.3 90.3 40.5 90.3 90.3c0 49.9-40.5 90.3-90.3 90.3z m640.7 69l-99 99-99.1-99-42.6 42.5 99.1 99.1-99.1 99.1 42.6 42.6 99.1-99.1 99 99.1 42.6-42.6L826 818l99-99.1-42.5-42.5z" fill="currentColor"></path></svg>`,
        refresh: `<svg width="16" height="16" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" style="transform: scale(1.4); transform-origin: center;"><path d="M950.371072 532.795629l-84.398202-84.393085c-6.101975-6.096858-14.093996-9.145287-22.087041-9.143241-7.995091-0.001023-15.988136 3.047406-22.079878 9.148357l-84.519975 84.530209c-12.20088 12.195763-12.20088 31.971156 0 44.171012 6.099928 6.094812 14.09195 9.145287 22.082948 9.145287s15.993253-3.050476 22.082948-9.150404l33.171494-33.175587c-16.019859 175.330214-163.813926 313.145-343.250668 313.145-190.096523 0-344.749812-154.653289-344.749812-344.749812s154.653289-344.754928 344.749812-344.754928c92.084255 0 178.658006 35.859719 243.779166 100.975762 12.20088 12.20088 31.966039 12.20088 44.166919 0 12.20088-12.195763 12.20088-31.971156 0-44.166919-76.914764-76.91988-179.176822-119.27657-287.946085-119.27657-224.543056 0-407.217539 182.679599-407.217539 407.222655 0 224.537939 182.674483 407.217539 407.217539 407.217539 212.604142 0 387.574153-163.800623 405.591505-371.808074l29.239951 29.238928c6.099928 6.094812 14.09195 9.145287 22.082948 9.145287 7.990998 0 15.98302-3.050476 22.082948-9.150404C962.571952 564.770877 962.571952 544.995485 950.371072 532.795629zM411.244248 429.099918l22.082948-22.082948c12.20088-12.195763 12.20088-31.971156 0-44.166919-12.20088-12.20088-31.966039-12.20088-44.166919 0l-22.082948 22.082948c-12.20088 12.195763-12.20088 31.971156 0 44.166919 6.099928 6.099928 14.09195 9.150404 22.082948 9.150404S405.143297 435.199847 411.244248 429.099918zM565.846372 539.536146l-22.082948 22.082948c-12.20088 12.195763-12.20088 31.971156 0 44.166919 6.099928 6.099928 14.09195 9.150404 22.082948 9.150404s15.98302-3.050476 22.082948-9.150404l22.082948-22.082948c12.20088-12.195763 12.20088-31.971156 0-44.165896C597.812411 527.335267 578.047252 527.335267 565.846372 539.536146zM336.453868 521.093099c-4.869914 20.679995-4.809539 63.99757 26.373671 95.175663 22.663162 22.658046 51.944046 29.03222 74.18049 29.03222 8.194636 0 15.433504-0.868787 21.025872-2.104941 16.694217-3.691065 27.115568-20.070104 23.638373-36.810371-3.477194-16.730033-19.968797-27.578102-36.754089-24.258497-0.25378 0.035816-23.5166 4.376681-37.923728-10.030447-14.010085-14.020318-9.953699-35.539424-9.658987-37.003775 3.741207-16.673751-6.639211-33.302477-23.312962-37.231973C357.26587 493.89055 340.408947 504.296551 336.453868 521.093099z" fill="currentColor"/></svg>`,
        retry: `<svg width="18" height="18" viewBox="0 0 1024 1024" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg" style="transform: scale(1.1); vertical-align: -4px;"><path d="M233.088 189.141333A425.002667 425.002667 0 0 1 512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667 0 91.136-28.586667 175.616-77.226667 244.906667L725.333333 512h128A341.333333 341.333333 0 0 0 275.626667 265.728l-42.538667-76.586667z m557.824 645.717334A425.002667 425.002667 0 0 1 512 938.666667C276.352 938.666667 85.333333 747.648 85.333333 512c0-91.136 28.586667-175.616 77.226667-244.906667L298.666667 512H170.666667a341.333333 341.333333 0 0 0 577.706666 246.272l42.538667 76.586667z" /></svg>`,
        settings: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>`,
        home: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>`,
        recent: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path><line x1="12" y1="11" x2="12" y2="17"></line><line x1="9" y1="14" x2="15" y2="14"></line></svg>`,
        history: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/></svg>`,
        trash: `<svg width="24" height="24" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M870.453928 231.233432l-139.824559 0L730.629369 119.372761c0-30.890544-25.040303-55.929824-55.929824-55.929824L339.118558 63.442938c-30.890544 0-55.929824 25.040303-55.929824 55.929824l0 111.859647-139.824559 0c-15.442714 0-27.964912 12.522198-27.964912 27.964912s12.522198 27.964912 27.964912 27.964912l27.964912 0 0 559.300282c0 61.780065 50.079582 111.859647 111.859647 111.859647l447.439612 0c61.780065 0 111.859647-50.079582 111.859647-111.859647L842.487993 287.163255l27.964912 0c15.442714 0 27.964912-12.522198 27.964912-27.964912S885.896642 231.233432 870.453928 231.233432zM339.118558 119.372761l335.579965 0 0 111.859647L339.118558 231.232408 339.118558 119.372761zM786.559193 846.463538c0 30.890544-25.040303 55.929824-55.929824 55.929824L283.188734 902.393361c-30.890544 0-55.929824-25.040303-55.929824-55.929824L227.25891 287.163255l559.300282 0L786.559193 846.463538zM590.803787 734.602867c15.442714 0 27.964912-12.522198 27.964912-27.964912L618.768699 426.987814c0-15.442714-12.522198-27.964912-27.964912-27.964912s-27.964912 12.523221-27.964912 27.964912l0 279.650141C562.838875 722.080669 575.362096 734.602867 590.803787 734.602867zM423.014316 734.602867c15.442714 0 27.964912-12.522198 27.964912-27.964912L450.979228 426.987814c0-15.442714-12.523221-27.964912-27.964912-27.964912s-27.964912 12.523221-27.964912 27.964912l0 279.650141C395.049405 722.080669 407.571602 734.602867 423.014316 734.602867z" stroke="currentColor" stroke-width="40" stroke-linejoin="round"></path></svg>`,
        emptyTrash: `<svg width="16" height="24" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M870.45 231.23h-139.82l-0.01-111.86c0-30.89-25.04-55.93-55.93-55.93h-335.58c-30.89 0-55.93 25.04-55.93 55.93l0 111.86h-139.82c-15.44 0-27.96 12.52-27.96 27.96s12.52 27.96 27.96 27.96l27.96 0 0 559.3c0 61.78 50.08 111.86 111.86 111.86h447.44c61.78 0 111.86-50.08 111.86-111.86l0-559.3 27.96 0c15.44 0 27.96-12.52 27.96-27.96s-12.52-27.96-27.96-27.96zM339.12 119.37h335.58v111.86h-335.58V119.37zM786.56 846.46c0 30.89-25.04 55.93-55.93 55.93h-447.44c-30.89 0-55.93-25.04-55.93-55.93v-559.3h559.3v559.3z" /><path d="M620 440l-216 216m216 0l-216-216" stroke="currentColor" stroke-width="70" stroke-linecap="round"/></svg>`,
        restore: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>`,
        delForever: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
        newfolder: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="transform: scale(1.2); transform-origin: center;"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/><line x1="12" x2="12" y1="10" y2="16"/><line x1="9" x2="15" y1="13" y2="13"/></svg>`,
        del: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>`,
        deselect: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="m9 9 6 6"/><path d="m15 9-6 6"/></svg>`,
        copy: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`,
        cut: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /><polyline points="16 17 21 12 16 7" /><line x1="21" x2="9" y1="12" y2="12" /></svg>`,
        paste: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>`,
        rename: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1"><path d="M56.925091 777.495273v189.579636h189.579636L805.655273 407.924364l-189.579637-189.579637L56.925091 777.495273zM952.32 261.306182a50.315636 50.315636 0 0 0 0-71.354182L834.048 71.68a50.315636 50.315636 0 0 0-71.400727 0L670.254545 164.165818 859.787636 353.745455l92.532364-92.439273v-0.093091z" fill="currentColor"></path></svg>`,
        bulkrename: `<svg width="18" height="18" viewBox="0 0 1024 1024" fill="none" version="1.1" xmlns="http://www.w3.org/2000/svg" style="display:block;shape-rendering:geometricPrecision;text-rendering:geometricPrecision;"><path d="M882.88 280.64l42.88-45.76a13.76 13.76 0 0 0 0-19.2L829.12 128a13.12 13.12 0 0 0-18.88 0L768 173.12zM739.84 202.56l-218.88 234.88a17.28 17.28 0 0 0-3.52 8.64L512 547.84a13.44 13.44 0 0 0 15.04 14.08l102.08-12.48a13.76 13.76 0 0 0 7.68-5.44l218.88-233.6z" fill="currentColor"/><path d="M864 381.12a24 24 0 0 0-24 24v317.76H304.96V189.12h317.76a24 24 0 0 0 0-48H296.96A40 40 0 0 0 256 181.12v82.56H174.72a40 40 0 0 0-40 40v549.44a40 40 0 0 0 40 40h549.44a40 40 0 0 0 40-40v-75.84a20.8 20.8 0 0 0 0-6.4h83.84a40.32 40.32 0 0 0 40-40V405.12a24.32 24.32 0 0 0-24-24z m-147.84 396.16v67.84H182.72V311.68H256v419.2a40 40 0 0 0 40 40h421.44a20.8 20.8 0 0 0-1.28 6.4z" fill="currentColor" stroke="currentColor" stroke-width="24" stroke-linejoin="round"/></svg>`,
        unzip: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M1024 927.168V237.696c0-37.12-29.248-74.112-73.152-74.112H526.72L394.88 30.08H65.92C29.248 30.08 0 59.776 0 96.832v830.336c0 37.12 29.248 66.688 65.856 66.688h892.352c36.48 0 65.792-29.632 65.792-66.688zM943.552 245.12v667.2H80.448V111.68H358.4L497.344 245.12h146.304v96.384h95.104v96.384h-95.104v96.384h95.104v96.32h-95.104v185.344h190.208V534.272h-95.104V437.888h95.104V341.504h-95.104V245.12h204.8z" fill="currentColor"></path></svg>`,
        migrate: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" style="transform: scale(1.1);"><path d="M212.224 495.530667l50.517333-17.898667 5.824-53.269333c15.466667-141.738667 135.68-252.096 281.557334-252.096 108.416 0 202.773333 60.949333 250.389333 150.613333h93.632c-53.269333-138.026667-187.2-235.946667-344.021333-235.946667-189.888 0-346.24 143.616-366.378667 328.170667C79.488 452.053333 4.821333 551.509333 4.821333 668.394667c0 148.394667 120.277333 268.672 268.650667 268.672h206.506667v-85.333334h-206.506667a183.317333 183.317333 0 0 1-183.317333-183.317333 183.445333 183.445333 0 0 1 122.069333-172.885333z"></path><path d="M811.946667 528.576l-46.933334-46.933333 60.330667-60.330667 192.64 192.618667H897.28v-0.021334H508.416v-85.333333H811.946667z m207.232 202.453333v85.333334H715.669333l46.933334 46.933333-60.330667 60.330667-192.618667-192.597334h509.525334z"></path></svg>`,
        prune: `<svg width="16" height="16" viewBox="0 0 1064 1024" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M155.648 419.84H909.312l15.40096 369.90976a215.04 215.04 0 0 1-214.8352 224.01024H355.1232a215.04 215.04 0 0 1-214.8352-224.01024L155.648 419.84z m98.26304 102.4l-11.34592 271.81056A112.64 112.64 0 0 0 355.1232 911.36h354.7136a112.64 112.64 0 0 0 112.51712-117.30944l-11.30496-271.81056H253.91104z" /><path d="M358.4 184.32a174.08 174.08 0 0 1 348.16 0v30.72h133.12a174.08 174.08 0 0 1 174.08 174.08V450.56A71.68 71.68 0 0 1 942.08 522.24H122.88A71.68 71.68 0 0 1 51.2 450.56V389.12A174.08 174.08 0 0 1 225.28 215.04h133.12V184.32zM532.48 112.64a71.68 71.68 0 0 0-71.68 71.68v81.92c0 28.2624-22.9376 51.2-51.2 51.2H225.28A71.68 71.68 0 0 0 153.6 389.12v30.72h757.76V389.12a71.68 71.68 0 0 0-71.68-71.68H655.36c-28.2624 0-51.2-22.9376-51.2-51.2v-81.92A71.68 71.68 0 0 0 532.48 112.64zM442.90048 686.16192a51.2 51.2 0 0 1 48.5376 53.6576l-10.24 204.8a51.2 51.2 0 0 1-102.23616-5.07904l10.24-204.8a51.2 51.2 0 0 1 53.6576-48.57856z" /></svg>`,
        blacklist: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1"><path d="M134.8 86.7H261c25.1 0 46.6 17.9 51.2 42.6l20.9 111.9c11.7 62.6 66.4 108 130.1 108h385.6c29.9 0 54.2 24.3 54.2 54.3v45.8c0 22.2 18 40.3 40.3 40.3s40.3-18 40.3-40.3v-45.8c0-74.3-60.5-134.8-134.8-134.8H463.3c-24.9 0-46.3-17.8-50.9-42.3l-20.9-111.9C379.7 51.8 324.9 6.2 261 6.2H134.8C60.5 6.2 0 66.7 0 141v633.6C0 847 58.9 905.9 131.3 905.9h279.4c22.2 0 40.3-18 40.3-40.3 0-22.2-18-40.3-40.3-40.3H131.3c-28 0-50.7-22.8-50.7-50.7V141c-0.1-29.9 24.3-54.3 54.2-54.3z" p-id="32404"></path><path d="M554.2 140.4h338.3c22.2 0 40.3-18 40.3-40.3 0-22.2-18-40.3-40.3-40.3H554.2c-22.2 0-40.3 18-40.3 40.3 0.1 22.3 18.1 40.3 40.3 40.3zM792.7 555.1c-127.6 0-231.4 103.8-231.4 231.4 0 127.6 103.8 231.3 231.4 231.3 127.6 0 231.3-103.8 231.3-231.3 0-127.6-103.8-231.4-231.3-231.4z m0 64.4c34.4 0 66.4 10.5 93 28.4L654.1 879.4c-17.9-26.6-28.4-58.6-28.4-93 0-92 74.9-166.9 167-166.9z m0 333.9c-34.4 0-66.4-10.5-93-28.4l231.5-231.5c17.9 26.6 28.4 58.6 28.4 93 0 92-74.9 166.9-166.9 166.9z" p-id="32405"></path></svg>`,
        invert: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M910.69 235.796H788.834V112.482c0-61.833-50.136-112.01-112.01-112.01H113.232C51.318 0.473 1.18 50.61 1.18 112.483v563.673c0 61.873 50.137 112.05 112.05 112.05h121.895v123.273c0 61.873 50.137 112.05 112.05 112.05H910.69c61.913 0 112.09-50.137 112.09-112.05V347.845c0-61.873-50.177-112.05-112.09-112.05zM235.126 347.845V712.9h-91.412c-37.14 0-67.23-30.13-67.23-67.23V143.006c0-37.1 30.09-67.23 67.23-67.23H646.38c37.061 0 67.151 30.13 67.151 67.23v92.79H347.175c-61.912 0-112.049 50.176-112.049 112.049zM844.25 533.937L598.016 780.091c-4.923 4.923-11.146 7.365-17.605 8.192-11.618 6.499-26.466 5.238-36.352-4.687L413.735 653.273c-12.012-12.013-12.012-31.469 0-43.481l14.455-14.454c12.012-12.012 31.468-12.012 43.48 0l97.595 97.634 217.088-217.009c11.934-12.012 31.39-12.012 43.402 0l14.533 14.494c11.855 12.012 11.855 31.468-0.04 43.48z" fill="currentColor"></path></svg>`,
        folderFirst: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="17" height="17"><path d="M496.439 270.671l5.909 4.937 256 256c16.662 16.662 16.662 43.677 0 60.34-14.811 14.811-37.802 16.457-54.431 4.937l-5.909-4.937L514.981 408.929l0.041 494.182c0 23.564-19.103 42.667-42.667 42.667-21.422 0-39.157-15.787-42.204-36.362l-0.463-6.305-0.041-494.592-183.3 183.429c-14.811 14.811-37.802 16.457-54.431 4.937l-5.909-4.937c-14.811-14.811-16.457-37.802-4.937-54.431l4.937-5.909 256-256c14.812-14.811 37.803-16.457 54.432-4.937z m231.739-178.227c23.564 0 42.667 19.103 42.667 42.667 0 21.422-15.787 39.157-36.362 42.204l-6.305 0.463h-512c-23.564 0-42.667-19.103-42.667-42.667 0-21.422 15.787-39.157 36.362-42.204l6.305-0.463h512z" fill="currentColor" stroke="currentColor" stroke-width="35" stroke-linejoin="round"></path></svg>`,
        viewList: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><circle cx="4" cy="6" r="1.5" fill="currentColor" stroke="none"></circle><circle cx="4" cy="12" r="1.5" fill="currentColor" stroke="none"></circle><circle cx="4" cy="18" r="1.5" fill="currentColor" stroke="none"></circle></svg>`,
        viewGrid: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5"></rect><rect x="14" y="3" width="7" height="7" rx="1.5"></rect><rect x="3" y="14" width="7" height="7" rx="1.5"></rect><rect x="14" y="14" width="7" height="7" rx="1.5"></rect></svg>`,
        analyze: `<svg width="18" height="18" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M529.664 213.333333H896a42.666667 42.666667 0 0 1 42.666667 42.666667v597.333333a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667h316.330667zM170.666667 213.333333v597.333334h682.666666V298.666667h-358.997333l-85.333333-85.333334z m341.333333 170.666667v170.666667h170.666667a170.666667 170.666667 0 1 1-170.666667-170.666667z"></path></svg>`,
        scanDup: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="transform: scale(1.2); transform-origin: center;"><path d="M200.1408 123.3408A115.2 115.2 0 0 1 281.6 89.6h326.4a38.4 38.4 0 0 1 27.136 11.264l211.2 211.2c7.2192 7.168 11.264 16.9472 11.264 27.136V819.2a115.2 115.2 0 0 1-115.2 115.2H614.4a38.4 38.4 0 0 1 0-76.8h128a38.4 38.4 0 0 0 38.4-38.4V355.1232L592.0768 166.4H281.6a38.4 38.4 0 0 0-38.4 38.4v204.8a38.4 38.4 0 0 1-76.8 0V204.8c0-30.5664 12.1344-59.8528 33.7408-81.4592z" fill="currentColor"></path><path d="M588.8 89.6a38.4 38.4 0 0 1 38.4 38.4v192H819.2a38.4 38.4 0 0 1 0 76.8h-230.4a38.4 38.4 0 0 1-38.4-38.4V128a38.4 38.4 0 0 1 38.4-38.4zM201.0112 537.856a38.4 38.4 0 0 1-3.584 54.2208 166.4 166.4 0 0 0-56.5248 119.04 165.5296 165.5296 0 0 0 48.128 122.6752 167.2704 167.2704 0 0 0 122.88 49.3568 167.936 167.936 0 0 0 120.4224-55.04 38.4 38.4 0 0 1 56.9344 51.456 243.968 243.968 0 0 1-175.5136 80.384 244.7872 244.7872 0 0 1-179.2-72.0384 243.0464 243.0464 0 0 1-70.4-179.4048 242.432 242.432 0 0 1 82.6368-174.1824 38.4 38.4 0 0 1 54.2208 3.584z" fill="currentColor"></path><path d="M280.064 484.864A38.4 38.4 0 0 1 307.2 473.6 243.2 243.2 0 0 1 550.4 716.8a38.4 38.4 0 0 1-38.4 38.4H307.2a38.4 38.4 0 0 1-38.4-38.4v-204.8a38.4 38.4 0 0 1 11.264-27.136z m65.536 70.0416v123.4944h123.4944a166.4 166.4 0 0 0-123.4944-123.4944z" fill="currentColor"></path></svg>`,
        export: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M392.843947 286.762667h508.16a122.538667 122.538667 0 0 0 0-244.096H392.843947a122.538667 122.538667 0 0 0 0 244.096z m0-151.04h508.16a29.098667 29.098667 0 0 1 0 57.984H392.843947a29.098667 29.098667 0 0 1 0-57.984z" fill="currentColor"></path><path d="M425.099947 576.384a112.384 112.384 0 0 0 103.253333 75.52h372.650667a122.538667 122.538667 0 0 0 0-244.138667h-372.650667a112.298667 112.298667 0 0 0-103.253333 75.562667H163.211947v-203.093333a121.088 121.088 0 0 0 77.909333-115.712 117.418667 117.418667 0 0 0-111.786667-122.026667h-17.408A117.418667 117.418667 0 0 0 0.09728 164.522667a121.130667 121.130667 0 0 0 77.909333 115.712v539.050666a117.418667 117.418667 0 0 0 111.658667 122.282667h235.306667a112.341333 112.341333 0 0 0 103.253333 75.52h372.650667a122.538667 122.538667 0 0 0 0-244.138667h-372.650667a112.256 112.256 0 0 0-103.253333 75.52H189.66528a27.904 27.904 0 0 1-26.581333-28.970666v-243.2z m103.253333-75.52h372.650667a29.098667 29.098667 0 0 1 0 57.941333h-372.650667a29.098667 29.098667 0 0 1 0-57.941333z m0 365.098667h372.650667a29.098667 29.098667 0 0 1 0 57.984h-372.650667a29.098667 29.098667 0 0 1 0-57.984zM111.926613 135.722667h17.408a29.098667 29.098667 0 0 1 0 57.984h-17.408a29.098667 29.098667 0 0 1 0-57.984z" fill="currentColor"></path></svg>`,
        stop: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><rect x="9" y="9" width="6" height="6"/></svg>`,
        ext: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="transform: scale(1.1);"><polygon points="6 3 20 12 6 21 6 3" fill="var(--pk-bg)" stroke="currentColor"></polygon></svg>`,
        download: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,
        aria2:`<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg" style="transform: scale(1.0); margin-right: 1px;"><path d="M512 0C229.233705 0 0 229.216503 0 511.982798s229.233705 512 512 512 512-229.216503 512-512S794.731891 0 512 0z m269.262734 881.584733q-48.853649 0-84.995028-125.677731-14.105631-47.735519-35.625319-167.100121-53.721812 7.603279-140.505846 26.594275l-140.144604 29.140169q-26.594275 69.410026-90.774896 202.98347c-11.404919 19.696277-26.594275 29.656229-46.118533 29.656229-14.105631 0-26.577073-5.074587-37.844375-15.378578a50.143798 50.143798 0 0 1-16.634324-38.154012q0-25.493348 79.920441-193.677194a57.041795 57.041795 0 0 1-9.048246-31.823679c0-28.572504 17.374009-47.013036 51.915603-55.149577q60.206961-113.378309 152.254804-260.403709 125.57452-200.712807 156.245666-200.730009c28.02204 0 47.013036 19.352238 57.317027 58.228732l33.268647 178.126596 79.215159 368.122564 30.189491 84.083322c10.321193 28.572504 15.378578 47.752721 15.378578 57.33423a50.350222 50.350222 0 0 1-16.462304 38.446445c-11.026475 10.321193-23.497917 15.378578-37.603547 15.378578zM593.915872 275.249026l43.572638 204.153205q-130.459884 23.325897-194.382476 39.7882z" fill="currentColor"></path></svg>`,
        info: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>`,
        moon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>`,
        sun: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>`,
        cloudDownload: `<svg viewBox="0 0 800 800" fill="currentColor" style="overflow:visible;"><g transform="translate(-45,845) scale(0.12,-0.12)"><path d="M3895 5919 c-480 -46 -922 -309 -1159 -689 -90 -145 -146 -288 -185 -476 l-17 -86 -90 -23 c-383 -97 -709 -356 -881 -700 -151 -300 -176 -664 -67 -991 63 -190 204 -408 352 -544 312 -289 736 -424 1217 -390 114 9 312 36 323 45 3 2 -55 286 -63 308 -5 10 -14 16 -24 13 -47 -15 -267 -36 -373 -36 -445 0 -801 177 -1016 505 -56 85 -102 193 -129 303 -24 97 -24 312 0 416 95 403 435 704 872 772 50 7 113 14 141 14 l51 0 6 118 c8 148 27 242 71 366 126 354 429 613 833 713 94 23 129 27 273 27 176 1 233 -8 395 -61 235 -76 495 -267 621 -457 l36 -55 38 32 c33 29 194 170 213 187 11 10 -112 160 -208 253 -316 308 -795 477 -1230 436z"/><path d="M5728 4880 c-75 -12 -199 -54 -257 -87 -111 -65 -190 -137 -362 -334 -46 -53 -178 -202 -294 -333 -115 -130 -211 -243 -213 -250 -2 -11 101 -111 224 -218 20 -18 22 -16 135 114 339 390 591 666 635 695 96 63 140 77 244 78 107 0 158 -15 234 -69 184 -131 223 -409 89 -634 -31 -53 -227 -282 -432 -507 -25 -28 -88 -99 -140 -158 l-93 -108 24 -22 c14 -12 70 -64 125 -115 l101 -92 84 93 c284 317 530 599 571 656 309 422 195 1009 -238 1222 -44 21 -107 46 -140 54 -75 19 -222 27 -297 15z"/><path d="M5688 4207 c-98 -114 -466 -533 -633 -722 -83 -93 -216 -244 -296 -334 -79 -91 -197 -226 -263 -300 l-118 -135 33 -29 c19 -16 78 -69 131 -118 l97 -89 19 25 c25 31 369 427 477 550 45 49 177 200 295 334 248 283 316 359 467 531 88 99 108 128 99 139 -6 8 -63 60 -128 117 l-116 104 -64 -73z"/><path d="M4224 3453 c-270 -300 -314 -360 -367 -503 -123 -330 -29 -694 233 -903 229 -183 537 -216 806 -87 44 21 109 60 143 86 62 47 505 538 499 553 -4 10 -241 221 -249 221 -3 0 -75 -80 -161 -177 -226 -259 -289 -324 -349 -360 -244 -146 -552 -21 -623 252 -31 119 -12 257 52 363 20 33 82 112 137 173 55 62 142 159 193 216 50 56 92 107 92 111 0 9 -65 72 -171 165 l-77 67 -158 -177z"/></g></svg>`,
        share: `<svg width="16" height="16" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M288 373.333333c-82.432 0-149.333333 66.922667-149.333333 149.333334a149.333333 149.333333 0 0 0 149.333333 149.333333c82.496 0 149.333333-66.816 149.333333-149.333333 0-82.410667-66.88-149.333333-149.333333-149.333334z m0 64c47.104 0 85.333333 38.250667 85.333333 85.333334 0 47.146667-38.186667 85.333333-85.333333 85.333333a85.333333 85.333333 0 1 1 0-170.666667zM757.333333 672a128.021333 128.021333 0 1 0 128 128c0-70.656-57.344-128-128-128z m0 64a64.021333 64.021333 0 1 1-64 64c0-35.328 28.672-64 64-64zM757.333333 117.333333a128.021333 128.021333 0 1 0 128 128c0-70.656-57.344-128-128-128z m0 64a64.021333 64.021333 0 1 1-64 64c0-35.328 28.672-64 64-64z" ></path><path d="M356.565333 580.864a32 32 0 0 1 43.904-10.965333l266.666667 160a32 32 0 0 1-32.938667 54.869333l-266.666666-160a32 32 0 0 1-10.965334-43.904zM643.050667 264.789333a32 32 0 0 1 36.565333 52.522667l-256 178.282667a32 32 0 0 1-36.565333-52.522667l256-178.282667z" ></path></svg>`,
        maximize: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>`,
        minimize: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/></svg>`,
        close: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>`,
        help: `<svg width="19" height="19" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M878.08 731.274667a32 32 0 0 1-54.88-32.938667A360.789333 360.789333 0 0 0 874.666667 512c0-200.298667-162.368-362.666667-362.666667-362.666667S149.333333 311.701333 149.333333 512s162.368 362.666667 362.666667 362.666667a360.789333 360.789333 0 0 0 186.314667-51.445334 32 32 0 0 1 32.928 54.88A424.778667 424.778667 0 0 1 512 938.666667C276.362667 938.666667 85.333333 747.637333 85.333333 512S276.362667 85.333333 512 85.333333s426.666667 191.029333 426.666667 426.666667c0 78.293333-21.152 153.568-60.586667 219.274667zM650.666667 437.333333c0 65.898667-46.72 120.853333-109.194667 135.082667V608a32 32 0 0 1-64 0v-64a32 32 0 0 1 32-32C552.266667 512 586.666667 478.4 586.666667 437.333333s-34.4-74.666667-77.194667-74.666666c-26.773333 0-51.082667 13.248-65.173333 34.624a73.088 73.088 0 0 0-8.522667 17.717333 32 32 0 0 1-60.885333-19.690667c3.797333-11.754667 9.173333-22.933333 15.978666-33.237333 25.856-39.253333 70.186667-63.413333 118.613334-63.413333C587.274667 298.666667 650.666667 360.576 650.666667 437.333333zM512 736a32 32 0 1 1 0-64 32 32 0 0 1 0 64z" fill="currentColor"></path></svg>`,
        warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;margin-right:8px;"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>`,
        eyeOff: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"></path><circle cx="12" cy="12" r="3"></circle></svg>`,
        eye: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l18 18"></path><path d="M10.6 10.6a2 2 0 0 0 2.8 2.8"></path><path d="M9.9 4.4A9.8 9.8 0 0 1 12 4c6.5 0 10 8 10 8a18.4 18.4 0 0 1-3.1 4.4"></path><path d="M6.1 6.7C3.5 8.6 2 12 2 12s3.5 8 10 8a9.7 9.7 0 0 0 4.1-.9"></path></svg>`,
        lock: `<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
        uploadBtn: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="transform:translateY(-1px);"><path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"/><path d="M12 12v9"/><path d="m16 16-4-4-4 4"/></svg>`,
        upFile: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>`,
        upFolder: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 2H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/></svg>`,
        navUpload: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>`,
        taskStart: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>`,
        taskPause: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>`,
        cleanAll: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>`,
        logout: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>`,
        blMarker: `<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" style="width:100% !important; height:100% !important; display:block;"><path d="M512.3 64.2c-247.4 0-448 200.6-448 448s200.6 448 448 448 448-200.6 448-448-200.6-448-448-448z m0 61.4c95.6 0 183.2 34.9 250.8 92.6L218.2 763c-57.7-67.6-92.6-155.2-92.6-250.8 0-213.2 173.5-386.6 386.7-386.6z m0 773.3c-95.6 0-183.2-34.9-250.7-92.6l544.9-544.9c57.7 67.6 92.6 155.2 92.6 250.7-0.2 213.3-173.6 386.8-386.8 386.8z" fill="#d93025"/></svg>`,
        vault: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 981.333c-211.2 0-384-172.8-384-384V563.2c0-89.6 72.533-164.267 164.267-164.267H755.2c78.933 0 142.933 64 142.933 142.934v55.466c-2.133 211.2-174.933 384-386.133 384z m-221.867-518.4c-55.466 0-100.266 44.8-100.266 100.267v34.133c0 177.067 142.933 320 320 320s320-142.933 320-320V544c0-44.8-36.267-78.933-78.934-78.933h-460.8z" fill="currentColor"></path><path d="M697.6 422.4c-17.067 0-32-14.933-32-32V260.267c0-85.334-68.267-153.6-153.6-153.6s-153.6 68.266-153.6 153.6V390.4c0 17.067-14.933 32-32 32s-32-14.933-32-32V260.267c0-119.467 98.133-217.6 217.6-217.6s217.6 98.133 217.6 217.6V390.4c0 17.067-14.933 32-32 32z" fill="currentColor"></path><path d="M512 759.467c-17.067 0-32-14.934-32-32V588.8c0-17.067 14.933-32 32-32s32 14.933 32 32v138.667c0 17.066-14.933 32-32 32z" fill="currentColor"></path></svg>`
    },
    typeIcons: {
        folder: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4C2.9 4 2 4.9 2 6V18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V8C22 6.9 21.1 6 20 6H12L10 4Z" fill="#E8A723"/><rect x="4.5" y="7" width="15" height="9" rx="1.5" fill="white" fill-opacity="0.95"/><path d="M2 11C2 9.895 2.895 9 4 9H20C21.105 9 22 9.895 22 11V18C22 19.105 21.105 20 20 20H4C2.895 20 2 19.105 2 18V11Z" fill="#FFC107"/></svg>`,
        systemFolder: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" fill="#E8A723"/><rect x="4.5" y="7" width="15" height="9" rx="1.5" fill="white" fill-opacity="0.95"/><path d="M2 11c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2v7c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-7z" fill="#FFC107"/><path d="M7.1 12L8.6 10.6c.1-.1.3-.1.4-.1h2.4c.2 0 .3.1.2.3l-1.4 1.2H7.1ZM16.9 12l-1.5-1.4c-.1-.1-.3-.1-.4-.1h-2.4c-.2 0-.3.1-.2.3l1.4 1.2h3.1z" fill="#D68C09"/><path d="M7 12h10v5.2c0 .4-.4.8-.9.8H7.9c-.5 0-.9-.4-.9-.8V12z" fill="#D68C09"/><rect x="9.5" y="13.5" width="1.5" height="2.5" rx="0.75" fill="#FFC107"/><rect x="13" y="13.5" width="1.5" height="2.5" rx="0.75" fill="#FFC107"/></svg>`,
        video: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#647EFF"/><rect x="6.5" y="8.5" width="8" height="7" rx="1" fill="white"/><path d="M15.5 10.5L18 9v6l-2.5-1.5v-3z" fill="white"/><path d="M9.5 10.5v3l2.5-1.5-2.5-1.5z" fill="#647EFF"/></svg>`,
        image: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#48C78E"/><rect x="5.5" y="5.5" width="13" height="13" rx="2" fill="white"/><circle cx="8.5" cy="9" r="1.5" fill="#48C78E"/><path d="M5.5 16.5l3.5-4.5 2.5 2.5 4-7.5 3 9.5h-13z" fill="#48C78E"/></svg>`,
        audio: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#D883FF"/><path d="M13 6h4v3h-2v6.5c0 2.21-1.79 4-4 4s-4-1.79-4-4 1.79-4 4-4c.75 0 1.45.21 2 .58V6z" fill="white"/></svg>`,
        archive: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#FFC107"/><rect x="7" y="2" width="3" height="3" fill="white"/><rect x="10" y="5" width="3" height="3" fill="white"/><rect x="7" y="8" width="3" height="3" fill="white"/><rect x="10" y="11" width="3" height="3" fill="white"/><rect x="7" y="14" width="3" height="3" fill="white"/></svg>`,
        archiveUnzipped: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#FFC107"/><rect x="7" y="2" width="3" height="3" fill="white"/><rect x="10" y="5" width="3" height="3" fill="white"/><rect x="7" y="8" width="3" height="3" fill="white"/><rect x="10" y="11" width="3" height="3" fill="white"/><rect x="7" y="14" width="3" height="3" fill="white"/><path d="M19.5 13l-4 6h2.5v4l4.5-6h-3l1.5-4z" stroke="white" stroke-width="2" stroke-linejoin="round" fill="white"/><path d="M19.5 13l-4 6h2.5v4l4.5-6h-3l1.5-4z" fill="#FFC107"/></svg>`,
        text: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#60C5F1"/><path d="M6 6.5h12v3h-4.5v8h-3v-8h-4.5v-3z" fill="white"/></svg>`,
        pdf: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#FF5252"/><g transform="translate(4,4) scale(0.015625)"><path d="M974.848 647.168c-28.672-30.72-86.016-48.128-167.936-48.128-44.032 0-94.208 4.096-149.504 14.336-30.72-30.72-62.464-66.56-92.16-108.544-21.504-29.696-39.936-60.416-56.32-91.136 32.768-101.376 48.128-183.296 48.128-242.688 0-66.56-23.552-136.192-93.184-136.192-21.504 0-41.984 13.312-53.248 31.744-30.72 56.32-17.408 179.2 36.864 300.032-20.48 60.416-40.96 118.784-67.584 183.296-22.528 54.272-49.152 111.616-76.8 162.816-155.648 63.488-256 137.216-265.216 194.56-4.096 21.504 3.072 41.984 18.432 57.344 5.12 4.096 25.6 21.504 59.392 21.504 103.424 0 211.968-169.984 267.264-273.408 41.984-14.336 84.992-27.648 126.976-39.936 46.08-13.312 93.184-23.552 135.168-30.72C753.664 741.376 849.92 757.76 898.048 757.76c59.392 0 80.896-24.576 88.064-45.056 11.264-25.6 3.072-54.272-10.24-69.632l-1.024 4.096z m-55.296 41.984c-4.096 21.504-25.6 35.84-55.296 35.84-8.192 0-15.36-1.024-23.552-3.072-54.272-13.312-104.448-40.96-155.648-83.968 50.176-8.192 92.16-10.24 118.784-10.24 29.696 0 55.296 1.024 71.68 6.144 19.456 4.096 50.176 17.408 44.032 55.296z m-300.032-67.584c-36.864 7.168-75.776 16.384-116.736 27.648-32.768 9.216-66.56 18.432-100.352 30.72 18.432-35.84 33.792-70.656 48.128-103.424 17.408-40.96 30.72-81.92 45.056-120.832 14.336 24.576 29.696 49.152 45.056 70.656 25.6 33.792 52.224 66.56 78.848 95.232zM434.176 83.968c6.144-11.264 17.408-17.408 26.624-17.408 29.696 0 34.816 34.816 34.816 62.464 0 46.08-14.336 116.736-37.888 197.632-40.96-112.64-44.032-205.824-23.552-242.688zM279.552 756.736c-71.68 120.832-141.312 196.608-183.296 196.608-8.192 0-15.36-3.072-21.504-7.168-8.192-8.192-12.288-18.432-10.24-30.72 8.192-43.008 89.088-103.424 215.04-158.72z" fill="white"/></g></svg>`,
        subtitle: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#48C78E"/><g transform="translate(4,4) scale(0.015625)"><path d="M512 150.97856V274.0224H364.544C242.40128 274.0224 143.36 384.24576 143.36 520.192c0 135.9872 99.04128 246.1696 221.184 246.1696H512v123.0848H371.54816C177.68448 889.4464 20.48 724.13184 20.48 520.192c0-203.93984 157.20448-369.21344 351.06816-369.21344H512z m491.52 0V274.0224h-147.456c-122.14272 0-221.184 110.22336-221.184 246.1696 0 135.9872 99.04128 246.1696 221.184 246.1696H1003.52v123.0848h-140.45184C669.20448 889.4464 512 724.13184 512 520.192c0-203.93984 157.20448-369.21344 351.06816-369.21344H1003.52z" fill="white"/></g></svg>`,
        executable: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#48C78E"/><g transform="translate(5.85,5.85) scale(0.012)"><path d="M1005.65035 611.129741c-43.944112-14.307385-70.51497-54.163673-70.51497-100.151697s29.636727-84.822355 70.51497-100.151697c12.263473-4.087824 20.439122-18.39521 16.351298-31.680638-12.263473-48.031936-31.680639-91.976048-56.207585-134.898204-6.131737-12.263473-22.483034-16.351297-35.768463-10.219561-14.307385 8.175649-33.724551 12.263473-50.075849 12.263473-58.251497 0-104.239521-48.031936-104.239521-104.239521 0-16.351297 4.087824-35.768463 12.263474-50.075848 6.131737-12.263473 2.043912-26.570858-10.219561-35.768463C737.897855 32.702595 691.909831 14.307385 643.877895 2.043912c-12.263473-4.087824-26.570858 4.087824-31.680639 16.351298-14.307385 43.944112-54.163673 70.51497-100.151696 70.51497s-84.822355-29.636727-100.151697-70.51497C407.806039 6.131737 393.498654-2.043912 380.213224 2.043912c-48.031936 12.263473-91.976048 31.680639-134.898203 56.207585-12.263473 6.131737-16.351297 22.483034-10.219561 35.768463 8.175649 14.307385 12.263473 33.724551 12.263473 50.075848 0 58.251497-48.031936 104.239521-104.239521 104.239521-16.351297 0-35.768463-4.087824-50.075848-12.263473-12.263473-6.131737-26.570858-2.043912-35.768463 10.219561C32.748155 288.191617 13.330989 334.179641 1.067516 381.189621c-4.087824 12.263473 4.087824 26.570858 16.351297 31.680638 43.944112 14.307385 70.51497 54.163673 70.51497 100.151697S58.297057 597.844311 17.418813 613.173653c-12.263473 4.087824-20.439122 18.39521-16.351297 31.680638 12.263473 48.031936 31.680639 91.976048 56.207585 134.898204 6.131737 12.263473 22.483034 16.351297 35.768463 10.219561 14.307385-8.175649 33.724551-12.263473 50.075848-12.263473 58.251497 0 104.239521 48.031936 104.239521 104.239521 0 16.351297-4.087824 35.768463-12.263473 50.075848-6.131737 12.263473-2.043912 26.570858 10.219561 35.768463 41.9002 24.526946 87.888224 43.944112 134.898203 56.207585h6.131737c10.219561 0 20.439122-6.131737 24.526946-18.39521 14.307385-43.944112 54.163673-70.51497 100.151697-70.51497s84.822355 29.636727 100.151696 70.51497c4.087824 12.263473 18.39521 20.439122 31.680639 16.351298 48.031936-12.263473 91.976048-31.680639 134.898204-56.207585 12.263473-6.131737 16.351297-22.483034 10.219561-35.768463-8.175649-14.307385-12.263473-33.724551-12.263474-50.075848 0-58.251497 48.031936-104.239521 104.239521-104.239521 16.351297 0 35.768463 4.087824 50.075849 12.263473 12.263473 6.131737 26.570858 2.043912 35.768463-10.219561 24.526946-41.9002 43.944112-87.888224 56.207585-134.898204 5.10978-12.263473-1.021956-27.592814-16.351298-31.680638z m-490.538922 58.251497c-87.888224 0-158.403194-70.51497-158.403194-158.403194s70.51497-158.403194 158.403194-158.403194S673.514622 423.08982 673.514622 510.978044s-71.536926 158.403194-158.403194 158.403194z" fill="white"/></g></svg>`,
        web: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#6F88FC"/><g transform="translate(4,4) scale(0.015625)"><path d="M631.296 188.416c-40.96 17.664-79.616 40.3968-114.944 67.584-15.872-10.1376-68.352-38.4-132.352-51.2l52.6336-23.296a352.4608 352.4608 0 0 1 85.3504-10.4448c38.1952 0 74.9568 6.144 109.312 17.3056z m227.072 232.448c-3.072 7.168-6.4512 14.1824-9.984 21.1456-10.9056 29.5936-34.6112 64.3072-71.0144 104.192-9.216 10.3936-18.944 20.3776-29.0304 29.952a537.9072 537.9072 0 0 0-186.0096-287.232 485.0176 485.0176 0 0 1 131.9936-66.9184l6.656-2.048a350.6176 350.6176 0 0 1 157.3888 200.96z m-537.6-186.624l12.3392 2.6624-0.0512 1.4336c42.496 11.776 82.3296 26.0608 119.5008 42.8032 7.0144 3.7376 13.9776 7.6288 20.7872 11.6736a537.7024 537.7024 0 0 0-143.2064 229.9392c-15.3088 44.032-22.8352 84.0704-22.6816 120.064-1.28 18.0736-1.6896 36.1472-1.1776 54.272a483.328 483.328 0 0 1-106.496-37.5296 348.8256 348.8256 0 0 1 114.944-421.0176l6.0416-4.3008z m381.0304 380.7232a484.864 484.864 0 0 1-287.744 94.0544c-18.0736 0-35.84-0.9728-53.4016-2.8672a490.6496 490.6496 0 0 1 0.2048-52.48c6.912-51.7632 14.592-92.3648 23.04-121.7024a483.328 483.328 0 0 1 121.1392-194.816l9.216-8.8576 5.0176-4.608a483.7888 483.7888 0 0 1 182.528 291.2768z m58.88 162.1504c5.8368-46.336 5.6832-93.184-0.4608-139.4688C783.4624 618.2912 844.8 550.4 870.4 512c0 17.0496-0.512 34.3552-1.536 51.8656-11.264 101.7344-47.3088 157.0304-108.2368 213.248z m-515.1744-41.0112c21.1968 6.912 42.9056 12.544 65.1776 16.896 2.4576 18.3808 5.7856 36.4544 10.0352 54.272a353.3312 353.3312 0 0 1-72.5504-67.84l-2.6624-3.328z m464.2816-61.1328a483.7888 483.7888 0 0 1-12.5952 148.7872 350.464 350.464 0 0 1-175.2064 46.6432 350.976 350.976 0 0 1-134.144-26.4704 478.3104 478.3104 0 0 1-21.4528-83.2c15.7184 1.3824 31.5904 2.048 47.616 2.048a539.136 539.136 0 0 0 284.416-80.5376l11.3664-7.2704z" fill="white"/></g></svg>`,
        apk: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#48C78E"/><g transform="translate(4,4) scale(0.015625)"><path d="M512 161.87392a292.57728 292.57728 0 0 1 292.57728 292.57728v19.49696H219.42272v-19.49696A292.57728 292.57728 0 0 1 512 161.87392zM219.42272 493.48608h585.15456V942.08H219.42272z" fill="white"/><path d="M394.97728 317.93152h58.49088V376.4224H394.97728zM590.0288 317.93152h58.49088V376.4224h-58.49088z" fill="#48C78E"/><path d="M724.86912 141.7216l41.3696 41.3696-96.54272 96.50176-41.3696-41.3696zM299.13088 141.7216l-41.3696 41.3696L354.304 279.552l41.3696-41.3696zM102.4 512.98304h78.0288v273.03936H102.4zM843.5712 512.98304H921.6v273.03936h-78.0288z" fill="white"/></g></svg>`,
        file: `<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="20" height="20" rx="6" fill="#B2B2B2"/><g transform="translate(4,4) scale(0.015625)"><path d="M939.303655 84.62284a289.780202 289.780202 0 0 1 0 409.290765l-446.080124 446.080124a289.487642 289.487642 0 0 1-408.486226-0.731399 289.487642 289.487642 0 0 1-0.731399-408.486226l236.24181-236.24181a178.315026 178.315026 0 0 1 251.820605 0c69.482885 69.482885 69.482885 182.118299 0 251.966884l-223.076632 223.003492a48.27232 48.27232 0 1 1-68.312647-68.239507L503.828814 478.334811a81.843525 81.843525 0 0 0 0-115.48787 81.916665 81.916665 0 0 0-115.41473 0L152.172274 598.869332a193.308701 193.308701 0 0 0 0 272.884889 193.235561 193.235561 0 0 0 272.88489 0l446.080123-446.080123a193.016141 193.016141 0 0 0-272.884889-272.81175l-13.165179 13.165178a48.27232 48.27232 0 1 1-68.166367-68.312647l13.092038-13.165179a289.926482 289.926482 0 0 1 397.368965-11.263541l11.9218 11.263541z" fill="white"/></g></svg>`
    }
};
;
const CSS = `
    :root { --pk-zoom: 1; --pk-bg: #ffffff; --pk-bg-rgb: 255, 255, 255; --pk-fg: #1a1a1a; --pk-bd: #e5e5e5; --pk-hl: #f0f0f0; --pk-sel-bg: #e6f3ff; --pk-sel-bd: #cce8ff; --pk-pri: #0067c0; --pk-btn-hov: #e0e0e0; --pk-gh: #f5f5f5; --pk-gh-fg: #333; --pk-sb-bg: transparent; --pk-sb-th: #ccc; --pk-sb-hov: #aaa; --pk-icon-c: #888; --pk-tip-bg: rgba(255, 255, 255, 0.95); --pk-tip-fg: #1a1a1a; --pk-tip-bd: rgba(0, 0, 0, 0.06); --pk-tip-sd: rgba(0, 0, 0, 0.12); --pk-toast-bg: rgba(255, 255, 255, 0.95); --pk-toast-fg: #1a1a1a; --pk-toast-bd: rgba(0, 0, 0, 0.08); --pk-match-bg: #fff2cc; --pk-match-fg: #d93025; --pk-v-line: #d1d1d1; }
    .pk-no-transition, .pk-no-transition * { transition: none !important; }
    .pk-magnet-preview-wrap { width:420px; max-width:86vw; display:flex; flex-direction:column; color:var(--pk-fg); overflow:hidden; }
    .pk-magnet-hero { width:100%; height:210px; background:var(--pk-hl); display:flex; align-items:center; justify-content:center; overflow:hidden; border-radius:14px 14px 0 0; }
    .pk-magnet-hero img { width:100%; height:100%; object-fit:cover; display:block; }
    .pk-magnet-empty { width:100%; height:100%; display:flex; align-items:center; justify-content:center; font-size:14px; opacity:0.62; }
    .pk-magnet-body { padding:18px 20px 20px 20px; display:flex; flex-direction:column; gap:12px; }
    .pk-magnet-title { font-size:16px; font-weight:800; line-height:1.45; overflow:hidden; word-break:break-all; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; text-overflow:ellipsis; }
    .pk-magnet-desc { font-size:12px; line-height:1.5; opacity:0.68; }
    .pk-magnet-meta { display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; }
    .pk-magnet-meta-item { border:1px solid var(--pk-bd); border-radius:10px; padding:8px 9px; background:var(--pk-bg); min-width:0; }
    .pk-magnet-meta-label { font-size:11px; opacity:0.58; margin-bottom:4px; }
    .pk-magnet-meta-value { font-size:13px; font-weight:700; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
    .pk-magnet-hash { font-size:11px; line-height:1.45; opacity:0.62; word-break:break-all; background:var(--pk-hl); border-radius:8px; padding:8px 10px; }
    .pk-magnet-save-row { display:flex; align-items:center; gap:5px; min-width:0; height:26px; margin-top:-2px; font-size:12px; color:var(--pk-fg); }
    .pk-magnet-save-label { opacity:.72; flex-shrink:0; }
    .pk-magnet-save-icon { width:16px; height:16px; display:flex; align-items:center; justify-content:center; flex-shrink:0; overflow:hidden; }
    .pk-magnet-save-icon svg { width:16px !important; height:16px !important; display:block; }
    .pk-magnet-save-name { font-weight:700; max-width:150px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .pk-magnet-save-help { color:#aaa; display:flex; align-items:center; flex-shrink:0; }
    .pk-magnet-save-change { color:var(--pk-pri); cursor:pointer; font-size:12px; font-weight:700; flex-shrink:0; }
    .pk-magnet-save-change:hover { text-decoration:underline; }
    .pk-magnet-source { font-size:11px; line-height:1.35; opacity:.58; display:flex; align-items:center; gap:4px; }
    .pk-magnet-source a { color:var(--pk-pri); text-decoration:none; font-weight:700; }
    .pk-magnet-source a:hover { text-decoration:underline; }
    .pk-magnet-warn { font-size:12px; line-height:1.45; color:#d93025; background:rgba(217,48,37,0.08); border:1px solid rgba(217,48,37,0.16); border-radius:8px; padding:8px 10px; }
    .pk-magnet-shots { display:flex; gap:7px; overflow-x:auto; padding-bottom:2px; }
    .pk-magnet-thumb-wrap { width:68px; height:42px; border-radius:7px; overflow:hidden; flex:0 0 auto; position:relative; border:1px solid var(--pk-bd); cursor:pointer; user-select:none; transition:border-color .16s ease, box-shadow .16s ease; }
    .pk-magnet-thumb-wrap:hover { }
    .pk-magnet-thumb-wrap.active { border-color:var(--pk-pri); box-shadow:0 0 0 2px rgba(0,103,192,18); }
    .pk-magnet-thumb-wrap .pk-magnet-thumb { width:100%; height:100%; border:none; border-radius:0; display:block; object-fit:cover; }
    .pk-magnet-shot-time { position:absolute; right:3px; bottom:3px; padding:1px 4px; border-radius:4px; background:rgba(0,0,0,.62); color:#fff; font-size:10px; line-height:1.25; pointer-events:none; }
    .pk-magnet-thumb { width:68px; height:42px; border-radius:7px; object-fit:cover; flex:0 0 auto; border:1px solid var(--pk-bd); cursor:pointer; user-select:none; -webkit-user-drag:none; transition:border-color .16s ease, box-shadow .16s ease; }
    .pk-magnet-thumb:hover { }
    .pk-magnet-thumb.active { border-color:var(--pk-pri); box-shadow:0 0 0 2px rgba(0,103,192,18); }
    .pk-magnet-hero img { user-select:none; -webkit-user-drag:none; }
    .pk-magnet-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:2px; }
    .pk-dark { --pk-bg: #202020; --pk-bg-rgb: 32, 32, 32; --pk-fg: #f5f5f5; --pk-bd: #333333;
    --pk-hl: #2d2d2d; --pk-sel-bg: #2b3a4a; --pk-sel-bd: #0067c0; --pk-pri: #4cc2ff; --pk-btn-hov: #3a3a3a; --pk-gh: #2a2a2a; --pk-gh-fg: #eee; --pk-sb-th: #555; --pk-sb-hov: #777; --pk-icon-c: #aaa; --pk-tip-bg: rgba(20, 20, 20, 0.95); --pk-tip-fg: #ffffff; --pk-tip-bd: rgba(255, 255, 255, 0.1); --pk-tip-sd: rgba(0, 0, 0, 0.4); --pk-toast-bg: rgba(45, 45, 45, 0.95); --pk-toast-fg: #ffffff; --pk-toast-bd: rgba(255, 255, 255, 0.15); --pk-v-line: rgba(255, 255, 255, 0.25); }
    .pk-dark .pk-loading-ov { background: rgba(0,0,0,0.8); }
    .pk-ov { position: fixed; top: 0; left: 0; width: calc(100vw / var(--pk-zoom, 1)); height: calc(100vh / var(--pk-zoom, 1)); zoom: var(--pk-zoom, 1); transform-origin: top left; z-index: 10000; background: rgba(0,0,0,0.4); backdrop-filter: blur(5px); display: flex; align-items: center; justify-content: center; font-family: inherit; outline: none; overscroll-behavior: none; -webkit-user-select: none; user-select: none; }
    .pk-ov input, .pk-ov textarea { -webkit-user-select: text !important; user-select: text !important; cursor: text; }
    .pk-win { width: 90%; max-width: calc(1600px / var(--pk-zoom, 1)); min-width: 720px; min-height: 340px; height: 80%; background: var(--pk-bg); color: var(--pk-fg); border-radius: 8px; box-shadow: 0 25px 50px rgba(0,0,0,0.25); display: flex; flex-direction: row; overflow: hidden; border: 1px solid var(--pk-bd); position: relative; }
    .pk-sidebar { width: 68px; background: var(--pk-bg); border-right: 1px solid var(--pk-bd); display: flex; flex-direction: column; align-items: center; padding: 16px 0; flex-shrink: 0; z-index: 10; gap: 0; }
    .pk-nav-btn { width: 44px !important; height: 44px !important; border-radius: 10px; color: var(--pk-icon-c); padding: 0 !important; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; margin-bottom: 0; transition: background-color 0.1s ease, color 0.1s ease; position: relative !important; }
    .pk-nav-btn:hover { background: var(--pk-hl); color: var(--pk-fg); }
    .pk-nav-btn.act { background: var(--pk-sel-bg); color: var(--pk-pri); }
    #pk-btn-cloud { background: var(--pk-pri) !important; color: #fff !important; border-radius: 50% !important; margin-bottom: 12px !important; padding: 0 !important; overflow: visible !important; display: flex !important; align-items: center !important; justify-content: center !important; }
    #pk-btn-cloud:hover { filter: brightness(1.1); }
    #pk-btn-cloud svg { width: 24px !important; height: 24px !important; transform: scale(1.3); transform-origin: center center; margin: 0 !important; transition: transform 0.2s; }
    .pk-nav-btn svg { width: 24px !important; height: 24px !important; }
    .pk-maximized #pk-btn-cloud { background: var(--pk-pri) !important; border-radius: 8px !important; width: calc(100% - 20px) !important; height: 48px !important; padding: 0 15px !important; margin-bottom: 20px !important; }
    .pk-maximized #pk-btn-cloud svg { width: 24px !important; height: 24px !important; transform: scale(1.4); margin-right: 6px; }
    .pk-maximized #pk-btn-cloud:hover { filter: brightness(1.1); }
    .pk-sidebar #pk-settings { margin-top: auto; }
    .pk-btn-danger { background: #d93025 !important; color: #fff !important; border: none !important; }
    .pk-btn-danger:hover, #pk-empty-trash:hover { background: #d93025 !important; color: #fff !important; filter: brightness(1.15); opacity: 1 !important; }
    .pk-sidebar #pk-settings:hover { background: var(--pk-hl); color: var(--pk-fg); }
    .pk-sidebar #pk-settings svg { width: 26px !important; height: 26px !important; }
    .pk-main-col { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; min-width: 0; }
    .pk-hd { height: 48px; border-bottom: 1px solid var(--pk-bd); display: flex; align-items: center; justify-content: space-between; padding: 0 16px; background: var(--pk-bg); }
    .pk-tt { font-weight: 700; font-size: 20px; display: flex; align-items: center; gap: 10px; }
    .pk-tt svg { color: #333; margin-right: 10px; transition: color 0.2s ease; }
    .pk-ov.pk-dark .pk-tt svg { color: #fff; }
    .pk-tb { height: 50px !important; padding: 0 8px !important; border-bottom: 1px solid var(--pk-bd); display: flex; gap: clamp(4px, 0.6vw, 8px) !important; align-items: center !important; background: var(--pk-bg); overflow: visible !important; flex-wrap: nowrap !important; position: relative; }
    #pk-top-bar { z-index: 30; }
    #pk-actionbar, #pk-trash-bar { z-index: 20; }
    .pk-btn { height: 32px; padding: 0 12px; border-radius: 4px; border: 1px solid transparent; background: transparent; color: var(--pk-fg); cursor: pointer; font-size: 13px; display: flex; align-items: center; justify-content: center; gap: 6px; transition: background-color 0.1s; position: relative; font-weight: 500; white-space: nowrap; flex-shrink: 0; backface-visibility: hidden; }
    #pk-theme svg { transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
    #pk-theme:active svg { transform: rotate(90deg) scale(0.8); }
    .pk-btn:hover:not(:disabled) { background: var(--pk-btn-hov) !important; }
    .pk-btn.pri:hover:not(:disabled) { background: var(--pk-pri) !important; filter: brightness(1.15); color: #fff !important; }
    .pk-btn:disabled { opacity: 0.4; cursor: not-allowed; }
    .pk-btn.pri { color: var(--pk-pri); font-weight: 600; }
    .pk-btn svg { width: 18px; height: 18px; flex-shrink: 0; display: inline-block; vertical-align: -4px; }
    .pk-btn span { white-space: nowrap; pointer-events: none; }
    @media (max-width: 1360px) { .pk-maximized .pk-btn:not(#pk-btn-folder-first):not(#pk-grid-folder-first):not(#pk-btn-invert):not(#pk-grid-invert):not(#pk-filter-btn):not(#pk-btn-exit):not(#pk-scan-dup):not(#pk-analyze):not(#pk-export):not(#pk-migrate):not(#pk-ext):not(#pk-img-search):not(#pk-aria2):not(#pk-down) span { display: none !important; } .pk-maximized .pk-btn { padding: 0 8px !important; } }
    @media (max-width: 1360px) { .pk-btn:not(#pk-btn-folder-first):not(#pk-grid-folder-first):not(#pk-btn-invert):not(#pk-grid-invert):not(#pk-filter-btn):not(#pk-btn-exit):not(#pk-scan-dup):not(#pk-analyze):not(#pk-export):not(#pk-migrate):not(#pk-ext):not(#pk-img-search):not(#pk-aria2):not(#pk-down):not(#btn_cfg_clean):not(#btn_cfg_export):not(#btn_cfg_import) span { display: none !important; } .pk-btn { padding: 0 8px !important; } }
    @media (max-width: 1150px) { #pk-btn-folder-first span, #pk-btn-invert span, #pk-filter-btn span, #pk-btn-exit span, #pk-scan-dup span, #pk-analyze span, #pk-export span { display: none !important; } }
    @media (max-width: 1000px) { #pk-migrate span, #pk-ext span, #pk-img-search span, #pk-aria2 span, #pk-down span { display: none !important; } }
    .pk-blacklist-area { display: flex; align-items: center; gap: 8px; margin-left: auto; max-width: 300px; min-width: 150px; flex-shrink: 1; }
    .pk-blacklist-area input { height: 32px; padding: 0 8px; border: 1px solid var(--pk-bd); border-radius: 4px; background: var(--pk-bg); color: var(--pk-fg); font-size: 13px; width: 100%; transition: border-color 0.2s; }
    .pk-blacklist-area input:focus { border-color: var(--pk-pri); outline: none; }
    .pk-global-chk { display: flex; align-items: center; cursor: pointer; margin-right: 8px; font-size: 13px; color: var(--pk-fg); user-select: none; white-space: nowrap; }
    .pk-global-chk input { margin: 0 4px 0 0; width: 16px; height: 16px; accent-color: var(--pk-pri); }
    .pk-search { position: relative; display: flex !important; align-items: center !important; margin: 0 !important; flex: 1 1 auto; min-width: 100px; max-width: 300px; transition: all 0.2s; z-index: 100; }
    .pk-search input { height: 32px; padding: 0 56px 0 10px; border: 1px solid var(--pk-bd); border-radius: 4px; background: var(--pk-bg); color: var(--pk-fg); font-size: 13px; width: 100% !important; margin: 0 !important; transition: border-color 0.2s; box-sizing: border-box; }
    .pk-search input:focus { border-color: var(--pk-pri); outline: none; }
    #pk-search-btn { position: absolute; right: 10px; left: auto; width: 14px; height: 14px; color: #888; cursor: pointer; transition: color 0.2s; }
    .pk-search input:focus + svg { color: var(--pk-pri); }
    .pk-f-ext { cursor: pointer; color: var(--pk-fg); padding: 4px 8px; border-radius: 4px; transition: background 0.2s, color 0.2s; font-weight: 500; font-size: 13px; }
    .pk-f-ext:hover { background: var(--pk-hl); }
    .pk-f-ext.act { color: var(--pk-pri); font-weight: bold; }
    .pk-fc-btn { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 12px; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; background: var(--pk-hl); color: var(--pk-fg); transition: all 0.2s; border: 1px solid transparent; white-space: nowrap; }
    .pk-fc-btn:hover { background: rgba(0, 103, 192, 0.05); color: var(--pk-pri); border-color: var(--pk-pri); }
    .pk-fc-btn.act { background: var(--pk-pri); color: #fff; border: none; font-weight: bold; }
    .pk-fc-btn.act:hover { background: var(--pk-pri); color: #fff; }
    .pk-search-clear { position: absolute; right: 32px; top: 50%; transform: translateY(-50%); width: 20px; height: 20px; display: none; align-items: center; justify-content: center; cursor: pointer; color: #999; border-radius: 50%; transition: background 0.2s; }
    .pk-search-clear:hover { background: var(--pk-hl); color: #666; }
    .pk-search-clear svg { position: static !important; width: 14px !important; height: 14px !important; color: inherit !important; }
    .pk-hist-pop { position: absolute; top: 100%; left: 0; right: 0; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 10010 !important; display: none; flex-direction: column; overflow: hidden; padding: 4px; margin-top: 4px; }
    .pk-hist-hd { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; font-size: 11px; color: var(--pk-pri); font-weight: bold; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 1px solid var(--pk-bd); margin-bottom: 4px; }
    .pk-hist-clear-btn { cursor: pointer; }
    .pk-hist-clear-btn:hover { color: var(--pk-pri); }
    .pk-hist-item { padding: 8px 12px; font-size: 13px; cursor: pointer; color: var(--pk-fg); display: flex; align-items: center; gap: 8px; border-bottom: 1px solid transparent; }
    .pk-hist-item:hover { background: var(--pk-sel-bg); }
    .pk-hist-item svg { position: static !important; width: 14px !important; height: 14px !important; color: #999; margin: 0 !important; }
    .pk-dup-toolbar { display: none; align-items: center; gap: 4px; padding: 0 8px; height: 100%; margin-left: 8px; background: transparent; border: none; flex-shrink: 0; }
    .pk-dup-lbl, .pk-dup-chk { white-space: nowrap !important; font-weight: 500; color: var(--pk-fg); font-size: 13px; margin-right: 6px; opacity: 0.8; flex-shrink: 0; cursor: pointer; display: flex; align-items: center; }
    .pk-dup-chk input { margin: 0 4px 0 0; vertical-align: middle; accent-color: var(--pk-pri); width: 16px; height: 16px; }
    .pk-txt-short { display: none; }
    .pk-txt-long { display: inline; }
    #pk-search-path-con .pk-txt-short { font-weight: 500; color: var(--pk-fg); }
    #pk-dup-folder-sel-wrap { position: relative; display: inline-flex; align-items: center; width: 220px; min-width: 0; flex-shrink: 0; }
    #pk-dup-folder-sel { position: absolute !important; inset: 0 !important; width: 0 !important; height: 0 !important; opacity: 0 !important; pointer-events: none !important; }
    #pk-dup-folder-btn { width: 100%; height: 30px; border: 1px solid var(--pk-bd); border-radius: 4px; background: var(--pk-bg); color: var(--pk-fg); padding: 0 28px 0 10px; font-size: 12px; text-align: left; cursor: pointer; position: relative; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    #pk-dup-folder-btn:hover { background: var(--pk-btn-hov); border-color: var(--pk-pri); }
    #pk-dup-folder-btn::after { content: ""; position: absolute; right: 10px; top: 50%; width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: translateY(-65%) rotate(45deg); opacity: 0.7; pointer-events: none; }
    .pk-dup-folder-pop { position:fixed; min-width:220px; max-width:520px; max-height:320px; background:#ffffff; color:#1f1f1f; border:1px solid rgba(0,0,0,0.12); border-radius:8px; box-shadow:0 8px 24px rgba(0,0,0,0.18); z-index:2147483647; overflow:hidden; zoom:var(--pk-zoom,1); display:flex; flex-direction:column; }
    .pk-dup-folder-pop.pk-dark { background:rgba(28,28,28,0.98); color:#f5f5f5; border-color:rgba(255,255,255,0.12); box-shadow:0 10px 28px rgba(0,0,0,0.45); }
    .pk-dup-folder-head { flex:0 0 auto; padding:4px; border-bottom:1px solid rgba(0,0,0,0.08); background:#ffffff; }
    .pk-dup-folder-pop.pk-dark .pk-dup-folder-head { background:rgba(28,28,28,0.98); border-bottom:1px solid rgba(255,255,255,0.10); }
    .pk-dup-folder-list { flex:1 1 auto; min-height:0; max-height:272px; overflow-y:auto; overflow-x:hidden; padding:4px; }
    .pk-dup-folder-item { width:100%; min-height:30px; border:0; background:transparent; color:inherit; border-radius:6px; padding:0 10px; font-size:12px; text-align:left; cursor:pointer; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .pk-dup-folder-item:hover { background:rgba(0,0,0,0.06); }
    .pk-dup-folder-pop.pk-dark .pk-dup-folder-item:hover { background:rgba(255,255,255,0.08); }
    .pk-dup-folder-item.act { background:rgba(26,115,232,0.12); color:#1a73e8; font-weight:600; }
    .pk-dup-folder-pop.pk-dark .pk-dup-folder-item.act { background:rgba(138,180,248,0.18); color:#8ab4f8; }
    .pk-dup-folder-item.pk-reset { position:relative; top:auto; z-index:auto; color:#1a73e8; font-weight:700; background:transparent; border-bottom:0; border-radius:6px; }
    .pk-dup-folder-item.pk-reset:hover { background:#f7f9fc; }
    .pk-dup-folder-pop.pk-dark .pk-dup-folder-item.pk-reset { color:#8ab4f8; background:transparent; border-bottom:0; }
    .pk-dup-folder-pop.pk-dark .pk-dup-folder-item.pk-reset:hover { background:rgba(255,255,255,0.06); }
    @media (max-width: 1150px) { .pk-txt-long { display: none !important; } .pk-txt-short { display: inline !important; } #pk-btn-exit, #pk-scan-dup { padding: 0 8px !important; } #pk-dup-folder-sel-wrap { max-width: 100px !important; } }
    .pk-btn-toggle { border: 1px solid var(--pk-bd); background: var(--pk-bg); color: var(--pk-fg); height: 30px; border-radius: 4px; padding: 0 10px; font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; flex-shrink: 0; }
    .pk-btn-toggle:hover { background: var(--pk-btn-hov); border-color: var(--pk-pri); }
    .pk-btn-toggle span { font-weight: 700; color: var(--pk-pri); }
    #pk-dup-exit { flex-shrink: 0; }
    .pk-nav { display: flex !important; align-items: center !important; gap: 4px; overflow: hidden; white-space: nowrap; font-size: 13px; color: #666; margin: 0 8px; height: 100%; max-width: 60%; }
    .pk-nav span { cursor: pointer; padding: 2px 6px; border-radius: 4px; }
    .pk-nav span:hover { background: var(--pk-hl); color: var(--pk-fg); }
    .pk-nav span.act { font-weight: 600; color: var(--pk-fg); cursor: pointer; }
    .pk-grid-hd, .pk-row { display: grid; column-gap: 10px; align-items: center; font-size: 14px; color: var(--pk-fg); box-sizing: border-box; width: 100%; }
    .pk-grid-hd > div, .pk-row > div { display: flex; align-items: center; justify-content: flex-start !important; overflow: hidden; white-space: nowrap; text-align: left; }
    .pk-grid-hd > div:first-child, .pk-row > div:first-child { justify-content: center !important; overflow: visible !important; }
    .pk-grid-hd { height: 36px; border-bottom: 1px solid var(--pk-bd); font-size: 13px; color: #666; user-select: none; padding: 0 22px 0 16px; }
    .pk-row { padding: 0 16px; }
    .pk-col { cursor: pointer; font-weight: 600; display: flex; align-items: center; justify-content: flex-start; }
    .pk-col:hover { color: var(--pk-fg); }
    .pk-view-switch { display: inline-flex; align-items: center; gap: 4px; margin-left: 12px; padding: 3px; border: 1px solid var(--pk-bd); border-radius: 999px; background: var(--pk-bg); flex-shrink: 0; }
    .pk-view-switch[style*="display: none"] { margin-left: 0; }
    .pk-view-btn { width: 34px; height: 28px; border: none; border-radius: 999px; background: transparent; color: #777; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: background 0.2s,color 0.2s,transform 0.2s; padding: 0; }
    .pk-view-btn:hover { color: var(--pk-fg); background: var(--pk-hl); }
    .pk-view-btn.active { background: var(--pk-pri); color: #fff; box-shadow: 0 3px 10px rgba(0,103,192,0.22); }
    .pk-grid-hd.pk-grid-view-hd { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 18px 0 16px; overflow: visible; position: relative; z-index: 30; }
    .pk-grid-hd.pk-grid-view-hd > div { overflow: visible; min-width: 0; }
    .pk-grid-hd.pk-grid-view-hd > div:first-child { width: auto; flex: 0 1 auto; }
    .pk-grid-check-tools { display:flex; align-items:center; gap:10px; min-width:0; flex:0 1 auto; overflow:visible !important; padding-left:9px; box-sizing:border-box; }
    .pk-grid-check-tools > input[type="checkbox"] { flex:0 0 auto; margin:0; }
    .pk-grid-check-tools > #pk-grid-folder-first { flex:0 0 auto; }
    .pk-grid-check-tools { display:flex; align-items:center; gap:8px; min-width:0; overflow:visible !important; padding-left:9px; box-sizing:border-box; }
    .pk-grid-sort-wrap { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; flex-shrink: 0; overflow: visible !important; z-index: 2; }
    .pk-grid-sort-trigger { height: 30px; border: 1px solid var(--pk-bd); border-radius: 999px; background: var(--pk-bg); color: var(--pk-fg); padding: 0 12px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer; font-size: 12px; font-weight: 600; transition: border-color 0.2s,background 0.2s,color 0.2s; max-width: 100%; }
    .pk-grid-sort-trigger:hover, .pk-grid-sort-wrap.open .pk-grid-sort-trigger { border-color: var(--pk-pri); color: var(--pk-pri); background: var(--pk-hl); }
    .pk-grid-sort-trigger svg { flex-shrink: 0; }
    .pk-grid-sort-trigger span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    .pk-grid-sort-menu { position: absolute; top: calc(100% + 8px); right: 0; min-width: 156px; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 12px; box-shadow: 0 12px 36px rgba(0,0,0,0.16); padding: 6px; display: none; z-index: 10020; }
    .pk-grid-sort-wrap.open .pk-grid-sort-menu { display: block; }
    .pk-grid-sort-opt { border-radius: 10px; padding: 9px 12px; display: flex; align-items: center; gap: 10px; color: var(--pk-fg); cursor: pointer; transition: background 0.15s,color 0.15s; font-size: 13px; font-weight: 500; white-space: nowrap; }
    .pk-grid-sort-opt:hover, .pk-grid-sort-opt.active { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-grid-view .pk-vp { padding: 14px 0 18px; }
    .pk-grid-view .pk-in { left: 0; right: 0; }
    .pk-view-switching .pk-row, .pk-view-switching .pk-row::before, .pk-view-switching .pk-row::after, .pk-view-switching .pk-row *, .pk-grid-resizing .pk-row, .pk-grid-resizing .pk-row::before, .pk-grid-resizing .pk-row::after, .pk-grid-resizing .pk-row *, .pk-grid-scrolling .pk-row, .pk-grid-scrolling .pk-row::before, .pk-grid-scrolling .pk-row::after, .pk-grid-scrolling .pk-row * { transition: none !important; animation: none !important; }
    .pk-view-switching.pk-grid-view .pk-row, .pk-view-switching.pk-grid-view .pk-row:hover, .pk-view-switching.pk-grid-view .pk-row.sel, .pk-view-switching.pk-grid-view .pk-row.sel.pk-focused, .pk-dark .pk-view-switching.pk-grid-view .pk-row, .pk-dark .pk-view-switching.pk-grid-view .pk-row:hover, .pk-dark .pk-view-switching.pk-grid-view .pk-row.sel, .pk-dark .pk-view-switching.pk-grid-view .pk-row.sel.pk-focused, .pk-grid-resizing.pk-grid-view .pk-row, .pk-grid-resizing.pk-grid-view .pk-row:hover, .pk-grid-resizing.pk-grid-view .pk-row.sel, .pk-grid-resizing.pk-grid-view .pk-row.sel.pk-focused, .pk-dark .pk-grid-resizing.pk-grid-view .pk-row, .pk-dark .pk-grid-resizing.pk-grid-view .pk-row:hover, .pk-dark .pk-grid-resizing.pk-grid-view .pk-row.sel, .pk-dark .pk-grid-resizing.pk-grid-view .pk-row.sel.pk-focused { border-color: transparent !important; box-shadow: none !important; outline: none !important; }
    .pk-grid-view .pk-row { box-sizing: border-box; border-radius: 16px; border: 1px solid transparent; background: #e3e9f2; box-shadow: none; overflow: hidden; transition: background 0.2s, border-color 0.2s, box-shadow 0.2s; }
    .pk-grid-view .pk-row:hover { background: #d9e1ec; transform: none; box-shadow: none; }
    .pk-grid-view .pk-row.sel { background: #d4deeb; border-color: rgba(0,103,192,0.15); box-shadow: 0 0 0 1px rgba(0,103,192,0.15); }
    .pk-grid-view .pk-row.sel.pk-focused { border-color: var(--pk-pri); box-shadow: 0 0 0 1px var(--pk-pri); }
    .pk-grid-scrolling.pk-grid-view .pk-row.pk-focused:not(.sel), .pk-dark .pk-grid-scrolling.pk-grid-view .pk-row.pk-focused:not(.sel) { background: var(--pk-sel-bg) !important; border-color: var(--pk-pri) !important; box-shadow: none !important; outline: none !important; }
    .pk-grid-card-body { position: relative; display: flex; flex-direction: column; width: 100%; height: 100%; box-sizing: border-box; padding: 10px; gap: 10px; }
    .pk-gv-check { position: absolute; top: 12px; left: 12px; width: 24px; height: 24px; background: #fff; border-radius: 6px; z-index: 4; display: flex; align-items: center; justify-content: center; cursor: pointer; box-sizing: border-box; opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-2px); transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease; }
    .pk-gv-check::before { content: ''; position: absolute; left: 50%; top: 50%; width: 18px; height: 18px; margin-left: -9px; margin-top: -9px; border-radius: 50%; border: 1.5px solid #c7cdd6; background: transparent; box-sizing: border-box; transition: all 0.15s; }
    .pk-gv-check::after { content: ''; position: absolute; left: 50%; top: 50%; width: 4px; height: 8px; margin-left: -2px; margin-top: -5px; border: solid #fff; border-width: 0 1.5px 1.5px 0; opacity: 0; transform: rotate(45deg) scale(0.8); transform-origin: center; transition: opacity 0.15s, transform 0.15s; box-sizing: border-box; }
    .pk-grid-view .pk-row:hover .pk-gv-check, .pk-grid-view .pk-row.sel .pk-gv-check, .pk-grid-view .pk-row:focus-within .pk-gv-check { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0); }
    .pk-grid-view .pk-row:hover .pk-gv-check::before { border-color: #818c9b; }
    .pk-grid-view .pk-row.sel .pk-gv-check::before { background: var(--pk-pri); border-color: var(--pk-pri); }
    .pk-grid-view .pk-row.sel .pk-gv-check::after { opacity: 1; transform: rotate(45deg) scale(1); }
    .pk-grid-view .pk-row input[type="checkbox"] { position: absolute; inset: 0; width: 100% !important; height: 100% !important; margin: 0 !important; opacity: 0; cursor: pointer; z-index: 2; }
    .pk-gv-more { position: absolute; top: 12px; right: 12px; width: 24px; height: 24px; border: none; border-radius: 6px; background: #fff; color: #8a94a4; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; z-index: 5; padding: 0; opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-2px); transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease, color 0.2s; }
    .pk-grid-view .pk-row:hover .pk-gv-more, .pk-grid-view .pk-row:focus-within .pk-gv-more, .pk-grid-view .pk-row.sel.pk-focused .pk-gv-more, .pk-grid-view .pk-row.pk-sel-single .pk-gv-more { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0); }
    .pk-gv-more:hover { color: var(--pk-pri); }
    .pk-gv-more svg { width: 16px; height: 16px; flex-shrink: 0; }
    .pk-gv-cover { position: relative; height: calc(100% - 56px); min-height: 140px; background: transparent; display: flex; align-items: center; justify-content: center; overflow: hidden; border-radius: 12px; }
    .pk-gv-cover:not(.pk-gv-file) { align-items: flex-end; padding-bottom: 8px; box-sizing: border-box; }
    .pk-gv-cover.pk-gv-file { width: auto; max-width: 100%; aspect-ratio: 1 / 1; align-self: center; border-radius: 18px; flex: 0 0 auto; isolation: isolate; transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; }
    .pk-gv-cover.pk-gv-file .pk-gv-media-mount { width: 100%; height: 100%; display: block; position: relative; isolation: isolate; transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; }
    .pk-gv-cover.pk-gv-file .pk-gv-media-mount > div { width: 100%; height: 100%; position: relative; transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; }
    .pk-gv-cover.pk-gv-file .pk-gv-media-mount .pk-max-thumb { width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; object-fit: cover !important; border-radius: 0 !important; transform: translateZ(0); transform-origin: center center; backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform, opacity; }
    .pk-gv-cover.pk-gv-has-thumb { background: transparent; }
    .pk-gv-cover img { max-width: 100%; max-height: 100%; object-fit: cover; display: block; border-radius: 12px; }
    .pk-gv-cover.pk-gv-has-thumb > img { width: 100%; height: 100%; }
    .pk-gv-cover.pk-gv-blur > img, .pk-gv-cover.pk-gv-blur .pk-gv-media-mount [data-pk-thumb-ready="1"] > .pk-max-thumb, .pk-gv-cover.pk-gv-blur .pk-gv-media-mount .pk-max-thumb[data-pk-frozen-cover="1"], .pk-gv-cover.pk-gv-blur .pk-gv-folder-preview img[data-pk-id][data-pk-src], .pk-gv-cover.pk-gv-blur .pk-gv-folder-preview img[data-pk-frozen-cover="1"] { filter: blur(8px); transform: scale(1.04); }
    .pk-gv-cover.pk-gv-file .pk-gv-media-mount { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
    .pk-gv-cover.pk-gv-file .pk-gv-media-mount > div { width: 100%; height: 100%; }
    .pk-gv-cover .pk-gv-icon { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
    .pk-gv-cover .pk-gv-icon svg, .pk-gv-cover .pk-gv-icon img { width: 108px !important; height: 108px !important; max-width: 108px !important; max-height: 108px !important; object-fit: contain !important; }
    .pk-gv-cover .pk-gv-icon img { border-radius: 10px; }
    .pk-gv-folder-preview.pk-gv-folder-has-thumb { background: transparent; isolation: isolate; transform: translateZ(0); }
    .pk-gv-folder-preview.pk-gv-folder-has-thumb img { width: 100%; height: 100%; display: block; object-fit: cover; border-radius: 0; transform: translateZ(0) scale(1.02); transform-origin: center center; backface-visibility: hidden; will-change: transform; }
    .pk-gv-folder-shell { position: relative; width: 176px; height: 138px; margin: 12px auto 0; display: flex; align-items: flex-end; justify-content: center; transform: translateY(12px); }
    .pk-gv-folder-back { position: absolute; top: 19px; left: 0; right: 0; bottom: 0; background: #fbc645; border-radius: 12px; }
    .pk-gv-folder-tab { position: absolute; top: 6px; left: 15px; width: 62px; height: 23px; background: #fbc645; border-radius: 9px 9px 0 0; }
    .pk-gv-folder-preview { position: absolute; top: 0; left: 15px; right: 15px; bottom: 23px; border-radius: 10px; overflow: hidden; clip-path: inset(0 round 10px); background: #fff7e6; z-index: 1; display: flex; align-items: center; justify-content: center; isolation: isolate; transform: translateZ(0); }
    .pk-gv-folder-preview { position: absolute; top: 0; left: 15px; right: 15px; bottom: 23px; border-radius: 10px; overflow: hidden; clip-path: inset(0 round 10px); background: #fff7e6; z-index: 1; display: flex; align-items: center; justify-content: center; isolation: isolate; transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; }
    .pk-gv-folder-fallback { width: 102px !important; height: 102px !important; max-width: 102px !important; max-height: 102px !important; object-fit: contain !important; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; }
    .pk-gv-folder-front { position: absolute; left: -1px; right: -1px; bottom: -1px; height: 86px; background: #f9b126; border-radius: 12px; z-index: 2; box-shadow: 0 -2px 12px rgba(249, 177, 38, 0.2); transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; }
    .pk-gv-folder-shell { isolation: isolate; transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; }
    .pk-gv-folder-back, .pk-gv-folder-tab { transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; }
    .pk-gv-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 48px; height: 48px; border-radius: 50%; background: rgba(0,0,0,0.4); display: inline-flex; align-items: center; justify-content: center; pointer-events: none; z-index: 3; }
    .pk-gv-play svg { width: 28px !important; height: 28px !important; color: #fff; margin-left: 3px; }
    .pk-gv-fav { position: absolute; right: 14px; bottom: 14px; width: 24px; height: 24px; border-radius: 50%; background: rgba(255,255,255,0.95); color: #f4b400; display: grid; place-items: center; box-shadow: 0 4px 10px rgba(0,0,0,0.1); pointer-events: none; z-index: 3; line-height: 0; }
    .pk-dark .pk-gv-fav { background: rgba(39,45,54,0.96); box-shadow: 0 6px 14px rgba(0,0,0,0.24); }
    .pk-gv-fav .pk-star-icon { width: 14px !important; height: 14px !important; display: block !important; margin: 0 !important; padding: 0 !important; transform: none !important; transform-origin: center center !important; }
    .pk-gv-fav .pk-star-icon svg { width: 14px !important; height: 14px !important; display: block !important; margin: 0 !important; padding: 0 !important; transform: none !important; }
    .pk-gv-bl { position: absolute; left: 14px; bottom: 14px; width: 24px; height: 24px; border-radius: 50%; background: rgba(255,255,255,0.95); display: grid; place-items: center; box-shadow: 0 4px 10px rgba(0,0,0,0.1); pointer-events: none; z-index: 3; line-height: 0; }
    .pk-gv-bl svg { width: 14px !important; height: 14px !important; display: block !important; margin: 0 !important; padding: 0 !important; transform: none !important; transform-origin: center center !important; }
    .pk-dark .pk-gv-bl { background: rgba(39,45,54,0.96); box-shadow: 0 6px 14px rgba(0,0,0,0.24); }
    .pk-gv-info { display:grid; grid-template-rows:20px 16px; row-gap:2px; align-content:end; align-self:stretch; width:100%; min-width:0; padding:0 2px; height:38px; min-height:38px; box-sizing:border-box; overflow:hidden; text-align:left; }
    .pk-grid-view .pk-name { display:flex; align-items:center; justify-content:flex-start; align-self:stretch; width:100%; min-width:0; height:20px; overflow:hidden; text-align:left; }
    .pk-grid-view .pk-name .pk-name-main { display:inline-flex; align-items:center; min-width:0; max-width:100%; overflow:hidden; }
    .pk-grid-view .pk-name .pk-name-txt { display:block; flex:0 1 auto; width:auto; min-width:0; max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:20px; font-size:14px; font-weight:500; color:#1a1a1a; word-break:normal; text-align:left; }
    .pk-grid-view .pk-name .pk-tag-default { flex:0 0 auto; align-self:center; margin-left:6px; margin-top:0 !important; margin-bottom:0 !important; padding:0 6px; min-width:auto; height:16px; border-radius:10px; font-size:11px; line-height:1 !important; white-space:nowrap; display:inline-flex !important; align-items:center !important; justify-content:center !important; text-align:center; box-sizing:border-box; vertical-align:middle; transform:none !important; position:relative; top:-2px; }
    .pk-gv-meta { display:flex; align-items:center; justify-content:flex-start; align-self:stretch; width:100%; min-width:0; height:16px; gap:0; color:#1a1a1a; font-size:12px; line-height:16px; overflow:hidden; text-align:left; }
    .pk-gv-meta-item { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:0 1 auto; text-align:left; }
    .pk-gv-date-wrap { display:flex; align-items:center; min-width:0; flex:0 1 auto; overflow:hidden; gap:0; }
    .pk-gv-date { flex:0 1 auto; min-width:0; text-align:left; }
    .pk-gv-size { flex:0 0 auto; max-width:40%; text-align:left; }
    .pk-gv-dur { flex:0 0 auto; max-width:34%; text-align:left; }
    .pk-gv-fcount { flex:0 0 auto; max-width:34%; text-align:left; }
    .pk-gv-meta-sep { display:block; width:3px; height:3px; margin:0 6px; border-radius:50%; background:currentColor; opacity:0.5; flex:0 0 auto; align-self:center; }
    .pk-gv-date, .pk-gv-size, .pk-gv-dur, .pk-gv-fcount { color: inherit; opacity: 1; }
    .pk-dark .pk-grid-view .pk-row { background: rgba(39,45,54,0.96); border-color: transparent; box-shadow: 0 12px 26px rgba(0,0,0,0.22); outline: none; background-clip: padding-box; }
    .pk-dark .pk-grid-view .pk-row:hover { background: rgba(45,52,62,0.98); border-color: transparent; outline: none; }
    .pk-dark .pk-grid-view .pk-row.sel { background: rgba(34,60,96,0.9); border-color: rgba(92,169,255,0.42); box-shadow: 0 12px 28px rgba(18,44,82,0.32); outline: none; background-clip: padding-box; }
    .pk-dark .pk-grid-view .pk-row.pk-sel-single, .pk-dark .pk-grid-view .pk-row.pk-sel-single.pk-focused { transition: background 0.2s !important; }
    .pk-dark .pk-gv-check::before { background: rgba(37,42,50,0.92); border-color: rgba(124,137,156,0.9); }
    .pk-dark .pk-gv-more { background: rgba(34,40,48,0.94); color: #c0c7d2; box-shadow: 0 6px 14px rgba(0,0,0,0.24); }
    .pk-dark .pk-gv-more:hover { background: rgba(46,54,64,0.98); color: #fff; }
    .pk-dark .pk-gv-cover.pk-gv-file { background: transparent; }
    .pk-dark .pk-gv-cover.pk-gv-file.pk-gv-has-thumb:has(.pk-gv-media-mount > [data-pk-thumb-ready="1"]) { background: linear-gradient(180deg,#313844 0%,#2a313b 100%); }
    .pk-dark .pk-gv-cover.pk-gv-folder { background: transparent; }
    .pk-dark .pk-gv-folder-preview { background: #fff7e6; }
    .pk-dark .pk-grid-view .pk-name .pk-name-txt, .pk-dark .pk-gv-meta, .pk-dark .pk-gv-meta-item, .pk-dark .pk-gv-date, .pk-dark .pk-gv-size { color:#fff; }
    .pk-modal::-webkit-scrollbar, .pk-vp::-webkit-scrollbar, .pk-prev-list::-webkit-scrollbar, .pk-scroll::-webkit-scrollbar, #pk-rn-vp::-webkit-scrollbar, .pk-bl-area::-webkit-scrollbar, textarea::-webkit-scrollbar, .pk-sub-pane::-webkit-scrollbar, #pk_sub_search_list::-webkit-scrollbar, .pk-p-pop::-webkit-scrollbar, .pk-share-modal::-webkit-scrollbar { width: 6px; height: 6px; }
    .pk-no-scrollbar::-webkit-scrollbar { display: none !important; }
    .pk-no-scrollbar { -ms-overflow-style: none !important; scrollbar-width: none !important; }
    .pk-vp::-webkit-scrollbar-track, .pk-modal::-webkit-scrollbar-track, .pk-prev-list::-webkit-scrollbar-track, .pk-scroll::-webkit-scrollbar-track, #pk-rn-vp::-webkit-scrollbar-track, .pk-bl-area::-webkit-scrollbar-track, textarea::-webkit-scrollbar-track, .pk-sub-pane::-webkit-scrollbar-track, #pk_sub_search_list::-webkit-scrollbar-track, .pk-p-pop::-webkit-scrollbar-track { background: var(--pk-sb-bg); }
    .pk-vp::-webkit-scrollbar-thumb, .pk-modal::-webkit-scrollbar-thumb, .pk-prev-list::-webkit-scrollbar-thumb, .pk-scroll::-webkit-scrollbar-thumb, #pk-rn-vp::-webkit-scrollbar-thumb, .pk-bl-area::-webkit-scrollbar-thumb, textarea::-webkit-scrollbar-thumb, .pk-sub-pane::-webkit-scrollbar-thumb, #pk_sub_search_list::-webkit-scrollbar-thumb, .pk-p-pop::-webkit-scrollbar-thumb { background: var(--pk-sb-th); border-radius: 3px; }
    .pk-vp::-webkit-scrollbar-thumb:hover, .pk-modal::-webkit-scrollbar-thumb:hover, .pk-prev-list::-webkit-scrollbar-thumb:hover, .pk-scroll::-webkit-scrollbar-thumb:hover { background: var(--pk-sb-hov); } ::-webkit-scrollbar { cursor: default; }
    .pk-vp { flex: 1; overflow-y: auto; position: relative; background: var(--pk-bg); scrollbar-gutter: stable; }
    .pk-in { position: absolute; width: 100%; top: 0; }
    .pk-row { height: 40px; border: 1px solid transparent; cursor: default; padding: 0 16px; border-radius: 4px; }
    .pk-row:hover { background: var(--pk-hl); }
    .pk-row.sel { background: var(--pk-sel-bg); border: 1px solid transparent; }
    .pk-row.sel.pk-focused { border: 1px solid var(--pk-pri); border-radius: 4px; }
    .pk-grid-view .pk-row.sel.pk-focused { border-color: var(--pk-pri); box-shadow: 0 0 0 1px var(--pk-pri); border-radius: 16px; }
    .pk-name { display: flex; align-items: center; overflow: visible; min-width: 0; cursor: default; }
    .pk-name .pk-name-txt { transition: color 0.1s; border-bottom: 1px solid transparent; }
    .pk-name svg { flex-shrink: 0; margin-right: 8px; cursor: default; }
    .pk-win:not(.pk-mode-trash) .pk-name .pk-name-txt { cursor: pointer; }
    .pk-win:not(.pk-mode-trash) .pk-name .pk-name-txt:hover { color: var(--pk-pri); }
    .pk-tag-default { cursor: default !important; color: #999 !important; border: 1px solid #ccc !important; font-weight: normal !important; }
    .pk-tag-default:hover { color: #999 !important; }
    .pk-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; line-height: 1.5; padding: 2px 0; margin-top: -2px; }
    .pk-group-hd { display: flex; background: var(--pk-gh); color: var(--pk-gh-fg); font-weight: bold; align-items: center; padding: 0 16px; border-top: 4px solid var(--pk-bg) !important; border-bottom: 4px solid var(--pk-bg) !important; background-clip: padding-box; height: 40px !important; box-sizing: border-box; margin-top: 0 !important; }
    .pk-group-hd .pk-tag { margin-left: auto; background: #666; color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; border: 1px solid #555; }
    .pk-group-hd .pk-cnt { margin-left: 10px; color: var(--pk-fg); font-size: 12px; opacity: 0.9; }
    .pk-group-hd .pk-name { height:100%; align-items:center !important; }
    .pk-group-hd .pk-name .pk-name-txt, .pk-group-hd .pk-name span { display:flex !important; align-items:center !important; height:100% !important; line-height:1.2 !important; padding:0 !important; margin-top:0 !important; }
    .pk-loading-ov { position: absolute; inset: 0; background: rgba(var(--pk-bg-rgb), 0.75); z-index: 999; display: none; flex-direction: column; align-items: center; justify-content: center; color: var(--pk-fg); gap: 28px; backdrop-filter: blur(10px) saturate(180%); -webkit-backdrop-filter: blur(10px) saturate(180%); transition: all 0.3s ease; }
    .pk-spin-lg { width: 56px; height: 56px; border: 4px solid rgba(136, 136, 136, 0.25); border-top-color: var(--pk-pri); border-radius: 50%; position: relative; animation: pk-ultra-spin 1s linear infinite; transform: translateZ(0); }
    .pk-loading-txt { font-size: 15px; font-weight: 600; text-align: center; white-space: pre-line; line-height: 1.6; letter-spacing: 1px; }
    @keyframes pk-ultra-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
    @keyframes pk-text-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.6; transform: scale(0.98); } }
    .pk-stop-btn { display: flex !important; align-items: center !important; justify-content: center !important; gap: 6px; padding: 8px 24px; background: #d93025; color: white; border: none; border-radius: 20px; font-size: 14px; cursor: pointer; font-weight: bold; box-shadow: 0 4px 10px rgba(217, 48, 37, 0.3); transition: transform 0.1s; }
    .pk-stop-btn svg { display: block; }
    .pk-stop-btn:hover { background: #b02a20; transform: scale(1.05); }
    .pk-stop-btn:active { transform: scale(0.95); }
    .pk-ft { height: 48px; border-top: 1px solid var(--pk-bd); background: var(--pk-bg); display: flex; align-items: center; padding: 0 16px; justify-content: space-between; font-size: 13px; }
    .pk-stat { color: var(--pk-fg); font-size: 13px; }
    .pk-grp { display: flex; gap: 8px; }
    .pk-pop { position: fixed; pointer-events: none; z-index: 2147483647 !important; background: #000; border: 1px solid #333; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); border-radius: 6px; display: none; overflow: hidden; }
    .pk-pop img { display: block; max-width: 320px; max-height: 240px; object-fit: contain; }
    .pk-ctx { position: fixed; z-index: 2147483647 !important; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); min-width: 150px; padding: 4px 0; display: none; }
    .pk-ctx-item { padding: 8px 16px; font-size: 13px; cursor: pointer; display: flex; align-items: center; gap: 8px; color: var(--pk-fg); }
    .pk-ctx-item:hover { background: var(--pk-hl); }
    .pk-ctx-sep { height: 1px; background: var(--pk-bd); margin: 4px 0; }
    .pk-modal-ov { position: fixed; top: 0; left: 0; width: calc(100vw / var(--pk-zoom, 1)); height: calc(100vh / var(--pk-zoom, 1)); zoom: var(--pk-zoom, 1); transform-origin: top left; background: rgba(0, 0, 0, 0.5); z-index: 10001; display: flex; align-items: center; justify-content: center; overscroll-behavior: none; overflow: hidden; padding: 20px; box-sizing: border-box; }
    .pk-modal { position: relative; background: var(--pk-bg); padding: 25px; border-radius: 12px; width: 500px; max-height: 100%; overflow: hidden !important; display: flex; flex-direction: column; gap: 15px; border: 1px solid var(--pk-bd); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4); overscroll-behavior: none; margin: auto; flex-shrink: 1; }
    .pk-modal h3 { margin: 0 0 5px 0; font-size: 16px; border-bottom: 1px solid var(--pk-bd); padding-bottom: 10px; padding-right: 40px; color: var(--pk-fg); }
    .pk-modal-close { position: absolute; top: 15px; right: 15px; cursor: pointer; color: var(--pk-icon-c); width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 6px; transition: background 0.1s, color: 0.1s; }
    .pk-modal-close:hover { background: var(--pk-hl); color: var(--pk-fg); }
    .pk-field { display: flex; flex-direction: column; gap: 5px; font-size: 13px; }
    .pk-field input, .pk-field select { padding: 6px; border: 1px solid var(--pk-bd); border-radius: 4px; background: var(--pk-bg); color: var(--pk-fg); }
    .pk-field select { appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; background-size: 16px; padding-right: 32px !important; cursor: pointer; }
    .pk-modal-act { display: flex; justify-content: flex-end; gap: 10px; margin-top: 10px; }
    .pk-credit { font-size: 11px; color: #888; text-align: center; margin-top: 20px; border-top: 1px solid var(--pk-bd); padding-top: 10px; }
    .pk-credit a { color: #888; text-decoration: none; }
    .pk-credit a:hover { text-decoration: underline; }
    .pk-prev-list { flex: 1; overflow-y: auto; border: 1px solid var(--pk-bd); max-height: 300px; }
    .pk-prev-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 5px 10px; border-bottom: 1px solid var(--pk-bd); font-size: 12px; }
    .pk-prev-row:nth-child(odd) { background: var(--pk-hl); }
    .pk-sep { width: 1px; height: 16px; background: var(--pk-bd); margin: 0 4px; display: none; flex-shrink: 0; }
    .pk-sep-sm { width: 1px; height: 16px; background: var(--pk-bd); margin: 0 8px; flex-shrink: 0; }
    #pk-refresh, #pk-trash-refresh { width: auto !important; justify-content: center !important; flex-shrink: 0; }
    #pk-refresh svg, #pk-trash-refresh svg { width: 16px !important; height: 16px !important; }
    .pk-grid-hd input[type="checkbox"], .pk-row input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; margin: 0 auto !important; flex-shrink: 0; accent-color: var(--pk-pri); box-sizing: content-box; transform: translateZ(0); display: block; position: relative; }
    .pk-player-box { position: relative; width: 100%; height: 100%; background: #000; display: flex; flex-direction: column; user-select: none; overflow: hidden; }
    .pk-player-video { width: 100%; height: 100%; object-fit: contain; outline: none; transform: translateZ(0); backface-visibility: hidden; image-rendering: -webkit-optimize-contrast; -webkit-font-smoothing: antialiased; }
    .pk-player-top { position: absolute; top: 0; left: 0; right: 0; height: 64px; background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.3) 60%, transparent 100%); display: flex; align-items: center; justify-content: space-between; padding: 0 24px; z-index: 100 !important; opacity: 1; transition: opacity 0.3s; pointer-events: auto; }
    .pk-player-box.ui-hidden .pk-player-top { opacity: 0 !important; pointer-events: none !important; }
    .pk-player-title { color: #fff; font-size: 16px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); letter-spacing: 0.5px; }
    .pk-player-controls { position: absolute; bottom: 0; left: 0; right: 0; height: 64px; background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.35) 60%, transparent 100%); display: flex; align-items: center; padding: 0 16px; z-index: 80 !important; gap: 4px; opacity: 0; transition: opacity 0.3s; }
    .pk-player-box:hover .pk-player-controls, .pk-player-box.paused .pk-player-controls { opacity: 1; }
    .pk-player-progress-container { position: absolute; bottom: 64px; left: 12px; right: 12px; height: 14px; cursor: pointer; z-index: 11; display: flex; align-items: center; }
    .pk-player-progress-bg { width: 100%; height: 4px; background: rgba(255, 255, 255, 0.25); position: relative; border-radius: 2px; transition: height 0.1s, transform 0.1s; backdrop-filter: blur(2px); }
    .pk-player-progress-container:hover .pk-player-progress-bg { height: 6px; transform: scaleY(1.1); }
    .pk-player-progress-filled { height: 100%; background: var(--pk-pri); width: 0; position: relative; border-radius: 2px; transition: none !important; will-change: width; }
    .pk-player-progress-thumb { position: absolute; right: -7px; top: 50%; transform: translateY(-50%) scale(0); width: 14px; height: 14px; border-radius: 50%; background: #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); transition: transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
    .pk-player-progress-container:hover .pk-player-progress-thumb { transform: translateY(-50%) scale(1); }
    .pk-p-btn { color: #eee; cursor: pointer; display: flex; align-items: center; justify-content: center; width: 50px; height: 40px; border-radius: 4px; transition: all 0.2s ease; position: relative; flex-shrink: 0; }
    .pk-p-btn:hover { color: var(--pk-pri); background: transparent; transform: scale(1.05); }
    .pk-p-btn:active { color: var(--pk-pri); background: transparent; transform: scale(0.95); }
    #pk_p_play, #pk_p_vol, #pk_p_close { display: inline-flex !important; width: 40px !important; height: 40px !important; border-radius: 50% !important; margin: 0 4px !important; padding: 0 !important; box-sizing: border-box !important; justify-content: center !important; align-items: center !important; }
    #pk_p_play svg, #pk_p_vol svg, #pk_p_close svg { margin: 0 !important; padding: 0 !important; }
    #pk_p_play:hover, #pk_p_vol:hover, #pk_p_close:hover { color: #fff !important; filter: brightness(1.5); background: rgba(255, 255, 255, 0.15) !important; transform: none !important; }
    .pk-p-btn svg { width: 24px; height: 24px; fill: currentColor; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3)); }
    #pk_sub_trigger .pk-p-btn svg { width: 21px; height: 21px; }
    #pk_p_full svg { width: 28px; height: 28px; }
    .pk-p-time { color: #ddd; font-size: 13px; font-family: "Segoe UI", Roboto, monospace; min-width: 90px; text-align: center; font-variant-numeric: tabular-nums; margin: 0 5px; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); }
    .pk-p-menu-con { position: relative; display: flex; align-items: center; justify-content: center; height: 40px; cursor: pointer; font-size: 15px; color: #ddd; font-weight: 600; padding: 0; min-width: 50px; transition: color 0.2s; border-radius: 4px; }
    #pk_sub_trigger .pk-p-btn { width: 50px; height: 40px; }
    .pk-p-menu-con:hover { color: var(--pk-pri); background: transparent; }
    .pk-p-pop { position: absolute; bottom: 45px; left: 50%; transform: translateX(-50%); background: rgba(20, 20, 20, 0.9); border-radius: 8px; padding: 6px 0; display: none; flex-direction: column-reverse; min-width: 100px; text-align: center; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.1); z-index: 20; backdrop-filter: blur(10px); }
    .pk-p-pop::after { content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 45px; background: transparent; }
    .pk-p-menu-con:hover .pk-p-pop { display: flex; animation: pkFadeIn 0.2s ease; }
    @keyframes pkFadeIn { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
    .pk-p-item { padding: 8px 16px; color: #ccc; cursor: pointer; font-size: 13px; position: relative; z-index: 2; transition: background 0.1s; text-align: center; display: flex; align-items: center; justify-content: center; }
    .pk-p-item:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
    .pk-p-item.active { color: var(--pk-pri); font-weight: bold; }
    .pk-p-vol-wrap { display: flex; align-items: center; height: 100%; gap: 0px; margin-right: 5px; }
    .pk-p-vol-slider { -webkit-appearance: none; width: 70px; height: 4px; background: rgba(255, 255, 255, 0.3); border-radius: 2px; outline: none; cursor: pointer; transition: height 0.1s; }
    .pk-p-vol-slider:hover { height: 6px; }
    .pk-p-vol-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #fff; cursor: pointer; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); transition: transform 0.1s; }
    .pk-p-vol-slider::-webkit-slider-thumb:hover { transform: scale(1.2); }
    .pk-p-loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none; display: none; }
    .pk-player-box.buffering:not(.pk-is-seeking) .pk-p-loading { display: block; }
    .pk-player-box.pk-is-seeking .pk-p-loading { display: none !important; }
    .pk-p-center-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) translateZ(0); width: 72px; height: 72px; background: rgba(0, 0, 0, 0.35); border-radius: 50%; display: none; align-items: center; justify-content: center; z-index: 36; pointer-events: none; border: 1px solid rgba(255, 255, 255, 0.25);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); backface-visibility: hidden; will-change: transform, opacity; }
    .pk-p-center-play svg { width: 36px; height: 36px; fill: #fff; margin-left: 4px; filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.3)); }
    .pk-player-box.paused.pk-v-started:not(.buffering):not(.pk-is-seeking) .pk-p-center-play { display: flex !important; animation: pkPlayPop 0.35s cubic-bezier(0.2, 0, 0.2, 1) forwards; }
    .pk-player-box.buffering .pk-p-center-play { display: none !important; opacity: 0 !important; }
    .pk-player-box.pk-is-seeking .pk-p-center-play { display: none !important; opacity: 0 !important; }
    @keyframes pkPlayPop { from { opacity: 0; transform: translate(-50%, -50%) scale(0.8) translateZ(0); } to { opacity: 1; transform: translate(-50%, -50%) scale(1) translateZ(0); } }
    .pk-p-seek-indicator { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) translateZ(0); height: 54px; min-width: 190px; background: rgba(0, 0, 0, 0.65); color: #fff; padding: 0 22px; border-radius: 12px; font-size: 22px; font-weight: 300; font-family: "Inter", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", sans-serif; z-index: 50; display: none; align-items: center; justify-content: center; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.15); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4); font-variant-numeric: tabular-nums; pointer-events: none; white-space: nowrap; letter-spacing: 0px; }
    body.pk-dragging { cursor: pointer !important; user-select: none !important; -webkit-user-select: none !important; }
    .pk-player-box.pk-is-seeking .pk-player-progress-thumb { transform: translateY(-50%) scale(1) !important; opacity: 1 !important; transition: none !important; }
    .pk-player-box.pk-is-seeking .pk-player-progress-bg { height: 6px !important; transform: scaleY(1.1) !important; transition: none !important; }
    .pk-player-box.pk-is-seeking .pk-player-progress-filled { transition: none !important; will-change: width; }
    .pk-p-resume-toast { position: absolute; bottom: 85px; left: 24px; background: rgba(28, 28, 28, 0.95); color: #fff; padding: 8px 16px; border-radius: 99px; font-size: 13px; display: flex; align-items: center; gap: 8px; z-index: 100; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.1); animation: pkFadeInUp 0.2s ease; }
    .pk-p-plist-ov { position: absolute; top: 100%; left: 0; right: 0; z-index: 25; display: flex; flex-direction: column; pointer-events: none; }
    .pk-p-plist-strip { height: 84px; background: rgba(20, 20, 20, 0.9); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); display: flex; align-items: center; position: relative; border-top: none; pointer-events: auto; }
    .pk-p-plist-tab { position: absolute !important; bottom: 100%; left: 50%; transform: translateX(-50%); height: 25px; pointer-events: auto !important; z-index: 70; margin-bottom: -1px; }
    .pk-p-plist-tab { align-self: center; position: relative; z-index: 26; color: rgba(255, 255, 255, 0.8); padding: 0 20px; height: 30px; font-size: 12px; font-weight: 600; cursor: pointer; border: none; display: flex; align-items: center; justify-content: center; gap: 4px; background: transparent; margin-bottom: -1px; letter-spacing: 0.5px; transition: opacity 0.3s ease; opacity: 0; }
    .pk-p-plist-tab:hover, .pk-player-box.plist-active .pk-p-plist-tab { opacity: 1; }
    .pk-p-plist-tab:hover, .pk-player-box.plist-active .pk-p-plist-tab, .pk-img-box.plist-active .pk-p-plist-tab { opacity: 1; }
    .pk-p-plist-tab::before { content: ''; position: absolute; inset: 0; z-index: -1; left: -50px; right: -50px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='20' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 20 C 25 20, 25 0, 40 0 H 60 C 75 0, 75 20, 100 20 Z' fill='rgba(20, 20, 20, 0.9)'/%3E%3Cpath d='M0 20 C 25 20, 25 0, 40 0 H 60 C 75 0, 75 20, 100 20' fill='none' stroke='rgba(255,255,255,0.1)' stroke-width='1' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E"); background-size: 100% 100%; background-repeat: no-repeat; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); transform: translateZ(0); backface-visibility: hidden; -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='20' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 20 C 25 20, 25 0, 40 0 H 60 C 75 0, 75 20, 100 20 Z' fill='black'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='20' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 20 C 25 20, 25 0, 40 0 H 60 C 75 0, 75 20, 100 20 Z' fill='black'/%3E%3C/svg%3E"); -webkit-mask-size: 100% 100%; mask-size: 100% 100%; }
    #pk_p_box:fullscreen, #pk_p_box:-webkit-full-screen, #pk_p_box:-moz-full-screen { width: 100vw !important; height: 100vh !important; top: 0 !important; left: 0 !important; transform: none !important; margin: 0 !important; border-radius: 0 !important; overflow: hidden !important; }
    #pk_p_box:fullscreen #pk_video, #pk_p_box:-webkit-full-screen #pk_video, #pk_p_box.full #pk_video, #pk_p_box:fullscreen #pk_p_poster, #pk_p_box:-webkit-full-screen #pk_p_poster, #pk_p_box.full #pk_p_poster { height: 100% !important; bottom: auto !important; top: 0 !important; transform: translateZ(0); }
    #pk_p_box:fullscreen.plist-active #pk_video, #pk_p_box:-webkit-full-screen.plist-active #pk_video, #pk_p_box.full.plist-active #pk_video, #pk_p_box:fullscreen.plist-active #pk_p_poster, #pk_p_box:-webkit-full-screen.plist-active #pk_p_poster, #pk_p_box.full.plist-active #pk_p_poster { height: calc(100% - 84px) !important; }
    #pk_p_box:fullscreen.plist-active .pk-p-side-nav, #pk_p_box:fullscreen.plist-active .pk-p-center-play, #pk_p_box:fullscreen.plist-active .pk-p-seek-indicator, #pk_p_box:-webkit-full-screen.plist-active .pk-p-side-nav, #pk_p_box:-webkit-full-screen.plist-active .pk-p-center-play, #pk_p_box:-webkit-full-screen.plist-active .pk-p-seek-indicator { top: calc(50% - 42px) !important; }
    #pk_p_box:fullscreen #pk_p_plist, #pk_p_box:-webkit-full-screen #pk_p_plist { top: auto !important; bottom: 0 !important; transform: translateY(100%) translateZ(0); backface-visibility: hidden; perspective: 1000px; transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
    #pk_p_box:fullscreen .pk-p-plist-tab, #pk_p_box:-webkit-full-screen .pk-p-plist-tab { bottom: auto !important; top: 0 !important; transform: translateY(-100%) translateZ(0) !important; margin-top: 1px !important; margin-bottom: 0 !important; backface-visibility: hidden; z-index: 100 !important; }
    #pk_p_box:fullscreen.plist-active #pk_p_plist, #pk_p_box:-webkit-full-screen.plist-active #pk_p_plist { transform: translateY(0); }
    #pk_p_box:fullscreen.plist-active .pk-player-controls, #pk_p_box:-webkit-full-screen.plist-active .pk-player-controls { bottom: 84px !important; }
    #pk_p_box:fullscreen.plist-active .pk-p-prog-wrap, #pk_p_box:-webkit-full-screen.plist-active .pk-p-prog-wrap { bottom: 148px !important; }
    .pk-p-plist-tab:hover { color: rgba(255, 255, 255, 0.8); }
    .pk-p-plist-tab:hover::before { opacity: 1; filter: none; }
    .pk-p-plist-tab svg { transition: transform 0.3s; }
    .pk-p-plist-ov.open .pk-p-plist-tab svg { transform: rotate(180deg); }
    .pk-p-plist-strip { height: 110px; background: rgba(20, 20, 20, 0.9); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); display: flex; align-items: center; position: relative; border-top: 1px solid rgba(255, 255, 255, 0.1); }
    .pk-p-plist-scroll { flex: 1; display: flex; overflow-x: auto; gap: 2px; padding: 0 50px; height: 100%; align-items: center; }
    .pk-p-plist-scroll::-webkit-scrollbar { display: none; }
    .pk-p-plist-item { flex-shrink: 0; width: 140px; height: 80px; background: #333; cursor: pointer; position: relative; overflow: hidden; border: 2px solid transparent; border-radius: 4px; will-change: opacity, transform; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
    .pk-p-plist-item.active { border-color: var(--pk-pri); box-shadow: 0 0 10px var(--pk-pri); }
    .pk-p-plist-ph { position:absolute; inset:0; z-index:1; display:flex; align-items:center; justify-content:center; overflow:hidden; background:#3a3a3a; }
    .pk-p-plist-ph img, .pk-p-plist-ph svg { width:100% !important; height:100% !important; max-width:none !important; max-height:none !important; display:block; border-radius:0 !important; }
    .pk-p-plist-ph img { object-fit:cover !important; }
    .pk-p-plist-ph svg { object-fit:fill !important; }
    .pk-p-plist-item > img { position:relative; z-index:2; width:100%; height:100%; object-fit:cover; opacity:1; transition:opacity 0.3s ease-in-out; display:block; }
    .pk-p-plist-item img:error { opacity:0 !important; }
    .pk-p-plist-nav { position: absolute; top: 0; bottom: 0; width: 50px; background: transparent !important; color: rgba(255, 255, 255, 0.7); cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 35; transition: all 0.2s; border: none; }
    .pk-p-plist-nav:hover { background: rgba(255, 255, 255, 0.15) !important; color: #fff; }
    .pk-p-plist-nav.L { left: 0; }
    .pk-p-plist-nav.R { right: 0; }
    .pk-p-plist-nav svg { width: 28px; height: 28px; stroke-width: 3; }
    .pk-p-plist-tip { position: fixed; background: rgba(0, 0, 0, 0.9); color: #fff; padding: 8px 12px; border-radius: 6px; font-size: 12px; pointer-events: none; z-index: 2147483647; max-width: 340px; line-height: 1.4; display: none; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.1); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pk-p-resume-btn { color: #4aa1ff; cursor: pointer; font-weight: bold; text-decoration: none; }
    .pk-p-resume-btn:hover { text-decoration: underline; }
    .pk-p-resume-close { cursor: pointer; color: #888; margin-left: 4px; display: flex; align-items: center; }
    @keyframes pkFadeInUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
    .pk-img-ov { position: fixed; top: 0; left: 0; width: calc(100vw / var(--pk-zoom, 1)); height: calc(100vh / var(--pk-zoom, 1)); zoom: var(--pk-zoom, 1); transform-origin: top left; z-index: 2147483640; background: rgba(0, 0, 0, 0.85); backdrop-filter: blur(5px); display: flex; align-items: center; justify-content: center; outline: none; user-select: none; }
    .pk-img-box { position: absolute !important; top: 10%; left: 50%; transform: translateX(-50%); background: #000; border-radius: 8px; box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); overflow: visible !important; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 90%; max-width: calc(1600px / var(--pk-zoom, 1)); min-width: 480px; height: 80%; transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1), height 0.2s cubic-bezier(0.4, 0, 0.2, 1), border-radius 0.2s, top 0.2s, left 0.2s, transform 0.2s; z-index: 10; box-sizing: border-box; border: none; }
    .pk-img-box.plist-active { height: calc(80% - 84px); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
    .pk-img-box.full { width: 100%; max-width: none; height: 100%; border-radius: 0; top: 0 !important; left: 0 !important; transform: none !important; }
    .pk-img-box.full.plist-active { height: calc(100vh - 84px); }
    .pk-img-obj { flex: 1; width: 100%; height: 100%; min-height: 0; object-fit: contain; cursor: grab; transition: transform 0.1s linear; transform-origin: center center; }
    #pk_img_plist { position: absolute; top: 100%; left: 0; right: 0; z-index: 25; height: 84px; display: flex; flex-direction: column; pointer-events: none; }
    #pk_img_plist .pk-p-plist-strip { pointer-events: none; opacity: 0; transition: opacity 0.2s; }
    .pk-img-box.plist-active #pk_img_plist .pk-p-plist-strip { display: flex !important; opacity: 1 !important; pointer-events: auto !important; animation: pkFadeInOnly 0.2s ease; }
    #pk_img_plist_tab { pointer-events: auto !important; z-index: 30; cursor: pointer; }
    #pk_img_plist_scroll { pointer-events: auto !important; }
    .pk-img-obj:active { cursor: grabbing; }
    .pk-img-bar { position: absolute; top: 0; left: 0; right: 0; height: 50px; background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent); display: flex; align-items: center; justify-content: space-between; padding: 0 20px; z-index: 20; opacity: 0; transition: opacity 0.3s; pointer-events: none; }
    .pk-img-bar > * { pointer-events: auto; }
    .pk-img-box:hover .pk-img-bar { opacity: 1; }
    .pk-img-title { color: #fff; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: bold; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); }
    .pk-img-actions { display: flex; gap: 10px; }
    .pk-img-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 50px; height: 100px; display: flex; align-items: center; justify-content: center; color: rgba(255, 255, 255, 0.5); cursor: pointer; z-index: 5; transition: background 0.2s, color 0.2s; border-radius: 4px; opacity: 0; }
    .pk-img-box:hover .pk-img-nav { opacity: 1; }
    .pk-img-nav:hover { background: rgba(0, 0, 0, 0.3); color: #fff; }
    .pk-img-prev { left: 10px; }
    .pk-img-next { right: 10px; }
    .pk-img-nav svg { width: 36px; height: 36px; stroke-width: 3; }
    .pk-img-btn { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; color: #eee; cursor: pointer; border-radius: 50%; background: transparent; transition: all 0.2s ease; flex-shrink: 0; }
    .pk-img-btn:not(#pk_img_close):hover { color: var(--pk-pri) !important; background: transparent !important; }
    #pk_img_close:hover { background: rgba(255, 255, 255, 0.15) !important; color: #fff !important; }
    .pk-img-btn svg { width: 20px; height: 20px; }
    .pk-tag-default { margin-top: -1px; margin-left: 10px; flex-shrink: 0; min-width: 32px; box-sizing: border-box; font-size: 10px; height: 18px; padding: 1px 6px 0 6px !important; border-radius: 20px; font-weight: normal; white-space: nowrap; cursor: default; display: inline-flex; align-items: center; justify-content: center; user-select: none; background-color: transparent; color: #999; border: 1px solid #ccc; }
    .pk-ov.pk-dark .pk-tag-default { background-color: transparent; color: #888; border-color: #555; text-shadow: none; }
    #pk-scan-dup, #pk-btn-exit { align-items: center !important; margin: 0 !important; flex-shrink: 0 !important; }
    @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
    .pk-status-dot::after { content: ''; position: absolute; top: 8px; right: 8px; width: 8px; height: 8px; background: #1a5eff; border-radius: 50%; border: 2px solid var(--pk-bg); box-shadow: 0 0 5px rgba(26, 94, 255, 0.5); animation: pk-pulse 2s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite; z-index: 11; pointer-events: none; }
    @keyframes pk-pulse { 0% { transform: scale(0.9); opacity: 0.6; box-shadow: 0 0 0 0 rgba(26, 94, 255, 0.7); } 50% { transform: scale(1.1); opacity: 1; box-shadow: 0 0 0 4px rgba(26, 94, 255, 0); } 100% { transform: scale(0.9); opacity: 0.6; box-shadow: 0 0 0 0 rgba(26, 94, 255, 0); } }
    .pk-tooltip { position: fixed; z-index: 2147483647 !important; background: var(--pk-tip-bg); color: var(--pk-tip-fg); border: 1px solid var(--pk-tip-bd); padding: 6px 12px; border-radius: 8px; font-size: 12px; font-weight: 500; line-height: 1.4; pointer-events: none; opacity: 0; transform: translateY(5px) scale(0.95); transition: opacity 0.15s ease, transform 0.15s ease; box-shadow: 0 4px 16px var(--pk-tip-sd); backdrop-filter: blur(4px); max-width: 300px; white-space: pre-wrap; }
    .pk-tooltip.show { opacity: 1; transform: translateY(0) scale(1); }
    .pk-drag-ghost { position: fixed; z-index: 2147483647; background: var(--pk-bg); border: 1px solid var(--pk-pri); color: var(--pk-fg); padding: 8px 12px; border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); pointer-events: none; font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; opacity: 0.9; transform: translate(15px, 15px); }
    .pk-row.pk-drop-target { background: var(--pk-sel-bg) !important; outline: 2px dashed var(--pk-pri); outline-offset: -2px; z-index: 10; }
    #pk-crumb span.pk-drop-target { background: var(--pk-sel-bg) !important; color: var(--pk-pri) !important; outline: 2px dashed var(--pk-pri); outline-offset: -2px; border-radius: 4px; z-index: 10; opacity: 1 !important; }
    .pk-crumb-item.pk-drop-target { background: var(--pk-sel-bg) !important; color: var(--pk-pri) !important; outline: 2px dashed var(--pk-pri); outline-offset: -2px; }
    .pk-empty { position: absolute; inset: 0; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; padding-bottom: 10vh; z-index: 1; opacity: 0; animation: pkFadeInOnly 0.4s ease forwards; }
    .pk-empty svg { width: 35vmin; max-width: 220px; height: auto; margin-bottom: 3vmin; filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.05)); }
    .pk-empty-txt { font-size: clamp(13px, 3vmin, 16px); color: #94a3b8; font-weight: 500; letter-spacing: 1px; }
    .pk-ov.pk-dark .pk-empty-txt { color: #666e75; }
    .pk-selection-box { position: fixed; inset: 0 auto auto 0; background: rgba(0, 103, 192, 0.1); border: 1px solid rgba(0, 103, 192, 0.4); z-index: 2147483647 !important; pointer-events: none; display: none; will-change: transform; box-sizing: border-box; }
    .pk-p-vol-indicator { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.75); color: #fff; padding: 12px 24px; border-radius: 12px; font-size: 20px; font-weight: bold; z-index: 120; display: none; align-items: center; gap: 10px; backdrop-filter: blur(8px); border: 1px solid rgba(255,255,255,0.15); pointer-events: none; font-family: "Inter", sans-serif; }
    .pk-p-vol-indicator svg { fill: #fff !important; width: 100%; height: 100%; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3)); }
    .pk-is-vol-active .pk-p-center-play, .pk-is-vol-active .pk-p-loading { display: none !important; opacity: 0 !important; }
    .pk-input-err-msg { color: #ff4d4f; font-size: 12px; margin-top: 8px; min-height: 18px; visibility: hidden; transition: opacity 0.2s; }
    .pk-ana-select { position: relative; width: 85px; flex-shrink: 0; }
    #an_val_min::-webkit-outer-spin-button, #an_val_min::-webkit-inner-spin-button, #an_val_max::-webkit-outer-spin-button, #an_val_max::-webkit-inner-spin-button, #sc_val_min::-webkit-outer-spin-button, #sc_val_min::-webkit-inner-spin-button, #sc_val_max::-webkit-outer-spin-button, #sc_val_max::-webkit-inner-spin-button, #sh_mod_cnt_val::-webkit-outer-spin-button, #sh_mod_cnt_val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
    #an_val_min, #an_val_max, #sc_val_min, #sc_val_max, #sh_mod_cnt_val { -moz-appearance: textfield; }
    #an_val_min, #an_val_max { padding-right:32px !important; }
    .pk-num-ctrl { position: absolute; right: 8px; top: 4px; bottom: 4px; display: flex; flex-direction: column; width: 24px; gap: 1px; z-index: 5; }
    .pk-num-btn { flex: 1; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--pk-icon-c); border-radius: 3px; transition: all 0.1s; }
    .pk-num-btn:hover { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-num-btn:active { transform: scale(0.9); }
    .pk-num-btn svg { width: 14px; height: 14px; stroke-width: 3; }
    .pk-ana-trigger { width: 100%; height: 42px; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; border: 2px solid var(--pk-bd); border-radius: 8px; background: var(--pk-bg); color: var(--pk-fg); cursor: pointer; font-weight: 700; font-size: 14px; transition: border-color 0.2s; box-sizing: border-box; }
    .pk-ana-trigger:hover { border-color: var(--pk-pri); }
    .pk-ana-menu { position: absolute; top: 100%; left: 0; right: 0; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; margin-top: 6px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); z-index: 10010; display: none; overflow: hidden; }
    .pk-ana-item { padding: 10px 12px; cursor: pointer; font-size: 13px; color: var(--pk-fg); transition: background 0.1s; }
    .pk-ana-item:hover { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-ana-item.act { color: var(--pk-pri); font-weight: 700; background: rgba(0, 103, 192, 0.05); }
    .pk-msg-toast { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: var(--pk-toast-bg); color: var(--pk-toast-fg); padding: 12px 28px; border-radius: 12px; z-index: 20000; font-size: 14px; font-weight: 600; pointer-events: none; opacity: 0; transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1); text-align: left; display: flex; align-items: center; justify-content: center; gap: 12px; backdrop-filter: blur(8px); border: 1px solid var(--pk-toast-bd); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); }
    .pk-msg-toast.show { opacity: 1; transform: translate(-50%, -60%); }
    .pk-crumb-sep { width: 22px; height: 22px; margin: 0 2px; cursor: pointer; border-radius: 4px; color: #aaa; transition: all 0.2s; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .pk-crumb-sep:hover, .pk-crumb-sep.pk-active { background: var(--pk-hl); color: currentColor; }
    .pk-crumb-sep svg { width: 14px; height: 14px; stroke-width: 3.5; transition: transform 0.2s ease; }
    .pk-crumb-pop { position: fixed; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35); z-index: 2147483647 !important; min-width: 180px; max-width: 320px; max-height: 50vh; overflow-y: auto; padding: 6px 0; opacity: 0; pointer-events: none; transition: opacity 0.1s ease; border-top: 1px solid rgba(255, 255, 255, 0.05); }
    .pk-crumb-pop.pk-show { opacity: 1; pointer-events: auto; }
    .pk-crumb-item { padding: 8px 12px; font-size: 13px; line-height: 1.5; cursor: pointer; display: flex; align-items: center; justify-content: flex-start; gap: 8px; color: var(--pk-fg); }
    .pk-crumb-item:hover { background: var(--pk-hl); }
    .pk-crumb-item.pk-moving { opacity: 0.4; filter: grayscale(1); cursor: wait; pointer-events: none; }
    .pk-crumb-item svg { flex-shrink: 0; }
    .pk-crumb-name-wrap { display: inline-flex; align-items: center; justify-content: flex-start; min-width: 0; max-width: calc(100% - 26px); flex: 0 1 auto; overflow: hidden; }
    .pk-crumb-name { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; padding-bottom: 2px; margin-bottom: -2px; }
    .pk-crumb-name-wrap .pk-tag-default { flex: 0 0 auto !important; margin-left: 6px; min-width: auto; height: 16px; padding: 0 6px; line-height: 1 !important; display: inline-flex; align-items: center; justify-content: center; white-space: nowrap !important; overflow: visible !important; text-overflow: clip !important; padding-bottom: 0 !important; margin-bottom: 0 !important; }
    @keyframes pkFadeInOnly { from { opacity: 0; } to { opacity: 1; } }
    .pk-share-modal { width: 540px !important; box-sizing: border-box; display: flex; flex-direction: column; gap: 20px; padding: 0 !important; overflow: visible !important; }
    .pk-s-sec { display: flex; flex-direction: column; gap: 12px; padding: 0 28px; box-sizing: border-box; }
    .pk-detail-cancel-btn { cursor: pointer; color: var(--pk-pri); font-size: 15px; padding: 8px 12px; border-radius: 6px; transition: background 0.2s; margin-left: -12px; }
    .pk-detail-cancel-btn:hover { background: var(--pk-hl); }
    #pk_edit_pwd_cancel, #pk_edit_phrase_cancel, #pk_mod_cnt_cancel { display:inline-flex; align-items:center; justify-content:center; min-width:64px; height:32px; padding:0 14px; border-radius:8px; color:#666; transition:background 0.18s ease, box-shadow 0.18s ease, color 0.18s ease; }
    .pk-modal-ov:not(.pk-dark) #pk_edit_pwd_cancel:hover, .pk-modal-ov:not(.pk-dark) #pk_edit_phrase_cancel:hover, .pk-modal-ov:not(.pk-dark) #pk_mod_cnt_cancel:hover { background:rgba(0,0,0,0.06); box-shadow:0 2px 8px rgba(0,0,0,0.08); color:#444; }
    .pk-modal-ov.pk-dark #pk_edit_pwd_cancel:hover, .pk-modal-ov.pk-dark #pk_edit_phrase_cancel:hover, .pk-modal-ov.pk-dark #pk_mod_cnt_cancel:hover { background:rgba(255,255,255,0.08); box-shadow:0 2px 8px rgba(0,0,0,0.22); color:#ddd; }
    .pk-share-stat-box { display: flex; background: var(--pk-hl); border-radius: 10px; padding: 10px 0; margin-bottom: 18px; border: 1px solid var(--pk-bd); }
    .pk-share-stat-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; }
    .pk-share-stat-item:first-child::after { content: ''; position: absolute; right: 0; top: 25%; bottom: 25%; width: 1px; background: var(--pk-bd); }
    .pk-share-stat-val { font-size: 20px; font-weight: 700; color: var(--pk-fg); line-height: 1.1; font-family: "Inter", system-ui, sans-serif; }
    .pk-share-stat-lbl { font-size: 11px; color: #888; margin-top: 2px; font-weight: 500; }
    .pk-s-lbl { font-size: 13px; font-weight: 700; color: var(--pk-fg); opacity: 0.6; text-transform: uppercase; letter-spacing: 0.5px; }
    .pk-s-tabs { display: flex; background: var(--pk-hl); border-radius: 8px; padding: 4px; gap: 4px; border: 1px solid var(--pk-bd); }
    .pk-s-tab { flex: 1; height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--pk-fg); font-size: 14px; border-radius: 6px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); opacity: 0.7; }
    .pk-s-tab.act { background: var(--pk-bg); color: var(--pk-pri); opacity: 1; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); font-weight: 700; }
    .pk-s-opts { display: flex; flex-flow: row nowrap; gap: 20px; align-items: center; justify-content: flex-start; }
    .pk-s-opt { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; color: var(--pk-fg); white-space: nowrap; flex-shrink: 0; }
    .pk-s-lbl { font-size: 13px; font-weight: 600; color: var(--pk-fg); opacity: 0.8; }
    .pk-s-tabs { display: flex; background: var(--pk-hl); border-radius: 6px; padding: 3px; gap: 2px; border: 1px solid var(--pk-bd); }
    .pk-s-tab { flex: 1; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--pk-fg); font-size: 13px; border-radius: 4px; transition: all 0.2s; opacity: 0.7; }
    .pk-s-tab.act { background: var(--pk-bg); color: var(--pk-pri); opacity: 1; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); font-weight: bold; }
    .pk-s-opts { display: flex; flex-wrap: nowrap; gap: 12px; align-items: center; justify-content: space-between; }
    .pk-s-opt { display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 13px; color: var(--pk-fg); white-space: nowrap; flex-shrink: 0; }
    .pk-s-opt input[type="radio"] { appearance: none; width: 16px; height: 16px; border: 1px solid var(--pk-icon-c); border-radius: 50%; position: relative; cursor: pointer; margin: 0; background: var(--pk-bg); transition: all 0.2s; }
    .pk-s-opt input:checked { border-color: var(--pk-pri); border-width: 5px; }
    .pk-s-input { flex: 1; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 4px; color: var(--pk-fg); padding: 5px 10px; font-size: 13px; outline: none; transition: border-color 0.2s; min-width: 0; }
    .pk-s-input:focus { border-color: var(--pk-pri); }
    .pk-s-input:disabled { opacity: 0.3; cursor: not-allowed; background: var(--pk-hl); }
    .pk-share-res-val { font-family: "SF Mono", "Consolas", monospace; font-weight: 600; color: var(--pk-pri) !important; }
    .pk-cal-pop { position: absolute; background: var(--pk-bg); color: var(--pk-fg); border: 1px solid var(--pk-bd); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); border-radius: 8px; z-index: 10005; display: flex; font-size: 13px; overflow: hidden; animation: pkFadeIn 0.2s; user-select: none; }
    .pk-cal-side { width: 110px; background: var(--pk-hl); border-right: 1px solid var(--pk-bd); display: flex; flex-direction: column; padding: 8px 0; }
    .pk-cal-side-item { padding: 8px 16px; cursor: pointer; color: var(--pk-fg); transition: background 0.2s; }
    .pk-cal-side-item:hover { background: rgba(0, 0, 0, 0.05); }
    .pk-cal-side-item.active { color: var(--pk-pri); font-weight: bold; background: var(--pk-bg); }
    .pk-cal-main { width: 280px; padding: 10px; display: flex; flex-direction: column; }
    .pk-cal-hd { display: flex; justify-content: space-between; align-items: center; padding: 0 8px 10px 8px; border-bottom: 1px solid var(--pk-bd); }
    .pk-cal-nav-btn { cursor: pointer; padding: 4px; border-radius: 4px; color: #888; }
    .pk-cal-nav-btn:hover { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-top: 10px; }
    .pk-cal-th { text-align: center; color: #888; font-size: 12px; padding-bottom: 6px; }
    .pk-cal-td { height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 4px; cursor: pointer; transition: all 0.2s; position: relative; color: var(--pk-fg); }
    .pk-cal-td:hover:not(.disabled):not(.selected) { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-cal-td.selected { background: var(--pk-pri); color: #fff; font-weight: bold; }
    .pk-cal-td.disabled { color: #888; cursor: not-allowed; opacity: 0.7; }
    .pk-share-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; padding: 16px 24px 24px 24px; border-top: 1px solid var(--pk-bd); background: transparent; }
    .pk-btn-quiet-red { color: #d93025; font-size: 14px; font-weight: 500; cursor: pointer; padding: 8px 12px; border-radius: 6px; transition: background 0.2s; margin-left: -8px; }
    .pk-btn-quiet-red:hover { background: rgba(217, 48, 37, 0.08); }
    .pk-btn-primary-action { background: var(--pk-pri); color: #fff; border: none; height: 38px; padding: 0 20px; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
    .pk-btn-primary-action:hover { filter: brightness(1.08); box-shadow: 0 2px 8px rgba(0, 103, 192, 0.25); }
    .pk-cal-td.today::after { content: ''; position: absolute; bottom: 4px; width: 4px; height: 4px; background: currentColor; border-radius: 50%; opacity: 0.5; }
    .pk-share-icon-wrap { position: relative; width: 30px; height: 30px; margin-right: 12px; flex-shrink: 0; transition: transform 0.2s ease; transform-origin: center center; display:flex; align-items:center; justify-content:center; line-height:0; overflow:visible; }
    .pk-share-icon-wrap > svg, .pk-share-icon-wrap > img { width: 100%; height: 100%; display: block; flex-shrink: 0; margin:0 !important; }
    .pk-share-icon-wrap > div:not(.pk-share-lock) { width:100%; height:100%; display:flex; align-items:center; justify-content:center; margin:0 !important; flex:0 0 100%; }
    .pk-share-icon-wrap > div:not(.pk-share-lock) > svg, .pk-share-icon-wrap > svg { width:100%; height:100%; display:block !important; margin:0 !important; transform:scale(0.94) !important; transform-origin:center center !important; }
    .pk-share-icon-disabled { transform:none !important; }
    .pk-share-lock { position: absolute; bottom: -2px; right: -4px; width: 14px; height: 14px; background: transparent; display: flex; align-items: center; justify-content: center; z-index: 10; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3)); border: none; box-shadow: none; pointer-events: none; }
    .pk-ov.pk-dark .pk-share-lock { filter: drop-shadow(0 0 2px rgba(0, 0, 0, 1)) drop-shadow(0 2px 5px rgba(0, 0, 0, 1)); }
    .pk-name { display: flex !important; align-items: center; min-width: 0; width: 100%; overflow: hidden; }
    .pk-name-txt { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    .pk-select-item { padding: 10px 12px; border-radius: 5px; cursor: pointer; color: var(--pk-fg); font-size: 14px; transition: background 0.1s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pk-select-item:hover { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-select-item.act { background: rgba(0, 103, 192, 0.1); color: var(--pk-pri); font-weight: 700; }
    .pk-select-label { position: absolute; top: 0; transform: translate3d(0, -50%, 0); -webkit-transform: translate3d(0, -50%, 0); backface-visibility: hidden; -webkit-backface-visibility: hidden; will-change: transform; left: 10px; background: var(--pk-bg); padding: 0 5px; font-size: 11px; color: var(--pk-pri); font-weight: bold; pointer-events: none; z-index: 10; line-height: 1; pointer-events: none; }
    .pk-field input, .pk-field select, .pk-share-modal input, .pk-ov input { transform: translateZ(0); will-change: transform; }
    .pk-maximized { position: fixed !important; width: 100% !important; height: 100% !important; max-width: none !important; top: 0 !important; left: 0 !important; border-radius: 0 !important; border: none !important; z-index: 2147483647 !important; }
    .pk-maximized .pk-sidebar { width: 190px !important; align-items: flex-start !important; padding: 20px 10px !important; }
    .pk-maximized .pk-nav-btn { width: 100% !important; justify-content: flex-start !important; padding: 0 15px !important; gap: 10px; height: 54px !important; border-radius: 8px !important; }
    .pk-nav-btn span { display: none; font-size: 14px; font-weight: 600; white-space: nowrap; }
    .pk-maximized .pk-nav-btn span { display: inline-block; }
    .pk-maximized #pk-quota-panel { align-items: flex-start !important; padding: 0 10px !important; margin-bottom: 6px !important; opacity: 0.9 !important; gap: 4px !important; transition: none !important; }
    .pk-cloud-area { width: 100%; height: 180px; background: #f1f3f5; border: none; border-radius: 8px; padding: 15px; font-size: 14px; line-height: 1.6; color: #1a1a1a; resize: none; outline: none; font-family: inherit; cursor: auto; }
    .pk-dark .pk-cloud-area { background: #2d2d2d !important; color: #f5f5f5 !important; caret-color: #fff !important; }
    .pk-dark .pk-cloud-area::placeholder { color: #666 !important; }
    .pk-cloud-area::placeholder { color: #adb5bd; }
    #pk_cloud_torrent_trigger:hover, #pk_cloud_change_dir:hover { text-decoration: underline; }
    .pk-maximized #pk-quota-bar-box { width: 100% !important; height: 6px !important; transition: none !important; border-radius: 3px !important; }
    .pk-maximized #pk-quota-txt { font-size: 12px !important; transform: none !important; opacity: 1; font-weight: normal !important; white-space: nowrap; }
    .pk-maximized .pk-hd { height: 60px !important; padding: 0 20px !important; }
    .pk-maximized .pk-tt { font-size: 24px !important; }
    .pk-maximized .pk-tt svg { width: 32px !important; height: 32px !important; }
    .pk-maximized .pk-tb { height: 60px !important; padding: 0 16px !important; gap: 10px !important; }
    .pk-maximized .pk-btn { height: 40px !important; font-size: 15px !important; padding: 0 16px !important; }
    .pk-btn svg { width: auto; height: auto; }
    #pk-down svg { width: 16px !important; height: 16px !important; display: block !important; transform: none !important; transform-origin: center center !important; shape-rendering: geometricPrecision; }
    #pk-theme svg, #pk-maximize svg { width: 16px !important; height: 16px !important; }
    #pk-help svg { width: 19px !important; height: 19px !important; }
    #pk-close svg { width: 19px !important; height: 19px !important; }
    .pk-maximized #pk-theme svg, .pk-maximized #pk-maximize svg { width: 24px !important; height: 24px !important; }
    .pk-maximized #pk-help svg { width: 26px !important; height: 29px !important; }
    .pk-maximized #pk-close svg { width: 28px !important; height: 28px !important; }
    .pk-maximized .pk-grid-hd { height: 50px !important; font-size: 15px !important; padding: 0 26px 0 20px !important; }
    .pk-maximized:not(.pk-grid-view) .pk-row, .pk-maximized:not(.pk-grid-view) .pk-group-hd { height: 60px !important; font-size: 16px !important; padding: 0 20px !important; }
    .pk-maximized:not(.pk-grid-view) .pk-group-hd { border-top-width: 10px !important; border-bottom-width: 10px !important; }
    .pk-maximized.pk-grid-view .pk-group-hd { height: 60px !important; font-size: 16px !important; padding: 0 20px !important; border-top-width: 10px !important; border-bottom-width: 10px !important; box-sizing: border-box !important; align-items: center !important; }
    .pk-maximized.pk-grid-view .pk-group-hd > div { height: 100% !important; display: flex !important; align-items: center !important; }
    .pk-maximized.pk-grid-view .pk-group-hd .pk-name { height: 100% !important; display: flex !important; align-items: center !important; min-height: 0 !important; }
    .pk-maximized.pk-grid-view .pk-group-hd .pk-name .pk-name-txt, .pk-maximized.pk-grid-view .pk-group-hd .pk-name span { display: inline-flex !important; align-items: center !important; height: 100% !important; line-height: 1 !important; padding-top: 0 !important; padding-bottom: 0 !important; margin-top: 0 !important; }
    .pk-maximized:not(.pk-grid-view) .pk-name svg { width: 60px !important; height: 60px !important; margin-right: 20px !important; }
    .pk-maximized:not(.pk-grid-view) .pk-row .pk-name .pk-name-txt { white-space: normal !important; display: -webkit-box !important; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden !important; text-overflow: ellipsis !important; line-height: 1.35 !important; word-break: break-all !important; margin-top: 0 !important; }
    .pk-maximized.pk-grid-view .pk-row { padding: 0 !important; }
    .pk-max-icon-box { position: relative !important; width: 50px !important; height: 50px !important; margin-right: 20px !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; flex-shrink: 0 !important; vertical-align: middle !important; overflow: visible !important; }
    .pk-placeholder-icon { position: absolute !important; inset: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; z-index: 1 !important; transition: opacity 0.2s, visibility 0.2s; }
    .pk-placeholder-icon svg { width: 44px !important; height: 44px !important; object-fit: contain !important; }
    .pk-max-icon-box .pk-max-thumb { position: absolute !important; top: 0 !important; left: 0 !important; width: 50px !important; height: 50px !important; object-fit: cover !important; border-radius: 4px !important; opacity: 0; z-index: 2 !important; transition: opacity 0.25s ease-in-out; background: transparent; }
    .pk-maximized .pk-share-icon-wrap { width: 50px !important; height: 50px !important; margin-right: 20px !important; background: transparent !important; overflow: visible !important; }
    .pk-maximized .pk-row:has(.pk-max-thumb) .pk-name-txt { padding-left: 0 !important; }
    .pk-maximized .pk-nav-btn svg { width: 28px !important; height: 28px !important; }
    body:not(.pk-body-max):has(.pk-modal-ov) #pk-launch { filter: grayscale(1) brightness(0.6) !important; opacity: 0.5 !important; pointer-events: none !important; cursor: not-allowed !important; transition: filter 0.3s, opacity 0.3s; }
    body:has(.pk-ov:not([style*="display: none"])), body:has(.pk-img-ov), body:has(#pk-player-ov) { #pk-launch { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; } }
    .pk-maximized .pk-search input { height: 40px !important; font-size: 15px !important; padding: 0 70px 0 15px !important; }
    .pk-maximized #pk-search-btn { width: 18px !important; height: 18px !important; right: 14px !important; }
    .pk-maximized .pk-search-clear { width: 24px !important; height: 24px !important; right: 40px !important; }
    .pk-maximized input[type="checkbox"] { width: 18px !important; height: 18px !important; transform: none !important; margin: 0 8px 0 0 !important; }
    .pk-maximized .pk-star-icon, .pk-maximized .pk-star-toggle, .pk-maximized .pk-col[data-k="starred"] svg { width: 16px !important; height: 16px !important; }
    .pk-maximized .pk-view-switch { padding: 4px !important; }
    .pk-maximized .pk-view-btn { width: 38px !important; height: 32px !important; }
    .pk-maximized .pk-grid-hd { height: 58px !important; }
    .pk-maximized .pk-grid-hd.pk-grid-view-hd { padding: 0 24px 0 20px !important; gap: 14px !important; overflow: visible !important; } .pk-maximized .pk-grid-check-tools { padding-left: 5px !important; box-sizing: border-box !important; }
    .pk-maximized .pk-grid-sort-trigger { height: 36px !important; font-size: 13px !important; padding: 0 14px !important; min-width: 122px !important; }
    .pk-maximized .pk-grid-view .pk-row { border-radius: 22px !important; }
    .pk-maximized .pk-grid-card-body { padding: 12px 12px 14px !important; gap: 12px !important; }
    .pk-maximized .pk-gv-check { top: 18px !important; left: 18px !important; width: 22px !important; height: 22px !important; }
    .pk-maximized .pk-gv-more { top: 16px !important; right: 16px !important; width: 28px !important; height: 28px !important; }
    .pk-maximized .pk-gv-cover { height: calc(100% - 86px) !important; min-height: 180px !important; border-radius: 18px !important; }
    .pk-maximized .pk-gv-cover .pk-gv-icon svg, .pk-maximized .pk-gv-cover .pk-gv-icon img { width: 122px !important; height: 122px !important; max-width: 122px !important; max-height: 122px !important; }
    .pk-maximized .pk-gv-folder-shell { width: 216px !important; max-width: 216px !important; height: 152px !important; min-height: 152px !important; margin-top: 12px !important; transform: translateY(12px) !important; }
    .pk-maximized .pk-gv-folder-back { top: 19px !important; border-radius: 12px !important; }
    .pk-maximized .pk-gv-folder-tab { top: 6px !important; left: 17px !important; width: 68px !important; height: 25px !important; border-radius: 10px 10px 0 0 !important; }
    .pk-maximized .pk-gv-folder-preview { top: 0 !important; left: 17px !important; right: 17px !important; bottom: 25px !important; border-radius: 10px !important; }
    .pk-maximized .pk-gv-folder-fallback, .pk-maximized .pk-gv-folder-fallback svg { width: 116px !important; height: 116px !important; max-width: 116px !important; max-height: 116px !important; }
    .pk-maximized .pk-gv-folder-front { height: 90px !important; border-radius: 12px !important; }
    .pk-maximized .pk-gv-info { grid-template-rows:22px 18px !important; row-gap:4px !important; align-self:stretch !important; width:100% !important; min-width:0 !important; height:44px !important; min-height:44px !important; padding:0 2px !important; text-align:left !important; }
    .pk-maximized.pk-grid-view .pk-name .pk-name-txt { font-size:16px !important; line-height:22px !important; text-align:left !important; }
    .pk-maximized.pk-grid-view .pk-name { display:flex !important; align-items:center !important; justify-content:flex-start !important; align-self:stretch !important; width:100% !important; min-width:0 !important; height:22px !important; overflow:hidden !important; text-align:left !important; }
    .pk-maximized.pk-grid-view .pk-share-icon-wrap { width: 40px !important; height: 40px !important; margin-right: 16px !important; position: relative !important; display: block !important; overflow: visible !important; }
    .pk-maximized.pk-grid-view .pk-share-icon-wrap { width: 50px !important; height: 50px !important; margin-right: 20px !important; display: flex !important; align-items: center !important; justify-content: center !important; transform: none !important; overflow: visible !important; }
    .pk-maximized.pk-grid-view .pk-share-icon-wrap > svg { width: 100% !important; height: 100% !important; min-width: 100% !important; min-height: 100% !important; transform: scale(1.25) !important; transform-origin: center center !important; margin: 0 !important; }
    .pk-maximized:not(.pk-grid-view) .pk-name { display:flex !important; align-items:center !important; min-width:0 !important; width:100% !important; height:auto !important; overflow:visible !important; text-align:left !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-icon-wrap { position:relative !important; width:50px !important; height:50px !important; min-width:50px !important; min-height:50px !important; margin-right:20px !important; display:flex !important; align-items:center !important; justify-content:center !important; flex:0 0 50px !important; transform:none !important; overflow:visible !important; background:transparent !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-icon-wrap > svg { width:100% !important; height:100% !important; min-width:100% !important; min-height:100% !important; margin:0 !important; transform:scale(1.16) !important; transform-origin:center center !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-icon-wrap > div:not(.pk-share-lock) { width:100% !important; height:100% !important; display:flex !important; align-items:center !important; justify-content:center !important; transform:none !important; margin:0 !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-icon-wrap > div:not(.pk-share-lock) > svg { width:100% !important; height:100% !important; min-width:100% !important; min-height:100% !important; transform:scale(1.16) !important; transform-origin:center center !important; margin:0 !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-icon-wrap img { width:100% !important; height:100% !important; min-width:100% !important; min-height:100% !important; object-fit:contain !important; margin:0 !important; transform:none !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-lock { width:18px !important; height:18px !important; bottom:-2px !important; right:-6px !important; z-index:15 !important; }
    .pk-maximized:not(.pk-grid-view) .pk-share-lock svg { width:100% !important; height:100% !important; min-width:100% !important; min-height:100% !important; margin:0 !important; transform:none !important; display:block !important; color:#fff !important; }
    .pk-maximized .pk-row > div { font-size: 16px !important; font-weight: normal !important; }
    .pk-maximized .pk-row div[style*="font-size:12px"], .pk-maximized .pk-row div[style*="font-size: 12px"] { font-size: 15px !important; font-weight: 400 !important; opacity: 0.9; }
    .pk-maximized .pk-row div[style*="tabular-nums"] { font-weight: 400 !important; }
    .pk-maximized .pk-ft { height: 50px !important; font-size: 15px !important; padding: 0 20px !important; }
    .pk-maximized .pk-stat { font-size: 15px !important; }
    .pk-maximized .pk-ft .pk-btn { height: 36px !important; font-size: 15px !important; }
    .pk-maximized #pk-filter-cat-label { height: 40px !important; font-size: 15px !important; padding: 0 16px !important; }
    .pk-maximized #pk-filter-exts-wrap { height: 40px !important; }
    .pk-maximized .pk-f-ext { font-size: 15px !important; padding: 6px 12px !important; }
    .pk-maximized #pk-filter-exit-btn { height: 40px !important; font-size: 15px !important; padding: 0 20px !important; }
    .pk-maximized .pk-bl-area { font-size: 15px !important; line-height: 1.6 !important; }
    .pk-maximized #pk-crumb span { font-size: 16px !important; display: inline-flex !important; align-items: center !important; height: 32px !important; padding: 0 8px !important; border-radius: 4px !important; margin: auto 2px !important; } .pk-maximized #pk-crumb div span { font-size: 18px !important; } .pk-maximized #pk-crumb div span[style*="font-size:11px"], .pk-maximized #pk-crumb div span[style*="font-size: 11px"] { font-size: 14px !important; margin-left: 12px !important; } .pk-maximized #pk-crumb svg { width: 18px !important; height: 18px !important; vertical-align: middle !important; margin-top: -1px !important; }
    .pk-maximized .pk-crumb-sep { width: 26px !important; height: 26px !important; margin: auto 4px !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; } .pk-maximized .pk-crumb-sep svg { width: 20px !important; height: 20px !important; }
    .pk-custom-select { position: relative; width: 100%; }
    .pk-select-trigger { display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 15px; border: 2px solid var(--pk-bd); border-radius: 8px; background: var(--pk-bg); color: var(--pk-fg); font-size: 14px; font-weight: 600; cursor: pointer; box-sizing: border-box; transition: all 0.2s; }
    .pk-select-trigger:hover { border-color: var(--pk-pri); }
    .pk-select-trigger svg { width: 16px !important; height: 16px !important; min-width: 16px; min-height: 16px; color: #999; flex-shrink: 0; }
    .pk-dropdown-wrap { position: relative; display: inline-flex; height: 32px; align-items: center; }
    .pk-dropdown-menu { position: absolute; top: 100%; right: 0; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); display: none; z-index: 10010; min-width: 140px; padding: 4px 0; margin-top: 6px; flex-direction: column; overflow: hidden; }
    .pk-dropdown-item { padding: 10px 16px; display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--pk-fg); font-size: 13px; transition: background 0.1s; white-space: nowrap; }
    .pk-dropdown-item:hover { background: var(--pk-hl); color: var(--pk-pri); }
    .pk-pop-max { width: 170px !important; min-width: 170px !important; padding: 6px 0 !important; border-radius: 10px !important; box-shadow: 0 8px 30px rgba(0,0,0,0.3) !important; }
    .pk-pop-max .pk-dropdown-item { padding: 12px 15px !important; font-size: 15px !important; gap: 10px !important; font-weight: 600 !important; }
    .pk-pop-max .pk-dropdown-item svg { width: 22px !important; height: 22px !important; }
    .pk-btn-arrow { margin-left: 2px; opacity: 0.6; transition: transform 0.2s; }
    .pk-aria-status-box { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: bold; margin-top: 6px; transform: translateZ(0); -webkit-transform: translateZ(0); backface-visibility: hidden; will-change: transform; cursor: default; }
    .pk-token-eye { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; border: none; border-radius: 6px; background: transparent; color: #8a94a4; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; transition: background 0.15s, color 0.15s; z-index: 20; }
    .pk-token-eye:hover { background: rgba(0,103,192,0.1); color: var(--pk-pri); }
    .pk-token-eye svg { width: 18px; height: 18px; display: block; }
    .pk-aria-dot { width: 8px; height: 8px; border-radius: 50%; background: #ccc; transform: translateZ(0); }
    .pk-aria-dot.ok { background: #52c41a; box-shadow: 0 0 8px rgba(82, 196, 26, 0.5); }
    .pk-aria-dot.err { background: #ff4d4f; }
    .pk-aria-dot.wait { background: var(--pk-pri); animation: pk-pulse 1.5s infinite; }
    .pk-dropdown-wrap.active .pk-btn-arrow { transform: rotate(180deg); }
    .pk-select-menu { position: absolute; top: 100%; left: 0; right: 0; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; margin-top: 6px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); z-index: 10010; display: none; overflow: hidden; max-height: 240px; }
    .pk-share-icon-wrap { position: relative; width: 30px; height: 30px; margin-right: 12px; flex-shrink: 0; display:flex; align-items:center; justify-content:center; transform:none !important; }
    .pk-share-icon-wrap > svg, .pk-share-icon-wrap > img { width: 100%; height: 100%; display: block; flex-shrink: 0; }
    .pk-share-icon-wrap > div:not(.pk-share-lock) { width:100%; height:100%; display:flex; align-items:center; justify-content:center; transform:none !important; margin:0 !important; }
    .pk-share-icon-wrap > div:not(.pk-share-lock) > svg, .pk-share-icon-wrap > svg { transform:scale(0.94) !important; transform-origin:center center !important; }
    .pk-share-lock { position: absolute; bottom: -3px; right: -6px; width: 17px; height: 17px; background: transparent; display: flex; align-items: center; justify-content: center; z-index: 10; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3)); border: none; box-shadow: none; pointer-events: none; }
    .pk-ov.pk-dark .pk-share-lock { filter: drop-shadow(0 0 2px rgba(0, 0, 0, 1)) drop-shadow(0 2px 5px rgba(0, 0, 0, 1)); }
    .pk-bl-marker { position: absolute !important; bottom: 0px !important; left: 4px !important; width: 10px !important; height: 10px !important; display: flex !important; align-items: center; justify-content: center; z-index: 99 !important; pointer-events: none; filter: drop-shadow(0 0 1px #fff) drop-shadow(0 0 2px rgba(255,255,255,0.5)); }
    .pk-maximized .pk-bl-marker { width: 18px !important; height: 18px !important; bottom: -1px !important; left: 9px !important; }
    .pk-min-icon, .pk-max-icon-box { position: relative !important; overflow: visible !important; display: inline-flex !important; }
    .pk-active-border { border-color: var(--pk-pri) !important; } #pk_dl_group { transform: translateZ(0); backface-visibility: hidden; will-change: border-color; } #pk_dl_group:hover { border-color: var(--pk-pri) !important; } #pk_dl_group.pk-typing-active:hover { border-color: var(--pk-bd) !important; }
    .pk-row > div.pk-name, .pk-min-icon, .pk-max-icon-box, .pk-min-media-box { overflow: visible !important; }
    .pk-maximized .pk-row .pk-name>img[style*="width:24px"] { width: 48px !important; height: 48px !important; margin-right: 20px !important; margin-left: -4px !important; }
    .pk-maximized .pk-row .pk-name>div[style*="width:24px"] { width: 48px !important; height: 48px !important; margin-right: 20px !important; margin-left: -4px !important; }
    .pk-maximized .pk-row .pk-name>div[style*="width:24px"] svg { width: 36px !important; height: 36px !important; }
    .pk-maximized .pk-row div[style*="width:100px"] { width: 200px !important; height: 8px !important; }
    .pk-min-icon-box { position: relative; width: 24px; height: 24px; margin-right: 12px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
    .pk-min-placeholder { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 1; transition: opacity 0.2s; }
    .pk-min-thumb { position: absolute; inset: 0; width: 24px; height: 24px; object-fit: cover; border-radius: 4px; z-index: 2; opacity: 0; transition: opacity 0.2s; }
    .pk-drag-mask { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.92); z-index: 9999; display: none; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; backdrop-filter: blur(4px); }
    .pk-dark .pk-drag-mask { background: rgba(32, 32, 32, 0.92); }
    .pk-drag-icon { width: 80px; height: 80px; background: var(--pk-pri); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; margin-bottom: 24px; box-shadow: 0 10px 30px rgba(26, 94, 255, 0.3); }
    .pk-drag-hint { font-size: 20px; font-weight: 700; color: var(--pk-fg); margin-bottom: 12px; }
    .pk-drag-path { font-size: 14px; color: #888; display: flex; align-items: center; gap: 6px; }
    .pk-help-scroll { max-height: 380px; overflow-y: auto; overscroll-behavior: contain; }
    .pk-body-max .pk-help-scroll { max-height: 75vh; }
    body.pk-hide-all-ui #pk-launch, body.pk-hide-all-ui .pk-ov, body.pk-hide-all-ui .pk-modal-ov, body.pk-hide-all-ui .pk-img-ov, body.pk-hide-all-ui #pk-player-ov, body.pk-hide-all-ui .pk-cal-pop, body.pk-hide-all-ui .pk-crumb-pop, body.pk-hide-all-ui .pk-hist-pop, body.pk-hide-all-ui .pk-tooltip, body.pk-hide-all-ui .pk-msg-toast, body.pk-hide-all-ui .pk-float-bar-item, body.pk-hide-all-ui .pk-selection-box, body.pk-hide-all-ui .pk-drag-ghost { display: none !important; opacity: 0 !important; pointer-events: none !important; }
    .pk-ana-select-btn { border: 1px solid var(--pk-bd); background: var(--pk-bg); color: var(--pk-fg); height: 32px; border-radius: 6px; padding: 0 10px; font-size: 13px; cursor: pointer; display: none; align-items: center; gap: 6px; transition: all 0.2s; white-space: nowrap; margin-right: 8px; }
    .pk-ana-select-btn:hover { border-color: var(--pk-pri); color: var(--pk-pri); background: rgba(var(--pk-bg-rgb), 0.05); }
    .pk-ana-pop { position: absolute; background: var(--pk-bg); border: 1px solid var(--pk-bd); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 1000; display: none; flex-direction: column; padding: 8px; width: 340px; gap: 4px; pointer-events: auto; }
    .pk-ana-pop-row { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
    .pk-ana-opt { padding: 8px 4px; border-radius: 4px; cursor: pointer; font-size: 12px; color: var(--pk-fg); transition: background 0.1s; text-align: center; border: 1px solid transparent; background: var(--pk-hl); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pk-ana-opt:hover { background: var(--pk-sel-bg); color: var(--pk-pri); border-color: var(--pk-sel-bd); }
`;

;
const sleep = ms => new Promise(r => setTimeout(r, ms));

function getLogicalRect(el) {
    const rect = el.getBoundingClientRect();
    const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
    if (scale === 1) return rect;

    if (window._isRectScaledByZoom === undefined) {
        const dummy = document.createElement('div');
        dummy.style.cssText = 'position:fixed;top:0;left:0;width:100px;height:100px;zoom:0.5;pointer-events:none;opacity:0;border:none;margin:0;padding:0;';
        document.body.appendChild(dummy);
        const dRect = dummy.getBoundingClientRect();
        document.body.removeChild(dummy);
        window._isRectScaledByZoom = Math.abs(dRect.width - 50) < 1;
    }

    if (window._isRectScaledByZoom) {
        return {
            top: rect.top / scale,
            right: rect.right / scale,
            bottom: rect.bottom / scale,
            left: rect.left / scale,
            width: rect.width / scale,
            height: rect.height / scale
        };
    }
    return rect;
}
const esc = s => (s || '').replace(/[&<>"']/g, m => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m]));
const fmtSize = n => { n = parseInt(n || 0, 10); if (isNaN(n)) return ''; if (n === 0) return '0 KB'; const u = ['B', 'KB', 'MB', 'GB', 'TB']; let i = 0; while (n >= 1024 && i < u.length - 1) { n /= 1024; i++; } return (n < 10 ? n.toFixed(2) : n.toFixed(1)) + ' ' + u[i]; };
const fmtDate = t => {
    if (!t) return '-';
    const d = new Date(new Date(t).getTime() + (8 * 60 * 60 * 1000));
    const pad = n => String(n).padStart(2, '0');
    return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
};
const fmtDur = s => {
    s = Math.max(0, parseInt(s, 10) || 0);
    if (s <= 0) return "00:00";
    const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60), sc = s % 60;
    const mmss = String(m).padStart(2, '0') + ':' + String(sc).padStart(2, '0');
    return h > 0 ? String(h).padStart(2, '0') + ':' + mmss : mmss;
};
function gmGet(key, def) { if (typeof GM_getValue !== 'undefined') { let v = GM_getValue(key, def); return (v === null) ? def : v; } return def; }
function gmSet(key, val) { if (typeof GM_setValue !== 'undefined') GM_setValue(key, val); }

function getBlurScope() {
    const legacyBlur = gmGet('pk_blur_thumb', false);
    const scope = gmGet('pk_blur_scope', legacyBlur ? 'list' : 'off');
    return ['off', 'list', 'grid', 'both'].includes(scope) ? scope : (legacyBlur ? 'list' : 'off');
}
function isBlurEnabledForView(view) {
    const scope = getBlurScope();
    return scope === 'both' || scope === view;
}

const calcSha1 = async (file) => {
    if (!window.crypto || !window.crypto.subtle) return "";
    const CHUNK_SIZE = 20 * 1024 * 1024;
    const chunks = Math.ceil(file.size / CHUNK_SIZE);

    if (file.size > 100 * 1024 * 1024) {
        const head = file.slice(0, 1024 * 1024);
        const mid = file.slice(file.size / 2, file.size / 2 + 1024 * 1024);
        const tail = file.slice(file.size - 1024 * 1024);
        const combined = new Blob([head, mid, tail]);
        const buffer = await combined.arrayBuffer();
        const hash = await crypto.subtle.digest('SHA-1', buffer);
        return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
    }

    const buffer = await file.arrayBuffer();
    const hash = await crypto.subtle.digest('SHA-1', buffer);
    return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
};

function getLang(){const u=gmGet('pk_lang','');if(u)return u;const n=navigator.language.toLowerCase();return (n==='zh'||n.startsWith('zh-cn')||n.startsWith('zh-sg'))?'zh':(n.startsWith('zh-tw')||n.startsWith('zh-hk')||n.startsWith('zh-mo'))?'tc':(n.startsWith('id')||n.startsWith('in'))?'id':n.startsWith('ms')?'ms':n.startsWith('ko')?'ko':n.startsWith('ja')?'ja':'en';}
const I18N_CONF={defaultLang:'zh',remoteLangs:['tc','en','ko','ja','id','ms'],manifestUrl:'https://cdn.jsdelivr.net/gh/digbug82/PikPak_Enhancement_Master@main/i18n/manifest.json',baseUrl:'https://cdn.jsdelivr.net/gh/digbug82/PikPak_Enhancement_Master@main/i18n/',cachePrefix:'pk_i18n_',manifestKey:'pk_i18n_manifest',cacheTTL:7*24*60*60*1000,manifestTTL:24*60*60*1000,requiredKeys:['title','modal_settings_title','label_lang','msg_settings_saved','btn_nav_home']};
let pkRemoteI18n=null;
let pkI18nReadyPromise=null;
let pkI18nReadyLang='';
function getI18nCacheKey(lang){return `${I18N_CONF.cachePrefix}${lang}`;}
function readI18nCache(lang,expectedVersion=''){try{const raw=gmGet(getI18nCacheKey(lang),'');if(!raw)return null;const parsed=typeof raw==='string'?JSON.parse(raw):raw;if(!parsed||parsed.lang!==lang||!parsed.data||typeof parsed.data!=='object'||Array.isArray(parsed.data))return null;if(parsed.expiresAt&&parsed.expiresAt<Date.now())return null;if(expectedVersion&&String(parsed.version||'')!==String(expectedVersion))return null;return parsed;}catch(e){return null;}}
function writeI18nCache(lang,version,data){try{gmSet(getI18nCacheKey(lang),JSON.stringify({lang,version:String(version||''),data,expiresAt:Date.now()+I18N_CONF.cacheTTL}));}catch(e){}}
function readI18nManifestCache(){try{const raw=gmGet(I18N_CONF.manifestKey,'');if(!raw)return null;const parsed=typeof raw==='string'?JSON.parse(raw):raw;if(!parsed||!parsed.data||typeof parsed.data!=='object')return null;if(parsed.expiresAt&&parsed.expiresAt<Date.now())return null;return parsed.data;}catch(e){return null;}}
function writeI18nManifestCache(data){try{gmSet(I18N_CONF.manifestKey,JSON.stringify({data,expiresAt:Date.now()+I18N_CONF.manifestTTL}));}catch(e){}}
async function fetchI18nJson(url){const res=await fetch(url,{cache:'no-store'});if(!res.ok)throw new Error(`HTTP ${res.status}`);return await res.json();}
function mergeI18nPack(base,extra){return Object.assign({},base||{},extra&&typeof extra==='object'&&!Array.isArray(extra)?extra:{});}
function isValidI18nManifest(manifest){return !!(manifest&&typeof manifest==='object'&&!Array.isArray(manifest)&&manifest.files&&typeof manifest.files==='object'&&String(manifest.version||'').trim());}
function isValidI18nPayload(data){return !!(data&&typeof data==='object'&&!Array.isArray(data)&&I18N_CONF.requiredKeys.every(k=>Object.prototype.hasOwnProperty.call(data,k)));}
async function loadI18nManifest(force=false){if(!force){const cached=readI18nManifestCache();if(isValidI18nManifest(cached))return cached;}const manifestUrl=`${I18N_CONF.manifestUrl}${I18N_CONF.manifestUrl.includes('?')?'&':'?'}_t=${Date.now()}`;const manifest=await fetchI18nJson(manifestUrl);if(!isValidI18nManifest(manifest))throw new Error('Invalid i18n manifest');writeI18nManifestCache(manifest);return manifest;}
async function loadRemoteLangPack(force=false,langOverride=''){const lang=langOverride||getLang();if(!I18N_CONF.remoteLangs.includes(lang)||!I18N_CONF.manifestUrl){pkRemoteI18n=null;return null;}const manifest=await loadI18nManifest(force);const version=String(manifest.version||'');if(!force){const cached=readI18nCache(lang,version);if(cached){pkRemoteI18n={lang,version:cached.version,data:cached.data};return pkRemoteI18n;}}const file=manifest.files&&manifest.files[lang];if(!file){pkRemoteI18n=null;return null;}const baseUrl=manifest.baseUrl||I18N_CONF.baseUrl;const fileUrl=/^https?:\/\//.test(file)?file:`${baseUrl}${file}`;const data=await fetchI18nJson(`${fileUrl}${fileUrl.includes('?')?'&':'?'}_v=${encodeURIComponent(version)}`);if(!isValidI18nPayload(data))throw new Error('Invalid i18n payload');writeI18nCache(lang,version,data);pkRemoteI18n={lang,version,data};return pkRemoteI18n;}
function ensureI18nReady(force=false,langOverride=''){const lang=langOverride||getLang();if(force||!pkI18nReadyPromise||pkI18nReadyLang!==lang){pkI18nReadyLang=lang;pkI18nReadyPromise=loadRemoteLangPack(force,lang).catch(()=>null);}return pkI18nReadyPromise;}
async function ensureI18nReadyBeforeOpen(langOverride=''){const lang=langOverride||getLang();if(lang==='zh')return null;try{return await Promise.race([ensureI18nReady(false,lang),sleep(4000).then(()=>null)]);}catch(e){return null;}}
const T_LOCAL = {
    zh: {
        /* --- 通用与基础UI --- */
        title: "PikPak 增强大师",
        str_original: "原画",
        str_original_fast: "原画 (高速)",
        str_folders: "文件夹",
        str_files: "文件",
        unit_folders: "个文件夹",
        unit_days: "天",
        unit_month: "月",
        unit_sec: "秒",
        str_no_files: "暂无文件",
        str_items: "项",
        col_name: "名称",
        col_size: "大小",
        col_dur: "类型/时长",
        col_duration_only: "时长",
        col_progress: "播放进度",
        col_play_time: "播放时间",
        col_date: "修改日期",
        col_remaining: "剩余时长",
        col_path: "路径",
        col_old: "原名称",
        col_new: "新名称",
        col_type: "类型",
        col_path_name: "路径 / 名称",
        col_action: "操作",
        lbl_folder_first: "文件夹置顶",
        tag_default: "默认",
        current_dir: "当前目录",
        str_same_folder: "(同文件夹)",
        lbl_dont_show: "不再提醒",
        lbl_dont_show_session: "本次查重不再提示",
        str_empty_filename: "(空文件名)",
        str_empty_dir: "(空目录)",
        btn_filter: "筛选",
        title_file_filter: "文件筛选",
        cat_all: "全部",
        cat_video: "视频",
        cat_audio: "音频",
        cat_image: "图片",
        cat_document: "文档",
        cat_software: "软件",
        cat_archive: "压缩包",
        cat_torrent: "BT种子",
        cat_other: "其他",
        btn_exit_filter: "退出筛选",

        /* --- 属性面板 --- */
        ctx_property: "属性",
        title_property: "文件属性",
        lbl_prop_name: "文件名称",
        lbl_prop_size: "文件大小",
        lbl_prop_count: "文件数量",
        lbl_prop_ctime: "创建时间",
        lbl_prop_mtime: "修改时间",
        lbl_prop_source: "添加来源",
        lbl_prop_link: "资源链接",
        lbl_prop_path: "文件位置",
        str_prop_cloud: "云添加",
        str_prop_share: "来自分享",
        str_prop_user: "用户上传",
        str_prop_unknown: "未知来源",
        fmt_prop_count: "包含 {f} 个文件,{d} 个文件夹",
        str_prop_offline: "离线任务",

        /* --- 导航、视图模式与右键菜单 --- */
        btn_nav_home: "主页",
        btn_nav_share: "我的分享",
        btn_nav_offline: "离线下载",
        btn_nav_recent: "最近添加",
        btn_nav_history: "播放历史",
        btn_nav_starred: "收藏夹",
        btn_nav_trash: "回收站",
        btn_nav_upload: "我的上传",
        title_offline: "我的离线",
        trash_title: "回收站",
        trash_notice: "回收站的文件最多保存15天",
        history_notice: "仅记录在脚本环境内产生的播放进度",
        ctx_open: "打开",
        ctx_add_bl: "添加到资源管理器",
        ctx_remove_bl: "从资源管理器移除",
        ctx_rename: "重命名",
        ctx_copy: "复制",
        ctx_copy_name: "复制文件名",
        ctx_copy_link: "复制链接",
        ctx_cut: "移动",
        ctx_del: "删除",
        ctx_down: "下载",
        ctx_star: "添加星标",
        ctx_unstar: "取消星标",
        ctx_locate: "在文件夹中查看",
        ctx_share: "分享",

        /* --- 通用文件操作按钮 --- */
        btn_down: "下载",
        tip_down: "下载 [Alt] + [D]",
        btn_aria2: "发送 Aria2",
        tip_aria2: "发送 Aria2 [Alt] + [A]",
        btn_refresh_short: "刷新",
        tip_refresh: "刷新 [F5]",
        btn_newfolder: "新建文件夹",
        tip_newfolder: "新建文件夹 [F8]",
        btn_del: "删除",
        tip_del: "删除 [Delete]",
        btn_deselect: "取消选择",
        tip_deselect: "取消选择 [Esc]",
        btn_invert: "反选",
        btn_copy: "复制",
        tip_copy: "复制 [Ctrl] + [C]",
        btn_cut: "移动",
        tip_cut: "移动 [Ctrl] + [X]",
        btn_paste: "粘贴",
        tip_paste: "粘贴 [Ctrl] + [V]",
        btn_clear_history: "删除历史",
        tip_clear_history: "删除历史 [Delete]",
        btn_restore: "还原",
        tip_restore: "还原 [R]",
        btn_del_forever: "永久删除",
        tip_del_forever: "永久删除 [Delete]",
        btn_empty_trash: "清空回收站",
        tip_empty_trash: "清空回收站 [Shift] + [Delete]",
        btn_exit: "退出",
        btn_close: "关闭",
        tip_close: "关闭 [Esc]",
        tip_theme: "切换主题 [Alt] + [T]",
        tip_rotate: "旋转 [R]",
        tip_mirror: "镜像翻转 [H]",
        tip_flip_v: "垂直翻转 [V]",
        tip_maximize: "最大化 [M]",
        tip_minimize: "最小化 [M]",
        tip_full_screen: "全屏 [Enter]",
        btn_help: "帮助",
        tip_help: "帮助 [Alt] + [H]",
        btn_view_file: "查看文件",
        btn_jump: "跳转",
        btn_copy_text: "复制",
        btn_stop: "停止",
        btn_exit_script: "返回登录界面",
        btn_settings: "设置",
        btn_logout: "退出 PikPak",
        msg_logout_confirm: "确定要退出登录吗?",
        tip_settings: "设置和更多 [Alt] + [S]",
        lbl_upload_to: "上传文件至: ",
        msg_move_done: "移动完成。",
        msg_upload_dup_confirm: "发现 {n} 个名称与大小相同的文件,是否跳过?",

        /* --- 离线、上传与云下载 --- */
        btn_upload: "本地上传",
        btn_up_file: "上传文件",
        btn_up_folder: "上传文件夹",
        btn_cloud_download: "云下载",
        btn_up_pause: "暂停任务",
        tip_up_pause: "暂停任务 [Alt] + [P]",
        btn_up_start: "开始任务",
        tip_up_start: "开始任务 [Alt] + [G]",
        btn_up_del: "删除任务",
        tip_up_del: "删除任务 [Delete]",
        btn_up_clear_all: "清空任务",
        tip_up_clear_all: "清空任务 [Shift] + [Delete]",
        btn_retry_task: "重试任务",
        tip_retry_task: "重试任务 [R]",
        col_task_status: "任务状态",
        col_task_progress: "离线进度",
        col_up_speed: "速度",
        col_up_status: "状态",
        lbl_task_run: "进行中",
        lbl_task_fail: "已失败",
        lbl_task_ok: "已完成",
        lbl_up_run: "进行中",
        lbl_up_pause: "已中断",
        lbl_up_downloading: "下载中",
        lbl_up_done: "已完成",
        tip_up_pause_desc: "包含手动暂停及报错的任务",
        title_cloud_task: "创建云下载任务",
        ph_cloud_links: "支持链接格式:\n- 各种下载链接,如magnet。\n- YouTube、X (Twitter)、TikTok、Facebook等分享链接。\n通过换行可一次性添加多条链接。",
        lbl_save_to: "文件将被保存至:",
        lbl_default_folder: "默认文件夹",
        btn_via_torrent: "通过 Torrent 创建",
        tip_cloud_save_path: "常规云下载文件会保存在 My Pack 目录,来自其他 App 的云下载文件会保存至 My [XYZ] 目录,[XYZ] 为 App 名称。",
        lbl_smart_fix: "自动修复防屏蔽磁链 (提取特征码/剔除文字干扰)",
        title_save_method: "保存方式",
        msg_save_snapshot_desc: "此链接只能被存储为网页快照。",
        tip_snapshot_details: "PikPak 不能从此链接中直接采集媒体文件,您可以保存网页快照。PikPak 将尽可能保存完整的网页内容到快照文件中。",
        btn_save_snapshot: "保存快照",
        btn_create_now: "立即创建",
        btn_modify: "修改",
        str_snap_link_count_suffix: " 等 {n} 个链接",

        /* --- 分享管理 --- */
        btn_cancel_share: "取消分享",
        share_copy_suffix: "复制这段内容后打开 PikPak-App,畅享极速秒播",
        share_copy_pwd: "密码",
        title_share_detail: "分享详情",
        ctx_share_detail: "查看分享详情",
        ctx_share_copy: "复制链接和密码",
        col_view: "浏览",
        col_save: "保存",
        col_share_time: "分享时间",
        col_share_status: "分享状态",
        lbl_limit_reached: "次数已满",
        lbl_limit_tip: "限制次数",
        lbl_share_view: "浏览",
        lbl_share_save: "保存",
        lbl_share_link_title: "分享链接",
        lbl_share_pwd_title: "密码",
        lbl_share_expire_title: "有效期",
        btn_copy_link_pwd: "复制链接和密码",
        str_expire_suffix: "天后过期",
        ph_edit_pwd: "输入分享密码,支持 4-10 位",
        btn_close_pwd: "关闭密码",
        str_no_pwd: "无密码",
        title_edit_share_code: "分享代码修改",
        ph_edit_share_code: "支持 5-18 位文字、数字、及符号等",
        btn_add_share_code: "添加分享代码",
        btn_del_share_code: "删除分享代码",
        share_title: "分享文件",
        share_mode: "分享方式",
        share_public: "公开链接",
        share_encrypted: "加密链接",
        share_expiry: "设置有效期",
        share_pass: "设置提取码",
        share_count: "设置提取次数",
        share_count_ed: "提取次数",
        share_perm: "永久有效",
        share_unlimit: "不限",
        share_rand: "系统随机",
        share_custom: "自定义",
        share_days: "天",
        share_times: "次",
        btn_share_start: "立即分享",
        cal_custom_title: "自定义有效期",
        lbl_share_link: "链接",
        lbl_share_code: "提取码",
        btn_copy_share: "复制全部",
        str_share_expired: "已过期",
        str_share_deleted: "文件已删",
        title_edit_pwd: "密码修改",
        lbl_share_code_title: "分享代码",
        btn_migrate: "数据迁移",
        tip_migrate: "打包选中项并准备迁移至新账号 [Alt] + [M]",
        msg_migrate_confirm: "确定要将选中的 {n} 个项目迁移至其他账号吗?\n\n打包成功后,会自动退出。\n登录【目标账号】以完成接收。",
        msg_migrate_packing: "正在构建加密迁移包...",
        msg_migrate_ready: "✅ 迁移包已就绪!\n\n即将退出当前账号,请登录您的【目标账号】。\n重新登录后系统将自动接管转存工作。\n(注:迁移数据包将在 1 天后自动过期失效)",
        err_migrate_ban: "打包失败:选中的内容中包含违规或被官方限制分享的资源。\n请剔除违规文件后重试。",
        msg_migrate_detect: "📦 检测到来自另一个账号的迁移数据包!\n\n共包含 {n} 个项目。\n接收后将保存到以下路径:\n主页 / Pack From Shared\n若该文件夹不存在,将自动创建。\n\n是否立即开始迁移?\n选择“是”将执行迁移;选择“否”将取消本次迁移。",
        msg_migrate_same_account: "迁移已取消:您登录的仍然是原账号,存根已清除。",
        msg_migrate_saving: "正在从加密通道高速转存数据...",
        msg_migrate_success: "🎉 跨账号迁移完成!\n所有文件已成功转存至当前账号。",
        ph_password: "密码",
        ph_pass_range: "4-10位字符",
        cal_week_days:["日", "一", "二", "三", "四", "五", "六"],
        cal_months:["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],

        /* --- 文件分析与文件夹分析 --- */
        title_file_analysis: "文件分析",
        btn_scan: "文件透视",
        lbl_scan_selected: "对选中的 {n} 个项目执行文件透视",
        lbl_keyword_filter: "排除关键词",
        ph_keyword_filter: "排除包含的关键词,多个用逗号分隔",
        lbl_scan_current: "对当前路径下所有项目执行文件透视",
        tip_dup: "文件查重",
        lbl_dup_selected: "对选中的 {n} 个项目执行文件查重",
        lbl_dup_current: "对当前路径下所有项目执行文件查重",
        tip_scan_dup: "筛选或查重文件",
        lbl_dup_tool: "选择删除对象:",
        lbl_dup_reset: "↺ 复原 (取消置顶 & 清空选择)",
        lbl_dup_select_folder: "📂 按文件夹选择",
        lbl_dup_select_folder_short: "📂 文件夹",
        lbl_dup_invert: "反选模式",
        lbl_dup_invert_short: "反选",
        tip_dup_invert_limit: "仅按文件夹选择可用",
        fmt_dup_count: "({n}个重复)",
        btn_start_scan: "开始扫描",
        tag_hash: "精准匹配",
        tag_hash_short: "精准",
        tag_name: "名称相似",
        tag_name_short: "名称",
        tag_sim: "时长相似",
        tag_sim_short: "时长",
        label_dup_video: "视频文件 (精准匹配 + 时长相似 + 名称相似)",
        label_dup_image: "图片文件 (精准匹配 + 名称相似)",
        label_dup_other: "其他文件 (精准匹配 + 名称相似)",
        btn_analyze: "文件夹分析",
        tip_analyze: "筛选或查重文件夹",
        btn_export: "导出目录",
        tip_export: "生成并下载当前路径的文件树列表",
        title_export_format: "导出目录样式",
        lbl_export_current: "对当前路径下所有项目执行目录导出",
        opt_tree_view: "目录树",
        opt_list_view: "目录列表",
        opt_grid_view: "网格视图",
        msg_exporting: "正在生成目录树...",
        str_analyze_results: "匹配结果",
        lbl_size_threshold: "检测阈值",
        title_analyze_result: "文件夹分析结果",
        opt_ana_large: "文件夹透视",
        lbl_analyze_selected: "对选中的 {n} 个项目执行文件夹透视",
        lbl_analyze_current: "对当前路径下所有项目执行文件夹透视",
        opt_ana_sim: "文件夹查重",
        lbl_ana_sim_selected: "对选中的 {n} 个项目执行文件夹查重",
        lbl_ana_sim_current: "对当前路径下所有项目执行文件夹查重",
        title_algo_help: "查重算法说明",
        algo_help_content: "名称匹配:查找名称和体积相近的文件夹群组。\n相似度匹配:查找内部文件高度重合的文件夹群组。\n包含率匹配:查找小文件夹的内容被大文件夹完全覆盖的子集冗余。\n\n精度:相似度匹配 > 包含率匹配 > 名称匹配\n范围:包含率匹配 > 相似度匹配",
        lbl_threshold: "阈值",
        lbl_sim_score: "相似度",
        lbl_containment: "包含率",
        lbl_name_match: "名称匹配",
        lbl_sim_match: "相似度匹配",
        lbl_contain_match: "包含率匹配",
        lbl_ana_min: "下限",
        lbl_ana_max: "上限",

        /* --- 重命名、清理与资源管理器 --- */
        btn_prune: "清理空文件夹",
        tip_prune: "清理空文件夹 [Ctrl] + [Delete]",
        btn_rename: "重命名",
        tip_rename: "重命名 [F2]",
        btn_bulkrename: "批量重命名",
        tip_bulkrename: "批量重命名 [F2]",
        title_blacklist: "资源管理器",
        btn_blacklist_run: "立即运行清理",
        btn_clear_list: "清空列表",
        tip_bl_desc: "下列项目在【删除】时会跳过,仅通过【立即运行清理】查找删除",
        tip_blacklist_input: "资源管理器 [Alt] +[Delete]",
        label_bl_folder: "文件夹名单 (精准查找)",
        label_bl_file: "文件名单 (精准查找)",
        lbl_type_folder: "文件夹",
        lbl_type_file: "文件",
        ph_bl_folder: "请通过“粘贴”或文件右键菜单“添加到资源管理器”导入。",
        ph_bl_file: "请通过“粘贴”或文件右键菜单“添加到资源管理器”导入。",
        modal_bl_preview: "检索结果",
        btn_bl_delete: "删除选中项",
        modal_rename_title: "重命名",
        modal_rename_multi_title: "批量重命名",
        btn_preview: "预览",
        modal_preview_title: "确认更改",
        label_pattern: "模式 (例: Video {n})",
        label_replace: "替换/删除",
        label_replace_note: "区分大小写",
        label_include_ext: "包含后缀",
        label_regex: "正则 (Regex)",
        placeholder_find: "查找内容",
        placeholder_replace: "替换为 (留空删除)",
        label_jav: "FC2 纯净命名",
        lbl_rn_pattern: "命名模板",
        lbl_rn_case_convert: "大小写转换",
        opt_rn_keep_origin: "(保持原样)",
        opt_rn_lower: "全部小写 (abc)",
        lbl_rn_mode_series: "剧集模式",
        lbl_rn_mode_format: "格式化",
        lbl_rn_mode_ad: "前缀去广告",
        lbl_rn_mode_ext: "后缀修复",
        opt_rn_upper: "全部大写 (ABC)",
        opt_rn_title: "首字母大写 (Abc)",
        lbl_rn_width_convert: "全半角转换",
        opt_rn_width_half: "全角转半角 (A->A)",
        opt_rn_width_full: "半角转全角 (A->A)",
        lbl_rn_preview_title: "变更预览",
        tip_jav_mode_desc: "✨ 智能提取FC2并去除无关字符",
        tip_ad_remove_desc: "🧹 智能滤除头部广告、网址及垃圾符号,自动清洗Emoji并修复括号格式",
        tip_ext_fix_desc: "🧩 根据文件真实类型 (MIME) 智能修正后缀",
        label_replace_find: "查找内容",
        label_replace_to: "替换为",

        /* --- 解压相关 --- */
        btn_unzip: "批量解压",
        tip_unzip: "批量解压 [Alt] + [U]",
        btn_unzip_all: "全部解压",
        btn_understand_unzip: "理解并解压",
        title_input_pwd: "需要解压密码",
        lbl_pwd_prompt: "请输入密码:",

        /* --- 媒体播放器与以图搜图 --- */
        btn_ext: "外部播放",
        tip_ext: "使用PotPlayer播放或获取播放链接 [Alt] +[E]",
        btn_img_search: "以图搜图 [F]",
        tip_play_search: "以图搜图 [F]",
        tip_pip: "画中画 [P]",
        str_no_sub: "无字幕",
        lbl_sub_sel: "字幕选择",
        lbl_show_sub: "显示字幕",
        btn_sub_search: "搜索在线字幕",
        btn_sub_cloud: "打开云盘字幕",
        btn_sub_local: "打开本地字幕",
        lbl_sub_pos: "字幕位置",
        lbl_sub_bottom: "底部",
        lbl_sub_top: "顶部",
        lbl_sub_bg_op: "背景透明",
        lbl_sub_size: "字幕大小",
        lbl_sub_offset: "字幕进度",
        title_sel_sub: "选择字幕",
        ph_sub_search: "输入关键词,下方链接自动更新...",
        str_compat_mode: "兼容模式",
        lang_code: "zh",
        btn_go_search: "🔍 去 {n} 手动搜",
        btn_restart: "从头播放",
        btn_prev_video: "上一个 [Ctrl + ←]",
        btn_next_video: "下一个 [Ctrl + →]",
        tip_plist_open: "展开 [E]",
        tip_plist_close: "收起 [E]",
        tab_sub: "字幕",
        tab_size: "尺寸",
        tab_more: "更多",
        lbl_ratio: "比例",
        lbl_direction: "方向",
        opt_ratio_def: "默认",
        btn_rot_l: "向左旋转",
        btn_rot_r: "向右旋转",
        btn_flip_h: "水平翻转",
        btn_flip_v: "垂直翻转",
        lbl_play_end: "当播放结束",
        opt_list_loop: "列表循环",
        opt_single_loop: "单集循环",
        opt_play_stop: "播完暂停",
        lbl_skip_op: "跳过片头",
        lbl_skip_ed: "跳过片尾",
        export_link_title: "导出视频串流链接",
        btn_start_play: "开始播放",
        btn_copy_link: "复制链接",
        tip_copy_link: "复制链接 [Alt] + [C]",
        opt_player_other: "其他 (导出链接)",
        lbl_player: "播放器",
        btn_mark: "标记",
        lbl_resolution: "清晰度",
        str_switch_compat: "当前清晰度不可用,已为您恢复至 {n}",
        type_img: "图像",
        type_doc: "文档",
        type_archive: "压缩文件",
        type_sub: "字幕文件",
        type_app: "应用程序",
        type_suffix: "文件",

        /* --- 设置与搜索 --- */
        label_turbo_mode: "极速模式",
        desc_turbo_mode: "自动开启并替代网页界面 (推荐)",
        lbl_aria2_status: "RPC 测试",
        ph_aria2_secret: "密钥 (选填)",
        str_connected: "连接成功",
        str_conn_fail: "连接失败",
        str_connecting: "正在测试...",
        tip_mixed_content: "常用 RPC 端口:\n• 6800 (Aria2 默认)\n• 16800 (Motrix 默认)",
        picker_title: "选择文件夹",
        picker_all: "全部文件",
        picker_new: "新建文件夹",
        picker_sort_new: "更新",
        picker_sort_star_new: "星标·更新",
        picker_sort_star_old: "星标·更旧",
        picker_sort_type_dur_asc: "类型/时长·顺序",
        picker_sort_type_dur_desc: "类型/时长·逆序",
        picker_sort_old: "更旧",
        picker_sort_large: "更大",
        picker_sort_small: "更小",
        picker_sort_path_asc: "路径顺序",
        grid_folder_count: "{n}项",
        picker_sort_path_desc: "路径逆序",
        title_select_file: "选择文件",
        placeholder_search: "搜索文件...",
        placeholder_search_short: "搜索",
        title_search_hist: "搜索历史",
        btn_clear_hist: "清空",
        lbl_global_search: "全盘搜索",
        lbl_search_path: "搜索包含路径",
        lbl_search_path_short: "路径",
        str_search_results: "搜索结果",
        modal_settings_title: "设置",
        label_lang: "语言 (Language)",
        label_thumb: "模糊略缩图 (隐私模式)",
        label_keep_pos: "保持浏览位置 (返回时定位)",
        label_sort_pref: "排序偏好",
        opt_sort_indep: "每个文件夹独立",
        opt_sort_global: "全部相同",
        desc_sort_indep: "调整排序方式仅针对当前文件夹生效",
        desc_sort_global: "全部文件夹使用相同的排序 (时间倒序)",
        label_view_pref: "视图模式偏好",
        opt_view_indep: "每个文件夹独立",
        opt_view_global: "全部相同",
        desc_view_indep: "调整视图仅对当前文件夹生效",
        desc_view_global: "全部文件夹使用相同的列表/网格视图",
        label_search_engine: "搜图引擎",
        opt_engine_google: "Google Lens (综合)",
        opt_engine_yandex: "Yandex (综合)",
        opt_engine_saucenao: "SauceNAO (Pixiv/插画)",
        opt_engine_tracemoe: "trace.moe (动漫截图)",
        label_dup_strictness: "相似匹配阈值",
        opt_strict: "严格",
        opt_loose: "宽松",
        label_comic_mode: "媒体模式",
        desc_comic_mode: "纯图片或纯视频文件夹默认 A-Z 排序",
        label_aria2_url: "Aria2 地址",
        label_aria2_token: "Aria2 密钥",
        label_privacy_mode: "隐私图",
        label_blur_cover: "模糊媒体封面缩略图",
        label_privacy_scope: "作用范围",
        opt_privacy_off: "关闭",
        opt_privacy_list: "仅列表视图",
        opt_privacy_grid: "仅网格视图",
        opt_privacy_both: "列表和网格视图",
        label_dl_filter_ext: "下载后缀过滤 (例: .txt, .jpg)",
        label_dl_filter_name: "下载名称过滤 (关键词或全名)",
        lbl_dl_filter: "文件夹下载过滤",
        desc_dl_filter: "文件夹下载/Aria2推送时自动排除匹配的文件",
        lbl_config_manage: "配置管理",
        btn_export_data: "导出备份",
        btn_import_data: "导入备份",
        btn_clean_data: "清除本地数据",
        title_clean_data: "选择清理项目",
        msg_clean_confirm: "确定要彻底删除选中的本地数据吗?此操作无法撤销。",
        msg_clean_success: "本地数据已清理,页面即将刷新...",
        opt_cfg_index: "全盘索引 (已同步的目录结构/文件快照)",
        opt_cfg_pref: "偏好设置 (UI 外观/操作习惯/排序偏好)",
        opt_cfg_rules: "管理规则 (资源管理器/分享次数限制/搜索记录/下载规则)",
        opt_cfg_vault: "密码金库 (解压密码记忆)",
        opt_cfg_history: "视频缓存 (视频播放进度/视频时长缓存)",
        opt_cfg_cache: "运行缓存 (文件夹修改时间/最后浏览位置/指纹)",
        msg_import_confirm: "导入的配置将与当前设置合并(合并名单/记录,覆盖冲突的基础设置),是否继续?",
        msg_import_success: "配置导入成功,页面即将刷新...",
        err_invalid_config: "无效的配置文件:未检测到指纹标识或格式错误",
        err_json_format: "文件解析失败:JSON 语法错误或文件已损坏",
        lbl_storage: "存储空间",
        lbl_browse_exp: "浏览体验",
        lbl_skip_bl_on_del: "删除时跳过管理器中记录资源",
        lbl_pwd_manage: "解压密码管理",
        title_pwd_vault: "密码金库",
        lbl_pwd_try_count: "单个压缩包密码匹配上限",
        tip_pwd_manual: "每行记录一个密码,回车换行",
        str_root_dir_cn: "根目录",
        btn_ana_select: "一键勾选",
        btn_in_group_sort: "组内排序",
        opt_sort_time: "时间",
        opt_sort_size: "大小",
        opt_sort_path: "路径",
        opt_sort_name: "名称",
        opt_keep_new: "保留最新的", opt_keep_old: "保留最旧的",
        opt_keep_large: "保留最大的", opt_keep_small: "保留最小的",
        opt_keep_short: "保留名称最短的", opt_keep_long: "保留名称最长的",
        label_clipboard_magnet_focus: "剪贴板磁链识别",
        desc_clipboard_magnet_focus: "回到前台时检测剪贴板磁链",
        msg_magnet_preview_desc: "已识别到剪贴板磁链,确认后将直接创建云下载任务。",
        msg_magnet_preview_fail: "未获取到公开预览信息,仍可继续添加。",
        str_magnet_unknown_name: "未知资源",
        str_no_preview: "暂无预览图",
        lbl_magnet_count: "文件数",
        lbl_magnet_size: "总大小",
        lbl_magnet_type: "类型",
        lbl_magnet_hash: "磁链",
        btn_magnet_continue: "高速云下载",
        lbl_magnet_preview_source: "预览信息来自",
        msg_magnet_preview_rate_limited: "预览服务请求过于频繁,已临时降级。仍可继续添加。",
        msg_magnet_preview_timeout: "预览服务响应超时,仍可继续添加。",
        msg_magnet_preview_network: "预览服务暂不可用,仍可继续添加。",

        /* --- 状态、进度与加载短语 --- */
        loading: "加载中...",
        loading_detail: "正在全速索引目录结构...",
        loading_fetch: "获取中... ({n})",
        loading_dup: "分析重复项... ({p}%)",
        str_loading_placeholder: "加载中...",
        str_load_failed: " (加载失败)",
        str_load_failed_simple: "加载失败",
        str_waiting_token: "正在同步登录状态...",
        str_speed: "速度",
        status_scanning_selection: "扫描选中项... {n}",
        status_ready: "{n} 个项目",
        sel_count: "已选择 {n} 个项目",
        str_cached: "已缓存:",
        str_retries: "重试:",
        str_failed: "失败:",
        str_success: "成功:",
        str_stopping: "正在停止...",
        str_merging: "合并数据中...",
        str_rendering: "渲染列表中...",
        str_scanning: "扫描中...",
        str_analyzing: "分析中...",
        str_deleting: "删除中...",
        str_saving: "保存中...",
        str_saving_dots: "保存中...",
        str_checking_bl: "匹配名单记录中...",
        str_processing: "系统正在全速处理中...",
        str_cleanup_done: "清理完成。",
        str_waiting_preload: "等待预加载...",
        str_copying: "复制到剪贴板...",
        str_moving: "准备移动...",
        str_sorting: "正在排序...",
        str_refreshing: "刷新中...",
        str_refreshing_cache: "刷新缓存中...",
        str_syncing_stars: "同步星标状态...",
        str_updating_view: "更新视图中...",
        str_generating_view: "生成视图中...",
        str_group: "组",
        str_init_rename: "初始化重命名...",
        str_renaming: "重命名中...",
        str_calc_changes: "计算变更中...",
        str_scanning_dir: "扫描目录结构...",
        str_init_op: "初始化操作...",
        str_init_scan: "正在初始化全盘扫描...",
        str_rebuilding: "正在重建索引...",
        str_upload_1: "正在上传 (节点 1/3)...",
        str_upload_2: "节点1超时,切换节点 2...",
        str_upload_3: "节点2超时,尝试最后节点...",
        str_upload_fail_copy: "上传失败,准备写入剪贴板...",
        str_preparing: "准备解压...",
        str_unzipping: "正在解压: {n}",
        str_unzipping_state: "解压中...",
        str_unzipping_prog_0: "解压中: 0%",
        str_unzipping_prog_100: "解压中: 100%",
        str_unzipping_prog_fmt: "解压中: {n}%",
        msg_task_waiting: "等待中...",
        msg_task_hashing: "校验文件中...",
        msg_task_init_upload: "初始化上传...",
        msg_task_uploading: "正在上传...",
        msg_task_init_part: "初始化分片...",
        msg_task_uploading_2: "正在上传...",
        str_loc_tracing: "正在回溯路径...",
        str_loc_stale: "缓存已过期,正在同步云端...",
        str_verifying: "正在验证...",
        str_server_indexing: "服务器索引中... ({n}/5)",
        str_creating_task_n: "创建任务 ({n}/{t})...",
        msg_submit_request: "正在提交请求... {c}/{t}",
        msg_wait_server: "等待服务器处理... ({c}/{t})",
        msg_server_processing: "服务器处理中... ({c}/{t})",
        str_jav_querying: "正在查询...",
        lbl_done_check: "✔ 完成",
        msg_limit_updated: "提取次数已更新",

        /* --- 提示、确认与交互消息 --- */
        title_alert: "提示",
        title_confirm: "确认",
        title_prompt: "输入",
        btn_ok: "确定",
        btn_yes: "是",
        btn_no: "否",
        btn_save: "保存配置",
        btn_cancel: "取消",
        btn_create: "创建",
        btn_skip: "跳过",
        msg_down_success: "已成功调用浏览器下载 {n} 个文件。",
        msg_clear_history_done: "已从历史记录中移除",
        msg_skip_unzipped: "已跳过 {n} 个已解压的项目。",
        msg_unzip_skip_del_confirm: "检测到 {n} 个压缩包在当前路径存在同名文件夹且状态标记为已解压,大概率已完成解压。是否将其移入回收站?\n\n(建议:请先检查同名文件夹内容是否完整)",
        msg_cancel_share_confirm: "确定要取消选中的 {n} 个分享吗?\n链接将立即失效。",
        msg_pwd_updating: "正在更新密码...",
        msg_pwd_updated: "密码已更新",
        msg_exp_updated: "有效期已更新",
        msg_cancel_share_done: "已取消 {n} 个分享。",
        msg_drag_drop_hint: "将文件拖拽到此处并释放",
        str_drag_files: " 等 {n} 个文件",
        msg_creating_share: "正在创建分享...",
        title_share_result: "分享成功",
        msg_no_files: "选中的项目为空。",
        msg_no_selection: "请先选择项目。",
        warn_del: "确定要删除选中的 {n} 项吗?",
        msg_clear_sel_confirm: "已选中 {n} 个重复文件,确认要取消当前的勾选吗?",
        str_bl_stat: "匹配: {n} 项 | 已选中: {m} 项",
        str_hits: "命中",
        msg_settings_saved: "设置已保存。页面将刷新。",
        msg_name_exists: "名称已存在: {n}",
        str_name_conflict: "(可能重名)",
        msg_newfolder_prompt: "新文件夹名称:",
        msg_rename_prompt: "输入新名称:",
        msg_copy_done: "已复制。请选择粘贴位置。",
        msg_cut_done: "准备移动。请选择粘贴位置。",
        msg_paste_empty: "没有可粘贴的项目。",
        msg_copy_empty: "剪贴板为空或无文本数据。",
        msg_add_success: "已追加 {n} 行数据。",
        msg_del_done: "已删除选中行。",
        msg_del_select: "请先点击选中要删除的行!",
        msg_del_items_done: "已删除 {n} 个项目。",
        msg_copy_success: "复制成功",
        str_redirecting: "正在跳转 Google Lens...",
        msg_manual_paste: "图床上传超时。已复制截图,请在新窗口按 {cmd}",
        msg_starring: "正在添加星标...",
        msg_unstarring: "正在取消星标...",
        msg_star_added: "已添加星标",
        msg_unstar_done: "已取消星标",
        msg_empty_trash_confirm: "确定要清空回收站吗?此操作无法撤销!",
        msg_trash_emptied: "回收站已清空。",
        msg_del_forever_confirm: "确定要彻底删除这 {n} 个项目吗?此操作无法撤销!",
        msg_del_forever_done: "已彻底删除 {n} 个项目。",
        msg_restore_done: "已成功还原 {n} 个项目。",
        msg_auto_sub_load: "已自动加载字幕:{n}",
        msg_dl_sub: "正在下载字幕...",
        msg_fallback_report: "⚠️ 原画不可播放(MPEG4/HEVC),已自动切换至 {n}",
        tip_manual_sub: "提示:下载 .srt 或 .vtt 后,直接拖入播放器即可加载。",
        msg_sub_drop_load: "已通过拖拽加载字幕:{n}",
        msg_resume_hint: "已为您从 {t} 继续播放,点击这里 ",
        msg_unzip_confirm_n: "确定要在当前目录解压这 {n} 个文件吗?",
        msg_task_paused: "已暂停",
        msg_task_added: "已添加 {n} 个上传任务",
        msg_task_fast_success: "秒传成功",
        msg_task_upload_done: "上传完成",
        log_dirty_data: "拦截到脏数据入侵!请求模式与当前视图不符,已物理熔断。",
        msg_network_unstable: "网络连接波动,正在自动恢复...",
        msg_skip_locked: "{n} 个被锁定",
        msg_skip_self: "{n} 个原地移动",
        msg_skip_conflict: "{n} 个子路径忙碌",
        msg_skip_invalid: "已自动跳过无效项: ",
        msg_creating_cloud_task: "正在创建云下载任务...",
        str_parsing_torrent: "正在解析种子文件...",
        err_torrent_no_info: "解析失败:未发现有效信息",
        err_file_read: "文件读取失败",
        msg_cloud_task_finish: "创建完成:{s} 成功,{f} 失败",
        msg_cloud_task_success: "🎉 已成功创建 {n} 个任务",
        msg_prepare_restore: "准备还原...",
        msg_smart_matching_n: "正在智能匹配密码 ({n}个)...",
        msg_system_busy_retry: "系统繁忙,等待重试 ({n}/5)...",
        msg_unzip_running_bg: "[{n}] 已在云端解压中,请稍后刷新查看",
        msg_share_code_updated: "分享代码已更新",
        msg_ghost_task_resume: "云端未完成 (点击恢复重选文件)",
        msg_parsing_files: "正在解析文件,请稍候...",
        msg_token_expired_retry: "登录过期,正在重试...",
        str_empty_str: "(空)",
        err_clipboard_failed: "写入剪贴板失败",
        err_cors_blocked: "安全拦截: 跨域访问被拒绝",
        err_worker_failed: "工作线程遇到错误",
        str_target_folder: "目标文件夹",
        str_unknown_name: "未知名称",
        btn_default: "默认",
        msg_retry_submitted: "已重试提交 {n} 个任务",
        msg_aria2_batch_fail_log: "\n\n检测到失败项较多,已为您自动导出完整错误清单 (.txt)",
        str_aria2_fetch_err: "(获取链接失败)",
        str_aria2_rpc_err: "(投递失败)",
        str_aria2_aborted: "(已取消)",
        str_aria2_fail_file_name: "Aria2_失败清单",
        msg_op_blocked_moving: "⚠️ 操作拦截\n\n后台正在执行文件搬运,请等待当前任务完成后再操作。",
        msg_op_blocked_analyzing: "⚠️ 操作拦截\n\n后台正在执行文件搬运。为确保文件夹分析数据准确,请等待当前任务完成后再操作。",
        msg_op_blocked_exporting: "⚠️ 操作拦截\n\n后台正在执行文件搬运。为确保导出目录文本准确,请等待当前任务完成后再操作。",
        msg_analyze_only_normal_dir: "请选择文件夹。",
        msg_analyze_no_large_folders: "没有发现符合阈值范围的文件夹 ({s})",
        msg_analyze_summary_fmt: "共发现 <b>{n}</b> 个大于 {s}GB 的文件夹 (已按大小降序排列)",
        msg_prune_blocked_moving: "⚠️ 操作拦截\n\n后台正在执行文件搬运,暂时无法执行清理。",
        msg_global_index_blocked_moving: "⚠️ 操作拦截\n\n后台正在执行文件搬运。全盘索引需要稳定的目录结构,请等待当前任务完成后再开启全盘搜索。",
        msg_resource_locked_download: "⚠️ 操作拦截\n\n选中项包含正在移动的文件或文件夹。请等待搬运完成后再发起下载。",
        msg_resource_locked_aria2: "⚠️ 操作拦截\n\n选中项包含正在移动的文件或文件夹。请等待搬运完成后再推送至 Aria2。",
        msg_flatten_blocked_moving: "⚠️ 操作拦截\n\n后台正在执行文件搬运。此时执行文件分析会导致文件列表缺失或重复,请稍后再试。",
        err_task_conflict: "⚠️ 操作拦截\n\n后台正在执行文件搬运。全局清理需要稳定的目录结构,请等待搬运完成后再执行。",
        title_del_task_confirm_fmt: "确认删除 {n} 条传输任务?",
        lbl_del_cloud_files_too: "同时删除云盘内的文件",
        msg_file_del_failed: "文件删除失败: ",
        msg_task_del_success_fmt: "已删除 {n} 个任务",
        title_clear_task_confirm: "确认清空所有上传任务?",
        msg_task_clear_success_fmt: "已清空 {n} 个上传任务",
        msg_unzip_virtual_view_warn: "您正在虚拟视图中操作。解压后的文件将存放在<b>各压缩包所在的原始文件夹</b>中,而<b>不会</b>直接出现在当前列表中。<br><br>是否继续?",
        msg_smart_matching_file: "智能匹配密码中... ({n})",
        msg_unzip_batch_submitted: "✅ 已完成 {n} 个解压任务",
        msg_unzip_batch_skipped: " ({n} 个跳过)",
        msg_unzip_check_source: "。请前往源目录查看结果。",
        tip_jump_to_folder: "跳转到此文件夹",
        msg_task_deleted: "任务已删除",
        msg_scan_done: "扫描完成!\n共发现 {n} 个文件,遍历了 {f} 个文件夹。",
        msg_scan_fail: "\n\n❌ 有 {n} 个失败。",
        msg_scan_fix: "\n\n✅ 自动修复了 {n} 次网络错误。",
        msg_down_scanning: "正在解析文件夹内容...",
        msg_down_progress: "正在调用浏览器下载...",
        msg_down_confirm_total: "✅ 扫描完毕,共找到 {n} 个文件,总大小约 {s}。\n\n⚠️ 警告:当前任务规模较大,浏览器批量下载可能导致页面卡顿或被浏览器拦截。\n建议当文件数超过 {fc} 个或总大小超过 {fs} 时,优先使用 Aria2。\n\n是否仍然使用浏览器下载?",
        msg_aria2_sending_batch: "🚀 正在分批发送任务至 Aria2...",
        msg_aria2_check_fail: "Aria2 连接失败!\n请检查 URL 和 Token。",
        msg_aria2_check_ok: "Aria2 连接成功!",
        msg_aria2_sent: "已将 {n} 个文件发送到 Aria2。",
        msg_aria2_test_fail: "Aria2 连接失败。\n仍然保存设置吗?",
        title_aria2_fail: "连接测试失败",
        msg_batch_scanning: "🚀 正在高速扫描目录结构...",
        msg_batch_hydrating: "⚡ 正在并行提取下载链路...",
        msg_batch_no_files: "未发现可下载的文件。",
        msg_batch_filtered: "下载过滤规则已跳过 {n} 个文件。",
        msg_batch_all_filtered: "已全部过滤:{n} 个文件均命中下载过滤规则。",
        msg_dup_warn: "是否开始搜索重复文件?",
        msg_dup_result: "发现 {n} 组重复项。",
        msg_dup_none: "未发现重复文件。",
        msg_bl_stop: "操作已停止。",
        msg_bl_add_done: "已将 {n} 个项目添加到记录。",
        msg_bl_remove_done: "已从记录中移除 {n} 个项目。",
        msg_bl_empty: "名单列表为空,无法运行。",
        msg_bl_clear_confirm: "确定要清空所有记录条目吗?此操作不可恢复。",
        msg_blacklist_run_none: "网盘中未发现符合名单条件的项目。",
        msg_blacklist_run_confirm: "在网盘中发现了 {n} 个已记录项目。\n\n是否立即移入回收站?",
        msg_bl_run_limit: "⚠️ 模式限制\n\n清理操作涉及物理文件递归操作。目前处于非标准目录,无法准确定位物理扫描范围。\n\n请返回主页常规文件夹后再执行清理。",
        msg_del_protected: "资源管理器中已记录的 {n} 个文件,已保护并跳过删除。",
        msg_del_none: "没有可删除的文件。",
        msg_bl_scanning: "全盘搜索中... \n已扫描目录: {d} | 命中: {f}",
        rn_tip_wait: "请设置规则",
        rn_tip_jav: "点击上方按钮开始智能匹配",
        rn_tip_none: "没有匹配的项目或名称",
        rn_tip_series_folder_only: "选中项均为文件夹,没有匹配的文件",
        rn_stat: "匹配: {n} 项 | 有效变更: {m} 项",
        rn_warn_confirm: "确定要重命名 {n} 个文件吗?",
        msg_bulkrename_done: "已重命名 {n} 个项目。",
        msg_rn_all_skipped: "❌ 所有项目均因重名被跳过,未执行任何操作。",
        msg_rn_fail_count: "跳过已重名 {n} 个项目",
        msg_prune_confirm: "是否开始搜索当前列表中的空文件夹?",
        msg_prune_none: "未发现空文件夹。",
        msg_prune_found: "发现了 {n} 个空文件夹。\n是否立即删除?",
        msg_deleting_folders: "正在删除 {n} 个文件夹...",
        msg_global_warn: "即将开始全盘文件同步。\n\n文件同步后缓存到本地内存中,网页刷新前持续存在。\n\n是否继续?",
        msg_init_scan_sel: "正在初始化选中项扫描...",
        warn_clear_history: "确定要从历史记录中移除选中的 {n} 项吗?\n(这不会删除您的云端文件)",
        msg_img_copy_hint: "在新窗口中,请按下 {cmd} 即可搜索。",
        msg_aria2_not_set: "检测到您尚未配置 Aria2,请填写后继续:",
        str_jav_no_match: "(未匹配到番号)",
        msg_unzip_fail: "解压请求失败",
        msg_jszip_fail: "JSZip 加载失败,请检查网络。",
        msg_turbo_activated: "极速模式已激活:脚本已深度接管网页逻辑,确保稳定流畅。",
        msg_console_legal: "严禁商业用途:本项目仅供个人学习与交流使用。",
        msg_ana_warn: "文件夹查重提示:判定基于算法推测,删除前请务必人工核对,以防误删。",
        err_migrate_too_many: "⚠️ 选中项过多\n\n当前请求数据量 ({s}MB) 已超过服务器 4MB 的硬性限制。\n\n【解决方案】:\n请在常规目录中直接选中【文件夹】进行迁移,而不是在全盘透视模式下选中海量独立文件。",
        msg_migrate_quota_err: "⚠️ 目标账号空间不足!\n\n系统提示:{d}\n\n是否保留迁移记录?\n(选\"是\"保留记录,清理空间后刷新页面可重试;选\"否\"则终止本次迁移)",
        msg_migrate_err_keep: "迁移异常:\n{e}\n\n是否保留迁移记录以便稍后重试?\n(选\"否\"将清除记录,不再弹窗)",
        title_migrate_fail: "迁移失败",

        /* --- 错误提示 --- */
        err_invalid_links: "请输入正确的链接",
        err_pwd_format: "密码必须为 4-10 位字母或数字",
        err_invalid_torrent: "无效的种子文件格式",
        err_torrent_complex: "解析复杂度过高,可能是非法文件",
        err_torrent_format: "种子文件结构损坏",
        err_torrent_len: "字段长度解析异常",
        err_torrent_char: "解析遇到非法字符",
        err_share_code_exists: "该分享代码已被占用",
        err_folder_not_ready: "云端文件夹正在创建中,请稍后再试",
        err_item_deleted: "该项目不存在",
        err_network: "网络错误",
        err_clipboard_denied: "剪贴板访问被拒绝",
        err_worker: "工作线程错误",
        err_api: "API 错误",
        err_capture: "截图失败。",
        err_captcha_simple: "验证失败。请在网页列表手动收藏一次文件以完成验证。",
        err_sub_dl_fail: "字幕下载失败: ",
        err_req_blocked: "网络请求失败 (可能被拦截)",
        err_req_timeout: "请求超时",
        err_sub_drop_type: "只解析字幕类型文件",
        err_codec_t1: "无法播放视频编码 ({c})",
        err_codec_t2: "您的浏览器不支持该视频格式。<br>请点击下方按钮调用外部播放器。",
        err_pwd_simple: "密码错误",
        err_task_exists: "任务已存在",
        err_network_break: "图片节点网络断流,请再次点击重试",
        err_no_failed_task: "未选中失败的任务",
        err_unknown: "未知错误",
        err_invalid_regex: "无效的正则表达式",
        err_parent_not_found: "文件夹不存在",
        msg_sys_error: "不允许操作系统文件夹",
        msg_download_fail: "无法获取下载链接。",
        msg_video_fail: "无法获取视频链接。",
        err_star_sync_fail: "星标同步失败",
        err_paste_descendant: "不能移动或复制到当前或当前子目录下",
        err_quota_exceeded: "存储空间不足",
        err_name_exists: "文件名称不能重复",
        err_share_pass: "自定义提取码需为 4-10 位字符",
        err_share_limit: "分享失败:单次最多只能分享 100 个项目",
        err_migrate_limit: "迁移失败:单次最多只能迁移 100 个项目",
        lbl_hard_delete: "彻底删除 (不进入回收站)",
        str_error: "错误",
        str_error_crit: "严重错误",
        str_error_paste: "粘贴错误",
        str_action_failed: "操作失败",
        str_scan_error: "扫描错误",
        err_limit_too_low: "修改失败:新次数 ({n}) 必须大于当前已保存次数 ({s})",
        err_vault_max: "密码金库最多仅支持存储 50 个常用密码",
        err_pwd_len: "单个密码长度不能超过 127 个字符",

        /* --- 帮助文档 --- */
        modal_help_title: "帮助",
        help_desc: `
        <div class="pk-no-scrollbar pk-help-scroll" style="font-size:13px;line-height:1.6;color:var(--pk-fg);text-align:justify;text-justify:inter-ideograph;word-break:break-all;pointer-events:auto;display:block;">
            <div style="margin-bottom:24px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">✨ 体验与导航引擎</b><br>
                • <b>交互重构</b>:在官方功能基础上,界面按 <b>Windows 文件资源管理器</b> 逻辑深度重构,并支持<b>列表视图 / 网格视图一键切换</b>。<br>
                • <b>极速模式</b>:开启后接管原生逻辑,彻底解决海量文件下的卡顿与崩溃问题。<br>
                • <b>高级路径栏</b>:支持滚轮滑动,并通过下拉菜单进行同级切换与定位。全盘搜索、分析套件均集成路径栏,支持路径回显与溯源跳转。<br>
                • <b>体验增强</b>:支持星标、类型等多维排序,一键<b>模糊封面</b>、暗黑皮肤切换,并通过 <b>SWR 策略</b>静默刷新视图,尽量减少闪烁与打断感。<br>
                • <b>后台索引与保护</b>:主页蓝点闪烁表示正在同步全盘索引。系统内置并发操作物理锁,主动拦截冲突操作,降低脏数据与误操作风险。<br>
                <div style="color:var(--pk-fg); opacity:0.6; font-size:12px; margin-top:6px;">* 注:默认文件夹(My Pack)受官方保护,严防误删、复制、移动及重命名。</div>
            </div>
            <div style="margin-bottom:24px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">📂 批量与空间管理</b><br>
                • <b>批量重命名</b>:支持<b>正则替换/删除</b>、<b>剧集流水号</b>、文本<b>格式化</b>、<b>FC2 纯净命名</b>、<b>前缀去广告</b>及基于 MIME 的<b>后缀修复</b>。<br>
                • <b>分析套件</b>:<b>文件分析</b>整合了筛选与查重(哈希/时长/名称三模态);<b>文件夹分析</b>整合了筛选与查重(名称/相似度/包含率三模态);并支持导出当前目录的<b>树状列表</b>。<br>
                • <b>智能整理</b>:删除时支持<b>彻底删除</b>(跳过回收站);一键清理空文件夹;<b>批量解压</b>集成密码自动记忆与智能填充,支持跳过并删除已解压项。<br>
                • <b>资源管理器</b>:自定义<b>文件黑名单</b>一键清理垃圾资源;或作为<b>文件白名单</b>,在批量删除时自动保护。<br>
                <div style="color:var(--pk-fg); opacity:0.6; font-size:12px; margin-top:6px;">* 注:为避免数据同步冲突,处理期间请勿在其他客户端同时改动同一批文件。</div>
            </div>
            <div style="margin-bottom:24px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">🌐 传输与分享中心</b><br>
                • <b>分享管理</b>:支持设定提取次数上限,次数达标后链接自动失效并取消分享。<br>
                • <b>极速上传</b>:支持将本地<b>文件 / 文件夹</b>直接拖拽到网页上传,突破官方部分交互限制,并显著降低大量小文件场景下的传输中断率。<br>
                • <b>上传残留清理</b>:脚本会在异常退出或中断上传后尝试清理残留云端“幽灵文件”,减少目录污染与重复占位。<br>
                • <b>云下载增强</b>:批量离线链接支持<b>自动去重</b>;内置<b>磁链智能清洗引擎</b>,自动抽取 Base32 / Hex 哈希去除污染字符;支持解析 <b>.torrent</b> 种子,并为受限链接提供<b>网页快照兜底方案</b>。<br>
                <div style="color:var(--pk-fg); opacity:0.6; font-size:12px; margin-top:6px;">* 注:提取次数拦截仅在网页保持开启且设备未休眠时生效。</div>
            </div>
            <div style="margin-bottom:24px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">🎬 沉浸式媒体增强</b><br>
                • <b>播放引擎</b>:支持 0.5x-3.0x 倍速、旋转翻转、比例切换、子母画面、从头播放、上一集/下一集、<b>跳过片头片尾</b>、<b>清单循环 / 单集循环 / 播完暂停</b>等增强控制。<br>
                • <b>兼容回退</b>:内置兼容模式与画质回退逻辑,遇到黑屏、当前清晰度不可用或编码兼容性异常时,可自动切换到可播放画质。<br>
                • <b>字幕系统</b>:支持<b>云端字幕</b>、<b>本地字幕文件</b>与<b>在线字幕搜索</b>三路加载;支持字幕显示开关、位置切换、背景透明、大小调整与毫秒级进度偏移。<br>
                • <b>外部直连</b>:支持一键唤起 <b>PotPlayer</b> 外部播放,或直接导出当前视频流链接并复制使用。<br>
                • <b>视觉辅助</b>:支持对图片或视频当前帧执行<b>以图搜图</b>,快速反查封面、演员、番剧或素材来源。<br>
                <div style="color:var(--pk-fg); opacity:0.6; font-size:12px; margin-top:6px;">* 注:播放历史列表持续记录在脚本环境内产生的播放进度。</div>
            </div>
            <div style="margin-bottom:12px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">⚙️ 配置与数据管理</b><br>
                • <b>配置备份</b>:支持将偏好设置、管理规则、密码金库等导出为带数字指纹的 JSON 备份文件;导入时支持<b>智能合并去重</b>,避免重复覆盖。<br>
                • <b>数据清理</b>:支持按需清除<b>全盘索引</b>、<b>偏好设置</b>、<b>管理规则</b>、<b>密码金库</b>与<b>缓存</b>,释放本地空间并提升隐私安全。<br>
                • <b>密码金库</b>:内置常用解压密码集中管理,供批量解压场景自动调用与快速填充。<br>
                • <b>数据迁移</b>:支持将选中项目加密打包,通过自动接管机制在其他账号登录后直接识别并转存,实现数据跨账号无缝迁移。<br>
                <div style="color:var(--pk-fg); opacity:0.6; font-size:12px; margin-top:6px;">* 注:全盘索引会在网页关闭后清空;偏好设置、管理规则与密码金库等数据则会持久化保存。</div>
            </div>
            <div style="margin-bottom:24px;">
                <b style="font-size:14px; color:var(--pk-pri); display:inline-block; margin-bottom:4px;">⚡ 下载与分发</b><br>
                • <b>外部下载</b>:支持通过 RPC 协议将文件一键推送到 <b>Aria2 / Motrix</b> 等下载节点,并可实时检测连接状态。<br>
                • <b>目录结构还原</b>:RPC 推送整个文件夹时可自动恢复云盘中的<b>树状目录结构</b>,避免扁平化后难以整理。<br>
                • <b>分发增强</b>:支持<b>长连接监控</b>、失败后自动导出错误清单,并支持<b>文件夹下载过滤</b>,便于大批量任务精细化下发。<br>
            </div>
            <div style="margin-top:16px; color:#d93025; font-weight:bold; text-align:center; font-size:11px; border-top:1px dashed rgba(217,48,37,0.2); padding-top:12px; letter-spacing:0.5px; opacity:0.9;">
                本项目严格遵循 CC-BY-NC-SA-4.0 协议,严禁用于任何商业用途
            </div>
        </div>`
    },
};

function getStrings() {
    const lang = getLang();
    const base = T_LOCAL.zh || {};
    const local = T_LOCAL[lang] || {};
    const remote = pkRemoteI18n && pkRemoteI18n.lang === lang ? pkRemoteI18n.data : null;
    return mergeI18nPack(mergeI18nPack(base, remote), local);
};

ensureI18nReady();

let cachedCredKey = null;
let cachedCaptchaKey = null;
let memoryCapturedToken = '';

document.addEventListener('pk-token-captured', (e) => {
    memoryCapturedToken = e.detail;
});

function resetHeaderCache() {
    cachedCredKey = null;
    cachedCaptchaKey = null;
    memoryCapturedToken = '';
}

function purgeAllCachesOnLogout() {
    resetHeaderCache();
    if (typeof globalCache !== 'undefined') globalCache.clear();
    if (typeof globalLineageMap !== 'undefined') globalLineageMap.clear();
    if (typeof globalParentIndex !== 'undefined') globalParentIndex.clear();
    if (typeof globalDirtyFolders !== 'undefined') globalDirtyFolders.clear();
    if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.clear();
    if (typeof backgroundQueue !== 'undefined') backgroundQueue.length = 0;
    if (typeof isBackgroundRunning !== 'undefined') isBackgroundRunning = false;
    if (typeof DurationProber !== 'undefined') DurationProber.reset();

    const ui = document.querySelector('.pk-ov');
    if (ui) {
        ui.remove();
        document.body.style.overflow = '';
        document.documentElement.style.overflow = '';
    }

    const btn = document.getElementById('pk-launch');
    if (btn) btn.remove();

    if (typeof pkState !== 'undefined' && pkState) pkState = null;
    if (typeof globalSavedState !== 'undefined') globalSavedState = null;
    if (typeof globalPreloadPromise !== 'undefined') globalPreloadPromise = null;
}

async function confirmedLogout(reason = 'unknown', graceMs = 5000, waitMs = 5200) {
    console.warn(`[Auth] Confirmed logout requested: ${reason}`);

    if (typeof window.pkEnterAuthRecoveryWindow === 'function') {
        window.pkEnterAuthRecoveryWindow(`confirmed-logout:${reason}`, graceMs);
    }

    await sleep(800);

    const isAuthReady = await waitForAuth(waitMs);
    if (isAuthReady) {
        console.warn(`[Auth] Logout aborted, auth recovered: ${reason}`);
        if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();
        return false;
    }

    console.warn(`[Auth] Logout confirmed after recheck: ${reason}`);
    if (typeof purgeAllCachesOnLogout === 'function') purgeAllCachesOnLogout();
    if (!location.href.includes('/login')) window.location.href = 'https://mypikpak.com/drive/login';
    return true;
}

(() => {
    let authCheckTimer = null;
    let authRouteGraceTimer = null;
    let authRecoveryUntil = 0;
    let pageHiddenAt = document.hidden ? Date.now() : 0;
    const AUTH_PURGE_DELAY_MS = 1200;
    const AUTH_ROUTE_GRACE_MS = 4000;
    const AUTH_RESUME_GRACE_MS = 5000;
    const AUTH_RESUME_MIN_HIDDEN_MS = 45000;

    const isLoginRoute = () => location.href.includes('/login') || location.pathname.includes('login');

    const hasCredentialSnapshot = () => {
        if (memoryCapturedToken && memoryCapturedToken.length > 10) return true;
        for (let i = 0; i < localStorage.length; i++) {
            const k = localStorage.key(i);
            if (k && k.startsWith('credentials')) {
                try {
                    const v = JSON.parse(localStorage.getItem(k));
                    if (v && v.access_token) return true;
                } catch {}
            }
        }
        return false;
    };

    const markAuthRecovered = () => {
        authRecoveryUntil = 0;
        if (authRouteGraceTimer) {
            clearTimeout(authRouteGraceTimer);
            authRouteGraceTimer = null;
        }
    };

    const enterAuthRecoveryWindow = (reason = 'unknown', graceMs = AUTH_ROUTE_GRACE_MS) => {
        authRecoveryUntil = Math.max(authRecoveryUntil, Date.now() + graceMs);
        if (authRouteGraceTimer) clearTimeout(authRouteGraceTimer);
        authRouteGraceTimer = setTimeout(() => {
            authRouteGraceTimer = null;
            if (isLoginRoute() && !hasCredentialSnapshot()) {
                console.log(`[Auth Sync] Recovery grace expired (${reason}), auth still missing.`);
                checkAndPurgeAuth(`grace-expired:${reason}`);
            } else {
                console.log(`[Auth Sync] Recovery grace ended (${reason}), auth restored.`);
                markAuthRecovered();
            }
        }, graceMs + 50);
    };

    const checkAndPurgeAuth = (reason = 'unknown') => {
        if (authCheckTimer) clearTimeout(authCheckTimer);
        const extraWait = authRecoveryUntil > Date.now() ? (authRecoveryUntil - Date.now() + 50) : 0;
        authCheckTimer = setTimeout(() => {
            if (hasCredentialSnapshot()) {
                console.log(`[Auth Sync] Auth recovered before purge (${reason}).`);
                markAuthRecovered();
                return;
            }
            console.log(`[Auth Sync] No credentials found after delay (${reason}), executing deep purge.`);
            if (typeof purgeAllCachesOnLogout === 'function') purgeAllCachesOnLogout();
            if (!isLoginRoute()) window.location.href = 'https://mypikpak.com/drive/login';
        }, Math.max(AUTH_PURGE_DELAY_MS, extraWait));
    };

    const handlePageResume = (reason = 'resume') => {
        const hiddenFor = pageHiddenAt ? (Date.now() - pageHiddenAt) : 0;
        pageHiddenAt = 0;
        if (hiddenFor < AUTH_RESUME_MIN_HIDDEN_MS && reason !== 'pageshow-bfcache') return;
        console.log(`[Auth Sync] Page resumed (${reason}, hidden ${hiddenFor}ms), entering recovery grace.`);
        enterAuthRecoveryWindow(`resume:${reason}`, AUTH_RESUME_GRACE_MS);
        checkAndPurgeAuth(`resume:${reason}`);
    };

    document.addEventListener('pk-token-captured', () => {
        const wasRecovering = authRecoveryUntil > Date.now();
        markAuthRecovered();
        if (wasRecovering && typeof window.pkForceManagerReloadAfterAuth === 'function') {
            window.pkForceManagerReloadAfterAuth('token-captured');
        }
    });

    document.addEventListener('visibilitychange', () => {
        if (document.hidden) {
            pageHiddenAt = Date.now();
            return;
        }
        handlePageResume('visibility');
    });

    window.addEventListener('pageshow', (e) => {
        if (e.persisted) {
            handlePageResume('pageshow-bfcache');
            return;
        }
        if (pageHiddenAt) handlePageResume('pageshow');
    });

    window.addEventListener('focus', () => {
        if (pageHiddenAt) handlePageResume('focus');
    });

    window.addEventListener('storage', (e) => {
        if (e.key && (e.key.startsWith('credentials') || e.key.startsWith('captcha') || e.key === 'pk_captured_captcha')) {
            console.log(`[Auth Sync] Token storage change detected (${e.key}), flushing auth cache.`);
            if (!e.newValue) {
                enterAuthRecoveryWindow(`storage-remove:${e.key}`);
                checkAndPurgeAuth(`storage-remove:${e.key}`);
            } else {
                resetHeaderCache();
                markAuthRecovered();
            }
        }
    });

    const _origSetItem = Storage.prototype.setItem;
    Storage.prototype.setItem = function(key, value) {
        _origSetItem.apply(this, arguments);
        if (key && (key.startsWith('credentials') || key.startsWith('captcha') || key === 'pk_captured_captcha')) {
            resetHeaderCache();
            markAuthRecovered();
        }
    };

    const _origRemoveItem = Storage.prototype.removeItem;
    Storage.prototype.removeItem = function(key) {
        _origRemoveItem.apply(this, arguments);
        if (key && (key.startsWith('credentials') || key.startsWith('captcha'))) {
            console.log(`[Auth Sync] Local credential removed (${key}), entering recovery grace.`);
            enterAuthRecoveryWindow(`remove:${key}`);
            checkAndPurgeAuth(`remove:${key}`);
        }
    };

    const checkAuthRoute = () => {
        if (isLoginRoute()) {
            console.log(`[Auth Sync] SPA route changed to /login, entering recovery grace.`);
            enterAuthRecoveryWindow('route-login');
            checkAndPurgeAuth('route-login');
        } else {
            markAuthRecovered();
        }
    };

    const _origPushState = history.pushState;
    history.pushState = function() {
        _origPushState.apply(this, arguments);
        checkAuthRoute();
    };

    const _origReplaceState = history.replaceState;
    history.replaceState = function() {
        _origReplaceState.apply(this, arguments);
        checkAuthRoute();
    };

    window.pkEnterAuthRecoveryWindow = enterAuthRecoveryWindow;
    window.pkMarkAuthRecovered = markAuthRecovered;
    window.pkIsAuthRecoveryActive = () => authRecoveryUntil > Date.now();

    window.addEventListener('popstate', checkAuthRoute);
})();

function getHeaders() {
    const recoveryActive = typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive();
    let token = memoryCapturedToken || '', captcha = '';

    if (recoveryActive) {
        cachedCredKey = null;
        cachedCaptchaKey = null;
    }

    if (!token && cachedCredKey) {
        try {
            const v = JSON.parse(localStorage.getItem(cachedCredKey));
            if (v && v.access_token) token = v.token_type + ' ' + v.access_token;
        } catch {}
    }

    if (!token) {
        for (let i = 0; i < localStorage.length; i++) {
            const k = localStorage.key(i);
            if (k && k.startsWith('credentials')) {
                try {
                    const v = JSON.parse(localStorage.getItem(k));
                    if (v && v.access_token) {
                        token = v.token_type + ' ' + v.access_token;
                        cachedCredKey = k;
                        break;
                    }
                } catch {}
            }
        }
    }

    if (cachedCaptchaKey) {
        try {
            const v = JSON.parse(localStorage.getItem(cachedCaptchaKey));
            if (v) captcha = v.captcha_token;
        } catch {}
    }
    if (!captcha) {
        captcha = localStorage.getItem('pk_captured_captcha') || '';
        if (!captcha) {
            for (let i = 0; i < localStorage.length; i++) {
                const k = localStorage.key(i);
                if (k && k.startsWith('captcha')) {
                    try {
                        const v = JSON.parse(localStorage.getItem(k));
                        if (v) {
                            captcha = v.captcha_token;
                            cachedCaptchaKey = k;
                            break;
                        }
                    } catch {}
                }
            }
        }
    }

    const headers = { 'Content-Type': 'application/json', 'Authorization': token, 'x-device-id': localStorage.getItem('deviceid') || '', 'x-captcha-token': captcha };

    if (headers.Authorization && headers.Authorization.length > 10 && typeof window.pkMarkAuthRecovered === 'function') {
        window.pkMarkAuthRecovered();
    }

    return headers;
}

async function waitForAuth(timeout = 10000) {
    const start = Date.now();
    let probeCount = 0;
    let hardTimeout = timeout + ((typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) ? 3000 : 0);

    while (Date.now() - start < hardTimeout) {
        const h = getHeaders();
        if (h.Authorization && h.Authorization.length > 10) {
            if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();
            return true;
        }

        if ((probeCount++ % 5) === 0 && typeof resetHeaderCache === 'function') {
            resetHeaderCache();
        }

        if (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) {
            hardTimeout = Math.max(hardTimeout, timeout + 3000);
        }

        await sleep((typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) ? 250 : 200);
    }

    return false;
}

async function apiList(parentId, limit = 1000, onProgress, signal, trashed = false, isBackground = false) {
    let all = [], next = null, safe = 5000;
    const TIMEOUT_MS = 25000;
    const filters = trashed ? `&filters=${encodeURIComponent('{"trashed":{"eq":true}}')}&parent_id=*` : `&parent_id=${parentId || ''}`;

    do {
        let pageRetries = 0;
        const MAX_PAGE_RETRIES = 5;
        let pageSuccess = false;

        while (pageRetries < MAX_PAGE_RETRIES && !pageSuccess) {
            if (signal?.aborted) throw new DOMException('Aborted by user', 'AbortError');

            const url = `https://api-drive.mypikpak.com/drive/v1/files?thumbnail_size=SIZE_MEDIUM&limit=${limit}${filters}&with_audit=true&_t=${Date.now()}${next ? `&page_token=${next}` : ''}`;
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
            if (signal) signal.addEventListener('abort', () => controller.abort(), { once: true });

            try {
                const res = await fetch(url, { headers: getHeaders(), signal: controller.signal });
                clearTimeout(timeoutId);

                if (!res.ok) {
                    if (res.status === 404) {
                        console.warn(`[API] 404 Not Found (Skipped): ${parentId || 'Root'}`);
                        return [];
                    }

                    if (res.status === 401 || res.status === 403) {
                        console.warn(`[API] ${res.status} Unauthorized. Throwing hard auth error...`);
                        localStorage.removeItem('pk_captured_captcha');
                        resetHeaderCache();
                        throw new Error(`API Error ${res.status}`);
                    }

                    if (res.status === 400) {
                        console.warn(`[API] 400 Error. Possible captcha intercept.`);
                        localStorage.removeItem('pk_captured_captcha');
                        resetHeaderCache();
                        try { if (typeof showToast !== 'undefined') showToast(getStrings().err_captcha_simple, 'error'); } catch(e){}
                        throw new Error('CAPTCHA_INTERCEPT');
                    }
                    if (res.status === 429) {
                        await sleep(3000 * (pageRetries + 1));
                        throw new Error('RATE_LIMIT');
                    }
                    throw new Error(`HTTP_${res.status}`);
                }

                syncTime(res.headers);

                const data = await res.json();

                if (!data.files && data.next_page_token) {
                    throw new Error('PAGINATION_INCOMPLETE');
                }

                if (data.files) {
                    const validFiles = data.files.filter(f => trashed ? f.trashed : !f.trashed).map(f => minifyFile(f, isBackground));
                    all.push(...validFiles);
                    if (onProgress) onProgress(all.length);

                    if (isBackground && parentId !== undefined && typeof globalCache !== 'undefined') {
                        globalCache.set(parentId, { items: [...all], nextToken: data.next_page_token });
                    }
                }

                next = data.next_page_token;
                pageSuccess = true;
                if (next) await sleep(50);

            } catch (e) {
                clearTimeout(timeoutId);
                pageRetries++;
                safe--;

                let isTimeout = false;
                let errMsg = e.message;
                if (e.name === 'AbortError' && !(signal && signal.aborted)) {
                    isTimeout = true;
                    e = new Error('FETCH_TIMEOUT');
                    errMsg = 'Local Timeout';
                }

                const isNetworkError = e.name === 'TypeError' || errMsg.includes('fetch') || errMsg.includes('PAGINATION') || isTimeout;

                if ((isNetworkError || errMsg === 'AUTH_RETRY') && safe > 0) {
                    const backoff = pageRetries === 1 ? 500 : Math.min(pageRetries * 2000, 10000);
                    console.warn(`[API] Retry ${pageRetries}/${MAX_PAGE_RETRIES} for ${parentId || 'Root'} due to ${errMsg}. Wait ${backoff}ms`);
                    await sleep(backoff);
                    continue;
                }
                throw e;
            }
        }
    } while (next && safe > 0);

    return all;
}

async function apiGet(id) {
    const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/files/${id}?thumbnail_size=SIZE_MEDIUM&_t=${Date.now()}`, { headers: getHeaders() });
    if (!res.ok) throw new Error(`API Error ${res.status}`);
    return res.json();
}

async function apiAction(action, data) {
    const method = action.includes('batch') ? 'POST' : 'PATCH';
    const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/files${action}`, { method: method, headers: getHeaders(), body: JSON.stringify(data) });

    syncTime(res.headers);

    if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        throw new Error(err.error_description || `API Error ${res.status}`);
    }
    return res.json();
};

async function apiStar(ids, star = true) {
    const action = star ? 'star' : 'unstar';
    const url = `https://api-drive.mypikpak.com/drive/v1/files:${action}`;
    const res = await fetch(url, {
        method: 'POST',
        headers: getHeaders(),
        body: JSON.stringify({ "ids": ids })
    });

    syncTime(res.headers);

    if (!res.ok) throw new Error(`API Error ${res.status}`);
    return true;
}

async function apiShareList(limit = 100) {
    let all =[], next = null, safe = 50;
    do {
        const url = `https://api-drive.mypikpak.com/drive/v1/share/list?limit=${limit}&thumbnail_size=SIZE_SMALL&_t=${Date.now()}${next ? `&page_token=${next}` : ''}`;
        const res = await fetch(url, { headers: getHeaders() });
        if (!res.ok) throw new Error(`Share API Error ${res.status}`);
        const json = await res.json();
        const list = json.data || [];
        const normalized = list.map(item => ({
            id: item.share_id,
            kind: 'pikpak#share',
            name: item.title,
            size: item.file_size,
            modified_time: item.create_time,
            icon_link: item.icon_link,
            view_count: item.view_count,
            save_count: item.restore_count,
            limit_count: (function(){
                const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                return store[item.share_id] || parseInt(item.limit_count || 0);
            })(),
            share_status: item.share_status,
            share_status_text: item.share_status_text,
            expiration_days: item.expiration_days,
            expiration_left: item.expiration_left,
            expiration_at: item.expiration_at,
            phrase: item.phrase || "",
            share_url: item.share_url,
            pass_code: item.pass_code,
            parent_id: 'share_root'
        }));

        all.push(...normalized);

        if (!next) {
            const graveyard = JSON.parse(gmGet('pk_expired_shares', '[]'));
            const serverIds = new Set(all.map(x => x.id));
            const phantoms = graveyard.filter(x => !serverIds.has(x.id));
            all.push(...phantoms);
        }

        next = json.next_page_token;
        safe--;
    } while (next && safe > 0);
    const graveyard = JSON.parse(gmGet("pk_expired_shares", "[]"));
    const graveyardIds = new Set(graveyard.map(x => x.id));
    const filteredServerList = all.filter(it => !graveyardIds.has(it.id));
    return [...filteredServerList, ...graveyard];
}

async function apiTaskList(limit = 500, onBatch = null, session = null, signal = null) {
    let totalFetched = 0;
    const state = session || { phase: 'active', nextToken: null, completed: false };

    const fetchList = async (phases, maxCount, startToken = null) => {
        let next = startToken;
        let page = 0;
        const maxPages = 100;

        do {
            if (signal && signal.aborted) break;

            const filters = encodeURIComponent(JSON.stringify({ "phase": { "in": phases } }));
            const url = `https://api-drive.mypikpak.com/drive/v1/tasks?type=offline&limit=${limit}&filters=${filters}&thumbnail_size=SIZE_SMALL&with_reference_resource=true&_t=${Date.now()}${next ? `&page_token=${next}` : ''}`;

            let res = null;
            for (let i = 0; i < 3; i++) {
                try {
                    res = await fetch(url, { headers: getHeaders() });
                    if (res.ok) break;
                    if (res.status === 401 || res.status === 403) throw new Error(`API Error ${res.status}`);
                    if (res.status === 429) await sleep(2000);
                } catch (e) {
                    if (e.message.includes('401') || e.message.includes('403')) throw e;
                    await sleep(1000);
                }
            }

            if (!res || !res.ok) break;

            const data = await res.json();
            const tasks = data.tasks || [];

            if (tasks.length > 0) {
                state.nextToken = data.next_page_token;

                if (onBatch) onBatch(tasks, data.next_page_token, phases.includes('COMPLETE') ? 'done' : 'active');
                totalFetched += tasks.length;
            }

            next = data.next_page_token;
            page++;

            if (totalFetched >= maxCount || page >= maxPages) break;
            if (next) await sleep(50);
        } while (next);

        return next;
    };

    if (state.phase === 'active') {
        const activePhases = "PHASE_TYPE_UNKNOW,PHASE_TYPE_PENDING,PHASE_TYPE_RUNNING,PHASE_TYPE_PAUSED,PHASE_TYPE_ERROR";
        const lastToken = await fetchList(activePhases, 2000, state.nextToken);

        if (!lastToken) {
            state.phase = 'done';
            state.nextToken = null;
        } else {
            return totalFetched;
        }
    }

    if (state.phase === 'done') {
        const donePhases = "PHASE_TYPE_COMPLETE";
        const lastToken = await fetchList(donePhases, 15000, state.nextToken);

        if (!lastToken) {
            state.completed = true;
            state.nextToken = null;
        }
    }

    return totalFetched;
}

async function apiCancelTask(ids, deleteFiles = false) {
    let filesToDelete = [];
    if (deleteFiles && typeof pkState !== 'undefined' && pkState.itemMap) {
        ids.forEach(taskId => {
            const task = pkState.itemMap.get(taskId);
            if (task && task.id === taskId && task.file_id) {
                filesToDelete.push(task.file_id);
            }
        });
    }

    const BATCH_SIZE = 50;
    for (let i = 0; i < ids.length; i += BATCH_SIZE) {
        const chunk = ids.slice(i, i + BATCH_SIZE);
        const idStr = chunk.join(',');
        const url = `https://api-drive.mypikpak.com/drive/v1/tasks?task_ids=${idStr}&delete_files=${deleteFiles}&_t=${Date.now()}`;

        try {
            const res = await fetch(url, { method: 'DELETE', headers: getHeaders() });
            if (!res.ok && res.status !== 404) throw new Error(`Task Del Err ${res.status}`);
        } catch(e) {
            console.warn("Task delete warning:", e);
        }
    }

    if (filesToDelete.length > 0) {
        console.log(`[Task] Also deleting ${filesToDelete.length} associated files...`);
        const trashUrl = `https://api-drive.mypikpak.com/drive/v1/files:batchTrash`;
        for (let i = 0; i < filesToDelete.length; i += 100) {
            const fileChunk = filesToDelete.slice(i, i + 100);
            await fetch(trashUrl, {
                method: 'POST',
                headers: getHeaders(),
                body: JSON.stringify({ ids: fileChunk })
            });
        }
    }

    return true;
}

async function apiAddOfflineTask(url, parentId = '', extraParams = {}) {
    const apiUrl = `https://api-drive.mypikpak.com/drive/v1/files`;

    const payload = {
        kind: "drive#file",
        upload_type: "UPLOAD_TYPE_URL",
        url: { "url": url },
        params: { from: 'manual', with_thumbnail: 'true', ...extraParams }
    };

    if (parentId) {
        payload.parent_id = parentId;
    } else {
        payload.folder_type = 'DOWNLOAD';
    }

    const res = await fetch(apiUrl, {
        method: 'POST',
        headers: getHeaders(),
        body: JSON.stringify(payload)
    });

    if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        if (res.status === 400 && err.error_code === 9) throw new Error(getStrings().err_task_exists);
        throw new Error(err.error_description || `Add Task Error ${res.status}`);
    }
    return await res.json();
}

async function apiGetSharePhrases(shareId) {
    const url = `https://api-drive.mypikpak.com/phrase/v1/content/info/share?id=${shareId}`;
    const res = await fetch(url, { headers: getHeaders() });
    if (!res.ok) return [];
    const json = await res.json();
    return (json.data || []).map(x => x.phrase);
}

async function apiUpdateSharePhrase(shareId, newPhrase, oldPhrase = "") {
    const url = `https://api-drive.mypikpak.com/phrase/v1/content/share`;
    const isDelete = newPhrase === "";
    const isCreate = !oldPhrase && !isDelete;

    const payload = { content: shareId, type: "share" };
    if (isCreate) {
        payload.phrase = newPhrase;
    } else {
        payload.old_phrase = oldPhrase;
        payload.new_phrase = newPhrase;
    }

    const res = await fetch(url, {
        method: isCreate ? 'POST' : 'PATCH',
        headers: getHeaders(),
        body: JSON.stringify(payload)
    });

    if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        if (res.status === 400) throw new Error(getStrings().err_share_code_exists);
        throw new Error(err.error_description || `API Error ${res.status}`);
    }
    return res.json();
}

async function apiUpdateShare(shareId, data) {
    const url = `https://api-drive.mypikpak.com/drive/v1/share`;
    const payload = { share_id: shareId, ...data };

    const res = await fetch(url, {
        method: 'PATCH',
        headers: getHeaders(),
        body: JSON.stringify(payload)
    });
    syncTime(res.headers);

    if (!res.ok) {
        const err = await res.json().catch(()=>({}));
        throw new Error(err.error_description || `API Error ${res.status}`);
    }
    return await res.json();
}

async function apiCancelShare(ids) {
    const url = `https://api-drive.mypikpak.com/drive/v1/share:batchDelete`;
    const res = await fetch(url, {
        method: 'POST',
        headers: getHeaders(),
        body: JSON.stringify({ ids: ids })
    });

    if (!res.ok) {
        console.warn("[Share] Batch delete failed, trying sequential delete...");
        for (const id of ids) {
            await fetch(`https://api-drive.mypikpak.com/drive/v1/share/${id}`, {
                method: 'DELETE',
                headers: getHeaders()
            });
        }
        return true;
    }
    return true;
}

// Original Logic by digbug82. Modification does not grant ownership.
async function coreRecursiveEngine(roots, options) {
    const { signal, onFile, onFolder, onProgress, preferFresh = false } = options;
    const L = getStrings();

    let queue = [...roots];
    let activeTasks = new Set();
    let inFlight = new Set();
    let pendingRetries = 0;

    const stats = {
        folders: 0,
        files: 0,
        retries: 0,
        cacheHits: 0,
        currentConcurrency: 10
    };

    const USER_LIMIT = parseInt(localStorage.getItem('pk_user_limit') || "200");
    const ABSOLUTE_MAX = 128;
    const MIN_CONCURRENCY = 5;

    const processFolder = async (current) => {
        inFlight.add(current.id);
        try {
            let files = [];
            const folderId = current.id === 'root' ? '' : (current.id || '');

            if (!preferFresh && typeof globalCache !== 'undefined' && globalCache.has(folderId)) {
                const cachedData = globalCache.get(folderId);
                if (Array.isArray(cachedData)) {
                    files = cachedData;
                    stats.cacheHits++;
                }
            }

            let isFromNetwork = false;
            let start = 0;
            if (files.length === 0) {
                start = performance.now();
                files = await apiList(folderId, 1000, null, signal, false, true);
                isFromNetwork = true;
                if (typeof globalCache !== 'undefined') globalCache.set(folderId, files);
                if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.add(folderId);
            }

            if (signal && signal.aborted) return;

            const nextSubFolders = [];
            for (const f of files) {
                if (f.kind === 'drive#folder') {
                    const myLineage = [...(current.lineage || []), { id: f.id, name: f.name }];
                    nextSubFolders.push({
                        ...f,
                        lineage: myLineage,
                        depth: (current.depth || 0) + 1,
                        retryCount: 0
                    });
                } else {
                    stats.files++;
                    if (onFile) onFile(f, current);
                }
            }

            if (onFolder) onFolder(current, files, nextSubFolders);
            queue.push(...nextSubFolders);
            stats.folders++;

            if (isFromNetwork) {
                const rtt = performance.now() - start;
                const DYNAMIC_MAX = Math.min(USER_LIMIT, ABSOLUTE_MAX);

                if (rtt < 800) {
                    if (stats.currentConcurrency < DYNAMIC_MAX) stats.currentConcurrency += 1;
                } else if (rtt > 3000) {
                    stats.currentConcurrency = Math.max(MIN_CONCURRENCY, Math.floor(stats.currentConcurrency * 0.8));
                }
            }

        } catch (err) {
            if (err.name === 'AbortError' || (signal && signal.aborted)) return;

            stats.currentConcurrency = Math.max(MIN_CONCURRENCY, Math.floor(stats.currentConcurrency * 0.5));
            stats.retries++;
            current.retryCount = (current.retryCount || 0) + 1;

            pendingRetries++;
            try {
                const backoff = current.retryCount === 1 ? 1000 : Math.min(current.retryCount * 5000, 60000);
                const reason = err.message || "Unknown Error";
                console.warn(`[ZeroLoss] Folder: ${current.name} | Reason: ${reason} | Attempt: ${current.retryCount} | Re-queueing...`);
                stats.isRetrying = true;
                if (onProgress) onProgress(stats);

                await sleep(backoff);
                if (signal && !signal.aborted) {
                    queue.unshift(current);
                }
            } finally {
                stats.isRetrying = false;
                pendingRetries--;
            }
        } finally {
            inFlight.delete(current.id);
            if (onProgress) onProgress(stats);
        }
    };
    while ((queue.length > 0 || inFlight.size > 0 || pendingRetries > 0) && (!signal || !signal.aborted)) {

        while (queue.length > 0 && activeTasks.size < stats.currentConcurrency && (!signal || !signal.aborted)) {
            const folder = queue.pop();

            if (inFlight.has(folder.id) && folder.retryCount === 0) continue;

            const p = processFolder(folder);
            const pWrapper = p.finally(() => activeTasks.delete(pWrapper));
            activeTasks.add(pWrapper);
        }

        if (activeTasks.size > 0) {
            await Promise.race(activeTasks).catch(() => {});
        } else if (pendingRetries > 0 || inFlight.size > 0) {
            await sleep(100);
        }
    }
}

const version = (typeof GM_info !== 'undefined' && GM_info.script) ? GM_info.script.version : "1.0.0";

console.log("%c PikPak Enhancement Master %c v" + version + " %c digbug82 %c CC-BY-NC-SA-4.0 ",
    "color:#fff; background:#1a5eff; padding:3px 0; border-radius:4px 0 0 4px; font-weight:bold;",
    "color:#fff; background:#333; padding:3px 8px;",
    "color:#fff; background:#f57c00; padding:3px 8px; font-weight:bold;",
    "color:#fff; background:#d93025; padding:3px 8px; border-radius:0 4px 4px 0; font-weight:bold;");
console.log("%c" + getStrings().msg_console_legal + "%c", "color:#d93025; font-weight:bold;", "");

function getIcon(item) {
    const isFolder = item.kind === 'drive#folder' ||
                    (item.kind === 'drive#task' && (
                        (item.mime_type && item.mime_type.includes('folder')) ||
                        (item.icon_link && item.icon_link.includes('folder'))
                    ));

    if (isFolder) {
        const isRootMyPack = (item.name === CONF.SYSTEM_FOLDER_NAME) && (!item.parent_id || item.parent_id === '' || item.parent_id === 'root' || item._isSystemRoot);
        if (isRootMyPack) return CONF.typeIcons.systemFolder;
        return CONF.typeIcons.folder;
    }

    const name = (item.name || '').toLowerCase();
    const ext = name.split('.').pop();
    const mime = (item.mime_type || '').toLowerCase();
    const duration = (item.params && item.params.duration) || 0;

    if (ext === 'apk') return CONF.typeIcons.apk;
    if (ext === 'txt') return CONF.typeIcons.text;
    if (ext === 'html' || ext === 'htm') return CONF.typeIcons.web;
    if (['srt', 'vtt', 'ass', 'ssa'].includes(ext)) return CONF.typeIcons.subtitle;
    if (['exe', 'msi', 'bat', 'cmd', 'elf', 'dmg', 'pkg', 'app'].includes(ext)) return CONF.typeIcons.executable;

    const isArchiveExt = ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'iso'].includes(ext);
    if (mime.startsWith('video/') || duration > 0) return CONF.typeIcons.video;
    if (mime.startsWith('image/')) return CONF.typeIcons.image;
    if (mime.startsWith('audio/')) return CONF.typeIcons.audio;
    if (mime === 'application/pdf') return CONF.typeIcons.pdf;
    if (mime.startsWith('text/') || mime.includes('word') || mime.includes('excel') || mime.includes('powerpoint') || mime.includes('officedocument') || mime === 'application/rtf') return CONF.typeIcons.text;

    if (isArchiveExt || mime.includes('zip') || mime.includes('rar') || mime.includes('7z') || mime.includes('tar') || mime.includes('archive') || mime.includes('compressed')) {
        const isUnzipped = item.params && (item.params.global_file_kind === '1' || item.params.global_file_root);
        return isUnzipped ? CONF.typeIcons.archiveUnzipped : CONF.typeIcons.archive;
    }

    return CONF.typeIcons.file;
}

async function openManager(initialCache, preloadPromise) {
    const L = getStrings();
    const lang = getLang();

    function normalizeAriaRpcUrl(url) {
        let u = (url || '').trim();
        if (!u) u = 'http://localhost:6800/jsonrpc';
        if (!/^https?:\/\//i.test(u) && !/^wss?:\/\//i.test(u)) u = 'http://' + u;
        if (!u.includes('/jsonrpc') && !u.includes('?')) {
            u = u.endsWith('/') ? u + 'jsonrpc' : u + '/jsonrpc';
        }
        return u;
    }

    function buildAriaRpcParams(token, params = []) {
        const t = (token || '').trim();
        return t ? [`token:${t}`, ...params] : params;
    }

    function isAriaWsRpcUrl(url) {
        return /^wss?:\/\//i.test(url || '');
    }

    function getAriaRpcError(data) {
        const list = Array.isArray(data) ? data : [data];
        const hit = list.find(x => x && x.error);
        if (!hit) return null;
        const err = hit.error || {};
        return new Error(err.message || `RPC ${err.code || 'Error'}`);
    }

    function aria2RpcHttpRequest(url, payload, timeout = 5000) {
        return new Promise((resolveReq, rejectReq) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url,
                data: JSON.stringify(payload),
                headers: { 'Content-Type': 'application/json' },
                timeout,
                onload: (r) => {
                    if (r.status !== 200) {
                        rejectReq(new Error('HTTP ' + r.status));
                        return;
                    }
                    try {
                        const data = r.responseText ? JSON.parse(r.responseText) : null;
                        const rpcErr = getAriaRpcError(data);
                        if (rpcErr) rejectReq(rpcErr);
                        else resolveReq(data);
                    } catch (e) {
                        rejectReq(new Error('Invalid JSON'));
                    }
                },
                onerror: () => rejectReq(new Error('Network Error')),
                ontimeout: () => rejectReq(new Error('Timeout'))
            });
        });
    }

    function aria2RpcWsRequest(url, payload, timeout = 5000) {
        return new Promise((resolveReq, rejectReq) => {
            let ws = null;
            let settled = false;
            const requests = Array.isArray(payload) ? payload : [payload];
            const pending = new Set(requests.map(req => String(req.id)));
            const results = [];

            const finish = (ok, value) => {
                if (settled) return;
                settled = true;
                clearTimeout(timer);
                try {
                    if (ws && ws.readyState <= 1) ws.close();
                } catch (e) {}
                ok ? resolveReq(value) : rejectReq(value);
            };

            const timer = setTimeout(() => finish(false, new Error('Timeout')), timeout);

            try {
                ws = new WebSocket(url);
            } catch (e) {
                finish(false, e);
                return;
            }

            ws.onopen = () => {
                try {
                    requests.forEach(req => ws.send(JSON.stringify(req)));
                } catch (e) {
                    finish(false, e);
                }
            };

            ws.onmessage = (ev) => {
                try {
                    const data = JSON.parse(ev.data);
                    const list = Array.isArray(data) ? data : [data];
                    const rpcErr = getAriaRpcError(list);
                    if (rpcErr) {
                        finish(false, rpcErr);
                        return;
                    }

                    list.forEach(item => {
                        if (!item || item.id === undefined || item.id === null) return;
                        const id = String(item.id);
                        if (!pending.has(id)) return;
                        pending.delete(id);
                        results.push(item);
                    });

                    if (pending.size === 0) {
                        finish(true, Array.isArray(payload) ? results : results[0]);
                    }
                } catch (e) {
                    finish(false, new Error('Invalid JSON'));
                }
            };

            ws.onerror = () => finish(false, new Error('Network Error'));
            ws.onclose = () => {
                if (!settled) finish(false, new Error('Network Error'));
            };
        });
    }

    function aria2RpcRequest(url, payload, timeout = 5000) {
        const rpcUrl = normalizeAriaRpcUrl(url);
        return isAriaWsRpcUrl(rpcUrl) ? aria2RpcWsRequest(rpcUrl, payload, timeout) : aria2RpcHttpRequest(rpcUrl, payload, timeout);
    }

    if (document.querySelector('.pk-ov')) return;

    document.body.style.overflow = 'hidden';
    document.documentElement.style.overflow = 'hidden';

    const S = {
        path: (globalSavedState && globalSavedState.path) ? [...globalSavedState.path] : [{ id: '', name: L.btn_nav_home }],
        items: [], itemMap: new Map(), display: [], sel: new Set(), selMode: 'explicit', selEx: new Set(),
        cache: initialCache || new Map(),
        sort: (globalSavedState && globalSavedState.sort) ? globalSavedState.sort : 'modified_time',
        dir: (globalSavedState && globalSavedState.dir !== undefined) ? globalSavedState.dir : 1,

        scanning: false, dupMode: false, dupRunning: false,
        folderFirst: false,
        dupReasons: new Map(),
        dupGroups: new Map(),
        dupRawGroups: [],
        dupGridMeta: null,
        dupGridMetaKey: '',
        offlineFilters: { running: true, failed: true, complete: true },
        uploadFilters: { running: true, paused: true, complete: true },
        activeId: null,
        clipItems: [], clipType: '',
        clipSourceParentId: null,
        loading: false,
        lastSelIdx: -1,
        dupConfig: { video: true, image: true, other: true },
        search: '',
        viewMode: (globalSavedState && globalSavedState.viewMode) ? globalSavedState.viewMode : (() => {
            if (gmGet('pk_view_independent', false)) {
                try {
                    const prefs = JSON.parse(gmGet('pk_folder_view_prefs', '{}'));
                    const saved = prefs.root;
                    const mode = typeof saved === 'string' ? saved : (saved && saved.viewMode);
                    return mode === 'list' ? 'list' : 'grid';
                } catch(e) {
                    return 'grid';
                }
            }
            return gmGet('pk_file_view_mode', 'grid') === 'list' ? 'list' : 'grid';
        })(),
        sortId: 0,
        isFlattened: false,
        filterState: { active: false, cat: 'all', ext: 'all' },
        suppressClearConfirm: false,
        preSearchPath: null,
        lastGlobalResults: [],
        folderLineageMap: globalLineageMap,
        strictFolderFirstSnapshot: null,
        trashMode: (globalSavedState && globalSavedState.trashMode) || false,
        shareMode: (globalSavedState && globalSavedState.shareMode) || false,
        starredMode: (globalSavedState && globalSavedState.starredMode) || false,
        recentMode: (globalSavedState && globalSavedState.recentMode) || false,
        historyMode: (globalSavedState && globalSavedState.historyMode) || false,
        offlineMode: (globalSavedState && globalSavedState.offlineMode) || false,
        uploadMode: (globalSavedState && globalSavedState.uploadMode) || false,
        navBuckets: {
            home: { backStack: [], current: null, forwardStack: [] },
            starred: { backStack: [], current: null, forwardStack: [] },
            recent: { backStack: [], current: null, forwardStack: [] }
        },
        navScrollStore: {
            home: Object.create(null),
            starred: Object.create(null),
            recent: Object.create(null)
        },
        navActiveBucket: null,
        navFrozenBucket: null,
        navContext: 'none',
        navSyncSig: '',
        navMaxHistory: CONF.mouseSideNavHistoryMax,
        navSuspendRecord: false,
        navTransitionBusy: false,
        navLastModalType: '',
        navVideoBackArmed: false,
        scanId: 0,
        preloaded: (globalSavedState || (initialCache && initialCache.has('root'))) ? true : false,
        preLoadPromise: preloadPromise || null,
        blSet: new Set(),
        blFolderSet: new Set(),
        starredSet: new Set(),
        pendingMap: new Map(),
        durationMap: new Map(),
        loadedThumbs: new Set(),
        movingIds: new Set(),
        movingSourceId: null,
        movingDestId: null,
        uploadTasks: (globalSavedState && globalSavedState.uploadTasks) ? globalSavedState.uploadTasks : [],
        broadcast: new BroadcastChannel('pk_act_sync'),
        getNavBucketKey: () => {
            if (S.starredMode) return 'starred';
            if (S.recentMode) return 'recent';
            if (S.trashMode || S.shareMode || S.historyMode || S.offlineMode || S.uploadMode) return null;
            if (S.isFlattened || S.dupMode || S.analyzeMode) return null;

            const cur = Array.isArray(S.path) && S.path.length ? S.path[S.path.length - 1] : null;
            if (cur && typeof cur.id === 'string' && (cur.id.startsWith('virtual_') || cur.id === 'analyze_root')) return null;

            return 'home';
        },
        getActiveNavBucket: () => {
            const key = S.getNavBucketKey();
            return key ? S.navBuckets[key] : null;
        },
        cloneNavPath: (path = S.path) => (Array.isArray(path) ? path : []).map(n => ({ id: n.id, name: n.name })),
        getNavPathSig: (path = S.path) => {
            const arr = Array.isArray(path) ? path : [];
            return arr.map(n => n && n.id || '').join('>');
        },
        saveNavScrollTop: (bucketKey = null, path = S.path, scrollTop = null) => {
            const key = bucketKey || S.getNavBucketKey() || S.navActiveBucket || null;
            if (!key || !S.navScrollStore[key] || !UI.vp) return;
            const sig = S.getNavPathSig(path);
            if (!sig) return;
            S.navScrollStore[key][sig] = Math.max(0, Math.round(scrollTop == null ? UI.vp.scrollTop : scrollTop));
        },
        getNavScrollTop: (bucketKey = null, path = S.path) => {
            const key = bucketKey || S.getNavBucketKey() || S.navActiveBucket || null;
            if (!key || !S.navScrollStore[key]) return null;
            const sig = S.getNavPathSig(path);
            if (!sig) return null;
            const v = S.navScrollStore[key][sig];
            return Number.isFinite(v) ? v : null;
        },
        restoreNavScrollTop: (bucketKey = null, path = S.path) => {
            if (!gmGet('pk_keep_pos', true) || !UI.vp) return;
            const savedTop = S.getNavScrollTop(bucketKey, path);
            if (!Number.isFinite(savedTop)) return;
            requestAnimationFrame(() => {
                if (!UI.vp) return;
                const maxTop = Math.max(0, UI.vp.scrollHeight - UI.vp.clientHeight);
                UI.vp.scrollTop = Math.max(0, Math.min(savedTop, maxTop));
                if (typeof renderVisible === 'function') renderVisible();
            });
        },
        isSameNavPath: (a, b) => {
            if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
            for (let i = 0; i < a.length; i++) {
                if ((a[i] && a[i].id) !== (b[i] && b[i].id)) return false;
            }
            return true;
        },
        trimNavStack: (stack) => {
            if (!Array.isArray(stack) || stack.length <= S.navMaxHistory) return;
            stack.splice(0, stack.length - S.navMaxHistory);
        },
        getNavContext: () => {
            if (S.isStrictVirtualNavMode()) return 'virtual';
            const key = S.getNavBucketKey();
            return key || 'none';
        },
        recordNavSnapshot: (bucketKey, path = S.path) => {
            const bucket = bucketKey ? S.navBuckets[bucketKey] : null;
            if (!bucket) return;

            const snap = S.cloneNavPath(path);
            if (!snap.length) return;

            if (bucket.current && S.isSameNavPath(bucket.current, snap)) return;

            if (bucket.current && bucket.current.length) {
                bucket.backStack.push(S.cloneNavPath(bucket.current));
                S.trimNavStack(bucket.backStack);
            }

            bucket.current = snap;
            bucket.forwardStack = [];
        },
        restoreFrozenNavBucketPath: () => {
            const key = S.navFrozenBucket;
            const bucket = key ? S.navBuckets[key] : null;
            if (!bucket || !bucket.current || !bucket.current.length) return false;

            S.navSuspendRecord = true;
            S.path = S.cloneNavPath(bucket.current);
            S.navActiveBucket = key;
            S.navContext = key;
            S.navSyncSig = '';
            queueMicrotask(() => { S.navSuspendRecord = false; });
            return true;
        },
        loadNavPath: async (path, bucketKey = null, opts = null) => {
            const targetPath = S.cloneNavPath(path);
            if (!targetPath.length) return false;
            if (S.navTransitionBusy) return false;

            const key = bucketKey || S.getNavBucketKey() || S.navActiveBucket || null;
            const skipNavSync = !!(opts && opts.skipNavSync);

            const prevPath = S.cloneNavPath(S.path);
            const prevActiveBucket = S.navActiveBucket;
            const prevContext = S.navContext;
            const prevSyncSig = S.navSyncSig;
            const prevSuspendRecord = S.navSuspendRecord;
            const prevScrollTop = UI.vp ? UI.vp.scrollTop : 0;

            const prevSelMode = S.selMode;
            const prevSel = new Set(S.sel);
            const prevSelEx = new Set(S.selEx);
            const prevLastSelIdx = S.lastSelIdx;
            const prevActiveIdForRollback = S.activeId;

            const targetSig = S.getNavPathSig(targetPath);

            S.navTransitionBusy = true;
            S.navSuspendRecord = true;

            S.saveNavScrollTop(prevActiveBucket || S.getNavBucketKey(), prevPath, prevScrollTop);

            S.path = targetPath;
            S.navActiveBucket = key;
            S.navContext = key || 'none';
            S.navSyncSig = '';

            try {
                updateQuotaUI();

                const pendingLoad = Promise.resolve(load());
                await new Promise(r => requestAnimationFrame(r));
                S.clearSelection();

                pendingLoad.then(() => {
                    if (S.getNavPathSig() === targetSig) {
                        S.restoreNavScrollTop(key, targetPath);
                    }
                }).catch(err => {
                    S.sideNavLog('loadNavPath async error', err);
                });

                return true;
            } catch (err) {
                S.sideNavLog('loadNavPath rollback', err);
                S.path = prevPath;
                S.navActiveBucket = prevActiveBucket;
                S.navContext = prevContext;
                S.navSyncSig = prevSyncSig || '';

                S.selMode = prevSelMode;
                S.sel = new Set(prevSel);
                S.selEx = new Set(prevSelEx);
                S.lastSelIdx = prevLastSelIdx;
                S.activeId = prevActiveIdForRollback;
                if (typeof updateStat === 'function') updateStat();

                try {
                    updateQuotaUI();
                    await load();
                    S.restoreNavScrollTop(prevActiveBucket || S.getNavBucketKey(), prevPath);
                } catch (restoreErr) {
                    S.sideNavLog('loadNavPath restore failed', restoreErr);
                }
                return false;
            } finally {
                S.navSuspendRecord = prevSuspendRecord;
                S.navTransitionBusy = false;
                if (!skipNavSync) {
                    S.navSyncSig = '';
                    S.syncNavContextState();
                }
            }
        },
        navGoBack: async () => {
            const key = S.getNavBucketKey();
            const bucket = key ? S.navBuckets[key] : null;
            if (!bucket || !bucket.backStack.length || S.navTransitionBusy) return false;

            const prevPath = S.cloneNavPath(bucket.backStack[bucket.backStack.length - 1]);
            if (!prevPath.length) return false;

            const currentPath = bucket.current && bucket.current.length ? S.cloneNavPath(bucket.current) : S.cloneNavPath(S.path);
            const ok = await S.loadNavPath(prevPath, key, { skipNavSync: true });
            if (!ok) return false;

            bucket.backStack.pop();
            if (currentPath.length) {
                bucket.forwardStack.push(currentPath);
                S.trimNavStack(bucket.forwardStack);
            }
            bucket.current = S.cloneNavPath(prevPath);
            S.navActiveBucket = key;
            S.navContext = key;
            S.navFrozenBucket = null;
            S.navSyncSig = '';
            S.syncNavContextState();
            return true;
        },
        navGoForward: async () => {
            const key = S.getNavBucketKey();
            const bucket = key ? S.navBuckets[key] : null;
            if (!bucket || !bucket.forwardStack.length || S.navTransitionBusy) return false;

            const nextPath = S.cloneNavPath(bucket.forwardStack[bucket.forwardStack.length - 1]);
            if (!nextPath.length) return false;

            const currentPath = bucket.current && bucket.current.length ? S.cloneNavPath(bucket.current) : S.cloneNavPath(S.path);
            const ok = await S.loadNavPath(nextPath, key, { skipNavSync: true });
            if (!ok) return false;

            bucket.forwardStack.pop();
            if (currentPath.length) {
                bucket.backStack.push(currentPath);
                S.trimNavStack(bucket.backStack);
            }
            bucket.current = S.cloneNavPath(nextPath);
            S.navActiveBucket = key;
            S.navContext = key;
            S.navFrozenBucket = null;
            S.navSyncSig = '';
            S.syncNavContextState();
            return true;
        },
        exitVirtualNavMode: async () => {
            const cur = Array.isArray(S.path) && S.path.length ? S.path[S.path.length - 1] : null;
            const isAnalyzeGroupedGrid = S.analyzeMode && !!S.analyzeSimGroups && !!cur && cur.id === 'analyze_root' && S.viewMode === 'grid';
            const shouldSyncGroupedGridExit = (S.dupMode && S.viewMode === 'grid') || isAnalyzeGroupedGrid;

            S.scanning = false;
            S.dupMode = false;
            S.suppressClearConfirm = false;
            S.isFlattened = false;
            S.scanFilter = null;
            if (S.filterState) S.filterState = { active: false, cat: 'all', ext: 'all' };
            if (shouldSyncGroupedGridExit) S._groupedGridExitSyncPending = true;

            S._sortAppliedForId = null;
            S._comicApplied = false;

            if (S.analyzeMode) {
                S.analyzeMode = false;
                S.analyzeResultItems = null;
                S.analyzeSimGroups = null;
                S.analyzeMap = null;
            }

            S.dupRawGroups = [];
            S.dupBuckets = null;
            S.dupItemMap = null;
            S.pinnedDupPath = null;

            if (UI.selDupFolder) UI.selDupFolder.value = "";
            const invertChk = document.getElementById('pk-dup-invert');
            if (invertChk) {
                invertChk.checked = false;
                invertChk.disabled = true;
                invertChk.parentNode.style.opacity = '0.5';
            }
            if (UI.chkSearchPath) UI.chkSearchPath.checked = false;

            isGUISensitive = false;

            S.sort = 'modified_time';
            S.dir = 1;

            if (UI.crumb) UI.crumb.style.display = '';

            const frozenKey = S.navFrozenBucket;
            const frozenBucket = frozenKey ? S.navBuckets[frozenKey] : null;
            const fallbackPath = [{ id: '', name: L.btn_nav_home }];
            const targetPath = (frozenBucket && frozenBucket.current && frozenBucket.current.length)
                ? frozenBucket.current
                : fallbackPath;

            S.navFrozenBucket = null;

            return await S.loadNavPath(targetPath, frozenBucket ? frozenKey : null);
        },
        getTopModalOverlay: () => {
            const list = Array.from(document.querySelectorAll('.pk-modal-ov')).filter(m => {
                if (!m || !m.isConnected) return false;
                const st = window.getComputedStyle(m);
                return st.display !== 'none' && st.visibility !== 'hidden';
            });
            return list.length ? list[list.length - 1] : null;
        },
        hasFrontOverlayForSideNav: () => {
            return !!(document.getElementById('pk-player-ov') || document.querySelector('.pk-img-ov') || S.getTopModalOverlay());
        },
        isManagerSideNavScope: () => {
            const win = document.querySelector('.pk-ov');
            return !!(win && win.style.display !== 'none');
        },
        sideNavLog: (...args) => {
            if (!CONF.mouseSideNavDebug) return;
            console.log('[PK SideNav]', ...args);
        },
        closePlayerOverlay: () => {
            const player = document.getElementById('pk-player-ov');
            if (!player) return false;

            S.navVideoBackArmed = false;

            const pTip = document.getElementById('pk_p_plist_tip_global');
            if (pTip) pTip.style.display = 'none';

            if (typeof player._pkDestroyPlayer === 'function') {
                player._pkDestroyPlayer();
                S.sideNavLog('player close: unified destroy');
                return true;
            }

            const v = player.querySelector('#pk_video');
            if (v) {
                try {
                    v.pause();
                    v.removeAttribute('src');
                    v.load();
                } catch (e) {}
            }

            player.remove();
            S.sideNavLog('player close: unified');
            return true;
        },
        closeImageOverlay: () => {
            const d = document.querySelector('.pk-img-ov');
            if (!d) return false;

            const pTip = document.getElementById('pk_p_plist_tip_global');
            if (pTip) pTip.style.display = 'none';

            const cleanup = d._pkResizeHandler;
            if (cleanup) {
                window.removeEventListener('resize', cleanup);
                d._pkResizeHandler = null;
            }

            const imgList = d._pkImgList || [];
            const curIdx = Number.isInteger(d._pkCurIdx) ? d._pkCurIdx : -1;
            const currentItem = curIdx >= 0 ? imgList[curIdx] : null;

            if (currentItem) {
                const targetId = currentItem.id;
                const targetIdx = S.display.findIndex(x => x.id === targetId);
                if (targetIdx !== -1) {
                    S.sel.clear();
                    S.sel.add(targetId);
                    S.activeId = targetId;
                    const rowTop = getItemScrollTopByIndex(targetIdx);
                    const vpHeight = UI.vp.clientHeight;
                    UI.vp.scrollTop = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));
                    renderVisible();
                    updateStat();
                }
            }

            d.remove();
            S.sideNavLog('image close: unified');
            return true;
        },
        closeTopModalOverlay: () => {
            const openModal = S.getTopModalOverlay();
            if (!openModal) return false;
            const closeBtn = openModal.querySelector('.pk-modal-close');
            if (closeBtn) closeBtn.click();
            else openModal.remove();
            return true;
        },
        handleOverlayMouseSideBack: () => {
            const player = document.getElementById('pk-player-ov');
            if (player) {
                S.sideNavLog('back: video close');
                return S.closePlayerOverlay();
            }

            const imgPlayer = document.querySelector('.pk-img-ov');
            if (imgPlayer) {
                S.sideNavLog('back: image close');
                return S.closeImageOverlay();
            }

            if (S.closeTopModalOverlay()) {
                S.sideNavLog('back: modal close');
                return true;
            }

            return false;
        },
        canHandleMouseSideBack: () => S.isManagerSideNavScope(),
        canHandleMouseSideForward: () => S.isManagerSideNavScope(),
        handleMouseSideBack: async () => {
            if (!S.isManagerSideNavScope() || S.navTransitionBusy) return false;
            if (S.handleOverlayMouseSideBack()) return true;
            if (S.isStrictVirtualNavMode()) {
                await S.exitVirtualNavMode();
                return true;
            }
            if (S.getNavBucketKey() !== null) {
                await S.navGoBack();
                return true;
            }
            return true;
        },
        handleMouseSideForward: async () => {
            if (!S.isManagerSideNavScope() || S.navTransitionBusy) return false;

            const player = document.getElementById('pk-player-ov');
            if (player) {
                const v = player.querySelector('#pk_video');
                if (v) {
                    if (v.ended) {
                        S.sideNavLog('forward: video restart');
                        try { v.currentTime = 0; } catch (e) {}
                        v.play().catch(()=>{});
                        return true;
                    }
                    if (v.paused) {
                        S.sideNavLog('forward: video play');
                        v.play().catch(()=>{});
                        return true;
                    }
                    S.sideNavLog('forward: video pause');
                    v.pause();
                    return true;
                }
                return true;
            }

            if (S.hasFrontOverlayForSideNav()) return true;
            if (S.isStrictVirtualNavMode()) return true;
            if (S.getNavBucketKey() !== null) {
                await S.navGoForward();
                return true;
            }
            return true;
        },
        syncNavContextState: () => {
            const ctx = S.getNavContext();
            const sig = `${ctx}|${(Array.isArray(S.path) ? S.path.map(n => n.id).join('>') : '')}`;

            if (S.navSyncSig === sig) return;

            const prevCtx = S.navContext;
            const prevBucketKey = (prevCtx === 'home' || prevCtx === 'starred' || prevCtx === 'recent') ? prevCtx : null;
            const curBucketKey = (ctx === 'home' || ctx === 'starred' || ctx === 'recent') ? ctx : null;

            if (S.navSuspendRecord) {
                S.navContext = ctx;
                S.navSyncSig = sig;
                if (curBucketKey) S.navActiveBucket = curBucketKey;
                return;
            }

            if (ctx === 'virtual') {
                if (prevBucketKey) S.navFrozenBucket = prevBucketKey;
                S.navContext = 'virtual';
                S.navSyncSig = sig;
                return;
            }

            if (ctx === 'none') {
                if (prevBucketKey) S.navFrozenBucket = prevBucketKey;
                S.navContext = 'none';
                S.navSyncSig = sig;
                return;
            }

            S.navActiveBucket = curBucketKey;
            S.recordNavSnapshot(curBucketKey);

            if (prevCtx === 'virtual' && S.navFrozenBucket === curBucketKey) {
                S.navFrozenBucket = null;
            } else if (prevCtx !== 'virtual') {
                S.navFrozenBucket = null;
            }

            S.navContext = curBucketKey;
            S.navSyncSig = sig;
        },
        canUseMouseSideNav: () => S.canHandleMouseSideBack() || S.canHandleMouseSideForward(),
        isStrictVirtualNavMode: () => {
            if (S.isFlattened || S.dupMode || S.analyzeMode) return true;
            const cur = Array.isArray(S.path) && S.path.length ? S.path[S.path.length - 1] : null;
            return !!(cur && typeof cur.id === 'string' && (cur.id.startsWith('virtual_') || cur.id === 'analyze_root'));
        },
        getSelectableItems: () => {
            const out = [];
            const list = Array.isArray(S.display) ? S.display : [];
            for (let i = 0; i < list.length; i++) {
                const item = list[i];
                if (!item || item.isHeader) continue;
                if (S.movingIds && S.movingIds.has(item.id)) continue;
                out.push(item);
            }
            return out;
        },
        getSelectableIds: () => {
            const out = [];
            const list = Array.isArray(S.display) ? S.display : [];
            for (let i = 0; i < list.length; i++) {
                const item = list[i];
                if (!item || item.isHeader) continue;
                if (S.movingIds && S.movingIds.has(item.id)) continue;
                out.push(item.id);
            }
            return out;
        },
        getSelectableCount: () => {
            let count = 0;
            const list = Array.isArray(S.display) ? S.display : [];
            for (let i = 0; i < list.length; i++) {
                const item = list[i];
                if (!item || item.isHeader) continue;
                if (S.movingIds && S.movingIds.has(item.id)) continue;
                count++;
            }
            return count;
        },
        isAllSelectionMode: () => S.selMode === 'all',
        isSelected: (id) => {
            if (!id) return false;
            return S.selMode === 'all' ? !S.selEx.has(id) : S.sel.has(id);
        },
        getSelectedCount: () => {
            if (S.selMode !== 'all') return S.sel.size;
            const total = S.getSelectableCount();
            return Math.max(0, total - S.selEx.size);
        },
        getSelectedIds: () => {
            if (S.selMode !== 'all') return Array.from(S.sel);
            const ids = [];
            const list = S.getSelectableIds();
            for (let i = 0; i < list.length; i++) {
                const id = list[i];
                if (!S.selEx.has(id)) ids.push(id);
            }
            return ids;
        },
        setSelected: (id, selected) => {
            if (!id) return;
            if (S.selMode === 'all') {
                if (selected) S.selEx.delete(id);
                else S.selEx.add(id);
                return;
            }
            if (selected) S.sel.add(id);
            else S.sel.delete(id);
        },
        toggleSelected: (id) => {
            if (!id) return false;
            const next = !S.isSelected(id);
            S.setSelected(id, next);
            return next;
        },
        setExplicitSelection: (ids) => {
            S.selMode = 'explicit';
            S.selEx.clear();
            S.sel = new Set(ids || []);
        },
        setAllSelection: (enabled = true) => {
            if (enabled) {
                S.selMode = 'all';
                S.sel.clear();
                S.selEx.clear();
            } else {
                S.selMode = 'explicit';
                S.sel.clear();
                S.selEx.clear();
            }
        },
        invertSelection: () => {
            if (S.loading) return;

            const total = S.getSelectableCount();
            if (total <= 0) {
                S.clearSelection();
                return;
            }

            if (S.selMode === 'all') {
                S.setExplicitSelection(Array.from(S.selEx));
            } else {
                const nextExcluded = new Set(S.sel);
                S.selMode = 'all';
                S.sel.clear();
                S.selEx = nextExcluded;
            }

            S.lastSelIdx = -1;
            S.activeId = null;
        },
        clearSelection: () => {
            S.selMode = 'explicit';
            S.sel.clear();
            S.selEx.clear();
            S.lastSelIdx = -1;
            S.activeId = null;
            if (typeof updateStat === 'function') updateStat();
        },
        updateBlCache: () => {
            const parse = (key) => new Set(gmGet(key, '').toLowerCase().split('\n').map(s => s.trim()).filter(s => s));
            S.blSet = parse('pk_blacklist');
            S.blFolderSet = parse('pk_blacklist_folders');
        },
        getRealCacheKey: (id) => {
            if (id === 'offline_root') return 'offline_root';
            if (id === 'recent_root') return 'recent_root';
            if (id === 'history_root') return 'history_root';
            if (id === 'root' || id === '' || !id) {
                if (S.shareMode) return 'share_root';
                if (S.starredMode) return 'starred_root';
                if (S.recentMode) return 'recent_root';
                if (S.historyMode) return 'history_root';
                if (S.trashMode) return 'root_trashed';
                if (S.offlineMode) return 'offline_root';
                return 'root';
            }
            return id;
        }
    };
    pkState = S;

    if (S.uploadTasks.length > 0) {
        S.uploadTasks.forEach(task => {
            if (task.status === 'WAITING') task.message = L.msg_task_waiting;
            else if (task.status === 'DONE') task.message = L.msg_task_upload_done;
            else if (task.status === 'PAUSED') task.message = L.msg_task_paused;
            else if (task.status === 'HASHING') task.message = L.msg_task_hashing;
            else if (task.status === 'UPLOADING') task.message = L.msg_task_uploading;
        });
    }

    const FloatBarManager = (() => {
        const activeBars = [];
        const BASE_BOTTOM = 30;
        const GAP = 10;

        const reposition = () => {
            let currentBottom = BASE_BOTTOM;
            activeBars.forEach(item => {
                item.el.style.bottom = `${currentBottom}px`;
                currentBottom += (item.el.offsetHeight || 40) + GAP;
            });
        };

        const create = (initialText) => {
            const id = 'pk_fb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
            const el = document.createElement('div');
            el.className = 'pk-float-bar-item';
            el.style.cssText = `
                position: fixed; left: 50%; transform: translateX(-50%);
                background: var(--pk-toast-bg); color: var(--pk-toast-fg);
                padding: 10px 24px; border-radius: 30px; font-size: 13px; font-weight: 600;
                box-shadow: 0 10px 30px var(--pk-tip-sd); z-index: 2147483647 !important;
                border: 1px solid var(--pk-toast-bd); display: flex; align-items: center; gap: 12px;
                transition: bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
                opacity: 0; pointer-events: none; white-space: nowrap; backdrop-filter: blur(8px);
            `;

            el.innerHTML = `<div class="pk-spin-lg" style="width:16px; height:16px; border-width:2px;"></div><span class="pk-float-txt">${esc(initialText)}</span>`;

            const isDark = document.querySelector('.pk-ov')?.classList.contains('pk-dark');
            if (isDark) el.classList.add('pk-dark');

            const fsEl = document.fullscreenElement || document.webkitFullscreenElement;
            if (fsEl) fsEl.appendChild(el);
            else document.body.appendChild(el);

            const entry = { id, el };
            activeBars.push(entry);

            requestAnimationFrame(() => {
                reposition();
                el.style.opacity = '1';
            });

            return {
                update: (text) => {
                    const span = el.querySelector('.pk-float-txt');
                    if (span) span.textContent = text;
                },
                destroy: () => {
                    const idx = activeBars.findIndex(x => x.id === id);
                    if (idx !== -1) {
                        activeBars.splice(idx, 1);
                        el.style.opacity = '0';
                        el.style.transform = 'translateX(-50%) scale(0.9)';
                        reposition();
                        setTimeout(() => el.remove(), 300);
                    }
                }
            };
        };

        return { create };
    })();

    S.broadcast.onmessage = (e) => {
        const { type, ids, src, dst } = e.data;
        if (!ids) return;

        if (type === 'LOCK_ADD') {
            ids.forEach(id => S.movingIds.add(id));
            if (src) S.movingSourceId = src;
            if (dst) S.movingDestId = dst;
        }
        else if (type === 'LOCK_REM') {
            ids.forEach(id => S.movingIds.delete(id));
        }
        else if (type === 'LOCK_CLR') {
            S.movingIds.clear();
            S.movingSourceId = null;
            S.movingDestId = null;
        }

        if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();
        if (typeof updateStat === 'function') updateStat();
    };

    const updateGlobalLockCSS = () => {
        let style = document.getElementById('pk-lock-css');
        if (!style) {
            style = document.createElement('style');
            style.id = 'pk-lock-css';
            document.head.appendChild(style);
        }

        if (!S.movingIds || S.movingIds.size === 0 || !S.movingSourceId) {
            style.textContent = '';
            return;
        }

        const selector = Array.from(S.movingIds)
            .map(id => `.pk-win .pk-row[data-id="${id}"]`)
            .join(',');

        style.textContent = `${selector} {
            opacity: 0.4 !important;
            filter: grayscale(100%) !important;
            pointer-events: none !important;
            cursor: wait !important;
        }`;
    };

    const isPathBusy = (targetFolderId) => {
        if (!S.movingIds || S.movingIds.size === 0) return false;

        const sourceId = S.movingSourceId;
        const destId = S.movingDestId;
        const tid = targetFolderId === 'root' ? '' : (targetFolderId || '');

        if (tid === '') return true;

        if (tid === sourceId || tid === destId) return true;

        const checkAncestry = (startId, forbiddenId) => {
            let curr = startId;
            let safety = 50;
            while (curr && curr !== 'root' && safety > 0) {
                if (curr === forbiddenId) return true;
                const parent = globalParentIndex.get(curr);
                curr = parent ? parent.id : null;
                safety--;
            }
            return false;
        };

        if (checkAncestry(sourceId, tid) || checkAncestry(destId, tid)) return true;

        if (checkAncestry(tid, sourceId) || checkAncestry(tid, destId)) return true;

        return false;
    };

    const resumeBackgroundDiscovery = () => {
        if (S.scanning || S.loading) {
            return;
        }

        const isVirtual = S.isFlattened || S.dupMode || S.path.some(p => p.id === 'virtual_search_root');
        if (isVirtual) {
            return;
        }

        let addedCount = 0;

        if (typeof globalCache !== 'undefined') {
            for (const [parentFolderId, files] of globalCache) {
                if (!files || !Array.isArray(files)) continue;

                for (let i = 0; i < files.length; i++) {
                    const f = files[i];
                    if (f.kind === 'drive#folder' && !scannedFolderIds.has(f.id) && !globalCache.has(f.id)) {
                        backgroundQueue.push({ id: f.id, name: f.name, retryCount: 0 });
                        scannedFolderIds.add(f.id);
                        addedCount++;
                    }
                }
                if (addedCount > 500) break;
            }
        }

        if (addedCount === 0) {
            const visibleSubFolders = S.items.filter(f => f.kind === 'drive#folder');
            for (let i = visibleSubFolders.length - 1; i >= 0; i--) {
                const sub = visibleSubFolders[i];
                if (!scannedFolderIds.has(sub.id) && !globalCache.has(sub.id)) {
                    backgroundQueue.push({ id: sub.id, name: sub.name, retryCount: 0 });
                    scannedFolderIds.add(sub.id);
                    addedCount++;
                }
            }
        }

        if (addedCount > 0 || backgroundQueue.length > 0) {
            console.log(`♻️ Background crawler resumed: ${addedCount} new tasks found in map.`);
            runBackgroundCrawler();
        }
    };

    S.updateBlCache();

    if (S.items && S.items.length > 0) {
        S.itemMap.clear();
        S.items.forEach(i => S.itemMap.set(i.id, i));
    }

    const el = document.createElement('div'); el.className = 'pk-ov';
    let siteFont = window.getComputedStyle(document.body).fontFamily || '';
    siteFont = siteFont.replace(/,?\s*sans-serif\s*$/i, '');
    el.style.fontFamily = siteFont ? `${siteFont}, "Noto Sans", sans-serif` : '"Noto Sans", sans-serif';

    el.addEventListener('wheel', (e) => {
        e.stopPropagation();
        const scrollTarget = e.target.closest('.pk-vp, .pk-modal, #pk-rn-vp, .pk-prev-list, textarea, .pk-scroll, .pk-help-scroll');
        if (!scrollTarget) { e.preventDefault(); return; }
        const st = scrollTarget.scrollTop;
        const sh = scrollTarget.scrollHeight;
        const ch = scrollTarget.clientHeight;
        const isUp = e.deltaY < 0;
        const isDown = e.deltaY > 0;
        const isAtTop = st <= 0.5;
        const isAtBottom = (st + ch) >= (sh - 1.5);
        if ((isUp && isAtTop) || (isDown && isAtBottom)) {
            if (e.cancelable) e.preventDefault();
        }
    }, { passive: false });

    const mkLbl = (id, txt, shortTxt, tip) => {
        const tipAttr = tip ? ` data-pk-tip="${tip}"` : '';
        return `<label class="pk-dup-chk"${tipAttr}><input type="checkbox" id="${id}" checked> <span class="pk-txt-long">${txt}</span><span class="pk-txt-short">${shortTxt}</span></label>`;
    };

    const savedTheme = gmGet('pk_theme', 'auto');
    const sysDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
    let isDark = savedTheme === 'dark' || (savedTheme === 'auto' && sysDark);

    if (isDark) el.classList.add('pk-dark');

    const themeIcon = isDark ? CONF.icons.sun : CONF.icons.moon;
    const winClass = 'pk-win pk-lang-' + lang;

    const ctxIcons = {
        locate: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>`,
        info: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>`,
        open: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11V21H3V11"/><path d="M3 11L6 5"/><path d="M21 11L18 5"/><line x1="12" y1="17" x2="12" y2="3"/><polyline points="8 7 12 3 16 7"/></svg>`,
        star: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`,
        unstar: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/><line x1="2" y1="2" x2="22" y2="22"/></svg>`,
        download: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,
        copy: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`,
        move: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>`,
        rename: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1"><path d="M56.925091 777.495273v189.579636h189.579636L805.655273 407.924364l-189.579637-189.579637L56.925091 777.495273zM952.32 261.306182a50.315636 50.315636 0 0 0 0-71.354182L834.048 71.68a50.315636 50.315636 0 0 0-71.400727 0L670.254545 164.165818 859.787636 353.745455l92.532364-92.439273v-0.093091z" fill="currentColor"></path></svg>`,
        renameBulk: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1" style="transform: scale(1.2);"><path d="M882.88 280.64l42.88-45.76a13.76 13.76 0 0 0 0-19.2L829.12 128a13.12 13.12 0 0 0-18.88 0L768 173.12zM739.84 202.56l-218.88 234.88a17.28 17.28 0 0 0-3.52 8.64L512 547.84a13.44 13.44 0 0 0 15.04 14.08l102.08-12.48a13.76 13.76 0 0 0 7.68-5.44l218.88-233.6z" fill="currentColor"></path><path d="M864 381.12a24 24 0 0 0-24 24v317.76H304.96V189.12h317.76a24 24 0 0 0 0-48H296.96A40 40 0 0 0 256 181.12v82.56H174.72a40 40 0 0 0-40 40v549.44a40 40 0 0 0 40 40h549.44a40 40 0 0 0 40-40v-75.84a20.8 20.8 0 0 0 0-6.4h83.84a40.32 40.32 0 0 0 40-40V405.12a24.32 24.32 0 0 0-24-24z m-147.84 396.16v67.84H182.72V311.68H256v419.2a40 40 0 0 0 40 40h421.44a20.8 20.8 0 0 0-1.28 6.4z" fill="currentColor" stroke="currentColor" stroke-width="35"></path></svg>`,
        blAdd: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`,
        blRem: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/><path d="m9 12 2 2 4-4"/></svg>`,
        share: CONF.icons.share,
        copyLink: `<svg width="16" height="16" viewBox="0 0 1024 1024"><path d="M232.6 1023.8c-61.2 0-118.6-23.7-161.6-66.6l-4.2-4.2c-89.1-89.1-89.1-234.1 0-323.2l181.1-181.1c3.5 25.4 9.6 50.4 18.1 74.6l-152.8 152.8c-63.5 63.5-63.5 166.9 0 230.5l4.3 4.3c30.7 30.7 71.6 47.6 115.2 47.6s84.5-16.9 115.3-47.7l228.9-228.9c63.5-63.5 63.5-166.9 0-230.5l-4.3-4.2c-2.1-2.1-4.2-4.1-6.4-6l46.4-46.4c2.2 2 4.3 4 6.4 6.1l4.3 4.2c89.1 89.1 89.1 234.1 0 323.2l-228.9 228.9c-43 42.9-100.4 66.6-161.6 66.6zM411.5 629.3c-2.2-2-4.3-4.1-6.4-6.1l-4.2-4.2c-43.1-43.1-66.8-100.5-66.8-161.6 0-61.1 23.7-118.5 66.8-161.6l228.9-228.9c43.1-43.1 100.5-66.8 161.6-66.8 61.3 0 118.7 23.7 161.6 66.6l4.2 4.2c89 89.1 89 234.1 0 323.2l-181.1 181.1c-3.5-25.4-9.6-50.5-18.1-74.6l152.8-152.8c63.5-63.5 63.5-166.9 0-230.5l-4.3-4.3c-30.7-30.7-71.6-47.6-115.2-47.6s-84.5 16.9-115.2 47.7l-228.9 228.9c-30.7 30.7-47.7 71.7-47.7 115.3 0 43.6 16.9 84.5 47.7 115.2l4.2 4.2c2 2 4.2 4.1 6.4 6.1l-46.4 46.4z" fill="currentColor"></path></svg>`,
        copyName: `<svg width="16" height="16" viewBox="0 0 1024 1024" fill="currentColor" version="1.1" xmlns="http://www.w3.org/2000/svg" style="transform: scale(1.22); vertical-align: -3px;"><path d="M852.411679 250.1876a85.155752 85.155752 0 0 0-84.942863-78.811649h-340.623009v85.155753h340.623009v340.623009h85.155752v-340.623009l-0.212889-6.386682zM171.37855 767.466217v-340.623009a85.155752 85.155752 0 0 1 85.155752-85.155752h287.400664l138.378098 138.378097v287.400664a85.155752 85.155752 0 0 1-85.155752 85.155753h-340.62301a85.155752 85.155752 0 0 1-85.155752-85.155753z m425.778762-252.146182l-88.476827-88.476827H256.534302v340.623009h340.62301v-252.146182z"></path></svg>`,
        trash: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>`,
        restore: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>`,
        delForever: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`
    };

    el.innerHTML = `
        <style>${CSS}</style>
        <div class="${winClass}">
            <div class="pk-loading-ov" id="pk-loader">
                <div class="pk-spin-lg"></div>
                <div class="pk-loading-txt" id="pk-load-txt">${L.loading_detail}</div>
                <button class="pk-stop-btn" id="pk-stop-load">${CONF.icons.stop} <span>${L.btn_stop}</span></button>
            </div>

            <div class="pk-sidebar">
                <div class="pk-nav-btn" id="pk-btn-cloud" data-pk-tip="${L.btn_cloud_download}">${CONF.icons.cloudDownload}<span>${L.btn_cloud_download}</span></div>
                <div class="pk-nav-btn act" id="pk-nav-home" data-pk-tip="${L.btn_nav_home}">${CONF.icons.home}<span>${L.btn_nav_home}</span></div>
                <div class="pk-nav-btn" id="pk-nav-upload" data-pk-tip="${L.btn_nav_upload}">${CONF.icons.navUpload}<span>${L.btn_nav_upload}</span></div>
                <div class="pk-nav-btn" id="pk-nav-offline" data-pk-tip="${L.btn_nav_offline}">${CONF.icons.offline}<span>${L.btn_nav_offline}</span></div>
                <div class="pk-nav-btn" id="pk-nav-recent" data-pk-tip="${L.btn_nav_recent}">${CONF.icons.recent}<span>${L.btn_nav_recent}</span></div>
                <div class="pk-nav-btn" id="pk-nav-history" data-pk-tip="${L.btn_nav_history}">${CONF.icons.history}<span>${L.btn_nav_history}</span></div>
                <div class="pk-nav-btn" id="pk-nav-share" data-pk-tip="${L.btn_nav_share}">${CONF.icons.navShare}<span>${L.btn_nav_share}</span></div>
                <div class="pk-nav-btn" id="pk-nav-starred" data-pk-tip="${L.btn_nav_starred}">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
                    <span>${L.btn_nav_starred}</span>
                </div>
                <div class="pk-nav-btn" id="pk-nav-trash" data-pk-tip="${L.btn_nav_trash}">${CONF.icons.trash}<span>${L.btn_nav_trash}</span></div>

                <div id="pk-quota-panel" style="margin-top:auto; width:100%; display:flex; flex-direction:column; align-items:center; padding:0; gap:4px; cursor:default; opacity:0.6; transition:all 0.2s; margin-bottom:2px;">
                    <span id="pk-quota-txt" style="font-size:10px; color:var(--pk-fg); font-weight:700; transform:scale(0.8); font-variant-numeric:tabular-nums; line-height:1;">--%</span>
                    <div style="width:44px; height:4px; background:var(--pk-bd); border-radius:2px; overflow:hidden; position:relative;" id="pk-quota-bar-box">
                        <div id="pk-quota-bar" style="position:absolute; left:0; top:0; height:100%; width:0%; background:var(--pk-pri); transition:width 0.5s ease-out;"></div>
                    </div>
                </div>

                <div class="pk-nav-btn" id="pk-settings" data-pk-tip="${L.tip_settings}" style="margin-top:0 !important;">${CONF.icons.settings}<span>${L.btn_settings}</span></div>
            </div>

            <div class="pk-main-col">

                <div class="pk-hd">
                    <div class="pk-tt">
                        ${CONF.logoSVG}
                        <div style="display: inline-flex; align-items: baseline; gap: 10px;">
                            <span style="font-weight: 700;">${L.title}</span>
                            <span style="font-size: 13px; font-weight: 600; color: var(--pk-fg); cursor: default;">
                                by <a href="https://github.com/digbug82/PikPak_Enhancement_Master" target="_blank"
                                      style="color: inherit; text-decoration: none; transition: color 0.2s; font-weight: inherit; cursor: pointer;"
                                      onmouseover="this.style.color='var(--pk-pri)'"
                                      onmouseout="this.style.color='inherit'">digbug82</a>
                            </span>
                        </div>
                    </div>
                    <div style="display:flex; gap:4px;">
                        <div class="pk-btn" id="pk-theme" style="width:32px;padding:0;justify-content:center;" data-pk-tip="${L.tip_theme}">${themeIcon}</div>
                        <div class="pk-btn" id="pk-help" style="width:32px;padding:0;justify-content:center;" data-pk-tip="${L.tip_help}">${CONF.icons.help}</div>
                        <div class="pk-btn" id="pk-maximize" style="width:32px;padding:0;justify-content:center;" data-pk-tip="${L.tip_maximize}">${CONF.icons.maximize}</div>
                        <div class="pk-btn" id="pk-close" style="width:32px;padding:0;justify-content:center;" data-pk-tip="${L.tip_close}">${CONF.icons.close}</div>
                    </div>
                </div>

                <div class="pk-tb" id="pk-top-bar">
                    <div class="pk-nav" id="pk-crumb"></div>

                    <div id="pk-filter-bar" style="display:none; align-items:center; gap:8px; height:100%; margin-left:10px;">
                        <button class="pk-btn" id="pk-filter-btn" style="border:1px solid var(--pk-bd); background:var(--pk-bg); padding:0 12px; height:32px; border-radius:6px; box-shadow: 0 1px 2px rgba(0,0,0,0.05);">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
                            <span style="font-weight:500; color:var(--pk-fg);">${L.btn_filter}</span>
                        </button>

                        <div id="pk-filter-active-ui" style="display:none; align-items:center; gap:8px; height:100%;">
                            <div id="pk-filter-cat-label" style="background:var(--pk-bg); border: 1px solid var(--pk-bd); padding:0 12px; height:32px; display:flex; align-items:center; border-radius:4px; font-size:13px; font-weight:500; color:var(--pk-fg); cursor:pointer; transition:all 0.2s;" onmouseover="this.style.borderColor='var(--pk-pri)';this.style.color='var(--pk-pri)';" onmouseout="this.style.borderColor='var(--pk-bd)';this.style.color='var(--pk-fg)';"></div>

                            <div id="pk-filter-exts-wrap" style="display:flex; align-items:center; background:var(--pk-bg); border:1px solid var(--pk-bd); border-radius:4px; padding:0 4px; height:32px; position:relative;">
                                <div id="pk-filter-exts-main" style="display:flex; align-items:center; gap:4px; padding:0 4px;"></div>
                                <div id="pk-filter-exts-more-btn" style="cursor:pointer; padding:0 8px; display:flex; align-items:center; color:#888;">
                                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="6 9 12 15 18 9"></polyline></svg>
                                </div>
                            </div>

                            <button class="pk-btn pri" id="pk-filter-exit-btn" style="background:var(--pk-pri); color:#fff; border:none; height:32px; padding:0 15px; font-size:13px; font-weight:500; border-radius:4px;">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 14L4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5v0a5.5 5.5 0 0 1-5.5 5.5H11"/></svg>
                                <span>${L.btn_exit_filter}</span>
                            </button>
                        </div>
                    </div>

                    <div class="pk-dup-toolbar" id="pk-offline-tools" style="display:none; margin-left:10px; border-right:1px solid var(--pk-bd); padding-right:10px; margin-right:10px;">
                        <label class="pk-dup-chk" style="margin-right:10px;cursor:pointer;display:flex;align-items:center;">
                            <input type="checkbox" id="pk-off-run" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:var(--pk-fg);">${L.lbl_task_run}</span>
                        </label>
                        <label class="pk-dup-chk" style="margin-right:10px;cursor:pointer;display:flex;align-items:center;">
                            <input type="checkbox" id="pk-off-fail" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:#d93025;">${L.lbl_task_fail}</span>
                        </label>
                        <label class="pk-dup-chk" style="margin-right:0;cursor:pointer;display:flex;align-items:center;">
                            <input type="checkbox" id="pk-off-ok" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:#52c41a;">${L.lbl_task_ok}</span>
                        </label>
                    </div>

                    <div class="pk-dup-toolbar" id="pk-upload-tools" style="display:none; margin-left:10px; border-right:1px solid var(--pk-bd); padding-right:10px; margin-right:10px;">
                        <label class="pk-dup-chk" style="margin-right:10px;cursor:pointer;display:flex;align-items:center;">
                            <input type="checkbox" id="pk-chk-up-run" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:var(--pk-fg);">${L.lbl_up_run}</span>
                        </label>
                        <label class="pk-dup-chk" style="margin-right:10px;cursor:pointer;display:flex;align-items:center;" data-pk-tip="${L.tip_up_pause_desc}">
                            <input type="checkbox" id="pk-chk-up-pause" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:#faad14;">${L.lbl_up_pause}</span>
                        </label>
                        <label class="pk-dup-chk" style="margin-right:0;cursor:pointer;display:flex;align-items:center;">
                            <input type="checkbox" id="pk-chk-up-done" checked style="margin:0 4px 0 0;accent-color:var(--pk-pri);">
                            <span style="font-size:13px;color:#52c41a;">${L.lbl_up_done}</span>
                        </label>
                    </div>

                    <div class="pk-dup-toolbar" id="pk-dup-filters" style="margin-left:10px; padding-right:2px;">
                        ${mkLbl('pk-chk-hash', L.tag_hash, L.tag_hash_short)}
                        ${mkLbl('pk-chk-sim', L.tag_sim, L.tag_sim_short)}
                        ${mkLbl('pk-chk-name', L.tag_name, L.tag_name_short)}
                    </div>

                    <div class="pk-dup-toolbar" id="pk-dup-tools" style="display:none; align-items:center; gap:4px; padding:0 10px; height:100%; border-left:1px solid var(--pk-bd); border-right:1px solid var(--pk-bd); margin-right:10px;">
                        <div id="pk-dup-folder-sel-wrap" style="display:inline-flex; align-items:center; width:220px; margin-right:5px; position:relative;">
                            <select id="pk-dup-folder-sel" style="position:absolute; inset:0; width:0; height:0; opacity:0; pointer-events:none;">
                                <option value="">${L.lbl_dup_select_folder}</option>
                            </select>
                            <button type="button" id="pk-dup-folder-btn">
                                <span id="pk-dup-folder-btn-txt">${L.lbl_dup_select_folder}</span>
                            </button>
                        </div>

                        <label style="display:inline-flex; align-items:center; margin-right:8px; cursor:pointer; user-select:none; opacity:0.5;" data-pk-tip="${L.tip_dup_invert_limit}">
                            <input type="checkbox" id="pk-dup-invert" disabled style="margin:0 4px 0 0;">
                            <span class="pk-txt-long" style="font-size:12px; color:var(--pk-fg);">${L.lbl_dup_invert}</span>
                            <span class="pk-txt-short" style="font-size:12px; color:var(--pk-fg);">${L.lbl_dup_invert_short}</span>
                        </label>

                        <button class="pk-ana-select-btn" id="pk-dup-smart-btn" style="display:flex; margin-right:4px;">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
                            <span>${L.btn_ana_select}</span>
                        </button>
                        <button class="pk-ana-select-btn" id="pk-dup-sort-btn" style="display:flex; margin-right:0;">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><polyline points="19 12 12 19 5 12"></polyline></svg>
                            <span>${L.btn_in_group_sort}</span>
                        </button>
                    </div>

                    <div style="flex:1"></div>

                    <label class="pk-global-chk" id="pk-lbl-global">
                        <input type="checkbox" id="pk-chk-global" ${S.wasGlobalChecked ? 'checked' : ''}>
                        <span>${L.lbl_global_search}</span>
                    </label>
                    <label class="pk-global-chk" id="pk-search-path-con" style="display:none; margin-right:8px;" data-pk-tip="${L.lbl_search_path}">
                        <input type="checkbox" id="pk-chk-search-path">
                        <span class="pk-txt-long">${L.lbl_search_path}</span>
                        <span class="pk-txt-short">${L.lbl_search_path_short}</span>
                    </label>

                    <div class="pk-search">
                        <input type="text" id="pk-search-input" placeholder="${L.placeholder_search}" autocomplete="off">
                        <div class="pk-search-clear" id="pk-search-clear">${CONF.icons.close}</div>
                        <svg id="pk-search-btn" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
                        <div class="pk-hist-pop" id="pk-search-hist"></div>
                    </div>

                    <button class="pk-btn" id="pk-export" data-pk-tip="${L.tip_export}">
                        ${CONF.icons.export} <span>${L.btn_export}</span>
                    </button>

                    <button class="pk-btn" id="pk-scan-dup" data-pk-tip="${L.tip_scan_dup}">
                        ${CONF.icons.scanDup} <span>${L.title_file_analysis}</span>
                    </button>

                    <button class="pk-btn" id="pk-analyze" data-pk-tip="${L.tip_analyze}">
                        ${CONF.icons.analyze} <span>${L.btn_analyze}</span>
                    </button>

                    <button class="pk-btn" id="pk-btn-exit" style="display:none; color:#d93025; border-color:transparent; margin-left:10px; flex-shrink:0;">
                        ${CONF.icons.close} <span>${L.btn_exit}</span>
                    </button>
                </div>

                <div class="pk-tb" id="pk-trash-bar" style="display:none;">
                    <button class="pk-btn" id="pk-trash-refresh" data-pk-tip="${L.tip_refresh}">${CONF.icons.refresh} <span>${L.btn_refresh_short}</span></button>
                    <div class="pk-sep"></div>
                    <button class="pk-btn" id="pk-restore" data-pk-tip="${L.tip_restore}" disabled>
                       ${CONF.icons.restore} <span>${L.btn_restore}</span>
                    </button>
                    <button class="pk-btn" id="pk-del-forever" data-pk-tip="${L.tip_del_forever}" disabled>
                       ${CONF.icons.delForever} <span>${L.btn_del_forever}</span>
                    </button>
                    <button class="pk-btn pk-btn-danger" id="pk-empty-trash" data-pk-tip="${L.tip_empty_trash}" style="margin-left: 4px;">
                       ${CONF.icons.emptyTrash} <span>${L.btn_empty_trash}</span>
                    </button>

                    <div style="flex:1;"></div>
                    <button class="pk-btn" id="pk-trash-blacklist-manager" data-pk-tip="${L.tip_blacklist_input}" style="color:#d93025">
                        ${CONF.icons.blacklist} <span>${L.title_blacklist}</span>
                    </button>
                </div>

                <div class="pk-tb" id="pk-actionbar">
                <button class="pk-btn" id="pk-refresh" data-pk-tip="${L.tip_refresh}">${CONF.icons.refresh} <span>${L.btn_refresh_short}</span></button>
                <button class="pk-btn" id="pk-off-copy-link" style="display:none;" data-pk-tip="${L.tip_copy_link}">${ctxIcons.copyLink} <span>${L.btn_copy_link}</span></button>
                <button class="pk-btn" id="pk-retry-task" style="display:none;" data-pk-tip="${L.tip_retry_task}">${CONF.icons.retry} <span>${L.btn_retry_task}</span></button>
                <button class="pk-btn" id="pk-up-pause" style="display:none;" data-pk-tip="${L.tip_up_pause}">${CONF.icons.taskPause} <span>${L.btn_up_pause}</span></button>
                <button class="pk-btn" id="pk-up-start" style="display:none;" data-pk-tip="${L.tip_up_start}">${CONF.icons.taskStart} <span>${L.btn_up_start}</span></button>
                <button class="pk-btn" id="pk-up-del" style="display:none;" data-pk-tip="${L.tip_up_del}">${CONF.icons.del} <span>${L.btn_up_del}</span></button>
                <button class="pk-btn" id="pk-up-clear-all" style="display:none;" data-pk-tip="${L.tip_up_clear_all}">${CONF.icons.cleanAll} <span>${L.btn_up_clear_all}</span></button>

                <button class="pk-btn" id="pk-newfolder" data-pk-tip="${L.tip_newfolder}">${CONF.icons.newfolder} <span>${L.btn_newfolder}</span></button>
                <button class="pk-btn" id="pk-del" data-pk-tip="${L.tip_del}">${CONF.icons.del} <span>${L.btn_del}</span></button>
                <button class="pk-btn" id="pk-deselect" data-pk-tip="${L.tip_deselect}" style="display:none">${CONF.icons.deselect} <span>${L.btn_deselect}</span></button>
                <div class="pk-sep"></div>
                <button class="pk-btn" id="pk-copy" data-pk-tip="${L.tip_copy}">${CONF.icons.copy} <span>${L.btn_copy}</span></button>
                <button class="pk-btn" id="pk-cut" data-pk-tip="${L.tip_cut}">${CONF.icons.cut} <span>${L.btn_cut}</span></button>
                <button class="pk-btn" id="pk-paste" data-pk-tip="${L.tip_paste}" disabled>${CONF.icons.paste} <span>${L.btn_paste}</span></button>
                <div class="pk-sep"></div>
                <button class="pk-btn" id="pk-rename" data-pk-tip="${L.tip_rename}">${CONF.icons.rename} <span>${L.btn_rename}</span></button>
                <button class="pk-btn" id="pk-bulkrename" data-pk-tip="${L.tip_bulkrename}">${CONF.icons.bulkrename} <span>${L.btn_bulkrename}</span></button>
                <button class="pk-btn" id="pk-prune" data-pk-tip="${L.tip_prune}">${CONF.icons.prune} <span>${L.btn_prune}</span></button>
                <button class="pk-btn" id="pk-unzip" data-pk-tip="${L.tip_unzip}">${CONF.icons.unzip} <span>${L.btn_unzip}</span></button>
                <button class="pk-btn" id="pk-cancel-share" data-pk-tip="${L.btn_cancel_share} [Delete]" style="display:none;">${CONF.icons.unshare} <span>${L.btn_cancel_share}</span></button>
                <div style="flex:1"></div>

                <div class="pk-dropdown-wrap" id="pk-upload-wrap">
                    <button class="pk-btn pri" id="pk-btn-upload" style="background:var(--pk-pri); color:#fff; border:none; margin-right:8px;">
                        ${CONF.icons.uploadBtn} <span>${L.btn_upload}</span>
                        <svg class="pk-btn-arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="margin-left:4px;"><polyline points="6 9 12 15 18 9"/></svg>
                    </button>
                    <div class="pk-dropdown-menu">
                        <div class="pk-dropdown-item" id="pk-act-upload-file">
                            ${CONF.icons.upFile} ${L.btn_up_file}
                        </div>
                        <div class="pk-dropdown-item" id="pk-act-upload-folder">
                            ${CONF.icons.upFolder} ${L.btn_up_folder}
                        </div>
                    </div>
                    <input type="file" id="pk-file-selector" multiple style="display:none;">
                    <input type="file" id="pk-folder-selector" webkitdirectory directory multiple style="display:none;">
                </div>

                <button class="pk-btn" id="pk-blacklist-manager" data-pk-tip="${L.tip_blacklist_input}" style="color:#d93025">
                    ${CONF.icons.blacklist} <span>${L.title_blacklist}</span>
                </button>
            </div>

            <div class="pk-grid-hd" style="grid-template-columns: 36px 30px 1fr 80px 105px 130px;">
                <div><input type="checkbox" id="pk-all"></div>
                <div class="pk-col" data-k="starred" style="justify-content:center; display:flex; align-items:center;">
                    <svg viewBox="0 0 1024 1024" width="16" height="16" style="margin-top:-2px;">
                        <path d="M953.107692 425.353846c3.938462-19.692308-11.815385-39.384615-31.507692-43.323077l-259.938462-39.384615-118.153846-244.184616c-3.938462-7.876923-7.876923-11.815385-15.753846-15.753846-7.876923-3.938462-15.753846-3.938462-19.692308-3.938461h-3.938461c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462-3.938462 3.938462-3.938462 7.876923-7.876923 11.815384v3.938462l-118.153846 244.184615-259.938462 39.384616c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462 0 0-3.938462 0-3.938462 3.938461v3.938462c0 3.938462-3.938462 3.938462-3.938461 7.876923 0 0 0 3.938462-3.938462 3.938462v15.753846c0 3.938462 0 3.938462 3.938462 7.876923 0 3.938462 3.938462 7.876923 7.876923 11.815384L275.692308 638.030769 232.369231 905.846154V917.661538c0 3.938462 0 3.938462 3.938461 7.876924v3.938461c0 3.938462 3.938462 3.938462 7.876923 7.876923l3.938462 3.938462c3.938462 0 3.938462 3.938462 7.876923 3.938461H275.692308c3.938462 0 7.876923 0 11.815384-3.938461l78.769231-43.323077 149.661539-78.769231 3.938461-3.938462 232.369231 126.03077c7.876923 3.938462 15.753846 3.938462 23.630769 3.938461 19.692308-3.938462 31.507692-23.630769 27.569231-43.323077l-43.323077-267.815384 189.046154-189.046154c0-7.876923 0-11.815385 3.938461-19.692308z" fill="#FFC107"></path>
                    </svg>
                    <span style="font-size:10px; margin-left:0;"></span>
                </div>
                <div class="pk-col" data-k="name" style="display:flex; align-items:center;">
                    <div id="pk-name-text-wrap" style="display:flex; align-items:center; transition:color 0.2s;">
                        ${L.col_name}<span style="display:inline-block; min-width:18px; text-align:center;"></span>
                    </div>

                    <div id="pk-btn-folder-first" data-pk-tip="${L.lbl_folder_first}" style="margin-left:8px; cursor:pointer; display:flex; align-items:center; color:#666; transition:color 0.2s;">
                        ${CONF.icons.folderFirst}
                        <span style="margin-left:2px;">${L.lbl_folder_first}</span>
                    </div>

                    <div id="pk-btn-invert" data-pk-tip="${L.btn_invert}" style="margin-left:12px; cursor:pointer; display:none; align-items:center; color:var(--pk-fg); transition:color 0.2s; pointer-events:auto;">
                        ${CONF.icons.invert}
                        <span style="margin-left:2px; color:var(--pk-fg);">${L.btn_invert}</span>
                   </div>
                </div>
                <div class="pk-col" data-k="path" style="display:none; cursor:default; color:#888;">${L.col_path} <span></span></div>
                <div class="pk-col" data-k="size">${L.col_size} <span></span></div>
                <div class="pk-col" data-k="duration">${L.col_dur} <span></span></div>
                <div class="pk-col" data-k="modified_time">${L.col_date} <span></span></div>
            </div>
            <div class="pk-vp" id="pk-vp">
                <div class="pk-in" id="pk-in"></div>
            </div>
            <div class="pk-ft">
                <div class="pk-stat" id="pk-stat">${L.status_ready.replace('{n}', 0)}</div>
                <div class="pk-grp">
                    <button class="pk-btn" id="pk-migrate" data-pk-tip="${L.tip_migrate}" style="color: var(--pk-pri);">${CONF.icons.migrate} <span>${L.btn_migrate}</span></button>
                    <button class="pk-btn" id="pk-img-search" data-pk-tip="${L.tip_play_search}"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><g transform="scale(0.0234375)"><path d="M107.739429 580.388571a365.860571 365.860571 0 0 0 648.630857 85.504L635.611429 532.48a36.571429 36.571429 0 0 0-56.612572 2.852571l-39.131428 53.101715a109.714286 109.714286 0 0 1-167.716572 10.605714L235.008 455.387429a36.571429 36.571429 0 0 0-58.002286 6.729142l-69.266285 118.272z m-19.894858-110.738285l26.038858-44.470857a109.714286 109.714286 0 0 1 174.08-20.333715l137.216 143.798857a36.571429 36.571429 0 0 0 55.881142-3.584l39.131429-53.101714a109.714286 109.714286 0 0 1 169.691429-8.484571l102.985142 113.810285A365.714286 365.714286 0 1 0 87.844571 469.577143z m658.139429 318.317714a438.857143 438.857143 0 1 1 50.029714-52.736c1.316571 1.024 2.56 2.194286 3.803429 3.437714l206.921143 206.848a36.571429 36.571429 0 0 1-51.712 51.712l-206.921143-206.848a37.083429 37.083429 0 0 1-2.194286-2.413714zM526.628571 314.514286a73.142857 73.142857 0 1 1 0-146.285715 73.142857 73.142857 0 0 1 0 146.285715z" fill="currentColor"></path></g></svg> <span>${L.btn_img_search.replace(' [F]', '')}</span></button>
                    <button class="pk-btn" id="pk-ext" data-pk-tip="${L.tip_ext}">${CONF.icons.ext} <span>${L.btn_ext}</span></button>
                    <button class="pk-btn" id="pk-aria2" data-pk-tip="${L.tip_aria2}">${CONF.icons.aria2} <span>${L.btn_aria2}</span></button>
                    <button class="pk-btn" id="pk-down" data-pk-tip="${L.tip_down}">${CONF.icons.download} <span>${L.btn_down}</span></button>
                </div>
            </div>

            </div>

        </div>

        <div class="pk-pop" id="pk-pop"></div>
        <div class="pk-ctx" id="pk-ctx">
            <div class="pk-ctx-item" id="ctx-share">${CONF.icons.share} ${L.ctx_share}</div>
            <div class="pk-ctx-sep" id="sep-1"></div>

            <div class="pk-ctx-item" id="ctx-open">${ctxIcons.open} ${L.ctx_open}</div>
            <div class="pk-ctx-item" id="ctx-ext-play" style="display:none;">${CONF.icons.ext} ${L.btn_ext}</div>
            <div class="pk-ctx-item" id="ctx-star">${ctxIcons.star} ${L.ctx_star}</div>
            <div class="pk-ctx-item" id="ctx-property">${ctxIcons.info} ${L.ctx_property}</div>
            <div class="pk-ctx-item" id="ctx-locate" style="display:none;">${ctxIcons.locate} ${L.ctx_locate}</div>
            <div class="pk-ctx-sep" id="sep-2"></div>

            <div class="pk-ctx-item" id="ctx-down">${ctxIcons.download} ${L.ctx_down}</div>
            <div class="pk-ctx-item" id="ctx-copy-name">${ctxIcons.copyName} ${L.ctx_copy_name}</div>
            <div class="pk-ctx-item" id="ctx-copy-link" style="display:none;">${ctxIcons.copyLink} ${L.ctx_copy_link}</div>
            <div class="pk-ctx-sep" id="sep-3"></div>

            <div class="pk-ctx-item" id="ctx-cut">${ctxIcons.move} ${L.btn_cut}</div>
            <div class="pk-ctx-item" id="ctx-copy">${ctxIcons.copy} ${L.ctx_copy}</div>
            <div class="pk-ctx-item" id="ctx-rename">${ctxIcons.rename} ${L.ctx_rename}</div>
            <div class="pk-ctx-item" id="ctx-prune">${CONF.icons.prune} ${L.btn_prune}</div>
            <div class="pk-ctx-sep" id="sep-4"></div>


            <div class="pk-ctx-item" id="ctx-sh-cancel" style="display:none; color:#d93025;">${CONF.icons.unshare} ${L.btn_cancel_share}</div>

            <div class="pk-ctx-sep" id="sep-trash-1" style="display:none;"></div>
            <div class="pk-ctx-item" id="ctx-restore" style="display:none">${ctxIcons.restore} ${L.btn_restore}</div>
            <div class="pk-ctx-sep" id="sep-trash-2" style="display:none;"></div>
            <div class="pk-ctx-item" id="ctx-del-forever" style="display:none; color:#d93025;">${ctxIcons.delForever} ${L.btn_del_forever}</div>

            <div class="pk-ctx-item" id="ctx-del" style="color:#d93025">${ctxIcons.trash} ${L.ctx_del}</div>

            <div class="pk-ctx-item" id="ctx-add-bl" style="color:#d93025">${ctxIcons.blAdd} ${L.ctx_add_bl}</div>

            <div class="pk-ctx-sep" id="sep-share-extra" style="display:none;"></div>
            <div class="pk-ctx-item" id="ctx-sh-detail" style="display:none;">${ctxIcons.info} ${L.ctx_share_detail}</div>
            <div class="pk-ctx-item" id="ctx-sh-copy" style="display:none;">${ctxIcons.copy} ${L.ctx_share_copy}</div>
        </div>
    `;
    document.body.appendChild(el);

    const destroyTooltip = (() => {
        const tipEl = document.createElement('div');
        tipEl.className = 'pk-tooltip';

        const style = document.createElement('style');
        style.textContent = `
        .pk-tooltip {
            zoom: var(--pk-zoom, 1);
            width: max-content; max-width: 280px; min-width: auto;
            background: var(--pk-tip-bg); color: var(--pk-tip-fg);
            border: 1px solid var(--pk-tip-bd);
            padding: 6px; box-sizing: border-box; border-radius: 8px; font-size: 12px; line-height: 1.4;
            position: fixed; z-index: 2147483647 !important; pointer-events: none;
            box-shadow: 0 4px 16px var(--pk-tip-sd); backdrop-filter: blur(4px);
            display: flex; flex-direction: column; gap: 4px;

            white-space: normal;
            word-break: break-all;
            text-align: justify;

            opacity: 0; transform: translateY(5px) scale(0.95);
            transition: opacity 0.15s ease, transform 0.15s ease;
        }
        .pk-tooltip.pk-dark {
            --pk-tip-bg: rgba(20, 20, 20, 0.95); --pk-tip-fg: #ffffff;
            --pk-tip-bd: rgba(255, 255, 255, 0.1); --pk-tip-sd: rgba(0, 0, 0, 0.4);
        }
        .pk-tooltip.show { opacity: 1; transform: translateY(0) scale(1); }
        body.pk-dragging .pk-tooltip { display: none !important; opacity: 0 !important; }
        .pk-tooltip img { display: none; width: 100%; max-width: 100%; max-height: 320px; height: auto; object-fit: cover; border-radius: 4px; margin-bottom: 0 !important; display: block; }
    `;
    document.head.appendChild(style);

    document.body.appendChild(tipEl);

    let activeTarget = null;
        let isMenuOpen = false;
        let lastMouseX = 0, lastMouseY = 0;

        const hideTip = () => {
            tipEl.style.display = 'none';
            tipEl.classList.remove('show');
            tipEl.style.left = '-9999px';
            activeTarget = null;
        };

        const renderTip = (target) => {
            const isDark = document.querySelector('.pk-ov')?.classList.contains('pk-dark');
            tipEl.classList.toggle('pk-dark', !!isDark);

            const text = target.getAttribute('data-pk-tip');
            const thumb = target.getAttribute('data-pk-thumb');
            if (!text && !thumb) { hideTip(); return; }

            activeTarget = target;
            if (tipEl.parentNode && tipEl.nextSibling) {
                document.body.appendChild(tipEl);
            }
            let html = '';
            if (thumb && thumb !== 'undefined' && thumb !== 'null' && (thumb.startsWith('http') || thumb.startsWith('blob:'))) {
                const isBlur = gmGet('pk_blur_thumb', false);
                html += `<img src="${thumb}" style="${isBlur ? 'filter:blur(8px);' : ''}" onload="this.style.display='block'; this.style.marginBottom='6px';" onError="this.remove()">`;
            }
            if (text) {
                html += `<div>${text}</div>`;
            }

            tipEl.innerHTML = html;
            tipEl.style.display = 'block';
            requestAnimationFrame(() => tipEl.classList.add('show'));
        };

        document.addEventListener('mouseover', (e) => {
            lastMouseX = e.clientX; lastMouseY = e.clientY;
            if (isMenuOpen) return;
            if (document.body.classList.contains('pk-dragging')) { hideTip(); return; }
            if (document.querySelector('#pk-ctx')?.style.display === 'block') return;

            const target = e.target.closest('[data-pk-tip], [data-pk-thumb]');
            if (!target) return;
            if (target === activeTarget) return;

            const isName = target.classList.contains('pk-name') || target.closest('.pk-name');
            const isPath = target.classList.contains('pk-path') || target.closest('.pk-path');
            const isThumb = target.hasAttribute('data-pk-thumb');
            const isUI = target.closest('.pk-tb, .pk-sidebar, .pk-hd, .pk-ft, .pk-player-box, .pk-modal, .pk-img-box');
            const isForceTip = target.classList.contains('pk-force-tip');

            if (!isName && !isPath && !isThumb && !isUI && !isForceTip) {
                if (target.scrollWidth <= target.clientWidth + 1) return;
            }

            renderTip(target);
            updatePos({ clientX: lastMouseX, clientY: lastMouseY });
        });

        const updatePos = (e) => {
            if (!tipEl || isMenuOpen || tipEl.style.display === 'none') return;
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            let left = (e.clientX / scale) + 15;
            let top = (e.clientY / scale) + 15;
            const rect = getLogicalRect(tipEl);
            const w = rect.width;
            const h = rect.height;
            const winW = window.innerWidth / scale;
            const winH = window.innerHeight / scale;

            if (left + w > winW - 10) left = (e.clientX / scale) - w - 15;
            if (top + h > winH - 10) top = (e.clientY / scale) - h - 15;
            tipEl.style.left = `${left}px`;
            tipEl.style.top = `${top}px`;
        };

        const onGlobalMouseMove = (e) => {
            lastMouseX = e.clientX; lastMouseY = e.clientY;
            if (document.body.classList.contains('pk-dragging')) { hideTip(); return; }
            updatePos(e);
            if (activeTarget && tipEl.style.display !== 'none') {
                const actualTarget = e.target.closest('[data-pk-tip],[data-pk-thumb]');
                if (actualTarget !== activeTarget) {
                    hideTip();
                }
            }
        };
        document.addEventListener('mousemove', onGlobalMouseMove);

        const onGlobalMouseOut = (e) => {
            const target = e.target.closest('[data-pk-tip],[data-pk-thumb]');
            if (target && target === activeTarget) {
                if (target.contains(e.relatedTarget)) return;
                hideTip();
            }
        };
        document.addEventListener('mouseout', onGlobalMouseOut);

        const onGlobalCtxMenu = () => { isMenuOpen = true; hideTip(); };
        const onGlobalClick = () => { isMenuOpen = false; if(activeTarget) hideTip(); };
        const onGlobalWheel = () => { isMenuOpen = false; hideTip(); };

        window.addEventListener('contextmenu', onGlobalCtxMenu, true);
        window.addEventListener('click', onGlobalClick, true);
        window.addEventListener('wheel', onGlobalWheel, { passive: true, capture: true });

        window.pkRefreshTooltip = () => {
            if (isMenuOpen || document.body.classList.contains('pk-dragging')) {
                hideTip(); return;
            }
            const elUnder = document.elementFromPoint(lastMouseX, lastMouseY);
            if (!elUnder) { hideTip(); return; }

            const target = elUnder.closest('[data-pk-tip], [data-pk-thumb]');
            if (!target) {
                hideTip(); return;
            }

            if (target.closest('.pk-grid-card-row')) {
                hideTip();
                return;
            }

            const isName = target.classList.contains('pk-name') || target.closest('.pk-name');
            const isPath = target.classList.contains('pk-path') || target.closest('.pk-path');
            const isThumb = target.hasAttribute('data-pk-thumb');
            const isUI = target.closest('.pk-tb, .pk-sidebar, .pk-hd, .pk-ft, .pk-player-box, .pk-modal, .pk-img-box');
            const isForceTip = target.classList.contains('pk-force-tip');

            if (!isName && !isPath && !isThumb && !isUI && !isForceTip) {
                if (target.scrollWidth <= target.clientWidth + 1) {
                    hideTip();
                    return;
                }
            }

            renderTip(target);
            updatePos({ clientX: lastMouseX, clientY: lastMouseY });
        };

        return () => {
            document.removeEventListener('mousemove', onGlobalMouseMove);
            document.removeEventListener('mouseout', onGlobalMouseOut);
            window.removeEventListener('contextmenu', onGlobalCtxMenu, true);
            window.removeEventListener('click', onGlobalClick, true);
            window.removeEventListener('wheel', onGlobalWheel, { passive: true, capture: true });
            delete window.pkRefreshTooltip;
            if (tipEl && tipEl.parentNode) tipEl.remove();
            if (style && style.parentNode) style.remove();
        };
    })();
    const UI = {
        win: el.querySelector('.pk-win'), vp: el.querySelector('#pk-vp'), in: el.querySelector('#pk-in'),
        loader: el.querySelector('#pk-loader'), loadTxt: el.querySelector('#pk-load-txt'), stopBtn: el.querySelector('#pk-stop-load'),
        crumb: el.querySelector('#pk-crumb'), stat: el.querySelector('#pk-stat'),
        chkAll: el.querySelector('#pk-all'), scan: el.querySelector('#pk-scan-dup'),
        dupTools: el.querySelector('#pk-dup-tools'),
        dupFilters: el.querySelector('#pk-dup-filters'),
        chkName: el.querySelector('#pk-chk-name'),
        chkSim: el.querySelector('#pk-chk-sim'),
        chkHash: el.querySelector('#pk-chk-hash'),
        selDupFolder: el.querySelector('#pk-dup-folder-sel'),
        offTools: el.querySelector('#pk-offline-tools'),
        chkOffRun: el.querySelector('#pk-off-run'),
        chkOffFail: el.querySelector('#pk-off-fail'),
        chkOffOk: el.querySelector('#pk-off-ok'),
        upTools: el.querySelector('#pk-upload-tools'),
        chkUpRun: el.querySelector('#pk-chk-up-run'),
        chkUpPause: el.querySelector('#pk-chk-up-pause'),
        chkUpDone: el.querySelector('#pk-chk-up-done'),
        btnAnalyze: el.querySelector('#pk-analyze'),
        btnExport: el.querySelector('#pk-export'),
        btnFolderFirst: el.querySelector('#pk-btn-folder-first'),
        btnNavHome: el.querySelector('#pk-nav-home'),
        btnNavOffline: el.querySelector('#pk-nav-offline'),
        btnNavUpload: el.querySelector('#pk-nav-upload'),
        btnNavRecent: el.querySelector('#pk-nav-recent'),
        btnNavHistory: el.querySelector('#pk-nav-history'),
        btnNavShare: el.querySelector('#pk-nav-share'),
        btnNavStarred: el.querySelector('#pk-nav-starred'),
        btnNavTrash: el.querySelector('#pk-nav-trash'),
        trashBar: el.querySelector('#pk-trash-bar'),
        btnTrashRefresh: el.querySelector('#pk-trash-refresh'),
        bottomGrp: el.querySelector('.pk-ft .pk-grp'),
        actionBar: el.querySelector('#pk-actionbar'),
        btnRestore: el.querySelector('#pk-restore'),
        btnDelForever: el.querySelector('#pk-del-forever'),
        btnEmptyTrash: el.querySelector('#pk-empty-trash'),
        btnTrashBlacklistManager: el.querySelector('#pk-trash-blacklist-manager'),
        btnDupSmart: el.querySelector('#pk-dup-smart-btn'),
        btnDupSort: el.querySelector('#pk-dup-sort-btn'),
        btnExit: el.querySelector('#pk-btn-exit'),
        btnCopy: el.querySelector('#pk-copy'), btnCut: el.querySelector('#pk-cut'),
        btnDel: el.querySelector('#pk-del'), btnDeselect: el.querySelector('#pk-deselect'),
        btnRename: el.querySelector('#pk-rename'),
        btnBulkRename: el.querySelector('#pk-bulkrename'),
        btnPrune: el.querySelector('#pk-prune'),
        btnUnzip: el.querySelector('#pk-unzip'),
        btnMigrate: el.querySelector('#pk-migrate'),
        btnBlacklistManager: el.querySelector('#pk-blacklist-manager'),
        uploadWrap: el.querySelector('#pk-upload-wrap'),
        btnUpload: el.querySelector('#pk-btn-upload'),
        actUpFile: el.querySelector('#pk-act-upload-file'),
        actUpFolder: el.querySelector('#pk-act-upload-folder'),
        inpFile: el.querySelector('#pk-file-selector'),
        inpFolder: el.querySelector('#pk-folder-selector'),
        btnCancelShare: el.querySelector('#pk-cancel-share'),
        btnRetryTask: el.querySelector('#pk-retry-task'),
        btnCopyLinkOffline: el.querySelector('#pk-off-copy-link'),
        btnUpPause: el.querySelector('#pk-up-pause'),
        btnUpStart: el.querySelector('#pk-up-start'),
        btnUpDel: el.querySelector('#pk-up-del'),
        btnUpClearAll: el.querySelector('#pk-up-clear-all'),
        btnPaste: el.querySelector('#pk-paste'),
        btnPaste: el.querySelector('#pk-paste'),
        btnRefresh: el.querySelector('#pk-refresh'), btnNewFolder: el.querySelector('#pk-newfolder'),
        btnSettings: el.querySelector('#pk-settings'), btnClose: el.querySelector('#pk-close'),
        btnHelp: el.querySelector('#pk-help'),
        btnTheme: el.querySelector('#pk-theme'),
        btnExt: el.querySelector('#pk-ext'),
        btnImgSearch: el.querySelector('#pk-img-search'),
        btnAria2: el.querySelector('#pk-aria2'), btnDown: el.querySelector('#pk-down'),
        pop: el.querySelector('#pk-pop'), ctx: el.querySelector('#pk-ctx'), cols: el.querySelectorAll('.pk-col'),
        searchInput: el.querySelector('#pk-search-input'),
        chkGlobal: el.querySelector('#pk-chk-global'),
        lblGlobal: el.querySelector('#pk-lbl-global'),
        chkSearchPath: el.querySelector('#pk-chk-search-path'),
        lblSearchPath: el.querySelector('#pk-search-path-con'),
        btnAnaSelect: (function() {
            const parent = el.querySelector('#pk-search-path-con');
            const b = document.createElement('button'); b.className = 'pk-ana-select-btn'; b.id = 'pk-ana-select-btn'; b.style.marginRight = '4px';
            b.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg> <span>${L.btn_ana_select}</span>`;
            if (parent) parent.parentNode.insertBefore(b, parent);
            return b;
        })(),
        btnAnaSort: (function() {
            const parent = el.querySelector('#pk-search-path-con');
            const b = document.createElement('button'); b.className = 'pk-ana-select-btn'; b.id = 'pk-ana-sort-btn';
            b.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><polyline points="19 12 12 19 5 12"></polyline></svg> <span>${L.btn_in_group_sort}</span>`;
            if (parent) parent.parentNode.insertBefore(b, parent);
            return b;
        })(),
        topBar: el.querySelector('#pk-top-bar'),
        searchClear: el.querySelector('#pk-search-clear'),
        searchBtn: el.querySelector('#pk-search-btn'),
        searchHist: el.querySelector('#pk-search-hist'),
        filterBar: el.querySelector('#pk-filter-bar'),
        filterBtn: el.querySelector('#pk-filter-btn'),
        filterActiveUI: el.querySelector('#pk-filter-active-ui'),
        filterCatLabel: el.querySelector('#pk-filter-cat-label'),
        filterExtsWrap: el.querySelector('#pk-filter-exts-wrap'),
        filterExtsMain: el.querySelector('#pk-filter-exts-main'),
        filterExtsMoreBtn: el.querySelector('#pk-filter-exts-more-btn'),
        filterExitBtn: el.querySelector('#pk-filter-exit-btn')
    };

    const isAnalyzeGroupedRoot = () => {
        const cur = S.path && S.path.length ? S.path[S.path.length - 1] : null;
        return !!(S.analyzeMode && cur && cur.id === 'analyze_root' && S.analyzeSimGroups);
    };
    const isGroupedResultMode = () => !!(S.dupMode || isAnalyzeGroupedRoot());
    const canUseGridView = () => !S.shareMode && !S.offlineMode && !S.uploadMode && !S.historyMode && !S.trashMode;
    const isGridView = () => S.viewMode === 'grid' && canUseGridView();
    const getListRowHeight = () => UI.win && UI.win.classList.contains('pk-maximized') ? 60 : 40;
    const getGridCardHeight = () => UI.win && UI.win.classList.contains('pk-maximized') ? 324 : 286;
    const getGridCardRadius = () => UI.win && UI.win.classList.contains('pk-maximized') ? 22 : 18;
    const getGridLayout = () => {
        const vpRect = getLogicalRect(UI.vp || UI.in);
        const vpWidth = Math.max(0, Math.floor((vpRect && vpRect.width) || UI.vp?.clientWidth || 0));
        const isMax = UI.win && UI.win.classList.contains('pk-maximized');
        const padX = isMax ? 20 : 16;
        const gapX = isMax ? 18 : 16;
        const gapY = isMax ? 20 : 16;
        const minCardWidth = isMax ? 236 : 208;
        const innerWidth = Math.max(minCardWidth, vpWidth - padX * 2);
        const cols = Math.max(1, Math.floor((innerWidth + gapX) / (minCardWidth + gapX)));
        const cardWidth = Math.max(188, Math.floor((innerWidth - gapX * (cols - 1)) / cols));
        const cardHeight = Math.max(220, CONF.rowHeight - gapY);
        return { padX, gapX, gapY, cols, cardWidth, cardHeight };
    };
    const isGroupedGridView = () => !!(isGroupedResultMode() && isGridView());
    const getGroupedGridSectionMetrics = () => {
        const isMax = !!(UI.win && UI.win.classList.contains('pk-maximized'));
        return {
            headerHeight: isMax ? CONF.dupGridHeaderHeight.max : CONF.dupGridHeaderHeight.normal,
            sectionGap: isMax ? CONF.dupGridSectionGap.max : CONF.dupGridSectionGap.normal,
            bodyGapY: isMax ? CONF.dupGridBodyGapY.max : CONF.dupGridBodyGapY.normal
        };
    };
    const buildGroupedGridMetaKey = () => {
        const baseKey = getGridLayoutKey();
        const display = Array.isArray(S.display) ? S.display : [];
        const displayLen = display.length;
        if (!displayLen) return `${baseKey}::grouped-grid::empty`;
        let firstId = '';
        let lastId = '';
        let runSig = '';
        let prevRunKey = null;
        let runCount = 0;
        for (let i = 0; i < displayLen; i++) {
            const item = display[i];
            if (!item) continue;
            const itemId = item.id || '';
            if (!firstId) firstId = itemId;
            lastId = itemId || lastId;
            const runKey = item.isHeader ? `h:${itemId || i}` : `i:${S.dupGroups.has(itemId) ? S.dupGroups.get(itemId) : -1}`;
            if (runKey !== prevRunKey) {
                runSig += `${prevRunKey === null ? '' : '|'}${runKey}`;
                prevRunKey = runKey;
                runCount++;
            }
        }
        return `${baseKey}::grouped-grid::${displayLen}::${runCount}::${firstId}::${lastId}::${runSig}`;
    };
    const getGroupedGridMeta = (force = false) => {
        if (!isGroupedGridView()) {
            S.dupGridMeta = null;
            S.dupGridMetaKey = '';
            return null;
        }

        const metaKey = buildGroupedGridMetaKey();
        if (!force && S.dupGridMeta && S.dupGridMetaKey === metaKey) return S.dupGridMeta;

        const display = Array.isArray(S.display) ? S.display : [];
        const gridLayout = getGridLayout();
        const sectionMetrics = getGroupedGridSectionMetrics();
        const cols = Math.max(1, gridLayout.cols);
        const sectionGapY = Math.max(0, gridLayout.gapY || 0);
        const vpRect = getLogicalRect(UI.vp || UI.in);
        const fullWidth = Math.max(0, Math.floor((vpRect && vpRect.width) || UI.vp?.clientWidth || 0));

        const sections = [];
        const indexLayout = new Map();
        const indexToSection = new Map();

        let current = null;
        let totalHeight = 0;

        const pushSection = () => {
            if (!current) return;

            const itemCount = current.itemIndices.length;
            const rows = itemCount > 0 ? Math.ceil(itemCount / cols) : 0;
            const headerTop = totalHeight;
            const bodyTop = headerTop + sectionMetrics.headerHeight + (itemCount > 0 ? sectionGapY : 0);
            const bodyHeight = rows * CONF.rowHeight;
            const sectionHeight = sectionMetrics.headerHeight + (itemCount > 0 ? (sectionGapY + bodyHeight) : 0);

            const section = {
                key: current.key,
                groupIndex: current.groupIndex,
                reason: current.reason,
                title: current.title,
                headerIndex: current.headerIndex,
                itemIndices: [...current.itemIndices],
                itemIds: [...current.itemIds],
                itemCount,
                rows,
                cols,
                top: headerTop,
                headerHeight: sectionMetrics.headerHeight,
                bodyTop,
                bodyHeight,
                totalHeight: sectionHeight,
                bottom: headerTop + sectionHeight
            };

            if (current.headerIndex !== null) {
                indexLayout.set(current.headerIndex, {
                    kind: 'header',
                    sectionKey: current.key,
                    top: headerTop,
                    left: 0,
                    width: fullWidth,
                    height: sectionMetrics.headerHeight
                });
                indexToSection.set(current.headerIndex, section);
            }

            for (let localIdx = 0; localIdx < current.itemIndices.length; localIdx++) {
                const displayIndex = current.itemIndices[localIdx];
                const rowIdx = Math.floor(localIdx / cols);
                const colIdx = localIdx % cols;

                indexLayout.set(displayIndex, {
                    kind: 'item',
                    sectionKey: current.key,
                    localIndex: localIdx,
                    rowIdx,
                    colIdx,
                    top: bodyTop + rowIdx * CONF.rowHeight,
                    left: gridLayout.padX + colIdx * (gridLayout.cardWidth + gridLayout.gapX),
                    width: gridLayout.cardWidth,
                    height: gridLayout.cardHeight
                });
                indexToSection.set(displayIndex, section);
            }

            sections.push(section);
            totalHeight += sectionHeight;
            current = null;
        };

        for (let i = 0; i < display.length; i++) {
            const d = display[i];
            if (!d) continue;

            if (d.isHeader) {
                pushSection();

                const parsedGroupIndex = /^grp_\d+$/.test(d.id || '') ? parseInt(String(d.id).replace('grp_', ''), 10) : -1;
                current = {
                    key: d.id || `grp_auto_${i}`,
                    headerIndex: i,
                    groupIndex: parsedGroupIndex,
                    reason: d.type || '',
                    title: d.name || '',
                    itemIndices: [],
                    itemIds: []
                };
                continue;
            }

            if (!current) {
                current = {
                    key: `loose_${i}`,
                    headerIndex: null,
                    groupIndex: S.dupGroups.has(d.id) ? S.dupGroups.get(d.id) : -1,
                    reason: S.dupReasons.get(d.id) || '',
                    title: '',
                    itemIndices: [],
                    itemIds: []
                };
            }

            current.itemIndices.push(i);
            if (d.id) current.itemIds.push(d.id);
        }

        pushSection();

        if (sections.length > 0) totalHeight = Math.max(0, totalHeight);

        S.dupGridMetaKey = metaKey;
        S.dupGridMeta = {
            key: metaKey,
            cols,
            gridLayout,
            sectionMetrics,
            fullWidth,
            totalHeight: Math.max(0, totalHeight),
            sections,
            indexLayout,
            indexToSection
        };
        return S.dupGridMeta;
    };
    const findDupGridHitIndex = (logicalOffsetX, logicalOffsetY) => {
        if (!isGroupedGridView()) return -1;
        const dupGridMeta = getGroupedGridMeta();
        if (!dupGridMeta || !dupGridMeta.sections || !dupGridMeta.sections.length) return -1;

        const gridLayout = dupGridMeta.gridLayout || getGridLayout();
        if (logicalOffsetX < gridLayout.padX || logicalOffsetY < 0) return -1;

        const colStride = gridLayout.cardWidth + gridLayout.gapX;
        const relX = logicalOffsetX - gridLayout.padX;
        const colIdx = Math.floor(relX / Math.max(1, colStride));
        const withinColX = relX - colIdx * colStride;

        if (colIdx < 0 || colIdx >= gridLayout.cols || withinColX < 0 || withinColX > gridLayout.cardWidth) return -1;

        for (const section of dupGridMeta.sections) {
            if (logicalOffsetY < section.top) break;
            if (logicalOffsetY >= section.bottom) continue;
            if (logicalOffsetY < section.bodyTop || logicalOffsetY >= section.bodyTop + section.bodyHeight) return -1;

            const relY = logicalOffsetY - section.bodyTop;
            const rowIdx = Math.floor(relY / Math.max(1, CONF.rowHeight));
            const withinRowY = relY - rowIdx * CONF.rowHeight;

            if (rowIdx < 0 || rowIdx >= section.rows || withinRowY < 0 || withinRowY > gridLayout.cardHeight) return -1;

            const localIdx = rowIdx * gridLayout.cols + colIdx;
            if (localIdx < 0 || localIdx >= section.itemIndices.length) return -1;

            return section.itemIndices[localIdx];
        }

        return -1;
    };

    const getDupGridAnchorIndex = (logicalCenterX, logicalCenterY) => {
        if (!isGroupedGridView()) return -1;
        const dupGridMeta = getGroupedGridMeta();
        if (!dupGridMeta) return -1;

        const hitIdx = findDupGridHitIndex(logicalCenterX, logicalCenterY);
        if (hitIdx >= 0) return hitIdx;

        let bestIdx = -1;
        let bestScore = Infinity;

        dupGridMeta.indexLayout.forEach((layout, idx) => {
            if (!layout || layout.kind !== 'item') return;
            const cx = layout.left + (layout.width / 2);
            const cy = layout.top + (Math.min(layout.height, CONF.rowHeight) / 2);
            const score = Math.abs(cy - logicalCenterY) * 4 + Math.abs(cx - logicalCenterX);
            if (score < bestScore) {
                bestScore = score;
                bestIdx = idx;
            }
        });

        return bestIdx;
    };

    const getItemScrollTopByIndex = (targetIdx) => {
        if (targetIdx < 0) return 0;
        if (isGridView()) {
            if (isGroupedGridView()) {
                const dupGridMeta = getGroupedGridMeta();
                const layout = dupGridMeta && dupGridMeta.indexLayout ? dupGridMeta.indexLayout.get(targetIdx) : null;
                if (layout) return Math.max(0, layout.top);
            }
            const gridLayout = getGridLayout();
            const visualRow = Math.floor(targetIdx / Math.max(1, gridLayout.cols));
            return gridLayout.gapY + visualRow * CONF.rowHeight;
        }
        return targetIdx * CONF.rowHeight;
    };
    const getViewportAnchorId = (preferSelection = false) => {
        const selectedIds = S.getSelectedIds();
        const fallbackId = S.activeId || (selectedIds.length ? selectedIds[selectedIds.length - 1] : null);
        if (preferSelection && fallbackId && S.display.some(x => x.id === fallbackId)) return fallbackId;
        if (!UI.vp || !S.display.length) return fallbackId;
        const centerY = UI.vp.scrollTop + (UI.vp.clientHeight / 2);

        if (isGridView()) {
            const gridLayout = getGridLayout();

            if (isGroupedGridView()) {
                const usableWidth = Math.max(1, UI.vp.clientWidth - gridLayout.padX * 2);
                const logicalCenterX = gridLayout.padX + (usableWidth / 2);
                const targetIdx = getDupGridAnchorIndex(logicalCenterX, centerY);
                if (targetIdx >= 0 && targetIdx < S.display.length) {
                    return S.display[targetIdx]?.id || fallbackId;
                }
                return fallbackId;
            }

            const cols = Math.max(1, gridLayout.cols);
            const totalRows = Math.max(1, Math.ceil(S.display.length / cols));
            const adjustedCenterY = Math.max(0, centerY - gridLayout.gapY);
            const visualRow = Math.max(0, Math.min(totalRows - 1, Math.floor(adjustedCenterY / Math.max(1, CONF.rowHeight))));
            const usableWidth = Math.max(1, UI.vp.clientWidth - gridLayout.padX * 2);
            const centerX = usableWidth / 2;
            const visualCol = Math.max(0, Math.min(cols - 1, Math.floor(centerX / Math.max(1, gridLayout.cardWidth + gridLayout.gapX))));
            const targetIdx = Math.min(S.display.length - 1, visualRow * cols + visualCol);
            return S.display[targetIdx]?.id || fallbackId;
        }

        const targetIdx = Math.max(0, Math.min(S.display.length - 1, Math.floor(centerY / Math.max(1, CONF.rowHeight))));
        return S.display[targetIdx]?.id || fallbackId;
    };
    const syncLayoutMetrics = () => { CONF.rowHeight = isGridView() ? getGridCardHeight() : getListRowHeight(); };
    const getGridLayoutKey = () => {
        const vpRect = getLogicalRect(UI.vp || UI.in);
        const vpWidth = Math.max(0, Math.floor((vpRect && vpRect.width) || UI.vp?.clientWidth || 0));
        const vpHeight = Math.max(0, Math.floor(UI.vp?.clientHeight || 0));
        const gridLayout = getGridLayout();
        const isMax = UI.win && UI.win.classList.contains('pk-maximized') ? 1 : 0;
        return [vpWidth, vpHeight, gridLayout.cols, gridLayout.cardWidth, gridLayout.cardHeight, isMax].join(':');
    };
    const resetViewportRenderState = () => {
        if (S._gridRelayoutRaf1) {
            cancelAnimationFrame(S._gridRelayoutRaf1);
            S._gridRelayoutRaf1 = 0;
        }
        if (S._gridRelayoutRaf2) {
            cancelAnimationFrame(S._gridRelayoutRaf2);
            S._gridRelayoutRaf2 = 0;
        }

        if (!UI.in) return;

        const pool = UI.in._pkVisibleRowPool || [];
        for (let i = 0; i < pool.length; i++) {
            const row = pool[i];
            if (!row) continue;
            if (typeof resetPooledRow === 'function') resetPooledRow(row);
            if (row.parentNode === UI.in) UI.in.removeChild(row);
        }

        if (typeof cleanupNonPooledChildren === 'function') {
            cleanupNonPooledChildren(UI.in);
        }
    };

    const beginFolderViewSync = () => {
        if (S._folderViewSyncing) return;
        S._folderViewSyncing = true;

        resetViewportRenderState();

        if (UI.vp) {
            UI.vp.style.visibility = 'hidden';
            UI.vp.style.pointerEvents = 'none';
        }

        const hd = UI.win && UI.win.querySelector('.pk-grid-hd');
        if (hd) hd.style.visibility = 'hidden';
    };
    const endFolderViewSync = (force = false) => {
        if (!S._folderViewSyncing) return;
        if (S._folderViewSyncHold && !force) return;
        requestAnimationFrame(() => {
            if (S._folderViewSyncHold && !force) return;
            if (UI.vp) {
                UI.vp.style.visibility = '';
                UI.vp.style.pointerEvents = '';
            }
            const hd = UI.win && UI.win.querySelector('.pk-grid-hd');
            if (hd) hd.style.visibility = '';
            S._folderViewSyncing = false;
        });
    };
    const scheduleGridRelayout = (forceFull = false) => {
        if (!isGridView()) {
            endFolderViewSync();
            return;
        }
        if (S._gridRelayoutRaf1) cancelAnimationFrame(S._gridRelayoutRaf1);
        if (S._gridRelayoutRaf2) cancelAnimationFrame(S._gridRelayoutRaf2);

        const prevKey = S._gridLayoutKey || '';

        S._gridRelayoutRaf1 = requestAnimationFrame(() => {
            S._gridRelayoutRaf2 = requestAnimationFrame(() => {
                if (!UI.vp || !UI.in || !isGridView()) {
                    endFolderViewSync();
                    return;
                }

                const nextKey = getGridLayoutKey();
                if (forceFull || nextKey !== prevKey) {
                    S._gridLayoutKey = nextKey;
                    renderList();
                } else if (typeof renderVisible === 'function') {
                    renderVisible();
                    endFolderViewSync();
                } else {
                    endFolderViewSync();
                }
            });
        });
    };
    const ensureGridMediaStore = () => {
        if (!window.pkGlobalThumbCache) window.pkGlobalThumbCache = new Set();
        if (!window.pkThumbTriState) window.pkThumbTriState = { folder: Object.create(null), file: Object.create(null) };
        if (!window.pkThumbTriState.folder) window.pkThumbTriState.folder = Object.create(null);
        if (!window.pkThumbTriState.file) window.pkThumbTriState.file = Object.create(null);
        if (!window.pkGridMediaStore) window.pkGridMediaStore = { folder: Object.create(null), file: Object.create(null) };
        if (!window.pkGridMediaStore.folder) window.pkGridMediaStore.folder = Object.create(null);
        if (!window.pkGridMediaStore.file) window.pkGridMediaStore.file = Object.create(null);
        return window.pkGridMediaStore;
    };
    const getStableFolderMediaState = (item) => {
        ensureGridMediaStore();
        const tri = window.pkThumbTriState.folder;
        const store = window.pkGridMediaStore.folder;
        const id = item && item.id;
        if (!id) return { state: 'none', src: '' };
        const state = tri[id] || 'unknown';
        const cached = store[id] || '';
        if (state === 'ok' && cached) return { state: 'ok', src: cached };
        if (state === 'ok' && item.thumbnail_link) {
            store[id] = item.thumbnail_link;
            return { state: 'ok', src: item.thumbnail_link };
        }
        if ((state === 'unknown' || state === 'probing') && cached) return { state: 'ok', src: cached };
        return { state, src: '' };
    };
    const markStableFolderMediaState = (id, state, src = '') => {
        ensureGridMediaStore();
        if (!id) return;
        window.pkThumbTriState.folder[id] = state;
        if (state === 'ok' && src) {
            window.pkGridMediaStore.folder[id] = src;
            window.pkGlobalThumbCache.add(id);
        } else if (state === 'fail' && window.pkGridMediaStore.folder[id]) {
            delete window.pkGridMediaStore.folder[id];
        }
    };
    const getStableFileMediaState = (item) => {
        ensureGridMediaStore();
        const tri = window.pkThumbTriState.file;
        const store = window.pkGridMediaStore.file;
        const id = item && item.id;
        if (!id) return { state: 'none', src: '' };
        const state = tri[id] || 'unknown';
        const cached = store[id] || '';
        if (state === 'ok' && cached) return { state: 'ok', src: cached };
        if (state === 'ok' && item.thumbnail_link) {
            store[id] = item.thumbnail_link;
            return { state: 'ok', src: item.thumbnail_link };
        }
        if ((state === 'unknown' || state === 'probing') && cached) return { state: 'ok', src: cached };
        return { state, src: '' };
    };
    const markStableFileMediaState = (id, state, src = '') => {
        ensureGridMediaStore();
        if (!id) return;
        window.pkThumbTriState.file[id] = state;
        if (state === 'ok' && src) {
            window.pkGridMediaStore.file[id] = src;
            window.pkGlobalThumbCache.add(id);
        } else if (state === 'fail' && window.pkGridMediaStore.file[id]) {
            delete window.pkGridMediaStore.file[id];
        }
    };
    window.ensureGridMediaStore = ensureGridMediaStore;
    window.getStableFolderMediaState = getStableFolderMediaState;
    window.markStableFolderMediaState = markStableFolderMediaState;
    window.getStableFileMediaState = getStableFileMediaState;
    window.markStableFileMediaState = markStableFileMediaState;

    const ensureGridMediaDomCache = () => {
        if (!window.pkGridMediaDomCache) window.pkGridMediaDomCache = { folder: Object.create(null), file: Object.create(null) };
        return window.pkGridMediaDomCache;
    };
    const isTrustedChildRealThumb = (child) => {
        if (!child || !child.thumbnail_link || child.thumbnail_link === child.icon_link) return false;
        const mime = (child.mime_type || '').toLowerCase();
        const duration = Number((child.params && child.params.duration) || (child.medias && child.medias[0] && child.medias[0].duration) || 0);
        const hasMediaMeta = Array.isArray(child.medias) && child.medias.length > 0;
        const stableState = child.id && typeof getStableFileMediaState === 'function' ? getStableFileMediaState(child).state : 'unknown';
        return stableState === 'ok' || mime.startsWith('video/') || mime.startsWith('image/') || duration > 0 || hasMediaMeta;
    };
    const getFolderEmbedCacheStamp = (item) => {
        if (!item || item.kind !== 'drive#folder') return '';
        if (typeof globalCache === 'undefined') return 'nogc';

        const normalize = (data) => (data && !Array.isArray(data) && data.items) ? data.items : data;
        const raw = globalCache.get(item.id);

        if (!raw) return 'miss';
        if (!Array.isArray(raw) && raw.nextToken) {
            const pending = normalize(raw);
            const len = Array.isArray(pending) ? pending.filter(Boolean).length : 0;
            return `loading:${len}`;
        }

        const children = normalize(raw);
        if (!Array.isArray(children)) return 'invalid';

        const visibleChildren = children.filter(Boolean);
        if (!visibleChildren.length) return 'empty';

        const nonFolders = visibleChildren.filter(child => child.kind !== 'drive#folder');
        if (!nonFolders.length) {
            return `folders:${visibleChildren.length}:${visibleChildren.slice(0, 3).map(child => child.id || '').join(',')}`;
        }

        const hasRealChildThumb = (child) => isTrustedChildRealThumb(child);
        const isFallbackPlaceholderChild = (child) => {
            const mime = (child.mime_type || '').toLowerCase();
            const duration = (child.params && child.params.duration) || 0;
            return !hasRealChildThumb(child) && (mime.startsWith('video/') || mime.startsWith('image/') || duration > 0);
        };

        const realThumbChild = nonFolders.find(hasRealChildThumb);
        if (realThumbChild) {
            const stableChildMedia = typeof getStableFileMediaState === 'function' ? getStableFileMediaState(realThumbChild) : { state: 'unknown', src: '' };
            const realThumbSrc = stableChildMedia.src || realThumbChild.thumbnail_link || '';
            return ['real', realThumbChild.id || '', realThumbSrc || '', realThumbChild.icon_link || '', realThumbChild.mime_type || '', realThumbChild.name || ''].join(':');
        }

        if (nonFolders.every(isFallbackPlaceholderChild)) {
            const target = nonFolders[0] || {};
            return ['fallback', target.id || '', target.icon_link || '', target.thumbnail_link || '', target.mime_type || '', target.name || ''].join(':');
        }

        const ordinaryPlaceholderChild = nonFolders.find(child => !hasRealChildThumb(child) && !isFallbackPlaceholderChild(child));
        if (ordinaryPlaceholderChild) {
            return ['ordinary', ordinaryPlaceholderChild.id || '', ordinaryPlaceholderChild.icon_link || '', ordinaryPlaceholderChild.thumbnail_link || '', ordinaryPlaceholderChild.mime_type || '', ordinaryPlaceholderChild.name || ''].join(':');
        }

        return `blank:${visibleChildren.length}:${nonFolders.length}`;
    };
    const makeGridMediaCacheKey = (item) => [item?.id || '', item?.kind || '', item?.thumbnail_link || '', item?.icon_link || '', getFolderEmbedCacheStamp(item)].join('::');
    const stashFrozenGridMediaNode = (itemId, mediaKey, node, kind = '') => {
        if (!itemId || !mediaKey || !node) return;
        const cache = ensureGridMediaDomCache();
        const bucket = kind === 'drive#folder' ? cache.folder : cache.file;
        bucket[itemId] = { key: mediaKey, node };
    };
    const takeFrozenGridMediaNode = (item) => {
        if (!item || !item.id) return null;
        const cache = ensureGridMediaDomCache();
        const bucket = item.kind === 'drive#folder' ? cache.folder : cache.file;
        const hit = bucket[item.id];
        const mediaKey = makeGridMediaCacheKey(item);
        if (!hit || hit.key !== mediaKey || !hit.node) return null;
        delete bucket[item.id];
        return hit.node;
    };

    window.ensureGridMediaDomCache = ensureGridMediaDomCache;
    window.stashFrozenGridMediaNode = stashFrozenGridMediaNode;
    window.takeFrozenGridMediaNode = takeFrozenGridMediaNode;

    const buildGridFolderPreview = (folderItem, folderIconHtml) => {
        const hasThumb = !!(folderItem.thumbnail_link && folderItem.thumbnail_link !== folderItem.icon_link);
        const stableFolderMedia = hasThumb ? getStableFolderMediaState(folderItem) : { state: 'none', src: '' };
        const folderThumbState = stableFolderMedia.state;
        const folderResolvedSrc = stableFolderMedia.src || folderItem.thumbnail_link || '';
        const folderHasCoverThumb = hasThumb && folderThumbState === 'ok' && !!folderResolvedSrc;

        if (hasThumb && folderThumbState === 'unknown' && !folderResolvedSrc && folderItem.id && window.pkThumbTriState && window.pkThumbTriState.folder) {
            window.pkThumbTriState.folder[folderItem.id] = 'probing';
        }

        const makePreviewBaseHtml = (display = 'flex', opacity = '1', iconSrc = folderItem.icon_link, svgHtml = folderIconHtml) => iconSrc
        ? `<span class="pk-gv-folder-base" style="display:${display};align-items:center;justify-content:center;width:100%;height:100%;opacity:${opacity};transition:opacity .18s ease;"><img src="${iconSrc}" class="pk-gv-folder-fallback" draggable="false" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='inline-flex';"><span class="pk-gv-folder-fallback" style="display:none;">${svgHtml}</span></span>`
        : `<span class="pk-gv-folder-base" style="display:${display};align-items:center;justify-content:center;width:100%;height:100%;opacity:${opacity};transition:opacity .18s ease;"><span class="pk-gv-folder-fallback">${svgHtml}</span></span>`;

        const makeItemPlaceholderHtml = (child) => {
            const childIconHtml = getIcon(child).replace(/width="\d+"/g, 'width="108"').replace(/height="\d+"/g, 'height="108"');
            return makePreviewBaseHtml('flex', '1', child.icon_link, childIconHtml);
        };

        const resolveCachedPreview = () => {
            const normalize = (data) => (data && !Array.isArray(data) && data.items) ? data.items : data;

            if (typeof globalCache === 'undefined') return { decided: false, hasThumb: false, hidePreview: false, html: '' };

            const raw = globalCache.get(folderItem.id);
            if (!raw) return { decided: false, hasThumb: false, hidePreview: false, html: '' };

            if (!Array.isArray(raw) && raw.nextToken) {
                return { decided: false, hasThumb: false, hidePreview: false, html: '' };
            }

            const children = normalize(raw);
            if (!Array.isArray(children)) return { decided: false, hasThumb: false, hidePreview: false, html: '' };
            if (children.length === 0) return { decided: true, hasThumb: false, hidePreview: true, html: '' };

            const visibleChildren = children.filter(Boolean);
            if (!visibleChildren.length) return { decided: true, hasThumb: false, hidePreview: true, html: '' };

            const nonFolders = visibleChildren.filter(child => child.kind !== 'drive#folder');
            if (!nonFolders.length) {
                return { decided: true, hasThumb: false, hidePreview: false, html: makePreviewBaseHtml('flex', '1') };
            }

            const hasRealChildThumb = (child) => isTrustedChildRealThumb(child);
            const isFallbackPlaceholderChild = (child) => {
                const mime = (child.mime_type || '').toLowerCase();
                const duration = (child.params && child.params.duration) || 0;
                return !hasRealChildThumb(child) && (mime.startsWith('video/') || mime.startsWith('image/') || duration > 0);
            };

            const makeChildRealThumbHtml = (child) => {
                const stableChildMedia = typeof getStableFileMediaState === 'function' ? getStableFileMediaState(child) : { state: 'unknown', src: '' };
                const childThumbState = stableChildMedia.state;
                const childResolvedSrc = stableChildMedia.src || child.thumbnail_link || '';

                if (!childResolvedSrc || childThumbState === 'fail') return '';

                if (childThumbState === 'unknown' && child.id && window.pkThumbTriState && window.pkThumbTriState.file) {
                    window.pkThumbTriState.file[child.id] = 'probing';
                }

                const imgOpacity = childThumbState === 'ok' ? '1' : '0';
                const baseOpacity = childThumbState === 'ok' ? '0' : '1';

                return `<div style="position:relative;width:100%;height:100%;" data-pk-thumb-ready="${childThumbState === 'ok' ? '1' : '0'}">${makePreviewBaseHtml('flex', baseOpacity, folderItem.icon_link, folderIconHtml)}<img src="${childResolvedSrc}" class="pk-gv-folder-fallback" draggable="false" data-pk-id="${child.id || ''}" data-pk-src="${childResolvedSrc}" data-pk-frozen-cover="1" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:${imgOpacity};transition:opacity .18s ease;" onload="const ok=(this.naturalWidth>1||this.naturalHeight>1);const wrap=this.parentElement;if(ok){if(wrap)wrap.dataset.pkThumbReady='1';if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='ok';if(window.pkGridMediaStore&&window.pkGridMediaStore.file)window.pkGridMediaStore.file[this.dataset.pkId]=this.dataset.pkSrc||'';}this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.opacity='0';}else{if(wrap)wrap.dataset.pkThumbReady='0';if(this.dataset.pkId&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'fail','');}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='fail';}this.style.opacity='0';if(this.previousElementSibling)this.previousElementSibling.style.opacity='1';}" onerror="const wrap=this.parentElement;if(wrap)wrap.dataset.pkThumbReady='0';if(this.dataset.pkId&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'fail','');}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='fail';}this.style.opacity='0';if(this.previousElementSibling)this.previousElementSibling.style.opacity='1';"></div>`;
            };

            const realThumbChild = nonFolders.find(hasRealChildThumb);
            if (realThumbChild) {
                const realThumbHtml = makeChildRealThumbHtml(realThumbChild);
                if (realThumbHtml) {
                    return { decided: true, hasThumb: true, hidePreview: false, html: realThumbHtml };
                }
            }

            if (nonFolders.every(isFallbackPlaceholderChild)) {
                return { decided: true, hasThumb: false, hidePreview: false, html: makeItemPlaceholderHtml(nonFolders[0]) };
            }

            const ordinaryPlaceholderChild = nonFolders.find(child => !hasRealChildThumb(child) && !isFallbackPlaceholderChild(child));
            if (ordinaryPlaceholderChild) {
                return { decided: true, hasThumb: false, hidePreview: false, html: makeItemPlaceholderHtml(ordinaryPlaceholderChild) };
            }

            return { decided: true, hasThumb: false, hidePreview: false, html: '' };
        };

        if (folderHasCoverThumb) {
            return {
                hasThumb: true,
                hidePreview: false,
                html: `<img src="${folderResolvedSrc}" class="pk-gv-folder-fallback" draggable="false" data-pk-id="${folderItem.id}" data-pk-src="${folderResolvedSrc}" data-pk-frozen-cover="1" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:1;" onload="if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='ok';}" onerror="if(this.dataset.pkId&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'unknown');}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='unknown';}this.style.display='none';if(this.nextElementSibling){this.nextElementSibling.style.display='flex';this.nextElementSibling.style.opacity='1';}">${makePreviewBaseHtml('none', '1')}`
            };
        }

        const cachedPreview = resolveCachedPreview();
        const probeBasePreview = cachedPreview.decided
        ? cachedPreview
        : { hasThumb: false, hidePreview: false, html: '' };

        if (hasThumb && folderThumbState !== 'fail') {
            return {
                hasThumb: false,
                hidePreview: false,
                html: `${probeBasePreview.html}<img src="${folderItem.thumbnail_link}" class="pk-gv-folder-fallback" draggable="false" data-pk-id="${folderItem.id}" data-pk-src="${folderItem.thumbnail_link}" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:0;transition:opacity .18s ease;" onload="if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='ok';if(window.pkGlobalThumbCache)window.pkGlobalThumbCache.add(this.dataset.pkId);}this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.opacity='0';" onerror="if(this.dataset.pkId&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'unknown');}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='unknown';}if(this.previousElementSibling){this.previousElementSibling.style.display='flex';this.previousElementSibling.style.opacity='1';}this.remove();">`
            };
        }

        if (cachedPreview.decided) {
            return { hasThumb: cachedPreview.hasThumb, hidePreview: cachedPreview.hidePreview, html: cachedPreview.html };
        }

        return { hasThumb: false, hidePreview: false, html: '' };
    };

    const renderFrozenGridMedia = (item, iconFallback, gridFileFallbackHtml) => {
        const isFolder = item.kind === 'drive#folder';
        const hasThumb = !!(item.thumbnail_link && item.thumbnail_link !== item.icon_link);

        if (isFolder) {
            const folderEmbed = buildGridFolderPreview(item, iconFallback);
            return `<div class="pk-gv-folder-shell"><div class="pk-gv-folder-tab"></div><div class="pk-gv-folder-back"></div><div class="pk-gv-folder-preview${folderEmbed.hasThumb ? ' pk-gv-folder-has-thumb' : ''}"${folderEmbed.hidePreview ? ' style="display:none;"' : ''}>${folderEmbed.html}</div><div class="pk-gv-folder-front"></div></div>`;
        }

        const makeFileFallbackHtml = (opacity = '1') => item.icon_link
        ? `<div class="pk-gv-icon" style="opacity:${opacity};transition:opacity .18s ease;"><img src="${item.icon_link}" draggable="false" style="object-fit:contain;" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='flex';"><span style="display:none;align-items:center;justify-content:center;">${iconFallback}</span></div>`
        : `<div class="pk-gv-icon" style="opacity:${opacity};transition:opacity .18s ease;">${iconFallback}</div>`;

        if (hasThumb) {
            const stableFileMedia = getStableFileMediaState(item);
            const fileThumbState = stableFileMedia.state;
            const fileResolvedSrc = stableFileMedia.src || item.thumbnail_link || '';

            if (fileThumbState === 'fail' || !fileResolvedSrc) {
                return makeFileFallbackHtml();
            }

            if (fileThumbState === 'unknown') {
                window.pkThumbTriState.file[item.id] = 'probing';
            }

            const imgOpacity = fileThumbState === 'ok' ? '1' : '0';
            const baseOpacity = fileThumbState === 'ok' ? '0' : '1';

            return `<div style="position:relative;width:100%;height:100%;" data-pk-thumb-ready="${fileThumbState === 'ok' ? '1' : '0'}">${makeFileFallbackHtml(baseOpacity)}<img src="${fileResolvedSrc}" class="pk-max-thumb" draggable="false" data-pk-id="${item.id}" data-pk-src="${fileResolvedSrc}" data-pk-frozen-cover="1" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:${imgOpacity};transition:opacity .18s ease;" onload="const ok=(this.naturalWidth>1||this.naturalHeight>1);const wrap=this.parentElement;const card=this.closest('.pk-grid-card-body');const play=card&&card.querySelector('.pk-gv-play');if(ok){if(wrap)wrap.dataset.pkThumbReady='1';if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='ok';if(window.pkGridMediaStore&&window.pkGridMediaStore.file)window.pkGridMediaStore.file[this.dataset.pkId]=this.dataset.pkSrc||'';}this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.opacity='0';if(window.syncGridVideoPlayState)window.syncGridVideoPlayState(card);}else{if(wrap)wrap.dataset.pkThumbReady='0';if(this.dataset.pkId&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'fail');}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='fail';if(window.pkGridMediaStore&&window.pkGridMediaStore.file)delete window.pkGridMediaStore.file[this.dataset.pkId];}if(this.previousElementSibling)this.previousElementSibling.style.opacity='1';if(window.syncGridVideoPlayState)window.syncGridVideoPlayState(card);this.remove();}" onerror="const wrap=this.parentElement;const card=this.closest('.pk-grid-card-body');const play=card&&card.querySelector('.pk-gv-play');if(wrap)wrap.dataset.pkThumbReady='0';if(this.dataset.pkId&&window.markStableFileMediaState){window.markStableFileMediaState(this.dataset.pkId,'fail');}else if(window.pkThumbTriState&&window.pkThumbTriState.file&&this.dataset.pkId){window.pkThumbTriState.file[this.dataset.pkId]='fail';if(window.pkGridMediaStore&&window.pkGridMediaStore.file)delete window.pkGridMediaStore.file[this.dataset.pkId];}if(this.previousElementSibling)this.previousElementSibling.style.opacity='1';if(window.syncGridVideoPlayState)window.syncGridVideoPlayState(card);this.remove();"></div>`;
        }

        return makeFileFallbackHtml();
    };

    const isGridVideoItem = (item) => {
        const mime = ((item && item.mime_type) || '').toLowerCase();
        const duration = Number((item && item.params && item.params.duration) || (item && item.medias && item.medias[0] && item.medias[0].duration) || 0);
        return mime.startsWith('video/') || duration > 0;
    };

    const syncGridVideoPlayState = (scope, item = null) => {
        const root = scope && scope.nodeType === 1
        ? (scope.matches('.pk-grid-card-row, .pk-row') ? scope : (scope.closest('.pk-grid-card-row, .pk-row') || scope))
        : null;
        if (!root) return;

        const mount = root.querySelector('.pk-gv-media-mount');
        const play = root.querySelector('.pk-gv-play');
        if (!play) return;

        const isVideo = item ? isGridVideoItem(item) : !!(mount && mount.dataset && mount.dataset.pkIsVideo === '1');
        const hasThumb = item ? !!(item.thumbnail_link && item.thumbnail_link !== item.icon_link) : !!(mount && mount.dataset && mount.dataset.pkHasThumb === '1');

        if (!mount || !isVideo || !hasThumb) {
            play.style.display = 'none';
            return;
        }

        const wrap = mount.firstElementChild;
        const thumb = mount.querySelector('img.pk-max-thumb');
        const wrapperReady = !!(wrap && wrap.dataset && wrap.dataset.pkThumbReady === '1');
        const imgReady = !!(thumb && thumb.complete && (thumb.naturalWidth > 1 || thumb.naturalHeight > 1));

        play.style.display = (wrapperReady || imgReady) ? 'flex' : 'none';
    };

    window.syncGridVideoPlayState = syncGridVideoPlayState;

    const patchFrozenGridMedia = (row, item, prevMediaNode = null, prevMediaKey = '') => {
        const mount = row.querySelector('.pk-gv-media-mount');
        if (!mount || !item) return;

        const mediaKey = makeGridMediaCacheKey(item);
        const iconFallback = mount.dataset.pkIconFallback || '';
        const gridFileFallbackHtml = mount.dataset.pkFileFallback || '';

        mount.dataset.pkIsVideo = isGridVideoItem(item) ? '1' : '0';
        mount.dataset.pkHasThumb = (item.thumbnail_link && item.thumbnail_link !== item.icon_link) ? '1' : '0';

        const syncGridVideoPlay = () => {
            if (typeof syncGridVideoPlayState === 'function') {
                syncGridVideoPlayState(row, item);
            }
        };

        const queueSyncGridVideoPlay = () => requestAnimationFrame(syncGridVideoPlay);

        const cachedMediaNode = typeof takeFrozenGridMediaNode === 'function' ? takeFrozenGridMediaNode(item) : null;
        if (cachedMediaNode) {
            mount.innerHTML = '';
            mount.appendChild(cachedMediaNode);
            mount.dataset.pkMediaKey = mediaKey;
            queueSyncGridVideoPlay();
            return;
        }

        if (prevMediaNode && prevMediaKey === mediaKey) {
            mount.innerHTML = '';
            mount.appendChild(prevMediaNode);
            mount.dataset.pkMediaKey = mediaKey;
            queueSyncGridVideoPlay();
            return;
        }

        if (mount.dataset.pkMediaKey === mediaKey && mount.firstElementChild) {
            queueSyncGridVideoPlay();
            return;
        }

        mount.dataset.pkMediaKey = mediaKey;
        mount.innerHTML = renderFrozenGridMedia(item, iconFallback, gridFileFallbackHtml);
        queueSyncGridVideoPlay();
    };

    window.renderFrozenGridMedia = renderFrozenGridMedia;
    window.patchFrozenGridMedia = patchFrozenGridMedia;

    const getRowClassName = (isSelected, isFocused, isMoving) => {
        let cls = 'pk-row';
        if (isGridView()) cls += ' pk-grid-card-row';
        if (isSelected) cls += ' sel';
        if (isSelected && isGridView() && S.getSelectedCount() === 1) cls += ' pk-sel-single';
        if (isFocused) cls += ' pk-focused';
        if (isMoving) cls += ' pk-moving';
        return cls;
    };

    const ensureVisibleRowPool = (container, need) => {
        if (!container._pkVisibleRowPool) container._pkVisibleRowPool = [];
        const pool = container._pkVisibleRowPool;
        while (pool.length < need) {
            const row = document.createElement('div');
            row._pkPooledRow = true;
            row.style.position = 'absolute';
            pool.push(row);
        }
        return pool;
    };

    const resetPooledRow = (row) => {
        row.className = '';
        row.innerHTML = '';
        row.onclick = null;
        row.ondblclick = null;
        row.oncontextmenu = null;
        row.onmouseover = null;
        row.onmouseout = null;
        row.onmouseenter = null;
        row.onmouseleave = null;
        row.onmousedown = null;
        row.onmouseup = null;
        row.style.cssText = 'position:absolute;';
        row.removeAttribute('data-id');
        row.removeAttribute('data-pk-media-key');
        row.removeAttribute('data-pk-thumb');
        row.removeAttribute('data-pk-bound-id');
        row.removeAttribute('data-pk-bound-kind');
        delete row.dataset.pkBoundId;
        delete row.dataset.pkBoundKind;
        return row;
    };

    const cleanupNonPooledChildren = (container) => {
        Array.from(container.children).forEach(node => {
            if (!node._pkPooledRow) container.removeChild(node);
        });
    };

    const flushVisibleRowPool = (container, usedCount) => {
        const pool = container._pkVisibleRowPool || [];
        for (let i = 0; i < pool.length; i++) {
            const row = pool[i];
            if (i < usedCount) {
                if (row.parentNode !== container) container.appendChild(row);
            } else if (row.parentNode === container) {
                container.removeChild(row);
            }
        }
    };

    const refreshVisibleSelectionState = () => {
        const pool = (UI.in && UI.in._pkVisibleRowPool) || [];
        const singleGridSel = isGridView() && S.getSelectedCount() === 1;

        for (const row of pool) {
            if (!row || row.parentNode !== UI.in) continue;

            if (row.classList.contains('pk-group-hd')) {
                const grpChk = row.querySelector('.pk-grp-chk');
                if (!grpChk) continue;

                const gIds = Array.isArray(row._pkGroupIds) ? row._pkGroupIds : [];
                let selCount = 0;
                gIds.forEach(id => { if (S.isSelected(id)) selCount++; });

                const isAll = gIds.length > 0 && selCount === gIds.length;
                const isInd = selCount > 0 && selCount < gIds.length;

                if (grpChk.checked !== isAll) grpChk.checked = isAll;
                if (grpChk.indeterminate !== isInd) grpChk.indeterminate = isInd;
                continue;
            }

            const boundId = row.dataset.pkBoundId || '';
            if (!boundId) continue;

            const isSelected = S.isSelected(boundId);
            const isFocused = S.activeId === boundId;
            const isMoving = S.movingIds.has(boundId);

            row.className = getRowClassName(isSelected, isFocused, isMoving);

            const chk = row.querySelector('input[type="checkbox"]');
            if (chk && chk.checked !== isSelected) chk.checked = isSelected;

            if (singleGridSel && isSelected) row.classList.add('pk-sel-single');
            else row.classList.remove('pk-sel-single');

            if (isFocused && !isSelected) {
                row.style.backgroundColor = 'var(--pk-sel-bg)';
                row.style.border = '1px solid var(--pk-pri)';
                row.style.borderRadius = isGridView() ? `${getGridCardRadius()}px` : '4px';
            } else {
                row.style.backgroundColor = '';
                row.style.border = '';
                row.style.borderRadius = '';
            }
        }
    };
    const resolvePreferredViewMode = (folderId = null) => {
        const curNode = folderId !== null ? { id: folderId } : (S.path[S.path.length - 1] || { id: 'root' });
        const isStandard = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.historyMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!curNode.id.startsWith('virtual_') || curNode.id === 'virtual_search_root');
        if (!isStandard) return gmGet('pk_file_view_mode', 'grid') === 'list' ? 'list' : 'grid';

        if (gmGet('pk_view_independent', false)) {
            const safeId = folderId !== null ? (folderId || 'root') : (curNode.id || 'root');
            try {
                const prefStore = JSON.parse(gmGet('pk_folder_view_prefs', '{}'));
                const saved = prefStore[safeId];
                const mode = typeof saved === 'string' ? saved : (saved && saved.viewMode);
                return mode === 'list' ? 'list' : 'grid';
            } catch(e) {
                return 'grid';
            }
        }

        return gmGet('pk_file_view_mode', 'grid') === 'list' ? 'list' : 'grid';
    };
    const persistViewPreference = () => {
        const nextMode = S.viewMode === 'grid' ? 'grid' : 'list';
        gmSet('pk_file_view_mode', nextMode);

        const curNode = S.path[S.path.length - 1];
        const isStandard = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.historyMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!curNode.id.startsWith('virtual_') || curNode.id === 'virtual_search_root');
        if (!isStandard) return;

        if (gmGet('pk_view_independent', false)) {
            const folderId = curNode.id || 'root';
            try {
                const prefStore = JSON.parse(gmGet('pk_folder_view_prefs', '{}'));
                prefStore[folderId] = nextMode;
                gmSet('pk_folder_view_prefs', JSON.stringify(prefStore));
            } catch(e) {}
        }
    };
    const persistSortPreference = () => {
        const curNode = S.path[S.path.length - 1];
        const isStandard = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!curNode.id.startsWith('virtual_') || curNode.id === 'virtual_search_root');
        if (!isStandard) return;

        if (gmGet('pk_sort_independent', false)) {
            const folderId = curNode.id || 'root';
            try {
                const prefStore = JSON.parse(gmGet('pk_folder_sort_prefs', '{}'));
                const prev = prefStore[folderId] || {};
                prefStore[folderId] = { ...prev, sort: S.sort, dir: S.dir };
                gmSet('pk_folder_sort_prefs', JSON.stringify(prefStore));
            } catch(e) {}
        } else {
            try {
                const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                globalPref.sort = S.sort;
                globalPref.dir = S.dir;
                globalPref.folderFirst = S.folderFirst;
                gmSet('pk_global_sort_pref', JSON.stringify(globalPref));
            } catch(e) {
                gmSet('pk_global_sort_pref', JSON.stringify({ sort: S.sort, dir: S.dir, folderFirst: S.folderFirst }));
            }
        }

        try {
            const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
            globalPref.folderFirst = S.folderFirst;
            gmSet('pk_global_sort_pref', JSON.stringify(globalPref));
        } catch(e) {
            gmSet('pk_global_sort_pref', JSON.stringify({ sort: S.sort, dir: S.dir, folderFirst: S.folderFirst }));
        }

        gmSet('pk_folder_first', S.folderFirst);
    };
    const getGridSortOptions = () => {
        const cur = S.path[S.path.length - 1] || { id: 'root' };
        const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
        const isGlobalSearchRoot = cur.id === 'virtual_search_root' && UI.chkGlobal && UI.chkGlobal.checked;
        const supportsPathSort = S.dupMode || isAnalyzeRoot || S.isFlattened || isGlobalSearchRoot;
        const supportsDurationTypeSort = !isAnalyzeRoot;

        const opts = [
            { sort: 'starred', ascLabel: L.picker_sort_star_new, descLabel: L.picker_sort_star_old, ascIcon: CONF.crumbIcons.sortStarNew, descIcon: CONF.crumbIcons.sortStarOld },
            { sort: 'name', ascLabel: 'A-Z', descLabel: 'Z-A', ascIcon: CONF.crumbIcons.sortAZ, descIcon: CONF.crumbIcons.sortZA },
            { sort: 'size', ascLabel: L.picker_sort_large, descLabel: L.picker_sort_small, ascIcon: CONF.crumbIcons.sortLarge, descIcon: CONF.crumbIcons.sortSmall },
            { sort: 'modified_time', ascLabel: L.picker_sort_new, descLabel: L.picker_sort_old, ascIcon: CONF.crumbIcons.sortNew, descIcon: CONF.crumbIcons.sortOld }
        ];

        if (supportsPathSort) {
            opts.splice(2, 0,
                        { sort: 'path', ascLabel: L.picker_sort_path_asc, descLabel: L.picker_sort_path_desc, ascIcon: CONF.crumbIcons.sortPathAsc, descIcon: CONF.crumbIcons.sortPathDesc }
                       );
        }

        if (supportsDurationTypeSort) {
            opts.splice(opts.length - 1, 0,
                        { sort: 'duration', ascLabel: L.picker_sort_type_dur_asc, descLabel: L.picker_sort_type_dur_desc, ascIcon: CONF.crumbIcons.sortTypeDurAsc, descIcon: CONF.crumbIcons.sortTypeDurDesc }
                       );
        }

        return opts;
    };
    const resolveGridSortOptionState = (opt) => {
        const isActive = opt.sort === S.sort;
        const dir = isActive ? S.dir : 1;
        return {
            ...opt,
            dir,
            active: isActive,
            label: dir === -1 ? opt.descLabel : opt.ascLabel,
            icon: dir === -1 ? opt.descIcon : opt.ascIcon
        };
    };
    const getGridSortMeta = () => {
        const opts = getGridSortOptions();
        const target = opts.find(opt => opt.sort === S.sort) || opts[0];
        return resolveGridSortOptionState(target);
    };
    const closeGridSortMenu = (evt) => {
        if (!UI.gridSortWrap) return;
        if (!evt) {
            UI.gridSortWrap.classList.remove('open');
            S.gridSortMenuOpen = false;
            return;
        }

        const target = evt.target;
        const isNodeTarget = !!target && typeof target === 'object' && typeof target.nodeType === 'number';

        if (typeof evt.composedPath === 'function') {
            const path = evt.composedPath();
            if (Array.isArray(path) && path.includes(UI.gridSortWrap)) return;
        } else if (isNodeTarget && UI.gridSortWrap.contains(target)) {
            return;
        }

        UI.gridSortWrap.classList.remove('open');
        S.gridSortMenuOpen = false;
    };
    const applySortSelection = (sort) => {
        if (S.sort === sort) S.dir *= -1;
        else {
            S.sort = sort;
            S.dir = 1;
        }
        S.gridSortMenuOpen = true;
        persistSortPreference();
        refresh();
    };
    const rememberFolderFirstBeforeStrictMode = () => {
        if (S.strictFolderFirstSnapshot === null) {
            S.strictFolderFirstSnapshot = S.folderFirst === true;
        }
    };
    const restoreFolderFirstAfterStrictMode = () => {
        if (S.strictFolderFirstSnapshot === null) return;
        S.folderFirst = S.strictFolderFirstSnapshot === true;
        S.strictFolderFirstSnapshot = null;
        if (S.renderFolderFirst) S.renderFolderFirst();
    };
    const toggleFolderFirst = () => {
        S.folderFirst = !S.folderFirst;
        gmSet('pk_folder_first', S.folderFirst);

        try {
            const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
            globalPref.sort = S.sort;
            globalPref.dir = S.dir;
            globalPref.folderFirst = S.folderFirst;
            gmSet('pk_global_sort_pref', JSON.stringify(globalPref));
        } catch(e) {
            gmSet('pk_global_sort_pref', JSON.stringify({ sort: S.sort, dir: S.dir, folderFirst: S.folderFirst }));
        }

        closeGridSortMenu();
        if (S.renderFolderFirst) S.renderFolderFirst();
        refresh();
    };
    const ensureViewSwitch = () => {
        if (!UI.actionBar || UI.actionBar.querySelector('#pk-view-switch')) return;
        const wrap = document.createElement('div');
        wrap.className = 'pk-view-switch';
        wrap.id = 'pk-view-switch';
        wrap.innerHTML = `<button class="pk-view-btn" id="pk-view-list" type="button" data-pk-tip="${L.opt_list_view}">${CONF.icons.viewList}</button><button class="pk-view-btn" id="pk-view-grid" type="button" data-pk-tip="${L.opt_grid_view}">${CONF.icons.viewGrid}</button>`;
        if (UI.btnCancelShare) UI.btnCancelShare.insertAdjacentElement('afterend', wrap);
        else UI.actionBar.appendChild(wrap);
        UI.viewSwitch = wrap;
        UI.btnViewList = wrap.querySelector('#pk-view-list');
        UI.btnViewGrid = wrap.querySelector('#pk-view-grid');
        UI.btnViewList.onclick = (e) => {
            e.stopPropagation();
            if (S.viewMode === 'list') return;
            const anchorId = getViewportAnchorId(true);
            const finishViewSwitch = () => requestAnimationFrame(() => UI.win.classList.remove('pk-view-switching'));
            UI.win.classList.add('pk-view-switching');
            S.viewMode = 'list';
            persistViewPreference();
            UI.win.classList.remove('pk-grid-view');
            syncLayoutMetrics();
            renderViewSwitch();
            renderList();
            if (anchorId) {
                requestAnimationFrame(() => {
                    const targetIdx = S.display.findIndex(x => x.id === anchorId);
                    if (targetIdx !== -1) {
                        const rowTop = getItemScrollTopByIndex(targetIdx);
                        const vpHeight = UI.vp.clientHeight;
                        UI.vp.scrollTop = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));
                    }
                    finishViewSwitch();
                });
            } else {
                finishViewSwitch();
            }
        };
        UI.btnViewGrid.onclick = (e) => {
            e.stopPropagation();
            if (!canUseGridView() || S.viewMode === 'grid') return;
            const anchorId = getViewportAnchorId(true);
            const finishViewSwitch = () => requestAnimationFrame(() => UI.win.classList.remove('pk-view-switching'));
            UI.win.classList.add('pk-view-switching');
            S.viewMode = 'grid';
            persistViewPreference();
            UI.win.classList.add('pk-grid-view');
            syncLayoutMetrics();
            renderViewSwitch();
            renderList();
            if (anchorId) {
                requestAnimationFrame(() => {
                    const targetIdx = S.display.findIndex(x => x.id === anchorId);
                    if (targetIdx !== -1) {
                        const rowTop = getItemScrollTopByIndex(targetIdx);
                        const vpHeight = UI.vp.clientHeight;
                        UI.vp.scrollTop = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));
                    }
                    finishViewSwitch();
                });
            } else {
                finishViewSwitch();
            }
        };
    };
    const renderViewSwitch = () => {
        const usable = canUseGridView();

        if (!usable) {
            if (UI.viewSwitch) {
                UI.viewSwitch.style.display = 'none';
                UI.viewSwitch.style.visibility = 'hidden';
                UI.viewSwitch.style.pointerEvents = 'none';
            }
            return;
        }

        ensureViewSwitch();
        if (!UI.viewSwitch) return;

        UI.viewSwitch.style.display = 'inline-flex';
        UI.viewSwitch.style.visibility = '';
        UI.viewSwitch.style.pointerEvents = '';
        if (UI.btnViewList) UI.btnViewList.classList.toggle('active', !isGridView());
        if (UI.btnViewGrid) UI.btnViewGrid.classList.toggle('active', isGridView());
    };

    const invokeExternalPlayer = async (item) => {
        const player = gmGet('pk_ext_player', 'system');
        const L = getStrings();

        let link = item.web_content_link;
        if (!link) {
            try {
                const detail = await apiGet(item.id);
                link = detail.web_content_link;
            } catch (e) {
                showAlert(L.msg_video_fail);
                return;
            }
        }

        if (!link) { showAlert(L.msg_video_fail); return; }

        if (player === 'potplayer') {
            let cleanLink = link.replace('&ext=.m3u8', '');
            if (cleanLink.includes('ts_downloader') && cleanLink.includes('url=')) {
                const urlParam = new URL(cleanLink).searchParams.get('url');
                if (urlParam) cleanLink = decodeURIComponent(urlParam);
            }

            const ua = navigator.userAgent.replace(/"/g, '');
            const cmd = `${cleanLink} /user_agent="${ua}" /referer="https://mypikpak.com/"`;
            window.location.href = `potplayer://${cmd}`;
        }
        else {
            playVideo(item);
        }
    };

    const isSystemItem = (item) => {
        if (!item) return false;
        if (item.kind !== 'drive#folder') return false;

        if (item._isSystemRoot) return true;

        const isRootLocation = S.path.length === 1 && S.path[0].id === '';
        if (isRootLocation && item.name === CONF.SYSTEM_FOLDER_NAME) return true;

        if (!item.parent_id && item.name === CONF.SYSTEM_FOLDER_NAME) return true;

        return false;
    };

    const updateCrawlerUI = () => {
        if (!UI.btnNavHome) return;
        if (typeof isBackgroundRunning !== 'undefined' && isBackgroundRunning) {
            UI.btnNavHome.classList.add('pk-status-dot');
        } else {
            UI.btnNavHome.classList.remove('pk-status-dot');
        }
    };

    window.pkUpdateCrawlerUI = updateCrawlerUI;

    let isForcedHidden = false;

    const checkGuiResponsiveness = () => {
        const width = window.innerWidth;
        const height = window.innerHeight;
        const screenW = window.screen.width;
        const isGuiVisible = el.style.display !== 'none';
        const isTurboCurrent = typeof GM_getValue !== 'undefined' ? GM_getValue('pk_turbo_mode', false) : false;

        let z = 1;
        if (screenW <= 1600 || width <= 1600) z = 0.8;
        if (screenW <= 1280 || width <= 1280) z = 0.7;

        const MIN_WIDTH = 940 * z;
        const MIN_HEIGHT = 450;
        const isTooSmall = width < MIN_WIDTH || height < MIN_HEIGHT;
        const keepScaledContext = isGuiVisible || isForcedHidden;
        const shouldKeepZoom = keepScaledContext || (!isGuiVisible && !isForcedHidden && !!document.getElementById('pk-launch'));

        document.documentElement.style.setProperty('--pk-zoom', String(shouldKeepZoom ? z : 1));

        if (isTooSmall && !isTurboCurrent && keepScaledContext) {
            document.body.classList.add('pk-hide-all-ui');
            if (isGuiVisible) {
                el.style.display = 'none';
                isForcedHidden = true;
            }
        } else {
            document.body.classList.remove('pk-hide-all-ui');
            if (isForcedHidden) {
                el.style.display = 'flex';
                if (el.focus) el.focus();
                isForcedHidden = false;
            }
        }

        const isCompact = width <= 1200;

        if (UI.searchInput) {
            UI.searchInput.placeholder = isCompact ? L.placeholder_search_short : L.placeholder_search;
        }

        const folderSelPlaceholder = document.querySelector('#pk-dup-folder-sel option[value=""]');
        if (folderSelPlaceholder) {
            folderSelPlaceholder.textContent = isCompact ? L.lbl_dup_select_folder_short : L.lbl_dup_select_folder;
        }
    };

    const syncDupFolderButtonText = () => {
        const sel = UI.selDupFolder;
        const btn = document.getElementById('pk-dup-folder-btn');
        const txt = document.getElementById('pk-dup-folder-btn-txt');
        if (!sel || !btn || !txt) return;
        const placeholder = window.innerWidth <= 1200 ? L.lbl_dup_select_folder_short : L.lbl_dup_select_folder;
        let label = placeholder;
        const opt = sel.selectedIndex >= 0 ? sel.options[sel.selectedIndex] : null;
        if (opt && opt.value && opt.value !== "__RESET__") label = (opt.textContent || placeholder).trim();
        txt.textContent = label;
        btn.removeAttribute('title');
        btn.removeAttribute('data-pk-tip');
    };

    (() => {
        const sel = UI.selDupFolder;
        const btn = document.getElementById('pk-dup-folder-btn');
        const wrap = document.getElementById('pk-dup-folder-sel-wrap');
        if (!sel || !btn || !wrap) return;

        let pop = null;

        const closePop = () => {
            if (pop && pop.parentNode) pop.remove();
            pop = null;
            btn.classList.remove('act');
        };

        const openPop = () => {
            closePop();

            const opts = Array.from(sel.options).filter(o => !(o.disabled && !o.value));
            opts.sort((a, b) => (b.value === "__RESET__") - (a.value === "__RESET__"));
            if (!opts.length) return;

            pop = document.createElement('div');
            pop.className = 'pk-dup-folder-pop';
            pop.classList.toggle('pk-dark', !!document.querySelector('.pk-ov')?.classList.contains('pk-dark'));
            pop.innerHTML = '<div class="pk-dup-folder-head"></div><div class="pk-dup-folder-list pk-scroll"></div>';

            const head = pop.firstElementChild;
            const list = pop.lastElementChild;
            opts.forEach(o => {
                const item = document.createElement('button');
                item.type = 'button';
                item.className = `pk-dup-folder-item${o.value === sel.value ? ' act' : ''}${o.value === "__RESET__" ? ' pk-reset' : ''}`;
                const itemText = (o.textContent || '').trim();
                item.dataset.value = o.value;
                item.textContent = itemText;
                item.removeAttribute('title');
                if (itemText && o.value !== "__RESET__") item.setAttribute('data-pk-tip', itemText);
                if (o.value === "__RESET__") head.appendChild(item);
                else list.appendChild(item);
            });

            document.body.appendChild(pop);

            pop.addEventListener('mousedown', (e) => e.stopPropagation(), true);
            pop.addEventListener('click', (e) => e.stopPropagation(), true);
            pop.addEventListener('wheel', (e) => e.stopPropagation(), { passive: true });

            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const rect = typeof getLogicalRect === 'function' ? getLogicalRect(btn) : btn.getBoundingClientRect();
            const popRect = pop.getBoundingClientRect();
            const logicalWinW = window.innerWidth / scale;
            const logicalWinH = window.innerHeight / scale;

            let left = rect.left;
            let top = rect.bottom + 4;

            if (left + popRect.width > logicalWinW - 8) left = Math.max(8, logicalWinW - popRect.width - 8);
            if (top + popRect.height > logicalWinH - 8) top = Math.max(8, rect.top - popRect.height - 4);

            pop.style.left = `${left}px`;
            pop.style.top = `${top}px`;
            btn.classList.add('act');

            const activeItem = list.querySelector('.pk-dup-folder-item.act');
            if (activeItem) activeItem.scrollIntoView({ block: 'nearest' });
        };

        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            syncDupFolderButtonText();
            if (pop) closePop();
            else openPop();
        });

        document.addEventListener('click', (e) => {
            if (!pop) return;
            const t = e.target;

            if (pop.contains(t)) {
                const item = t.closest('.pk-dup-folder-item');
                if (!item) return;

                const pickedValue = item.dataset.value || '';
                S._dupFolderPickedScrollTop = !!(pickedValue && pickedValue !== "__RESET__");
                sel.value = pickedValue;
                syncDupFolderButtonText();
                closePop();
                sel.dispatchEvent(new Event('change', { bubbles: true }));
                return;
            }

            if (!wrap.contains(t)) closePop();
        }, true);

        const handleDupFolderViewportChange = (e) => {
            if (!pop) return;
            const t = e && e.target;
            if (t && pop.contains(t)) return;
            closePop();
        };

        window.addEventListener('resize', handleDupFolderViewportChange, true);
        window.addEventListener('scroll', handleDupFolderViewportChange, true);
        syncDupFolderButtonText();
    })();

    window.addEventListener('resize', () => {
        checkGuiResponsiveness();
        syncDupFolderButtonText();
        if (el.style.display === 'none') return;

        const isAuthManagerReloading = !!(
            typeof window.pkIsAuthManagerReloading === 'function' &&
            window.pkIsAuthManagerReloading()
        );

        if (isAuthManagerReloading) {
            if (typeof window.pkDeferAuthManagerRelayout === 'function') {
                window.pkDeferAuthManagerRelayout();
            }
            return;
        }

        const isDupAnalyzeMaskBusy = !!(
            S.loading &&
            S.dupMode &&
            S.dupRunning &&
            UI.loader &&
            UI.loader.style.display !== 'none'
        );

        if (isDupAnalyzeMaskBusy) return;

        if (isGridView() && typeof scheduleGridRelayout === 'function') {
            scheduleGridRelayout(true);
        } else if (typeof renderList === 'function') {
            requestAnimationFrame(() => {
                if (typeof syncLayoutMetrics === 'function') syncLayoutMetrics();
                renderList();
            });
        } else if (typeof renderVisible === 'function') {
            requestAnimationFrame(() => {
                if (typeof syncLayoutMetrics === 'function') syncLayoutMetrics();
                if (UI.in) UI.in.style.height = `${S.display.length * CONF.rowHeight}px`;
                renderVisible();
            });
        }
    });

    checkGuiResponsiveness();

    if (UI.btnClose) {
        UI.btnClose.addEventListener('click', () => {
            isForcedHidden = false;
            requestAnimationFrame(checkGuiResponsiveness);
        });
    }

    if (UI.btnTheme) {
        UI.btnTheme.onclick = (e) => {
            if (e) e.stopPropagation();
            el.classList.add('pk-no-transition');

            void el.offsetHeight;

            const wasDark = el.classList.contains('pk-dark');
            const newTheme = wasDark ? 'light' : 'dark';
            const newIcon = wasDark ? CONF.icons.moon : CONF.icons.sun;

            UI.btnTheme.innerHTML = newIcon;

            if (wasDark) {
                el.classList.remove('pk-dark');
            } else {
                el.classList.add('pk-dark');
            }

            const uploadMenu = (UI.uploadWrap && UI.uploadWrap.querySelector('.pk-dropdown-menu')) || document.querySelector('.pk-dropdown-menu[data-pk-portal="1"]');
            if (uploadMenu) uploadMenu.classList.toggle('pk-dark', !wasDark);

            gmSet('pk_theme', newTheme);

            void el.offsetHeight;

            requestAnimationFrame(() => {
                el.classList.remove('pk-no-transition');
            });
        };
    }

    const btnMax = el.querySelector('#pk-maximize');
    const isTurbo = gmGet('pk_turbo_mode', false);
    let isWinMaximized = (globalSavedState && typeof globalSavedState.isMaximized !== 'undefined') ? globalSavedState.isMaximized : isTurbo;
    let pkSyncHideMenuLock = false;
    const syncHideNativeExitDropdown = () => {
        if (pkSyncHideMenuLock) return;
        pkSyncHideMenuLock = true;
        try {
            document.querySelectorAll('.pk-dropdown-menu[data-pk-portal="1"]').forEach(n => n.remove());
            const keyInit = { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true, cancelable: true };
            document.dispatchEvent(new KeyboardEvent('keydown', keyInit));
            document.dispatchEvent(new KeyboardEvent('keyup', keyInit));
            document.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, clientX: 0, clientY: 0, button: 0 }));
            document.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, clientX: 0, clientY: 0, button: 0 }));
            document.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, clientX: 0, clientY: 0, button: 0 }));
        } catch (e) {}
        setTimeout(() => { pkSyncHideMenuLock = false; }, 0);
    };
    const pkWinHideObserver = new MutationObserver(() => {
        if (!el.isConnected) return;
        const cs = getComputedStyle(el);
        if (el.hidden || cs.display === 'none' || cs.visibility === 'hidden') {
            requestAnimationFrame(syncHideNativeExitDropdown);
        }
    });
    pkWinHideObserver.observe(el, { attributes: true, attributeFilter: ['style', 'class', 'hidden'] });

    if (isTurbo) {
        if (btnMax) btnMax.style.display = 'none';
        if (UI.btnClose) UI.btnClose.style.display = 'none';
    }

    const win = el.querySelector('.pk-win');
    if (isWinMaximized) {
        if (win) win.classList.add('pk-maximized');
        document.body.classList.add('pk-body-max');
        CONF.rowHeight = 60;
        if (btnMax) {
            btnMax.innerHTML = CONF.icons.minimize;
            btnMax.setAttribute('data-pk-tip', L.tip_minimize);
        }
    } else {
        if (win) win.classList.remove('pk-maximized');
        document.body.classList.remove('pk-body-max');
        CONF.rowHeight = 40;
        if (btnMax) {
            btnMax.innerHTML = CONF.icons.maximize;
            btnMax.setAttribute('data-pk-tip', L.tip_maximize);
        }
    }

    if (btnMax) {
        btnMax.onclick = (e) => {
            if (e) e.stopPropagation();
            el.classList.add('pk-no-transition');

            const vp = UI.vp;
            const oldRowHeight = CONF.rowHeight;
            const centerIndex = (vp.scrollTop + vp.clientHeight / 2) / oldRowHeight;

            isWinMaximized = !isWinMaximized;

            btnMax.innerHTML = isWinMaximized ? CONF.icons.minimize : CONF.icons.maximize;
            btnMax.setAttribute('data-pk-tip', isWinMaximized ? L.tip_minimize : L.tip_maximize);

            const win = el.querySelector('.pk-win');
            if (isWinMaximized) {
                win.classList.add('pk-maximized');
                document.body.classList.add('pk-body-max');
                CONF.rowHeight = 60;
            } else {
                win.classList.remove('pk-maximized');
                document.body.classList.remove('pk-body-max');
                CONF.rowHeight = 40;
            }

            if (typeof renderList === 'function') {
                renderList();
            } else if (typeof renderVisible === 'function') {
                if(UI.in) UI.in.style.height = `${S.display.length * CONF.rowHeight}px`;
                renderVisible();
            }

            if (typeof refreshQuotaText === 'function') refreshQuotaText();

            requestAnimationFrame(() => {
                void el.offsetHeight;

                const newRowHeight = CONF.rowHeight;
                const newVpHeight = vp.clientHeight;
                const targetScrollTop = (centerIndex * newRowHeight) - (newVpHeight / 2);
                vp.scrollTop = Math.max(0, targetScrollTop);

                renderVisible();

                el.classList.remove('pk-no-transition');
            });
        };
    }

    let modalZIndexCounter = 2147483640;
    function showModal(html) {
        let container = document.getElementById('pk-toast-container');
        if (container) document.body.appendChild(container);
        const m = document.createElement('div'); m.className = 'pk-modal-ov';
        m.style.zIndex = (++modalZIndexCounter).toString();

        if (document.querySelector('.pk-ov').classList.contains('pk-dark')) {
            m.classList.add('pk-dark');
        }

        m.innerHTML = `<div class="pk-modal" style="height:auto; min-height:auto; max-height:92vh; overflow:hidden; display:flex; flex-direction:column;"><div class="pk-modal-close" style="z-index:10;">${CONF.icons.close}</div>${html}</div>`;

        const actBars = m.querySelectorAll('.pk-modal-act');
        actBars.forEach(bar => {
            const btns = Array.from(bar.children).filter(child => child.classList.contains('pk-btn'));
            if (!bar.querySelector('.pk-bl-btn') && (btns.length === 1 || btns.length === 2)) {
                bar.style.setProperty('display', 'grid', 'important');
                bar.style.setProperty('grid-template-columns', btns.length === 1 ? '1fr' : '1fr 1fr', 'important');
                bar.style.setProperty('gap', '15px', 'important');
                bar.style.setProperty('width', '100%', 'important');
                bar.style.setProperty('margin', '0', 'important');
                bar.style.setProperty('margin-top', '20px', 'important');

                btns.forEach(btn => {
                    btn.style.setProperty('height', '46px', 'important');
                    btn.style.setProperty('border-radius', '12px', 'important');
                    btn.style.setProperty('font-size', '15px', 'important');
                    btn.style.setProperty('font-weight', '600', 'important');
                    btn.style.setProperty('justify-content', 'center', 'important');
                    btn.style.setProperty('padding', '0', 'important');
                    btn.style.setProperty('margin', '0', 'important');
                    btn.style.setProperty('min-width', '0', 'important');

                    if (btn.classList.contains('pri')) {
                        btn.style.setProperty('background', 'var(--pk-pri)', 'important');
                        btn.style.setProperty('color', '#fff', 'important');
                        btn.style.setProperty('border', 'none', 'important');
                        btn.style.setProperty('transition', 'filter 0.2s', 'important');
                    } else {
                        btn.style.setProperty('background', 'transparent', 'important');
                        btn.style.setProperty('color', 'var(--pk-fg)', 'important');
                        btn.style.setProperty('border', '1px solid transparent', 'important');
                        btn.onmouseover = () => btn.style.setProperty('background', 'var(--pk-hl)', 'important');
                        btn.onmouseout = () => btn.style.setProperty('background', 'transparent', 'important');
                    }
                });
            }
        });

        document.body.appendChild(m);

        m.querySelector('.pk-modal-close').addEventListener('click', () => m.remove());
        return m;
    }

    function showAlert(msg, title = L.title_alert) {
        return new Promise((resolve) => {
            const m = showModal(`
                <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg);">${title}</h3>
                <div style="margin-bottom:32px; line-height:1.6; font-size:14px; color:var(--pk-fg); opacity:0.9; word-break:break-all;">${msg.replace(/\n/g, '<br>')}</div>
                <div class="pk-modal-act" style="justify-content: flex-end;">
                    <button class="pk-btn pri" id="alert_ok"
                            style="height:40px; min-width:86px; padding:0 30px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; font-size:14px; justify-content:center;">
                        ${L.btn_ok}
                    </button>
                </div>
            `);
            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) {
                Object.assign(modalBox.style, { width: '420px', padding: '30px', boxSizing: 'border-box' });
                const closeBtn = m.querySelector('.pk-modal-close');
                if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
            }
            m.querySelector('#alert_ok').onclick = () => { m.remove(); resolve(); };
            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(); };

            m.tabIndex = 0;
            setTimeout(() => m.focus(), 10);
            m.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    m.querySelector('#alert_ok').click();
                }
            });
        });
    }

    function showConfirm(msg, title = L.title_confirm) {
        return new Promise((resolve) => {
            const m = showModal(`
                <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg);">${title}</h3>
                <div style="margin-bottom:30px; line-height:1.6; font-size:14px; color:var(--pk-fg);">${esc(msg).replace(/\n/g, '<br>')}</div>
                <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px; align-items:center;">
                    <button class="pk-btn" id="cfm_no" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; font-weight:500; justify-content:center; background:transparent;">${L.btn_no}</button>
                    <button class="pk-btn pri" id="cfm_yes" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center;">${L.btn_yes}</button>
                </div>
            `);
            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) {
                Object.assign(modalBox.style, { width: '420px', height: 'auto', minHeight: 'auto', padding: '30px' });
                const closeBtn = m.querySelector('.pk-modal-close');
                if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
            }
            m.querySelector('#cfm_no').onclick = () => { m.remove(); resolve(false); };
            m.querySelector('#cfm_yes').onclick = () => { m.remove(); resolve(true); };
            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(false); };

            m.tabIndex = 0;
            setTimeout(() => m.focus(), 10);
            m.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    m.querySelector('#cfm_yes').click();
                }
            });
        });
    }

    function showDeleteConfirm(msg, title = L.title_confirm) {
        return new Promise((resolve) => {
            const m = showModal(`
                <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg);">${title}</h3>
                <div style="margin-bottom:20px; line-height:1.6; font-size:14px; color:var(--pk-fg);">${esc(msg).replace(/\n/g, '<br>')}</div>
                <div style="margin-bottom:20px; display:flex; align-items:center;">
                    <label style="display:flex; align-items:center; cursor:pointer; font-size:13px; color:#d93025; user-select:none; font-weight:600; opacity:0.9;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.9">
                        <input type="checkbox" id="pk_hard_delete_chk" style="margin-right:8px; width:16px; height:16px; accent-color:#d93025; cursor:pointer;">
                        <span>${L.lbl_hard_delete}</span>
                    </label>
                </div>
                <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px; align-items:center;">
                    <button class="pk-btn" id="cfm_no" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; font-weight:500; justify-content:center; background:transparent;">${L.btn_no}</button>
                    <button class="pk-btn pri" id="cfm_yes" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center;">${L.btn_yes}</button>
                </div>
            `);
            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) {
                Object.assign(modalBox.style, { width: '420px', height: 'auto', minHeight: 'auto', padding: '30px' });
                const closeBtn = m.querySelector('.pk-modal-close');
                if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
            }
            m.querySelector('#cfm_no').onclick = () => { m.remove(); resolve({ confirm: false }); };
            m.querySelector('#cfm_yes').onclick = () => {
                const isHard = m.querySelector('#pk_hard_delete_chk').checked;
                m.remove();
                resolve({ confirm: true, hardDelete: isHard });
            };
            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve({ confirm: false }); };

            m.tabIndex = 0;
            setTimeout(() => m.focus(), 10);
            m.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    m.querySelector('#cfm_yes').click();
                }
            });
        });
    }

    const confirmSelectionClear = async () => {
        if (S.suppressClearConfirm) return true;

        return new Promise((resolve) => {
            const m = showModal(`
                <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_confirm}</h3>
                <div style="margin-bottom:20px; line-height:1.6; font-size:14px; color:var(--pk-fg);">${esc(L.msg_clear_sel_confirm.replace('{n}', S.getSelectedCount())).replace(/\n/g, '<br>')}</div>
                <div style="margin-bottom:20px; display:flex; align-items:center;">
                    <label style="display:flex; align-items:center; cursor:pointer; font-size:12px; color:#666; user-select:none;">
                        <input type="checkbox" id="pk_session_suppress" style="margin-right:6px; width:14px; height:14px; accent-color:var(--pk-pri);">
                        <span>${L.lbl_dont_show_session}</span>
                    </label>
                </div>
                <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px;">
                    <button class="pk-btn" id="cfm_no" style="height:40px; min-width:86px; border-radius:8px; font-weight:500; background:transparent;">${L.btn_no}</button>
                    <button class="pk-btn pri" id="cfm_yes" style="height:40px; min-width:86px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold;">${L.btn_yes}</button>
                </div>
            `);
            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) Object.assign(modalBox.style, { width: '420px', padding: '30px' });

            m.querySelector('#cfm_no').onclick = () => { m.remove(); resolve(false); };
            m.querySelector('#cfm_yes').onclick = () => {
                const isChecked = m.querySelector('#pk_session_suppress').checked;
                if (isChecked) S.suppressClearConfirm = true;
                m.remove();
                resolve(true);
            };
            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(false); };

            m.tabIndex = 0;
            setTimeout(() => m.focus(), 10);
            m.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    m.querySelector('#cfm_yes').click();
                }
            });
        });
    };

    function showPrompt(msg, val = '', title = L.title_prompt) {
        return new Promise((resolve) => {
            const cleanTitle = esc(msg).replace(/[::]$/, '');
            const isNewFolder = (title === L.btn_newfolder);
            const okBtnText = isNewFolder ? L.btn_create : L.btn_ok;

            const m = showModal(`
                <h3 style="border:none; margin-bottom:24px; font-size:18px; font-weight:700; color:var(--pk-fg);">${cleanTitle}</h3>
                <div style="position:relative;">
                    <input type="text" id="prm_input" value="${esc(val)}"
                           style="width:100%; height:44px; padding:0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:15px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box;">
                    <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1;">${title}</div>
                    <div id="prm_err" class="pk-input-err-msg" style="color:#ff4d4f; font-size:12px; margin-top:8px; min-height:18px; visibility:hidden; font-weight:500;">${L.err_name_exists}</div>
                </div>
                <div class="pk-modal-act" style="margin-top:15px; display:flex; justify-content:flex-end; gap:12px;">
                    <button class="pk-btn" id="prm_cancel" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; justify-content:center; background:transparent;">${L.btn_cancel}</button>
                    <button class="pk-btn pri" id="prm_ok" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center;">${okBtnText}</button>
                </div>
            `);

            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) {
                Object.assign(modalBox.style, { width: '480px', height: 'auto', minHeight: 'auto', padding: '30px' });
                const closeBtn = m.querySelector('.pk-modal-close');
                if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
            }

            const inp = m.querySelector('#prm_input');
            const err = m.querySelector('#prm_err');
            const okBtn = m.querySelector('#prm_ok');

            const validate = () => {
                const v = inp.value.trim();
                const isEmpty = v === '';
                const isDup = S.items.some(item => item.name === v && item.name !== val);

                err.style.visibility = isDup ? 'visible' : 'hidden';

                if (isDup || isEmpty) {
                    okBtn.disabled = true;
                    okBtn.style.opacity = '0.4';
                    okBtn.style.cursor = 'not-allowed';
                    inp.style.borderColor = isDup ? '#ff4d4f' : 'var(--pk-bd)';
                }
                else if (v === val) {
                    okBtn.disabled = false;
                    okBtn.style.opacity = '1';
                    okBtn.style.cursor = 'pointer';
                    inp.style.borderColor = 'var(--pk-bd)';
                }
                else {
                    okBtn.disabled = false;
                    okBtn.style.opacity = '1';
                    okBtn.style.cursor = 'pointer';
                    inp.style.borderColor = 'var(--pk-pri)';
                }
            };

            inp.focus();
            if (val && val.includes('.') && val.lastIndexOf('.') > 0) {
                inp.setSelectionRange(0, val.lastIndexOf('.'));
            } else {
                inp.select();
            }

            inp.addEventListener('input', validate);
            validate();

            inp.onkeydown = (e) => {
                if (e.key === 'Enter' && !okBtn.disabled) okBtn.click();
                if (e.key === 'Escape') {
                    e.stopPropagation();
                    m.remove();
                    resolve(null);
                }
            };

            m.querySelector('#prm_cancel').onclick = () => { m.remove(); resolve(null); };
            m.querySelector('#prm_ok').onclick = () => {
                const v = inp.value.trim();
                m.remove();
                resolve(v);
            };
            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(null); };
        });
    }

    function showToast(msg, type = 'success', duration = 0) {
        let container = document.getElementById('pk-toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'pk-toast-container';
            container.style.cssText = 'position:fixed; top:80px; left:50%; transform:translateX(-50%); display:flex; flex-direction:column; gap:12px; z-index:2147483647; pointer-events:none; align-items:center; zoom:var(--pk-zoom, 1);';
            document.body.appendChild(container);
        }

        const t = document.createElement('div');
        t.className = `pk-msg-toast ${type}`;

        const isDark = !!document.querySelector('.pk-ov.pk-dark');
        if (isDark) t.classList.add('pk-dark');
        t.style.cssText = 'position:relative; top:auto; left:auto; transform:translateY(-15px) scale(0.95); opacity:0; transition:all 0.3s cubic-bezier(0.23, 1, 0.32, 1); max-width:80vw;';

        if (type === 'error' || type === 'warning') {
            const icon = CONF.icons.warning.replace('style="', 'style="flex-shrink: 0; ');
            t.innerHTML = `${icon}<span style="flex: 1; line-height: 1.4;">${msg}</span>`;
            t.style.backgroundColor = type === 'warning' ? 'rgba(250, 173, 20, 0.95)' : 'rgba(217, 48, 37, 0.95)';
            t.style.color = '#ffffff';
            if (type === 'warning') t.style.border = '1px solid rgba(250, 173, 20, 0.2)';
        } else {
            t.textContent = msg;
        }

        container.prepend(t);

        requestAnimationFrame(() => {
            t.style.transform = 'translateY(0) scale(1)';
            t.style.opacity = '1';
        });

        const displayTime = duration > 0 ? duration : (type === 'success' ? 2200 : 3500);

        setTimeout(() => {
            t.style.opacity = '0';
            t.style.transform = 'translateY(-15px) scale(0.95)';
            setTimeout(() => { if(t.parentNode) t.remove(); }, 300);
        }, displayTime);
    }

    function showBlacklistModal() {
        const icons = {
            paste: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,
            delRow: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,
            trash: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>`,
            rocket: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.1 4 0 4 0"/><path d="M12 15v5s3.03-.55 4-2c1.1-1.62 0-4 0-4"/></svg>`,
            save: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>`
        };

        const toastStyle = `
            position: absolute; top: 120px; left: 50%; transform: translateX(-50%);
            background: var(--pk-toast-bg); backdrop-filter: blur(10px);
            color: var(--pk-toast-fg); border: 1px solid var(--pk-toast-bd);
            padding: 8px 24px; border-radius: 99px;
            font-size: 13px; z-index: 2000; pointer-events: none; opacity: 0; transition: opacity 0.3s, transform 0.3s;
            box-shadow: 0 8px 20px var(--pk-tip-sd); text-align: center; font-weight: 500;
            display: flex; align-items: center; gap: 8px;
        `;

        const textareaStyle = `
            flex: 1; resize: none;
            border: 2px solid transparent; border-radius: 8px;
            padding: 15px; background: var(--pk-hl);
            color: var(--pk-fg); font-size: 13px;
            font-family: inherit;
            cursor: auto; outline: none; line-height: 1.6; letter-spacing: 0.3px;
            transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
            width: 100%; box-sizing: border-box;
        `;

        const modalInnerStyle = `
            <style>
                .pk-bl-btn {
                    border: none; border-radius: 6px; cursor: pointer;
                    display: flex; align-items: center; justify-content: center; gap: 6px;
                    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
                    font-size: 12px; font-weight: 600; padding: 0 16px; height: 32px;
                }
                .pk-bl-btn:active { transform: scale(0.96); }

                .pk-tool-btn {
                    border: 1px solid var(--pk-bd); background: var(--pk-bg); color: var(--pk-fg);
                    padding: 0 12px; height: 32px; border-radius: 6px; cursor: pointer;
                    font-size: 12px; display: flex; align-items: center; gap: 6px;
                }
                .pk-tool-btn:hover { border-color: var(--pk-pri); color: var(--pk-pri); background: var(--pk-sel-bg); }

                .pk-main-clear { background: transparent; color: #d93025; padding: 0 10px; font-size: 13px; }
                .pk-main-clear:hover { background: rgba(217, 48, 37, 0.08); }

                .pk-main-run { background: #d93025; color: #fff; box-shadow: 0 4px 12px rgba(217, 48, 37, 0.25); border-radius: 8px; padding: 0 20px; font-size: 13px; }
                .pk-main-run:hover { filter: brightness(1.15); transform: translateY(-1px); box-shadow: 0 6px 16px rgba(217, 48, 37, 0.35); }

                .pk-main-save { background: var(--pk-pri); color: #fff; border: none; border-radius: 8px; padding: 0 24px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-size: 13px; }
                .pk-main-save:hover { filter: brightness(1.1); transform: translateY(-1px); }

                .pk-bl-area:focus { background: var(--pk-bg) !important; border-color: var(--pk-pri) !important; outline: none !important; }
                .pk-ov.pk-dark .pk-bl-area:focus { border-color: #ffffff !important; box-shadow: 0 0 0 1px #ffffff, 0 0 10px rgba(255,255,255,0.2) !important; outline: none !important; }
                .pk-bl-area::placeholder { color: #999; font-style: normal; opacity: 0.8; }
            </style>
        `;

        const m = showModal(`
        ${modalInnerStyle}

        <div style="display:flex; flex-direction:column; height:100%; padding: 10px 0;">
            <div style="flex-shrink:0;">
                <h3 style="margin:0; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_blacklist}</h3>

                <div style="margin-top:12px; display:${gmGet('pk_skip_bl_on_del', true) ? 'flex' : 'none'}; align-items:center; gap:8px; font-size:12px; color:var(--pk-pri); background:rgba(0,103,192,0.06); padding:8px 12px; border-radius:6px; line-height:1.4;">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
                    <span>${L.tip_bl_desc}</span>
                </div>

                <div style="display:flex; gap:20px; margin-top:16px; align-items:center;">
                    <label style="display:flex; align-items:center; cursor:pointer; user-select:none;">
                        <input type="radio" name="bl_mode" value="folder" checked style="accent-color:var(--pk-pri); transform:scale(1.1); margin:0;">
                        <div style="display:flex; align-items:center; margin-left:8px; color:var(--pk-fg);">
                            ${CONF.icons.upFolder.replace('width="16"', 'width="18"').replace('height="16"', 'height="18"')}
                            <span style="margin-left:6px; font-weight:600; font-size:13px;">${L.label_bl_folder}</span>
                        </div>
                    </label>

                    <div style="width:1px; height:14px; background:var(--pk-bd);"></div>

                    <label style="display:flex; align-items:center; cursor:pointer; user-select:none;">
                        <input type="radio" name="bl_mode" value="file" style="accent-color:var(--pk-pri); transform:scale(1.1); margin:0;">
                        <div style="display:flex; align-items:center; margin-left:8px; color:var(--pk-fg);">
                            ${CONF.icons.upFile.replace('width="16"', 'width="18"').replace('height="16"', 'height="18"')}
                            <span style="margin-left:6px; font-weight:600; font-size:13px;">${L.label_bl_file}</span>
                        </div>
                    </label>
                </div>
            </div>

            <div id="pk_bl_toast" style="${toastStyle}"></div>

            <div style="flex:1; display:flex; flex-direction:column; min-height:0; position:relative; margin-top:15px;">

                <div id="group_bl_folder" style="display:flex; flex-direction:column; height:100%;">
                    <div style="display:flex; justify-content:flex-end; gap:10px; margin-bottom:10px;">
                        <button type="button" id="btn_paste_folder" class="pk-tool-btn">
                            ${icons.paste} <span>${L.btn_paste}</span>
                        </button>
                        <button type="button" id="btn_del_folder" class="pk-tool-btn">
                            ${icons.delRow} <span>${L.btn_del}</span>
                        </button>
                    </div>
                    <textarea id="bl_folder_input" class="pk-bl-area" readonly spellcheck="false" wrap="off"
                        placeholder="${L.ph_bl_folder}" style="${textareaStyle}"></textarea>
                </div>

                <div id="group_bl_file" style="display:none; flex-direction:column; height:100%;">
                    <div style="display:flex; justify-content:flex-end; gap:10px; margin-bottom:10px;">
                        <button type="button" id="btn_paste_file" class="pk-tool-btn">
                            ${icons.paste} <span>${L.btn_paste}</span>
                        </button>
                        <button type="button" id="btn_del_file" class="pk-tool-btn">
                            ${icons.delRow} <span>${L.btn_del}</span>
                        </button>
                    </div>
                    <textarea id="bl_file_input" class="pk-bl-area" readonly spellcheck="false" wrap="off"
                        placeholder="${L.ph_bl_file}" style="${textareaStyle}"></textarea>
                </div>
            </div>

            <div class="pk-modal-act" style="justify-content: space-between; align-items: center; margin-top:20px; padding-top:15px; border-top:1px solid var(--pk-bd); flex-shrink:0;">
                <button class="pk-bl-btn pk-main-clear" id="bl_clear">
                    ${icons.trash} <span>${L.btn_clear_list}</span>
                </button>
                <div style="display:flex; gap:12px;">
                    <button class="pk-bl-btn pk-main-run" id="bl_run">
                        ${icons.rocket} <span>${L.btn_blacklist_run}</span>
                    </button>
                    <button class="pk-bl-btn pk-main-save" id="bl_save">
                        ${icons.save} <span>${L.btn_save}</span>
                    </button>
                </div>
            </div>
        </div>
        `);

        const modalEl = m.querySelector('.pk-modal');
        Object.assign(modalEl.style, { width: '600px', maxWidth: '90vw', padding: '30px', height: '600px', maxHeight: '85vh' });
        const closeBtn = m.querySelector('.pk-modal-close');
        if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });

        const areaFolder = m.querySelector('#bl_folder_input');
        const areaFile = m.querySelector('#bl_file_input');

        const radios = m.querySelectorAll('input[name="bl_mode"]');
        const groupFolder = m.querySelector('#group_bl_folder');
        const groupFile = m.querySelector('#group_bl_file');

        radios.forEach(r => {
            r.onchange = () => {
                const mode = r.value;
                groupFolder.style.display = mode === 'folder' ? 'flex' : 'none';
                groupFile.style.display = mode === 'file' ? 'flex' : 'none';
            };
        });
        const toast = m.querySelector('#pk_bl_toast');

        const showToast = (msg) => {
            toast.textContent = msg;
            toast.style.opacity = '1';
            setTimeout(() => { toast.style.opacity = '0'; }, 2000);
        };

        const loadLargeText = async (el, storageKey, originalPlaceholder) => {
            el.placeholder = L.str_loading_placeholder;
            await sleep(350);
            const data = await new Promise(r => setTimeout(() => r(gmGet(storageKey, '')), 0));
            if (data) {
                const prevDisplay = el.style.display;
                el.style.display = 'none';
                el.value = data;
                el.style.display = prevDisplay;
            }
            el.placeholder = originalPlaceholder;
            el.setSelectionRange(0, 0);
            el.blur();
        };

        loadLargeText(areaFolder, 'pk_blacklist_folders', L.ph_bl_folder);
        loadLargeText(areaFile, 'pk_blacklist', L.ph_bl_file);

        const highlightLine = (el) => {
            if (el.selectionStart !== el.selectionEnd) return;
            const val = el.value;
            if (!val) return;
            const cursor = el.selectionStart;
            let start = val.lastIndexOf('\n', cursor - 1);
            start = start === -1 ? 0 : start + 1;
            let end = val.indexOf('\n', cursor);
            if (end === -1) end = val.length;
            el.setSelectionRange(start, end);
        };

        const enableLineSnap = (el) => {
            el.addEventListener('click', () => highlightLine(el));
            el.addEventListener('keyup', (e) => {
                if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) highlightLine(el);
            });
        };

        enableLineSnap(areaFolder);
        enableLineSnap(areaFile);

        const setupSafeControls = (el, btnPaste, btnDel) => {
            btnPaste.onclick = async () => {
                try {
                    const text = await navigator.clipboard.readText();
                    if (!text || !text.trim()) return showToast(L.msg_copy_empty);

                    const cleanText = text.split(/\r?\n/).map(line => line.trim()).filter(line => line).join('\n');
                    const oldVal = el.value;
                    const prefix = (oldVal && !oldVal.endsWith('\n')) ? '\n' : '';

                    el.value = oldVal + prefix + cleanText;
                    el.scrollTop = el.scrollHeight;

                    const addedCount = cleanText.split('\n').length;
                    showToast(L.msg_add_success.replace('{n}', addedCount));
                } catch (err) {
                    showToast(L.err_clipboard_denied);
                }
            };

            btnDel.onclick = () => {
                const val = el.value;
                if (!val) return;

                const selStart = el.selectionStart;
                const selEnd = el.selectionEnd;

                if (selStart === selEnd) {
                    return showToast(L.msg_del_select);
                }

                let lineStart = val.lastIndexOf('\n', selStart - 1);
                lineStart = (lineStart === -1) ? 0 : lineStart + 1;

                let lineEnd = val.indexOf('\n', selEnd);
                if (lineEnd === -1) lineEnd = val.length;
                else lineEnd += 1;

                const newVal = val.substring(0, lineStart) + val.substring(lineEnd);
                el.value = newVal;

                el.setSelectionRange(lineStart, lineStart);
                highlightLine(el);
                el.focus();

                showToast(L.msg_del_done);
            };
        };

        setupSafeControls(areaFolder, m.querySelector('#btn_paste_folder'), m.querySelector('#btn_del_folder'));
        setupSafeControls(areaFile, m.querySelector('#btn_paste_file'), m.querySelector('#btn_del_file'));

        m.querySelector('#bl_save').onclick = async () => {
            const fDir = areaFolder.value.trim();
            const fFile = areaFile.value.trim();
            gmSet('pk_blacklist_folders', fDir);
            gmSet('pk_blacklist', fFile);
            S.updateBlCache();
            m.remove();
            renderVisible();
        };

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') {
                e.preventDefault(); e.stopPropagation();
                m.querySelector('#bl_save').click();
            }
        });

        m.querySelector('#bl_clear').onclick = async () => {
            if (areaFolder.value.trim() === '' && areaFile.value.trim() === '') return;
            if (await showConfirm(L.msg_bl_clear_confirm, L.title_confirm)) {
                areaFolder.value = "";
                areaFile.value = "";
                gmSet('pk_blacklist_folders', "");
                gmSet('pk_blacklist', "");
                S.updateBlCache();
                renderVisible();
                showToast(L.str_cleanup_done);
            }
        };

        m.querySelector('#bl_run').onclick = async () => {
            if (S.movingIds && S.movingIds.size > 0) {
                showAlert(L.err_task_conflict);
                return;
            }
            const fDir = areaFolder.value.trim();
            const fFile = areaFile.value.trim();
            gmSet('pk_blacklist_folders', fDir);
            gmSet('pk_blacklist', fFile);
            S.updateBlCache();

            const isGlobalSearch = S.path.some(p => p.id === 'virtual_search_root');

            if (S.trashMode || S.shareMode || S.offlineMode || S.starredMode || S.recentMode || S.historyMode || S.isFlattened || S.dupMode || S.analyzeMode || isGlobalSearch) {
                m.remove();
                showAlert(L.msg_bl_run_limit);
                return;
            }

            m.remove();
            setLoad(true);
            S.scanning = true;

            S.scanId = (S.scanId || 0) + 1;
            const myScanId = S.scanId;

            if (S.scanAbortController) S.scanAbortController.abort();
            S.scanAbortController = new AbortController();
            const signal = S.scanAbortController.signal;

            const parseBigTextAsync = async (text, typeLabel) => {
                const set = new Set();
                if (!text) return set;
                const len = text.length;
                let start = 0; let end = 0; let count = 0; let lastYieldTime = performance.now();
                updateLoadTxt(`${L.str_analyzing}\n${typeLabel}... 0%`);
                await sleep(16);
                while (start < len) {
                    if (!S.scanning || signal.aborted || myScanId !== S.scanId) return set;
                    end = text.indexOf('\n', start);
                    if (end === -1) end = len;
                    const line = text.substring(start, end).trim().toLowerCase();
                    if (line) set.add(line);
                    start = end + 1; count++;
                    if (count % 2000 === 0) {
                        const now = performance.now();
                        if (now - lastYieldTime > 16) {
                            const progress = Math.min(100, Math.round((start / len) * 100));
                            updateLoadTxt(`${L.str_analyzing}\n${typeLabel}... ${progress}% (${set.size})`);
                            await sleep(0);
                            lastYieldTime = performance.now();
                        }
                    }
                }
                return set;
            };

            UI.stopBtn.onclick = () => {
                S.scanning = false;
                if (S.scanAbortController) S.scanAbortController.abort();
                updateLoadTxt(L.str_stopping);
            };

            try {
                const folderText = areaFolder.value || '';
                const fileText = areaFile.value || '';

                if (folderText) S.blFolderSet = await parseBigTextAsync(folderText, L.lbl_type_folder);
                else S.blFolderSet = new Set();

                if (S.scanning && !signal.aborted && myScanId === S.scanId) {
                    if (fileText) S.blSet = await parseBigTextAsync(fileText, L.lbl_type_file);
                    else S.blSet = new Set();
                }

                if (!S.scanning || signal.aborted || myScanId !== S.scanId) {
                    if (myScanId === S.scanId) setLoad(false);
                    return;
                }

                if (S.blSet.size === 0 && S.blFolderSet.size === 0) {
                    setLoad(false);
                    showAlert(L.msg_bl_empty);
                    return;
                }

                updateLoadTxt(L.str_init_scan);
                await sleep(50);

                const foundMatches = [];
                const rootNodes = [{ id: '', name: 'Root', lineage: [], retryCount: 0 }];

                await coreRecursiveEngine(rootNodes, {
                    signal: signal,
                    preferFresh: true,
                    onFile: (f, parent) => {
                        const cleanName = f.name.replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim().toLowerCase();
                        if (S.blSet.has(cleanName)) {
                            f._lineage = parent.lineage || [];
                            foundMatches.push({ item: f, type: 'FILE' });
                        }
                    },
                    onFolder: (folder, filesInFolder, nextSubFolders) => {
                        const cleanName = folder.name.replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim().toLowerCase();

                        const isSystemRootFolder = (folder.parent_id === '' || folder.parent_id === 'root') && folder.name === CONF.SYSTEM_FOLDER_NAME;

                        if (S.blFolderSet.has(cleanName) && !isSystemRootFolder) {
                            folder._lineage = (folder.lineage || []).slice(0, -1);

                            foundMatches.push({ item: folder, type: 'FOLDER' });
                            nextSubFolders.length = 0;
                        }
                    },
                    onProgress: (st) => {
                        const folderText = `${L.str_scanning} ${st.folders} ${L.unit_folders}`;

                        const statusInfo = ` | ${L.str_hits}: ${foundMatches.length} | ${L.str_speed}: ${st.currentConcurrency} | ${L.str_cached} ${st.cacheHits} ${L.unit_folders}`;

                        updateLoadTxt(folderText + statusInfo);
                    }
                });

                if (!S.scanning || signal.aborted || myScanId !== S.scanId) {
                    if (myScanId === S.scanId) setLoad(false);
                    return;
                }

                setLoad(false);
                if (foundMatches.length === 0) {
                    showAlert(L.msg_blacklist_run_none);
                    return;
                }

                showPreviewModal(foundMatches);

            } catch (e) {
                if (e.name !== 'AbortError' && myScanId === S.scanId) {
                    setLoad(false);
                    showAlert(`${L.str_scan_error}: ${e.message}`);
                }
            } finally {
                if (myScanId === S.scanId) {
                    setLoad(false);
                    S.scanning = false;
                    S.scanAbortController = null;
                    if (typeof DurationProber !== 'undefined') DurationProber.checkAndRun();
                }
            }
        };
    }



    function showPreviewModal(matches) {
        const modalHtml = `
            <style>
                .pk-bl-prev-ov { position: relative; flex: 1; overflow: hidden; display: flex; flex-direction: column; border: 2px solid var(--pk-bd); border-radius: 8px; background: var(--pk-bg); }
                #bl_prev_list { position: relative; flex: 1; overflow-y: auto; overflow-x: hidden; user-select: none; }
                #bl_prev_del:hover:not(:disabled) { background: #d93025 !important; filter: brightness(1.15) !important; }
                .pk-bl-mq { position: absolute; background: rgba(0, 103, 192, 0.1); border: 1px solid rgba(0, 103, 192, 0.4); pointer-events: none; display: none; z-index: 10; }
                #bl_prev_list::-webkit-scrollbar { width: 6px; }
                #bl_prev_list::-webkit-scrollbar-thumb { background: var(--pk-sb-th); border-radius: 3px; }
            </style>

            <div style="display:flex; flex-direction:column; height:100%; overflow:hidden;">
                <div style="padding: 24px 30px 15px 30px; flex-shrink:0;">
                    <h3 style="margin:0; font-size:18px; font-weight:700; color:var(--pk-fg); line-height:1.2;">${L.modal_bl_preview.replace('{n}', matches.length)}</h3>
                    <div id="bl_top_stat" style="font-size:12px; color:#888; margin-top:4px; font-weight:500;"></div>
                </div>

                <div style="flex:1; padding: 0 30px; overflow:hidden; display:flex; flex-direction:column;">
                    <div class="pk-bl-prev-ov">
                        <div style="display:grid; grid-template-columns: 40px 50px 1fr 1fr; font-weight:600; padding:10px 0; border-bottom:1px dashed var(--pk-bd); background:var(--pk-bg); font-size:12px; position:sticky; top:0; align-items:center; z-index: 11; color:#888;">
                            <div style="display:flex; align-items:center; justify-content:center;"><input type="checkbox" id="bl_prev_all" checked style="cursor:pointer; margin:0; width:16px; height:16px; accent-color:var(--pk-pri);"></div>
                            <div style="display:flex; align-items:center; justify-content:center;">${L.col_type}</div>
                            <div>${L.col_name}</div>
                            <div>${L.col_path}</div>
                        </div>
                        <div id="bl_prev_list"><div id="bl_prev_in" style="position:relative; width:100%;"></div></div>
                    </div>
                </div>

                <div class="pk-modal-act" style="padding: 20px 30px 24px 30px; margin-top:0; display:flex; align-items:center; justify-content:space-between; flex-shrink:0;">
                    <label style="display:flex; align-items:center; cursor:pointer; font-size:13px; color:#d93025; user-select:none; font-weight:600; opacity:0.9;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.9">
                        <input type="checkbox" id="bl_prev_hard_delete" style="margin-right:8px; width:16px; height:16px; accent-color:#d93025; cursor:pointer;">
                        <span>${L.lbl_hard_delete}</span>
                    </label>
                    <div style="display:flex; gap:12px;">
                        <button class="pk-btn" id="bl_prev_cancel" style="height:40px; padding:0 20px; border-radius:8px; background:transparent; font-weight:600; color:var(--pk-fg);">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="bl_prev_del" style="height:40px; padding:0 24px; border-radius:8px; background:#d93025; border:none; color:#fff; font-weight:bold; box-shadow:0 4px 12px rgba(217,48,37,0.3); transition:all 0.2s;">${L.btn_bl_delete}</button>
                    </div>
                </div>
            </div>
        `;

        const m = showModal(modalHtml);
        const modalEl = m.querySelector('.pk-modal');
        Object.assign(modalEl.style, { width: "800px", maxWidth: "90vw", height: "80vh", padding: "0", display: "flex", flexDirection: "column" });

        const closeBtn = m.querySelector('.pk-modal-close');
        if (closeBtn) Object.assign(closeBtn.style, { top: "24px", right: "24px" });

        const listDiv = m.querySelector('#bl_prev_list');
        const listIn = m.querySelector('#bl_prev_in');
        const chkAll = m.querySelector('#bl_prev_all');
        const btnDel = m.querySelector('#bl_prev_del');
        const topStat = m.querySelector('#bl_top_stat');

        const ROW_HEIGHT = 44;
        listIn.style.height = `${matches.length * ROW_HEIGHT}px`;

        const selectedIds = new Set(matches.map(m => m.item.id));
        let lastIdx = -1;

        const updateCount = () => {
            const c = selectedIds.size;
            if (topStat) {
                topStat.innerHTML = L.str_bl_stat
                    .replace('{n}', matches.length)
                    .replace('{m}', `<b style="color:var(--pk-pri); margin:0 2px;">${c}</b>`);
            }
            btnDel.disabled = c === 0;
            btnDel.style.opacity = c === 0 ? 0.5 : 1;
            btnDel.style.cursor = c === 0 ? 'not-allowed' : 'pointer';

            chkAll.checked = c > 0 && c === matches.length;
            chkAll.indeterminate = c > 0 && c < matches.length;
        };
        updateCount();

        let isRenderScheduled = false;

        const renderPreviewList = () => {
            const top = listDiv.scrollTop;
            const h = listDiv.clientHeight || 500;
            const buffer = 15;
            const start = Math.max(0, Math.floor(top / ROW_HEIGHT) - buffer);
            const end = Math.min(matches.length, Math.ceil((top + h) / ROW_HEIGHT) + buffer);

            listIn.innerHTML = '';
            const fragment = document.createDocumentFragment();

            for (let i = start; i < end; i++) {
                const mMatch = matches[i];
                const row = document.createElement('div');
                row.dataset.id = mMatch.item.id;
                row.style.cssText = `position:absolute; top:${i * ROW_HEIGHT}px; width:100%; display:grid; grid-template-columns: 40px 50px 1fr 1fr; padding:0; border-bottom:1px dashed var(--pk-bd); font-size:13px; align-items:center; height:${ROW_HEIGHT}px; transition:background 0.1s; cursor:pointer;`;

                row.onmouseover = () => row.style.backgroundColor = 'var(--pk-hl)';
                row.onmouseout = () => row.style.backgroundColor = 'transparent';

                const isFolder = mMatch.type === 'FOLDER';
                const it = mMatch.item;
                const mime = (it.mime_type || '').toLowerCase();
                const isMedia = mime.startsWith('video/') || mime.startsWith('image/');

                if (isFolder && (!it.thumbnail_link || it.thumbnail_link === it.icon_link) && typeof globalCache !== 'undefined') {
                    const scanDeepCover = (targetId, depth) => {
                        if (depth > 5) return null;
                        const raw = globalCache.get(targetId);
                        if (!raw) return null;
                        const files = (raw && !Array.isArray(raw) && raw.items) ? raw.items : raw;
                        if (!files || files.length === 0) return null;
                        const vid = files.find(f => f.mime_type?.startsWith('video/') && f.thumbnail_link);
                        if (vid) return vid.thumbnail_link;
                        const img = files.find(f => f.mime_type?.startsWith('image/') && f.thumbnail_link);
                        if (img) return img.thumbnail_link;
                        const subFolders = files.filter(f => f.kind === 'drive#folder');
                        for (const sub of subFolders) {
                            if (globalCache.has(sub.id)) {
                                const childThumb = scanDeepCover(sub.id, depth + 1);
                                if (childThumb) return childThumb;
                                continue;
                            }
                            if (sub.thumbnail_link && sub.thumbnail_link !== sub.icon_link && !sub._coverResolved) {
                                return sub.thumbnail_link;
                            }
                            const childThumb = scanDeepCover(sub.id, depth + 1);
                            if (childThumb) return childThumb;
                        }
                        return null;
                    };
                    const foundThumb = scanDeepCover(it.id, 0);
                    if (foundThumb) {
                        it.thumbnail_link = foundThumb;
                    }
                }

                const hasCover = it.thumbnail_link && it.thumbnail_link !== it.icon_link;
                const fallbackSvg = getIcon(it).replace(/width="\d+"/, 'width="24"').replace(/height="\d+"/, 'height="24"');
                let iconHtml = '';

                if (!isFolder && isMedia && hasCover) {
                    iconHtml = `<img src="${it.thumbnail_link}" style="width:24px;height:24px;object-fit:cover;border-radius:4px;flex-shrink:0;" onerror="this.style.display='none';this.nextElementSibling.style.display='inline-flex';">`;
                    const secondFallback = it.icon_link
                        ? `<img src="${it.icon_link}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;">${fallbackSvg}</span>`
                        : fallbackSvg;
                    iconHtml += `<span style="display:none;align-items:center;justify-content:center;">${secondFallback}</span>`;
                } else {
                    const iconSrc = it.icon_link;
                    iconHtml = iconSrc
                        ? `<img src="${iconSrc}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;flex-shrink:0;">${fallbackSvg}</span>`
                        : fallbackSvg;
                }

                const lineage = mMatch.item._lineage ||[];
                const relativePath = lineage.map(x => x.name).join('/');
                const homeIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:4px;flex-shrink:0;vertical-align:-1px;"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>`;

                row.ondragstart = (e) => { e.preventDefault(); return false; };

                let pathHtml = `<div style="display:flex;align-items:center;overflow:hidden;white-space:nowrap;">
                    <span style="display:flex;align-items:center;flex-shrink:0;">${homeIcon}${esc(L.btn_nav_home)}</span>`;
                if (relativePath) {
                    pathHtml += `<span style="margin:0 4px;">/</span><span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${esc(relativePath)}</span>`;
                }
                pathHtml += `</div>`;

                const homeGroupTip = `<span style="display:inline-flex;align-items:center;vertical-align:bottom;">${homeIcon}${esc(L.btn_nav_home)}</span>`;
                const fullPathTip = `<div style="line-height:1.6;word-break:break-all;">${homeGroupTip}${relativePath ? '<span style="margin:0 4px;opacity:0.5;">/</span>' + esc(relativePath) : ''}</div>`;
                const thumbAttr = (typeof hasCover !== 'undefined' && hasCover) ? `data-pk-thumb="${it.thumbnail_link}"` : '';

                const isChecked = selectedIds.has(mMatch.item.id);

                row.innerHTML = `
                    <div style="display:flex; align-items:center; justify-content:center;">
                        <input type="checkbox" ${isChecked ? 'checked' : ''} data-id="${mMatch.item.id}" style="cursor:pointer; margin:0; display:block; width:16px; height:16px; accent-color:var(--pk-pri);">
                    </div>
                    <div style="display:flex; align-items:center; justify-content:center;" ${thumbAttr}>${iconHtml}</div>
                    <div style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:10px; font-weight:normal; color:var(--pk-fg);" ${thumbAttr} data-pk-tip="${esc(mMatch.item.name)}">${esc(mMatch.item.name)}</div>
                    <div style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--pk-fg);" data-pk-tip="${fullPathTip.replace(/"/g, '&quot;')}">${pathHtml}</div>
                `;

                const chk = row.querySelector('input');

                row.onclick = (e) => {
                    const curIdx = i;
                    const id = mMatch.item.id;

                    if (e.target === chk && !e.shiftKey && !e.ctrlKey && !e.metaKey) return;

                    if (e.shiftKey && lastIdx !== -1) {
                        const s = Math.min(lastIdx, curIdx);
                        const eIdx = Math.max(lastIdx, curIdx);
                        if (!e.ctrlKey && !e.metaKey) selectedIds.clear();
                        for (let k = s; k <= eIdx; k++) selectedIds.add(matches[k].item.id);
                    } else if (e.ctrlKey || e.metaKey) {
                        if (selectedIds.has(id)) selectedIds.delete(id);
                        else selectedIds.add(id);
                    } else {
                        selectedIds.clear();
                        selectedIds.add(id);
                    }

                    lastIdx = curIdx;
                    updateCount();
                    renderPreviewList();
                };

                chk.onchange = (e) => {
                    if(e.target.checked) { selectedIds.add(mMatch.item.id); lastIdx = i; }
                    else selectedIds.delete(mMatch.item.id);
                    updateCount();
                    renderPreviewList();
                };

                fragment.appendChild(row);
            }
            listIn.appendChild(fragment);
        };

        listDiv.onscroll = () => {
            if (!isRenderScheduled) {
                requestAnimationFrame(() => { renderPreviewList(); isRenderScheduled = false; });
                isRenderScheduled = true;
            }
        };

        renderPreviewList();

        m.addEventListener('click', (e) => {
            if (m._blockClick) return;
            if (e.target.closest('[data-id]') || e.target.closest('button') || e.target.closest('input') || e.target.closest('label')) return;

            if (selectedIds.size > 0) {
                selectedIds.clear();
                lastIdx = -1;
                updateCount();
                renderPreviewList();
            }
        });

        chkAll.onchange = (e) => {
            const checked = e.target.checked;
            if(checked) {
                matches.forEach(m => selectedIds.add(m.item.id));
            } else {
                selectedIds.clear();
            }
            updateCount();
            renderPreviewList();
        };

        const mqBox = document.createElement('div');
        mqBox.className = 'pk-bl-mq';
        listDiv.appendChild(mqBox);

        let isDragging = false, isMarquee = false, startX = 0, startY = 0;
        let blScrollSpeed = 0, blScrollRaf = null, blLastX = 0, blLastY = 0, blLastCtrl = false;

        listDiv.onmousedown = (e) => {
            if (e.button !== 0 || e.target.closest('input')) return;
            const rect = getLogicalRect(listDiv);

            isDragging = true;
            isMarquee = false;
            startX = e.clientX - rect.left;
            startY = e.clientY - rect.top + listDiv.scrollTop;
            const rawStartX = e.clientX, rawStartY = e.clientY;

            const updateMarqueeBox = () => {
                const curRect = getLogicalRect(listDiv);
                const curX = blLastX - curRect.left;
                const maxScrollHeight = listIn.offsetHeight;
                const curY = Math.max(0, Math.min(maxScrollHeight, blLastY - curRect.top + listDiv.scrollTop));

                const top = Math.min(startY, curY);
                const left = Math.min(startX, curX);
                const width = Math.abs(curX - startX);
                const height = Math.abs(curY - startY);

                mqBox.style.display = 'block';
                mqBox.style.top = top + 'px';
                mqBox.style.left = left + 'px';
                mqBox.style.width = width + 'px';
                mqBox.style.height = height + 'px';

                const sIdx = Math.floor(top / ROW_HEIGHT);
                const eIdx = Math.floor((top + height) / ROW_HEIGHT);

                for (let i = 0; i < matches.length; i++) {
                    const isInside = i >= sIdx && i <= eIdx;
                    if (isInside) {
                        selectedIds.add(matches[i].item.id);
                    } else if (!blLastCtrl) {
                        selectedIds.delete(matches[i].item.id);
                    }
                }
                updateCount();
                if (!isRenderScheduled) {
                    requestAnimationFrame(() => { renderPreviewList(); isRenderScheduled = false; });
                    isRenderScheduled = true;
                }
            };

            const runBlScroll = () => {
                if (!isMarquee || blScrollSpeed === 0) { blScrollRaf = null; return; }
                listDiv.scrollTop += blScrollSpeed;
                updateMarqueeBox();
                blScrollRaf = requestAnimationFrame(runBlScroll);
            };

            const onMouseMove = (me) => {
                if (!isDragging) return;

                if (!isMarquee) {
                    if (Math.abs(me.clientX - rawStartX) > 5 || Math.abs(me.clientY - rawStartY) > 5) {
                        isMarquee = true;
                        if (!me.ctrlKey && !me.metaKey && !me.shiftKey) {
                            selectedIds.clear();
                            lastIdx = -1;
                            updateCount();
                            renderPreviewList();
                        }
                    } else return;
                }

                blLastX = me.clientX;
                blLastY = me.clientY;
                blLastCtrl = me.ctrlKey || me.metaKey;
                const curRect = listDiv.getBoundingClientRect();

                if (blLastY > curRect.bottom - 5) blScrollSpeed = Math.min(45, 2 + Math.pow((blLastY - curRect.bottom + 5) / 5, 1.3));
                else if (blLastY < curRect.top + 5) blScrollSpeed = -Math.min(45, 2 + Math.pow((curRect.top + 5 - blLastY) / 5, 1.3));
                else blScrollSpeed = 0;

                if (blScrollSpeed !== 0 && !blScrollRaf) blScrollRaf = requestAnimationFrame(runBlScroll);

                updateMarqueeBox();
            };

            const onMouseUp = () => {
                if (isDragging && mqBox.style.display === 'block') {
                    m._blockClick = true;
                    setTimeout(() => m._blockClick = false, 100);
                }

                isDragging = false;
                blScrollSpeed = 0;
                if (blScrollRaf) { cancelAnimationFrame(blScrollRaf); blScrollRaf = null; }

                mqBox.style.display = 'none';
                window.removeEventListener('mousemove', onMouseMove);
                window.removeEventListener('mouseup', onMouseUp);
            };

            window.addEventListener('mousemove', onMouseMove);
            window.addEventListener('mouseup', onMouseUp);
        };

        m.querySelector('#bl_prev_cancel').onclick = () => m.remove();

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 50);

        m.addEventListener('keydown', (e) => {
            if ((e.ctrlKey || e.metaKey) && (e.key === 'a' || e.key === 'A')) {
                e.preventDefault();
                e.stopPropagation();

                matches.forEach(m => selectedIds.add(m.item.id));
                updateCount();
                renderPreviewList();
            }
        }, true);

        btnDel.onclick = async () => {
            if (selectedIds.size === 0) return;

            const confirmed = await showConfirm(L.warn_del.replace('{n}', selectedIds.size));
            if (!confirmed) return;

            const isHardDelete = m.querySelector('#bl_prev_hard_delete').checked;

            m.remove();

            const allIds = Array.from(selectedIds).filter(id => {
                const match = matches.find(m => m.item.id === id);
                if (match && isSystemItem(match.item)) {
                    console.warn(`[Security] Prevented deletion of system folder: ${match.item.name}`);
                    return false;
                }
                return true;
            });

            if (allIds.length < selectedIds.size) {
                console.log("System items were removed from deletion list.");
            }

            const itemsToDelete = matches.filter(m => allIds.includes(m.item.id)).map(m => m.item);
            await executeBatchDelete(allIds, {
                silent: false,
                explicitItems: itemsToDelete,
                hardDelete: isHardDelete
            });
        };
    }

    function updateLoadAction(isExitMode = false) {
        if (!UI.stopBtn) return;
        UI.stopBtn.removeAttribute('data-pk-tip');
        const txtEl = UI.stopBtn.querySelector('span');
        if (txtEl) txtEl.textContent = isExitMode ? L.btn_exit_script : L.btn_stop;
    }

    function setLoad(b, isInline = false) {
        S.loading = b;
        if (b) {
            if (isInline) {
                UI.loader.style.display = 'none';
            } else {
                UI.loader.style.display = 'flex';
                if (UI.loadTxt) UI.loadTxt.textContent = L.loading_detail;
                updateLoadAction(false);
            }
        } else {
            UI.loader.style.display = 'none';
            updateLoadAction(false);
        }
    }
    function updateLoadTxt(txt) {
        if (UI.loadTxt) UI.loadTxt.innerText = txt;
        updateLoadAction(txt === L.str_waiting_token);
    }

    let activeLoadId = 0;

    const computeDuplicateGroups = async (candidates, cfg, isRunningFn) => {
        const groups =[];
        const assigned = new Set();
        const strictness = gmGet('pk_dup_strictness', 'strict');
        const sizeRatioLimit = (strictness === 'loose') ? 0.10 : 0.05;

        const activeStages = ['hash', ...(cfg.video ? ['video'] : []), 'name'];
        const stageSpan = 100 / activeStages.length;
        const setDupProgress = (pct) => {
            const safePct = Math.max(0, Math.min(100, Math.round(pct)));
            updateLoadTxt(L.loading_dup.replace('{p}', safePct));
        };
        let lastDupPct = 0;
        const setStageProgress = (stageKey, processed, total, stageStartRatio = 0, stageEndRatio = 1) => {
            const stageIndex = activeStages.indexOf(stageKey);
            if (stageIndex < 0) return;
            const start = stageIndex * stageSpan;
            const end = (stageIndex + 1) * stageSpan;
            const safeStartRatio = Math.max(0, Math.min(1, stageStartRatio));
            const safeEndRatio = Math.max(safeStartRatio, Math.min(1, stageEndRatio));

            let innerRatio = safeEndRatio;
            if (total > 0) {
                const raw = Math.max(0, Math.min(1, processed / total));
                innerRatio = safeStartRatio + ((safeEndRatio - safeStartRatio) * raw);
            }

            const nextPct = start + ((end - start) * innerRatio);
            if (nextPct >= lastDupPct) {
                lastDupPct = nextPct;
                setDupProgress(nextPct);
            }
        };

        setDupProgress(0);

        if (isRunningFn()) {
            const hashMap = new Map();
            let hashProcessed = 0;
            const hashBuildTotal = candidates.length;

            for (const item of candidates) {
                const hash = item.gcid || item.md5_checksum || item.hash;
                const key = hash ? `${hash}|${item.size}` : null;
                if (key) {
                    if (!hashMap.has(key)) hashMap.set(key,[]);
                    hashMap.get(key).push(item);
                }

                hashProcessed++;
                if (hashProcessed % 500 === 0) {
                    if (!isRunningFn()) break;
                    setStageProgress('hash', hashProcessed, hashBuildTotal, 0, 0.7);
                    await sleep(0);
                }
            }

            if (!isRunningFn()) return groups;

            const hashEntries = Array.from(hashMap.entries());
            const hashTotal = hashBuildTotal + hashEntries.length;

            for (let i = 0; i < hashEntries.length; i++) {
                const [key, items] = hashEntries[i];
                if (items.length > 1) {
                    const ids = items.map(i => i.id);
                    ids.forEach(id => { assigned.add(id); S.dupReasons.set(id, L.tag_hash); });
                    groups.push({ ids: ids, type: L.tag_hash });
                }

                if ((i + 1) % 200 === 0) {
                    if (!isRunningFn()) break;
                    setStageProgress('hash', i + 1, hashEntries.length, 0.7, 1);
                    await sleep(0);
                }
            }

            if (!isRunningFn()) return groups;
            setStageProgress('hash', 1, 1, 1, 1);
        }

        if (isRunningFn() && cfg.video) {
            const simCandidates = candidates.filter(i => i.mime_type.startsWith('video') && !assigned.has(i.id));
            const validVideos = simCandidates.filter(item => (parseFloat(item.params?.duration || 0) > 0));
            validVideos.sort((a, b) => parseFloat(a.params?.duration || 0) - parseFloat(b.params?.duration || 0));

            let processedCount = 0;
            const totalCount = validVideos.length;

            if (totalCount <= 0) {
                setStageProgress('video', 0, 0);
            }

            for (let i = 0; i < totalCount; i++) {
                processedCount++;
                if (processedCount % 500 === 0) {
                    if (!isRunningFn()) break;
                    setStageProgress('video', processedCount, totalCount);
                    await sleep(0);
                }

                if (assigned.has(validVideos[i].id)) continue;

                const root = validVideos[i];
                const rootDur = parseFloat(root.params?.duration || 0);
                const rootSize = parseInt(root.size || 0);
                const groupItems = [root];

                for (let j = i + 1; j < totalCount; j++) {
                    const target = validVideos[j];
                    if (assigned.has(target.id)) continue;

                    const durThreshold = (strictness === 'loose') ? 3.0 : 2.0;
                    const targetDur = parseFloat(target.params?.duration || 0);
                    const durDiff = Math.abs(targetDur - rootDur);

                    if (durDiff > durThreshold) break;

                    const targetSize = parseInt(target.size || 0);
                    if (rootSize > 0 && targetSize > 0) {
                        const sizeDiff = Math.abs(targetSize - rootSize);
                        const maxBase = Math.max(targetSize, rootSize);
                        const ratio = sizeDiff / maxBase;

                        if (strictness === 'loose' || ratio <= 0.10) {
                            groupItems.push(target);
                        }
                    }
                }

                if (groupItems.length > 1) {
                    const ids = groupItems.map(x => x.id);
                    ids.forEach(id => { assigned.add(id); S.dupReasons.set(id, L.tag_sim); });
                    groups.push({ ids: ids, type: L.tag_sim });
                }
            }

            if (!isRunningFn()) return groups;
            setStageProgress('video', totalCount, totalCount);
        }

        if (isRunningFn()) {
            const cleanNameAd = (oldName) => {
                let cleanName = oldName;
                cleanName = cleanName.replace(/^【[^】]+】 *[-_.]? */, '');
                cleanName = cleanName.replace(/^[a-z0-9-]+[.](?:com|net|org|cc|xyz|vip|top|la) +/i, '');
                const adKw = "(?:[.]com|[.]net|[.]org|[.]cc|[.]xyz|[.]vip|[.]top|[.]la|2048|www[.])";
                const atRegex = new RegExp('^.*?' + adKw + '.*?(?:@|--+|_\\\\s)', 'i');
                cleanName = cleanName.replace(atRegex, '');
                const hyphenRegex = new RegExp('^[a-z0-9.-]+' + adKw + '-', 'i');
                cleanName = cleanName.replace(hyphenRegex, '');
                cleanName = cleanName.replace(/^(?:精品加群|福利合集)[0-9]+[-_]+ */, '');
                cleanName = cleanName.replace(/^[-_. ,,::;;\\p{Extended_Pictographic}]+/u, '');

                const pairs = [['【','】'], ['[',']'], ['《','》'],['<','>'], ['(',')'],['(',')'], ['{','}']];
                pairs.forEach(([L_char, R_char]) => {
                    const idxR_Fix = cleanName.indexOf(R_char);
                    const idxL_Check = cleanName.indexOf(L_char);
                    if (idxR_Fix > 0 && idxR_Fix <= 10 && (idxL_Check === -1 || idxL_Check > idxR_Fix)) {
                        cleanName = L_char + cleanName;
                    }
                    const chars = cleanName.split('');
                    const stack =[];
                    const toRemove = new Set();
                    for (let i = 0; i < chars.length; i++) {
                        const c = chars[i];
                        if (c === L_char) {
                            stack.push(i);
                        } else if (c === R_char) {
                            if (stack.length > 0) stack.pop();
                            else toRemove.add(i);
                        }
                    }
                    stack.forEach(i => toRemove.add(i));
                    if (toRemove.size > 0) {
                        cleanName = chars.filter((_, i) => !toRemove.has(i)).join('');
                    }
                });

                const quote2 = (cleanName.match(/'/g) ||[]).length;
                if (quote2 % 2 !== 0) cleanName = cleanName.replace(/"/, '');

                let result = cleanName.trim();
                const lastDot = result.lastIndexOf('.');
                if (lastDot > 0) result = result.substring(0, lastDot);

                let finalResult = result ? result.toLowerCase() : oldName.replace(/\.[^/.]+$/, "").toLowerCase().trim();

                if (strictness === 'loose') {
                    finalResult = finalResult.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
                }

                return finalResult;
            };

            const remainingItems = candidates.filter(i => !assigned.has(i.id));
            const getTypeGroup = (mime) => {
                if (!mime) return 'other';
                if (mime.startsWith('video')) return 'video';
                if (mime.startsWith('image')) return 'image';
                return 'other';
            };

            const typeNameMap = new Map();
            let nameBuildProcessed = 0;
            const nameBuildTotal = remainingItems.length;

            for (const item of remainingItems) {
                const tGroup = getTypeGroup(item.mime_type);
                const cleaned = cleanNameAd(item.name);
                if (cleaned) {
                    const key = tGroup + '|' + cleaned;
                    if (!typeNameMap.has(key)) typeNameMap.set(key,[]);
                    typeNameMap.get(key).push(item);
                }

                nameBuildProcessed++;
                if (nameBuildProcessed % 500 === 0) {
                    if (!isRunningFn()) break;
                    setStageProgress('name', nameBuildProcessed, nameBuildTotal, 0, 0.7);
                    await sleep(0);
                }
            }

            if (!isRunningFn()) return groups;

            const typeEntries = Array.from(typeNameMap.entries());
            const nameTotal = nameBuildTotal + typeEntries.length;

            for (let i = 0; i < typeEntries.length; i++) {
                const [key, items] = typeEntries[i];
                if (items.length > 1) {
                    const sortedForAlgo = [...items].sort((a, b) => parseInt(a.size || 0) - parseInt(b.size || 0));
                    let currentGroup = [sortedForAlgo[0]];
                    const tempGroups =[];

                    for (let j = 1; j < sortedForAlgo.length; j++) {
                        const target = sortedForAlgo[j];
                        const root = currentGroup[0];
                        const rootSize = parseInt(root.size || 0);
                        const targetSize = parseInt(target.size || 0);

                        let isMatch = false;
                        if (rootSize === 0 && targetSize === 0) {
                            isMatch = true;
                        } else if (rootSize > 0 && targetSize > 0) {
                            const sizeDiff = Math.abs(targetSize - rootSize);
                            const maxBase = Math.max(targetSize, rootSize);
                            if ((sizeDiff / maxBase) <= sizeRatioLimit) {
                                isMatch = true;
                            }
                        }

                        if (isMatch) currentGroup.push(target);
                        else {
                            if (currentGroup.length > 1) tempGroups.push(currentGroup);
                            currentGroup = [target];
                        }
                    }
                    if (currentGroup.length > 1) tempGroups.push(currentGroup);

                    tempGroups.forEach(grp => {
                        const ids = grp.map(i => i.id);
                        ids.forEach(id => { assigned.add(id); S.dupReasons.set(id, L.tag_name); });
                        groups.push({ ids: ids, type: L.tag_name });
                    });
                }

                if ((i + 1) % 200 === 0) {
                    if (!isRunningFn()) break;
                    setStageProgress('name', i + 1, typeEntries.length, 0.7, 1);
                    await sleep(0);
                }
            }

            if (!isRunningFn()) return groups;
            setStageProgress('name', 1, 1, 1, 1);
        }

        setDupProgress(100);
        return groups;
    };

    async function deepPreload(currentItems) {
        if (S.scanning) return;

        const folderItems = currentItems.filter(item => item.kind === 'drive#folder');
        if (folderItems.length === 0) return;
        const preloadTasks = folderItems.map(folder => {
            if (S.cache.has(folder.id)) return Promise.resolve();

            if (typeof globalCache !== 'undefined' && globalCache.has(folder.id)) {
                S.cache.set(folder.id, globalCache.get(folder.id));
                return Promise.resolve();
            }

            return apiList(folder.id, 1000)
                .then(files => {
                    S.cache.set(folder.id, files);
                    if (typeof globalCache !== 'undefined') globalCache.set(folder.id, files);
                })
                .catch(e => {
                });
        });

        const MAX_CONCURRENT_PRELOADS = 5;

        let activePromises = [];
        for(let i = 0; i < preloadTasks.length; i++) {
            activePromises.push(preloadTasks[i]);
            if(activePromises.length >= MAX_CONCURRENT_PRELOADS) {
                await Promise.race(activePromises.map(p => p.then(() => 0).catch(() => 0)));
                activePromises = activePromises.filter(p => p.isResolved);
            }
        }

        await Promise.allSettled(activePromises);

        console.log("Deep preload finished.");
    }

    async function load(isHistoryNav = false, forceUpdate = false) {
        if (S.scanning) return;

        const snapshot = {
            trash: S.trashMode,
            share: S.shareMode,
            starred: S.starredMode,
            recent: S.recentMode,
            pathId: S.path[S.path.length - 1]?.id || ''
        };

        const cur = S.path[S.path.length - 1];
        const folderId = cur.id || 'root';
        if (S.clipType === 'move' && S.movingIds.size > 0 && folderId === S.movingSourceId) {
            console.log(`[Cache Guard] Detected active move from this source: ${folderId}. Forcing network sync.`);
            forceUpdate = true;
        }

        let realCacheKey = S.getRealCacheKey(folderId);

        const isAnalyzeSub = S.path.some(p => p.id === 'analyze_root');
        if (isAnalyzeSub) {
            S.analyzeMode = true;
            S.sort = 'size';
            S.dir = 1;
            if (S.strictFolderFirstSnapshot !== null) {
                S.folderFirst = S.strictFolderFirstSnapshot === true;
                if (S.renderFolderFirst) S.renderFolderFirst();
            }
        }

        if (folderId === 'analyze_root') {
            renderCrumb();
            if (UI.scan) UI.scan.style.display = 'none';
            S.items = [...(S.analyzeResultItems || [])];
            S.itemMap.clear();
            S.items.forEach(i => S.itemMap.set(i.id, i));

            setLoad(false);
            refresh();
            updateStat();
            return;
        }

        if (globalDirtyFolders.has(folderId) || (folderId === 'root' && globalDirtyFolders.has(''))) {
            console.log(`[Load] ${folderId} is dirty. Forcing network fetch.`);
            forceUpdate = true;
            globalDirtyFolders.delete(folderId);
            globalDirtyFolders.delete('');
            globalDirtyFolders.delete('root');
        }

        S.clearSelection();

        if (typeof UI !== 'undefined' && UI.vp && !forceUpdate) {
            UI.vp.scrollTop = 0;
        }

        if (UI.win) UI.win.setAttribute('data-cur-pid', folderId);

        activeLoadId++;
        const currentId = activeLoadId;
        let nextToken = null;
        const fetchedIds = new Set();
        let isResuming = false;

        if (S.abortController) {
            S.abortController.abort();
        }
        S.abortController = new AbortController();
        const signal = S.abortController.signal;

        const isInVirtualSearch = S.path.some(p => p.id === 'virtual_search_root');

        if (!isInVirtualSearch) {
            S.search = '';
            if (UI.searchInput) {
                UI.searchInput.value = '';
                if (UI.searchClear) UI.searchClear.style.display = 'none';
            }
        }

        if (cur.id === 'virtual_search_root') {
            renderCrumb();
            if (UI.scan) UI.scan.style.display = 'none';
            setLoad(false);
            refresh();
            return;
        }

        const isInVirtualStack = S.path.some(p => p.id === 'virtual_search_root');
        if (isInVirtualStack) {
            if (UI.chkGlobal) UI.chkGlobal.checked = true;
        }

        if (UI.viewSwitch) {
            const usableViewSwitch = canUseGridView();
            UI.viewSwitch.style.display = usableViewSwitch ? 'inline-flex' : 'none';
            UI.viewSwitch.style.visibility = usableViewSwitch ? '' : 'hidden';
            UI.viewSwitch.style.pointerEvents = usableViewSwitch ? '' : 'none';
        }

        UI.scan.style.display = (S.trashMode || S.shareMode || S.offlineMode) ? 'none' : 'flex';
        UI.btnExit.style.display = 'none';
        if (UI.dupTools) UI.dupTools.style.display = 'none';
        if (UI.dupFilters) UI.dupFilters.style.display = 'none';
        if (UI.offTools) UI.offTools.style.display = S.offlineMode ? 'flex' : 'none';
        if (UI.upTools) UI.upTools.style.display = S.uploadMode ? 'flex' : 'none';
        if (UI.crumb) UI.crumb.style.display = '';
        if (UI.searchInput && UI.searchInput.parentNode) {
            UI.searchInput.parentNode.style.display = S.trashMode ? 'none' : 'flex';
        }
        if (UI.cntFolderFirst) {
            UI.cntFolderFirst.style.display = (S.trashMode || S.dupMode) ? 'none' : 'flex';
        }
        if (UI.btnAnalyze) {
            UI.btnAnalyze.style.display = (S.trashMode || S.shareMode || S.offlineMode) ? 'none' : 'flex';
        }
        if (UI.lblGlobal) {
            UI.lblGlobal.style.display = (S.trashMode || S.shareMode || S.starredMode || S.recentMode || S.historyMode || S.offlineMode || S.uploadMode || S.isFlattened || S.dupMode || S.analyzeMode) ? 'none' : 'flex';
            if (S.trashMode && UI.chkGlobal) {
                UI.chkGlobal.checked = false;
            }
            if (isInVirtualSearch) {
                UI.chkGlobal.checked = true;
            }
        }

        S.dupMode = false; S.lastSelIdx = -1;
        if (!isAnalyzeSub) {
            if (S.analyzeMode && UI.chkSearchPath) UI.chkSearchPath.checked = false;
            S.analyzeMode = false;
        }

        if (UI.btnNewFolder) {
            UI.btnNewFolder.style.display = (S.trashMode || S.shareMode) ? 'none' : 'flex';
        }

        S.isFlattened = false;
        renderCrumb();

        if (folderId === 'root' && !S.preloaded) {
            if (typeof globalCache !== 'undefined' && globalCache.has('root')) {
                S.cache.set('root', globalCache.get('root'));
                S.preloaded = true;
            }
            else if (S.preLoadPromise) {
                let preLoadSuccess = false;
                try {
                    const TIMEOUT_LIMIT = isTurbo ? 1000 : 200;
                    const raceResult = await Promise.race([
                        S.preLoadPromise,
                        new Promise(r => setTimeout(() => r('TIMEOUT'), TIMEOUT_LIMIT))
                    ]);

                    if (globalCache.has('root') || raceResult !== 'TIMEOUT') {
                        S.cache.set('root', globalCache.get('root'));
                        S.preloaded = true;
                        preLoadSuccess = true;
                    }
                } catch(e) {}

                if (!preLoadSuccess) {
                    console.log("[Load] Preload too slow or failed, forcing direct network fetch.");
                    forceUpdate = true;
                }
            } else {
                forceUpdate = true;
            }
        }

        const syncStars = () => {
            for (let k = 0; k < S.items.length; k++) {
                const it = S.items[k];
                if (it.starred || (it.tags && it.tags.some(t => t.name === 'STAR'))) {
                    S.starredSet.add(it.id);
                }
            }
        };

        if (!forceUpdate) {
            let cachedData = null;
            if (S.cache.has(realCacheKey)) {
                cachedData = S.cache.get(realCacheKey);
            }
            else if (typeof globalCache !== 'undefined') {
                let lookupKey = realCacheKey;
                if (realCacheKey === 'root' && globalCache.has('')) lookupKey = '';

                if (globalCache.has(lookupKey)) {
                    cachedData = globalCache.get(lookupKey);
                    S.cache.set(realCacheKey, cachedData);
                }
            }

            if (cachedData) {
                if (cachedData && !Array.isArray(cachedData) && cachedData.items) {
                    S.items = [...cachedData.items];
                    nextToken = cachedData.nextToken;
                    isResuming = !!nextToken;
                    if (isResuming) S.items.forEach(it => fetchedIds.add(it.id));
                } else {
                    S.items = Array.isArray(cachedData) ? [...cachedData] : [];
                    nextToken = null;
                }

                if (S.analyzeMode && S.analyzeMap) {
                    S.items.forEach(item => {
                        if (item.kind === 'drive#folder' && S.analyzeMap.has(item.id)) {
                            item.size = S.analyzeMap.get(item.id).size.toString();
                        }
                    });
                }

                S.itemMap.clear();
                for (let k = 0; k < S.items.length; k++) {
                    S.itemMap.set(S.items[k].id, S.items[k]);
                }

                syncStars();
                refresh();
                updateStat();

                if (S.offlineMode) {
                    const session = globalCache.get('offline_session');
                    if (session && session.nextToken && !session.completed) {
                        console.log(`[Resuming] Cache snapshot rendered. Continuing pagination from ${S.items.length}...`);
                        isResuming = true;
                        S.items.forEach(it => fetchedIds.add(it.id));
                    } else {
                        setLoad(false);
                        return;
                    }
                } else if (S.historyMode) {
                    const session = globalCache.get('history_session');
                    if (session && Array.isArray(session.targetIds) && !session.completed && (session.cursor || 0) < session.targetIds.length) {
                        console.log(`[Resuming] History cache snapshot rendered. Continuing detail fetch from ${session.cursor || 0}/${session.targetIds.length}...`);
                        isResuming = true;
                        nextToken = `history:${session.cursor || 0}`;
                        S.items.forEach(it => fetchedIds.add(it.id));
                    } else if (!nextToken) {
                        setLoad(false);
                        if (window.pkSmartRefreshTrigger) {
                            console.log(`[SWR] Intent detected: Validating ${realCacheKey} in background...`);
                            window.pkSmartRefreshTrigger(true);
                        }
                        return;
                    }
                } else if (!nextToken) {
                    setLoad(false);
                    if (window.pkSmartRefreshTrigger) {
                        console.log(`[SWR] Intent detected: Validating ${realCacheKey} in background...`);
                        window.pkSmartRefreshTrigger(true);
                    }
                    const visibleSubFolders = S.items.filter(f => f.kind === 'drive#folder');
                    for (let i = visibleSubFolders.length - 1; i >= 0; i--) {
                        const sub = visibleSubFolders[i];
                        if (!scannedFolderIds.has(sub.id) && !globalCache.has(sub.id)) {
                            backgroundQueue.push({ id: sub.id, name: sub.name, retryCount: 0 });
                            scannedFolderIds.add(sub.id);
                        }
                    }
                    runBackgroundCrawler();
                    return;
                } else {
                    console.log(`[Resuming] Cache snapshot rendered. Continuing pagination from ${S.items.length}...`);
                }
            }
        }

        if (!nextToken && !isResuming) {
            S.items = [];
            S.display = [];
            S.itemMap.clear();
            refresh();
            setLoad(true, true);
            updateLoadTxt(L.loading_detail);
        }

        isGUISensitive = false;

        const currentHeaders = getHeaders();
        if (!currentHeaders.Authorization || currentHeaders.Authorization.length < 10) {
            if (!isResuming) {
                setLoad(true);
                updateLoadTxt(L.str_waiting_token);
            }
            if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('load-missing-token', 5000);
            const initialWait = (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) ? 6500 : 5000;
            let isAuthReady = await waitForAuth(initialWait);
            if (!isAuthReady) {
                console.warn("PikPak Master: Auth token wait timeout. Entering recovery recheck before logout.");
                if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('load-missing-token-recheck', 5000);
                await sleep(1200);
                isAuthReady = await waitForAuth(5000);
            }
            if (!isAuthReady) {
                console.warn("PikPak Master: Auth token still missing after recovery recheck.");
                const didLogout = await confirmedLogout('load-missing-token-final', 5000, 5200);
                if (didLogout) return;
                setLoad(false);
                if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();
                return;
            }
            if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();
        }

        if (!isResuming && el) {
            el.focus();
        }

        UI.stopBtn.onclick = () => {
            const isAuthSyncMode = (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) || (UI.loadTxt && UI.loadTxt.innerText === L.str_waiting_token);

            if (isAuthSyncMode) {
                handleClose();
                window.location.href = `${window.location.origin}/login`;
                return;
            }

            S.abortController.abort();
            if (S.loading && !S.isFlattened && !S.dupMode && !S.analyzeMode && !S.shareMode && !S.offlineMode && S.path.length > 1) {
                const canceledNode = S.path.pop();
                console.log(`[Navigation] Cancelled entry to: ${canceledNode.name}, rolling back path.`);
                renderCrumb();

                const parentNode = S.path[S.path.length - 1];
                const parentKey = S.getRealCacheKey(parentNode.id);
                const cached = (typeof globalCache !== 'undefined') ? globalCache.get(parentKey) : null;
                if (cached) {
                    S.items = Array.isArray(cached) ? [...cached] : (cached.items || []);
                    ensureItemMap();
                    refresh();
                }
            }

            setLoad(false);
            isGUISensitive = false;
        };

        try {
            let pageCount = 0;
            const limit = 500;
            let totalItemsLoaded = S.items.length;

            do {
                if (currentId !== activeLoadId) return;
                if (signal.aborted) throw new DOMException('Aborted', 'AbortError');
                const isStarredRoot = S.starredMode && S.path.length === 1;
                let data = {};
                if (S.offlineMode) {
                    if (pageCount > 0) break;
                    if (!isResuming) {
                        S.items = [];
                        S.itemMap.clear();
                        fetchedIds.clear();
                        totalItemsLoaded = 0;
                    }

                    let session = isResuming ? globalCache.get('offline_session') : { phase: 'active', nextToken: null, completed: false };
                    if (!isResuming) globalCache.set('offline_session', session);

                    const handleBatch = (batchTasks, nextToken, currentPhase) => {
                        if (signal.aborted || currentId !== activeLoadId) return;
                        const uniqueTasks = batchTasks.filter(t => !fetchedIds.has(t.id) && !S.itemMap.has(t.id));
                        if (uniqueTasks.length === 0) return;

                        const newItems = uniqueTasks.map(t => {
                            const ref = t.reference_resource || {};
                            return {
                                id: t.id, kind: 'drive#task',
                                name: ref.name || t.name || t.file_name || 'Untitled Task',
                                size: t.file_size, phase: t.phase, progress: parseInt(t.progress || 0),
                                message: t.message, icon_link: t.icon_link,
                                thumbnail_link: ref.thumbnail_link ? ref.thumbnail_link : t.icon_link,
                                created_time: t.created_time,
                                modified_time: t.updated_time || ref.modified_time || '',
                                file_id: t.file_id || '',
                                source_url: (t.params && t.params.url) ? t.params.url : '',
                                params: Object.assign({}, t.params || {}, ref.params || {}),
                                mime_type: ref.mime_type || '',
                                starred: !!(ref.starred || (ref.tags && ref.tags.some(tg => tg.name === 'STAR')))
                            };
                        });

                        S.items.push(...newItems);
                        newItems.forEach(f => {
                            fetchedIds.add(f.id);
                            S.itemMap.set(f.id, f);
                        });
                        totalItemsLoaded = S.items.length;
                        session.phase = currentPhase;
                        session.nextToken = nextToken;
                        session.completed = false;
                        globalCache.set('offline_session', session);
                        globalCache.set('offline_root', { items: [...S.items], fetchedIds: fetchedIds });

                        S.items.sort((a, b) => new Date(b.created_time || 0) - new Date(a.created_time || 0));
                        if (UI.loader.style.display !== 'none') setLoad(false);
                        refresh();
                        updateStat();
                    };

                    await apiTaskList(500, handleBatch, session, signal);
                    if (signal.aborted || currentId !== activeLoadId) return;
                    const finalSession = { phase: 'done', nextToken: null, completed: true };
                    globalCache.set('offline_session', finalSession);

                    data = { files: [], next_page_token: null };
                    break;
                }
                else if (S.historyMode && S.path.length === 1) {
                    if (pageCount > 0) break;

                    let keys = [];
                    if (typeof GM_listValues !== 'undefined') {
                        keys = GM_listValues();
                    } else {
                        keys = Object.keys(localStorage);
                    }

                    const historyItems = [];
                    const historyMap = new Map();

                    keys.forEach(k => {
                        if (k.startsWith('pk_progress_')) {
                            const id = k.replace('pk_progress_', '');
                            const raw = gmGet(k);
                            let data = { t: 0, d: 0, ts: 0 };

                            if (typeof raw === 'number') {
                                data.t = raw;
                                data.ts = 0;
                            } else if (typeof raw === 'object' && raw !== null) {
                                data = raw;
                            }

                            if (data.t > 1 || data.ts > 0) {
                                historyItems.push({ id, ...data });
                                historyMap.set(id, data);
                            }
                        }
                    });

                    historyItems.sort((a, b) => b.ts - a.ts);

                    const topItems = historyItems.slice(0, 1000);
                    const targetIds = topItems.map(x => x.id);
                    const sourceSig = targetIds.join('|');

                    const fetchDetail = async (id) => {
                        try {
                            const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/files/${id}?thumbnail_size=SIZE_MEDIUM`, { headers: getHeaders(), signal: signal });
                            if (!res.ok) return null;
                            const f = await res.json();
                            if (f && !f.trashed) return f;
                            return null;
                        } catch (e) {
                            return null;
                        }
                    };

                    let historySession = (typeof globalCache !== 'undefined') ? globalCache.get('history_session') : null;
                    const canResumeSession = !!(historySession && historySession.sourceSig === sourceSig && Array.isArray(historySession.targetIds));

                    if (!canResumeSession) {
                        historySession = { sourceSig, targetIds: [...targetIds], cursor: 0, completed: false, updatedAt: Date.now() };
                    } else {
                        historySession.cursor = Math.max(0, Math.min(historySession.cursor || 0, targetIds.length));
                        historySession.completed = !!historySession.completed && historySession.cursor >= targetIds.length;
                        historySession.updatedAt = Date.now();
                    }

                    let historyList = (canResumeSession && Array.isArray(S.items) && S.items.length > 0) ? [...S.items] : [];
                    if (!historyList.length && typeof globalCache !== 'undefined') {
                        const cachedHistory = globalCache.get('history_root');
                        if (cachedHistory && !Array.isArray(cachedHistory) && Array.isArray(cachedHistory.items)) {
                            historyList = [...cachedHistory.items];
                        } else if (Array.isArray(cachedHistory)) {
                            historyList = [...cachedHistory];
                        }
                    }

                    const existingIds = new Set();
                    historyList.forEach(f => {
                        if (f && f.id) existingIds.add(f.id);
                    });

                    const syncHistoryCache = () => {
                        const hasPending = !historySession.completed && historySession.cursor < targetIds.length;
                        const snapshot = { items: [...historyList], nextToken: hasPending ? `history:${historySession.cursor}` : null };
                        S.cache.set('history_root', snapshot);
                        if (typeof globalCache !== 'undefined') {
                            globalCache.set('history_root', snapshot);
                            globalCache.set('history_session', { ...historySession, targetIds: [...historySession.targetIds] });
                        }
                    };

                    const syncHistoryUI = () => {
                        if (signal.aborted || currentId !== activeLoadId) return;
                        S.items = [...historyList];
                        S.itemMap.clear();
                        S.items.forEach(f => S.itemMap.set(f.id, f));
                        if (UI.loader.style.display !== 'none') setLoad(false);
                        refresh();
                        updateStat();
                    };

                    if (targetIds.length === 0) {
                        historySession.cursor = 0;
                        historySession.completed = true;
                        historySession.updatedAt = Date.now();
                        syncHistoryCache();
                        data = { files: [], next_page_token: null };
                        break;
                    }

                    syncHistoryCache();
                    syncHistoryUI();

                    const CONCURRENCY = 6;
                    let cursor = historySession.completed ? targetIds.length : (historySession.cursor || 0);

                    for (let i = cursor; i < targetIds.length; i += CONCURRENCY) {
                        if (signal.aborted || currentId !== activeLoadId) {
                            historySession.cursor = i;
                            historySession.completed = false;
                            historySession.updatedAt = Date.now();
                            syncHistoryCache();
                            return;
                        }

                        const chunk = targetIds.slice(i, i + CONCURRENCY);
                        const results = await Promise.all(chunk.map(id => fetchDetail(id)));
                        let appended = 0;

                        results.forEach(rawF => {
                            if (!rawF) return;
                            const f = minifyFile(rawF);
                            const local = historyMap.get(f.id) || {};
                            f._history_progress = local.t || 0;
                            const cloudDur = f.params?.duration || 0;
                            f._history_duration = cloudDur || local.d || 0;
                            f._history_ts = local.ts || 0;

                            if (existingIds.has(f.id)) {
                                const idx = historyList.findIndex(x => x.id === f.id);
                                if (idx !== -1) historyList[idx] = f;
                            } else {
                                existingIds.add(f.id);
                                historyList.push(f);
                                appended++;
                            }
                        });

                        historyList.sort((a, b) => (b._history_ts || 0) - (a._history_ts || 0));
                        historySession.cursor = Math.min(targetIds.length, i + chunk.length);
                        historySession.completed = historySession.cursor >= targetIds.length;
                        historySession.updatedAt = Date.now();

                        syncHistoryCache();
                        syncHistoryUI();

                        if (appended === 0 || historySession.cursor % 24 === 0) await sleep(10);
                    }

                    if (signal.aborted || currentId !== activeLoadId) return;

                    historySession.cursor = targetIds.length;
                    historySession.completed = true;
                    historySession.updatedAt = Date.now();

                    syncHistoryCache();
                    syncHistoryUI();

                    if (window.pkSmartRefreshTrigger) {
                        console.log(`[SWR] History cache completed. Validating history_root in background...`);
                        window.pkSmartRefreshTrigger(true);
                    }

                    data = { files: [], next_page_token: null };
                    break;
                }
                else if (S.recentMode && S.path.length === 1) {
                    const filters = encodeURIComponent('{"phase":{"in":"PHASE_TYPE_COMPLETE"}}');
                    const url = `https://api-drive.mypikpak.com/drive/v1/tasks?limit=${limit}&filters=${filters}&thumbnail_size=SIZE_MEDIUM&with_reference_resource=true&_t=${Date.now()}${nextToken ? `&page_token=${nextToken}` : ''}`;
                    const res = await fetch(url, { headers: getHeaders(), signal: signal });
                    if (!res.ok) throw new Error("Recent API " + res.status);
                    const json = await res.json();

                    const validTasks = (json.tasks || []).filter(t =>
                        t.phase === 'PHASE_TYPE_COMPLETE' &&
                        (t.type === 'offline' || t.type === 'upload') &&
                        t.file_id !== ""
                    );

                    data = {
                        files: validTasks.map(t => {
                            const ref = t.reference_resource || {};
                            const mime = ref.mime_type || '';
                            const isFolder = (ref.kind === 'drive#folder') ||
                                           (mime === 'application/x-directory') ||
                                           (t.icon_link && t.icon_link.includes('folder'));

                            return {
                                id: t.file_id || t.id,
                                kind: isFolder ? 'drive#folder' : 'drive#file',
                                name: ref.name || t.file_name || t.name,
                                size: t.file_size,
                                thumbnail_link: ref.thumbnail_link || t.icon_link || '',
                                icon_link: t.icon_link || '',
                                web_content_link: t.file_id ? null : null,
                                created_time: t.created_time,
                                modified_time: t.updated_time || ref.modified_time || t.created_time,
                                mime_type: mime,
                                parent_id: '',
                                starred: !!(ref.starred || (ref.tags && ref.tags.some(tg => tg.name === 'STAR'))),
                                trashed: false,
                                params: Object.assign({}, t.params || {}, ref.params || {}),
                                _sourceTaskId: t.id
                            };
                        }).filter(f => f.id && !fetchedIds.has(f.id)),
                        next_page_token: json.next_page_token
                    };
                }
                else if (S.shareMode) {
                    if (pageCount > 0) break;
                    const shares = await apiShareList();
                    data = { files: shares, next_page_token: null };
                }
                else if (S.uploadMode) {
                    if (pageCount > 0) break;

                    try {
                        const phases = "PHASE_TYPE_UNKNOW,PHASE_TYPE_PENDING,PHASE_TYPE_RUNNING,PHASE_TYPE_PAUSED,PHASE_TYPE_ERROR";
                        const filters = encodeURIComponent(JSON.stringify({ "phase": { "in": phases } }));
                        const url = `https://api-drive.mypikpak.com/drive/v1/tasks?type=upload&limit=100&filters=${filters}&_t=${Date.now()}`;
                        const res = await fetch(url, { headers: getHeaders(), signal: signal });

                        if (res.ok) {
                            const cloudData = await res.json();
                            const cloudTasks = cloudData.tasks || [];

                            const cloudIds = new Set(cloudTasks.map(ct => ct.id));
                            const cloudFileIds = new Set(cloudTasks.map(ct => ct.file_id).filter(Boolean));

                            S.uploadTasks = S.uploadTasks.filter(lt => {
                                if (lt.status === 'DONE' || (!lt.file_id && !lt._uploadId)) return true;

                                if ((lt.name === 'upload' || lt.name === 'Ghost Task') && !lt.file && lt.file_id) {
                                    console.log(`[Reconcile] Auto-cleaning orphaned local ghost file: ${lt.file_id}`);
                                    fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                                        method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: [lt.file_id] })
                                    }).catch(()=>{});
                                    if (S.upMng) S.upMng.removeTask(lt.id);
                                    return false;
                                }

                                const existsInCloud = cloudIds.has(lt.id) || (lt.file_id && cloudFileIds.has(lt.file_id)) || (lt._sourceTaskId && cloudIds.has(lt._sourceTaskId));
                                if (!existsInCloud) {
                                    console.log(`[Reconcile] 发现死任务,云端已清理,同步清理本地库: ${lt.name}`);
                                    if (S.upMng) S.upMng.removeTask(lt.id);
                                    return false;
                                }
                                return true;
                            });

                            cloudTasks.forEach(ct => {
                                const existsLocally = S.uploadTasks.some(lt => lt.id === ct.id || lt.file_id === ct.file_id || lt._sourceTaskId === ct.id);
                                if (!existsLocally && ct.file_id) {
                                    const ref = ct.reference_resource || {};
                                    const taskName = ref.name || ct.name || ct.file_name || 'Ghost Task';

                                    if (taskName === 'upload' || taskName === 'Ghost Task') {
                                        console.log(`[Reconcile] Auto-cleaning orphaned cloud ghost file: ${ct.file_id}`);
                                        fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                                            method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: [ct.file_id] })
                                        }).catch(()=>{});
                                        return;
                                    }

                                    const ghostTask = {
                                        id: 'up_' + Date.now() + '_' + Math.random().toString(36).substr(2),
                                        _sourceTaskId: ct.id,
                                        kind: 'pk#upload',
                                        file: null,
                                        name: taskName,
                                        size: ct.file_size,
                                        parentId: '',
                                        status: 'PAUSED',
                                        progress: parseInt(ct.progress || 0),
                                        speed: 0,
                                        message: L.msg_ghost_task_resume,
                                        file_id: ct.file_id,
                                        hash: (ct.params && ct.params.hash) || '',
                                        _xhr: null,
                                        _lastCalcTime: 0,
                                        _lastCalcLoaded: 0,
                                        _lastUiTime: 0,
                                        icon_link: ct.icon_link || '',
                                        thumbnail_link: ct.thumbnail_link || ''
                                    };
                                    S.uploadTasks.push(ghostTask);
                                    if (S.upMng) S.upMng.saveTask(ghostTask);
                                }
                            });
                        }
                    } catch (e) {
                        console.warn("[Upload] Failed to sync cloud upload tasks:", e);
                    }

                    data = { files: [...S.uploadTasks], next_page_token: null };
                }
                else {
                    const isStarredRoot = S.starredMode && S.path.length === 1;
                    const filterObj = { "trashed": { "eq": S.trashMode } };

                    if (isStarredRoot) {
                        filterObj.trashed = { "eq": false };
                        filterObj.system_tag = { "in": "STAR" };
                    } else if (!S.trashMode) {
                        filterObj.phase = { "eq": "PHASE_TYPE_COMPLETE" };
                    }

                    const filters = `&filters=${encodeURIComponent(JSON.stringify(filterObj))}`;
                    const targetParentId = (S.trashMode || isStarredRoot) ? '*' : (cur.id || '');
                    const url = `https://api-drive.mypikpak.com/drive/v1/files?thumbnail_size=SIZE_MEDIUM&limit=${limit}${filters}&parent_id=${targetParentId}&_t=${Date.now()}${nextToken ? `&page_token=${nextToken}` : ''}`;
                    const res = await fetch(url, { headers: getHeaders(), signal: signal, priority: 'high' });
                    if (currentId !== activeLoadId) return;

                    if (!res.ok) {
                        if (res.status === 429) { await sleep(1000); continue; }
                        throw new Error("API " + res.status);
                    }

                    data = await res.json();
                }

                if (data.files && data.files.length >= 0) {
                    if (currentId !== activeLoadId) return;
                    const currentPathId = S.path[S.path.length - 1]?.id || '';
                    if (S.trashMode !== snapshot.trash ||
                        S.shareMode !== snapshot.share ||
                        S.starredMode !== snapshot.starred ||
                        S.recentMode !== snapshot.recent ||
                        currentPathId !== snapshot.pathId) {
                        console.warn("[Load Guard] Context Drift Detected. Aborting render.");
                        return;
                    }
                    const hasDirtyData = data.files.some(f => f && (S.trashMode ? !f.trashed : f.trashed));

                    if (!S.shareMode && !S.offlineMode && hasDirtyData) {
                        console.warn(L.log_dirty_data);
                        return;
                    }

                    let validFiles = [];
                    if (S.shareMode || S.offlineMode || S.uploadMode) {
                        validFiles = data.files;
                    } else {
                        validFiles = data.files.map(f => minifyFile(f, false));
                    }

                    const newUniqueFiles = validFiles.filter(f => !fetchedIds.has(f.id));

                    if (S.analyzeMode && S.analyzeMap) {
                        newUniqueFiles.forEach(item => {
                            if (item.kind === 'drive#folder' && S.analyzeMap.has(item.id)) {
                                item.size = S.analyzeMap.get(item.id).size.toString();
                            }
                        });
                    }

                    if (newUniqueFiles.length > 0 || (pageCount === 0 && !isResuming)) {
                        if (pageCount === 0 && !isResuming) {
                            S.items = newUniqueFiles;
                            S.itemMap.clear();
                        } else {
                            S.items.push(...newUniqueFiles);
                        }

                        newUniqueFiles.forEach(f => {
                            fetchedIds.add(f.id);
                            S.itemMap.set(f.id, f);
                        });

                        totalItemsLoaded = S.items.length;

                        if (S.recentMode && S.path.length === 1) {
                            S.recentResultItems = [...S.items];
                        }

                        if (S.offlineMode) {
                            S.cache.set(realCacheKey, [...S.items]);
                            if (typeof globalCache !== 'undefined') globalCache.set(realCacheKey, [...S.items]);
                        } else {
                            const tempCache = { items: [...S.items], nextToken: data.next_page_token };
                            S.cache.set(realCacheKey, tempCache);
                            if (typeof globalCache !== 'undefined') globalCache.set(realCacheKey, tempCache);
                        }

                        if (pageCount === 0 || UI.loader.style.display !== 'none') {
                            refresh();

                            if (UI.loader.style.display !== 'none') {
                                setLoad(false);
                            }

                            if (S.items.length === 0) {
                                UI.in.innerHTML = '';
                                renderVisible();
                            }
                        } else {
                            if (pageCount % 4 === 0) refresh();
                        }
                    }
                }

                nextToken = data.next_page_token;
                pageCount++;

                if (currentId === activeLoadId) {
                    UI.stat.textContent = `${L.status_ready.replace('{n}', totalItemsLoaded)} (${L.loading_fetch.replace('{n}', pageCount)})`;
                }

                if (nextToken) await sleep(0);

            } while (nextToken && currentId === activeLoadId);

            if (currentId === activeLoadId) {
                if (S.trashMode && S.items.length === 0 && pageCount > 1) {
                } else {
                    S.cache.set(realCacheKey, [...S.items]);
                }
                if (typeof globalCache !== 'undefined') globalCache.set(realCacheKey, [...S.items]);

                if (window.pkSmartRefreshTrigger) {
                    window.pkSmartRefreshTrigger();
                }

                indexParents(folderId, cur.name, S.items);

                S.itemMap.clear();
                for (let k = 0; k < S.items.length; k++) S.itemMap.set(S.items[k].id, S.items[k]);

                syncStars();

                if (folderId === 'root' && !S.preloaded) S.preloaded = true;

                const visibleSubFolders = S.items.filter(f => f.kind === 'drive#folder');
                for (let i = visibleSubFolders.length - 1; i >= 0; i--) {
                    const sub = visibleSubFolders[i];
                    if (!scannedFolderIds.has(sub.id) && !globalCache.has(sub.id)) {
                        backgroundQueue.push({ id: sub.id, name: sub.name, retryCount: 0 });
                        scannedFolderIds.add(sub.id);
                    }
                }

                isGUISensitive = false;
                runBackgroundCrawler();

                refresh();
                updateStat();
                setLoad(false);
                S._networkRetryCount = 0;
            }

        } catch (e) {
            isGUISensitive = false;
            if (currentId === activeLoadId) {
                if (!S._isRetrying) setLoad(false);

                if (e.name === 'AbortError') {
                    console.log("Load aborted by user.");
                } else {
                    console.error("API Error encountered:", e);
                    if (typeof resetHeaderCache === 'function') resetHeaderCache();

                    const isAuthError = e.message.includes('401') || e.message.includes('403') || e.message.includes('400') || e.message.includes('CAPTCHA') || e.message.includes('AUTH_RETRY');
                    const isNotFoundError = e.message.includes('404');
                    const isNetworkError = e.name === 'TypeError' || e.message.includes('Failed to fetch') || e.message.includes('NetworkError');

                    if (!S._isRetrying || isNetworkError) {
                        S._isRetrying = true;
                        setLoad(true);

                        if (isAuthError) {
                            resetHeaderCache();
                            updateLoadTxt(L.str_waiting_token);
                            if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('auth-error', 5000);
                            await sleep(1000);

                            try {
                                let h = getHeaders();
                                if (!h.Authorization || h.Authorization.length < 10) {
                                    const isAuthReady = await waitForAuth(5000);
                                    if (!isAuthReady) {
                                        if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('auth-error-missing-token-recheck', 5000);
                                        await sleep(1200);
                                        const isAuthReadyAgain = await waitForAuth(5000);
                                        if (!isAuthReadyAgain) {
                                            const didLogout = await confirmedLogout('auth-error-missing-token-recheck-final', 5000, 5200);
                                            if (didLogout) return;
                                            S._isRetrying = false;
                                            setLoad(false);
                                            return;
                                        }
                                    }
                                    h = getHeaders();
                                }

                                let testRes = await fetch('https://api-drive.mypikpak.com/drive/v1/about', { headers: h });
                                if (!testRes.ok) {
                                    console.warn("[Auth] Token rejected by server, entering recovery recheck before logout.");
                                    if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('auth-error-about-recheck', 5000);
                                    await sleep(1200);
                                    const isAuthReady = await waitForAuth(5000);
                                    if (!isAuthReady) {
                                        const didLogout = await confirmedLogout('auth-error-about-first-reject', 5000, 5200);
                                        if (didLogout) return;
                                        h = getHeaders();
                                    } else {
                                        h = getHeaders();
                                    }

                                    testRes = await fetch('https://api-drive.mypikpak.com/drive/v1/about', { headers: h });
                                    if (!testRes.ok) {
                                        console.warn("[Auth] Token still rejected by server after recovery recheck.");
                                        const didLogout = await confirmedLogout('auth-error-about-second-reject', 5000, 5200);
                                        if (didLogout) return;
                                        h = getHeaders();
                                        testRes = await fetch('https://api-drive.mypikpak.com/drive/v1/about', { headers: h });
                                        if (!testRes.ok) {
                                            console.warn("[Auth] Token still unavailable after confirmed logout recheck, keeping current view.");
                                            S._isRetrying = false;
                                            setLoad(false);
                                            return;
                                        }
                                    }
                                }
                                if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();

                                const testData = await testRes.json();
                                if (testData.error) {
                                    console.warn("[Auth] API returned error inside response.");
                                    const didLogout = await confirmedLogout('auth-error-about-payload-error', 5000, 5200);
                                    if (didLogout) return;
                                    S._isRetrying = false;
                                    setLoad(false);
                                    return;
                                }

                                if (!testData.sub || testData.sub === '') {
                                    console.warn("[Auth] Token valid but no user info.");
                                    const didLogout = await confirmedLogout('auth-error-about-empty-sub', 5000, 5200);
                                    if (didLogout) return;
                                    S._isRetrying = false;
                                    setLoad(false);
                                    return;
                                }
                            } catch(testErr) {
                                console.warn("[Auth] Token verification failed (CORS/Network/Parse):", testErr);
                                const didLogout = await confirmedLogout('auth-error-about-verify-exception', 5000, 5200);
                                if (didLogout) return;
                                S._isRetrying = false;
                                setLoad(false);
                                return;
                            }

                            console.log("[Auth] Auth state recovered, resuming load...");
                            load(false, true).finally(() => { S._isRetrying = false; });
                            return;
                        }

                        if (isNotFoundError) {
                            if (S.path.length > 1 || S.path[0].id !== '') {
                                S.path = [{ id: '', name: L.btn_nav_home }];
                                load(false, true).finally(() => { S._isRetrying = false; });
                                return;
                            }
                        }

                        if (isNetworkError) {
                            S._networkRetryCount = (S._networkRetryCount || 0) + 1;
                            if (S._networkRetryCount > 5) {
                                console.error("[Network] Max retries reached.");
                                const didLogout = await confirmedLogout('network-retry-exhausted', 5000, 5200);
                                if (didLogout) return;
                                S._isRetrying = false;
                                S._networkRetryCount = 0;
                                setLoad(false);
                                showAlert(L.msg_network_unstable);
                                return;
                            }

                            const delay = 1000 + Math.random() * 2000;
                            const retryMsg = L.msg_network_unstable;
                            updateLoadTxt(retryMsg);
                            console.warn(`[Network] Connection drop detected. Retrying in ${Math.round(delay)}ms... (${S._networkRetryCount}/5)`);

                            await sleep(delay);
                            load(false, true).finally(() => {
                            });
                            return;
                        }

                        S._isRetrying = false;
                    }

                    if (S.items.length === 0) {
                        setLoad(false);
                        showAlert(L.str_failed + " " + e.message);
                    }
                }
            }
        }
    }

    async function refresh() {
        S.sortId++;
        const currentReqId = S.sortId;

        const prevViewMode = S.viewMode === 'grid' ? 'grid' : 'list';
        const nextCur = S.path[S.path.length - 1] || { id: '' };
        const nextIsStandardView = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.historyMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!nextCur.id.startsWith('virtual_') || nextCur.id === 'virtual_search_root');
        const groupedGridExitSyncPending = !!S._groupedGridExitSyncPending;

        if (nextIsStandardView) {
            const nextFolderId = nextCur.id || 'root';
            const nextViewMode = (typeof resolvePreferredViewMode === 'function')
            ? resolvePreferredViewMode(nextFolderId)
            : (gmGet('pk_file_view_mode', 'grid') === 'list' ? 'list' : 'grid');

            if (prevViewMode !== nextViewMode || (groupedGridExitSyncPending && nextViewMode === 'grid')) {
                beginFolderViewSync();
            }
            S._groupedGridExitSyncPending = false;
            S.viewMode = nextViewMode;
        } else {
            S._groupedGridExitSyncPending = false;
            endFolderViewSync();
        }

        renderCrumb();

        if (!S.dupMode) S.dupItemMap = null;

        const isStrictVirtual = S.isFlattened || S.dupMode || S.analyzeMode;
        const cur = S.path[S.path.length - 1];

        S.syncNavContextState();

        if (!isStrictVirtual) {
            restoreFolderFirstAfterStrictMode();
        }

        const isInSearchContext = S.path.some(p => p.id === 'virtual_search_root');
        const shouldHideHeavyOps = isStrictVirtual || isInSearchContext || S.offlineMode || S.uploadMode;
        if (UI.btnFolderFirst) {
            const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
            const isAnalyzeSub = S.analyzeMode && cur.id !== 'analyze_root';
            const shouldHideFF = S.isFlattened || S.dupMode || isAnalyzeRoot;
            if (UI.btnFolderFirst) {
                UI.btnFolderFirst.style.display = shouldHideFF ? 'none' : 'flex';
                if (isAnalyzeSub) S.renderFolderFirst();
            }
        }

        if (UI.btnAnalyze) {
            UI.btnAnalyze.style.display = (shouldHideHeavyOps || S.trashMode || S.shareMode || S.starredMode || S.recentMode || S.historyMode) ? 'none' : 'flex';
        }

        if (UI.btnExport) {
            UI.btnExport.style.display = (shouldHideHeavyOps || S.trashMode || S.shareMode || S.starredMode || S.recentMode || S.historyMode) ? 'none' : 'flex';
        }

        if (UI.scan) {
            UI.scan.style.display = (shouldHideHeavyOps || S.trashMode || S.shareMode || S.starredMode || S.recentMode || S.historyMode) ? 'none' : 'flex';
        }

        if (UI.btnExit) {
            UI.btnExit.style.display = isStrictVirtual ? 'flex' : 'none';
        }

        if (UI.lblSearchPath) {
            const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
            const showAnalyzeGroupTools = isAnalyzeRoot && !S.search && S.analyzeSimGroups;
            const isAnalyzeDupView = S.analyzeMode && S.analyzeSimGroups;
            UI.lblSearchPath.style.display = (S.dupMode || S.isFlattened || isAnalyzeRoot) ? 'flex' : 'none';
            if (UI.btnAnaSelect) UI.btnAnaSelect.style.display = showAnalyzeGroupTools ? 'flex' : 'none';
            if (UI.btnAnaSort) UI.btnAnaSort.style.display = showAnalyzeGroupTools ? 'flex' : 'none';
            if (UI.vp) UI.vp.style.overflowX = (S.dupMode || isAnalyzeDupView) ? 'hidden' : '';
        }

        if (UI.filterBar) {
            if (S.isFlattened && !S.dupMode) {
                UI.filterBar.style.display = 'flex';
                if (S.filterState.active) {
                    UI.filterBtn.style.display = 'none';
                    UI.filterActiveUI.style.display = 'flex';
                    if (typeof renderActiveFilterUI === 'function') renderActiveFilterUI();
                } else {
                    UI.filterBtn.style.display = 'flex';
                    UI.filterActiveUI.style.display = 'none';
                }
            } else {
                UI.filterBar.style.display = 'none';
            }
        }

        if (UI.cntFolderFirst) {  }

        const isStarredRoot = S.starredMode && S.path.length === 1;
        const isRecentRoot = S.recentMode && S.path.length === 1;

        if (UI.btnNewFolder) {
            const isNewFolderBlocked = S.isFlattened || S.dupMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || isStarredRoot || isRecentRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';
            UI.btnNewFolder.style.display = isNewFolderBlocked ? 'none' : (S.trashMode ? 'none' : 'flex');
        }

        if (UI.btnPaste) {
            const isPasteBlocked = S.isFlattened || S.dupMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || isStarredRoot || isRecentRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';
            UI.btnPaste.style.display = isPasteBlocked ? 'none' : (S.trashMode ? 'none' : 'inline-flex');
        }

        const isInVirtualSearch = S.path.some(p => p.id === 'virtual_search_root');
        const isInsideSearchResult = isInVirtualSearch && cur.id !== 'virtual_search_root';

        let listToProcess = [];

        if (S.search && !S.dupMode && !isInsideSearchResult) {
            const q = S.search.toLowerCase();
            const includePath = UI.chkSearchPath && UI.chkSearchPath.checked;

            if (S.analyzeMode && cur.id === 'analyze_root' && S.analyzeSimGroups) {
                const newDisplay = [];
                S.analyzeSimGroups.forEach((g, gIdx) => {
                    const groupItems = g.ids.map(id => S.itemMap.get(id)).filter(Boolean);

                    const isGroupHit = g.type.toLowerCase().includes(q) || groupItems.some(it => {
                        const nameMatch = it.name.toLowerCase().includes(q);
                        let pathMatch = false;
                        if (includePath && it._pathStr) {
                            const homeText = L.btn_nav_home;
                            const parentPath = (it._pathStr === homeText || it._pathStr.startsWith(homeText + '/')) ? it._pathStr : (homeText + '/' + it._pathStr);
                            const fullItemPath = parentPath.endsWith('/') ? (parentPath + it.name) : (parentPath + '/' + it.name);
                            pathMatch = fullItemPath.toLowerCase().includes(q);
                        }
                        return nameMatch || pathMatch;
                    });

                    if (isGroupHit) {
                        newDisplay.push({
                            id: `grp_${gIdx}`,
                            isHeader: true,
                            name: g.type,
                            count: g.ids.length,
                            type: g.type || L.str_group
                        });

                        groupItems.sort((a, b) => parseInt(b.size || 0) - parseInt(a.size || 0));
                        groupItems.forEach(it => {
                            if (it.name.toLowerCase().includes(q)) {
                                const name = it.name;
                                const idx = name.toLowerCase().indexOf(q);
                                const len = q.length;
                                const start = Math.max(0, idx - 15);
                                const end = Math.min(name.length, idx + len + 30);
                                let pre = start > 0 ? "..." : "";
                                let suf = end < name.length ? "..." : "";
                                it._hlNameHTML = `${pre}${esc(name.substring(start, idx))}<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">${esc(name.substring(idx, idx + len))}</b>${esc(name.substring(idx + len, end))}${suf}`;
                            } else {
                                delete it._hlNameHTML;
                            }
                            newDisplay.push(it);
                        });
                    }
                });
                S.display = newDisplay;
            }
            else if (UI.chkGlobal && UI.chkGlobal.checked && cur.id === 'virtual_search_root') {
                let results = [];
                const seenIds = new Set();

                let realMyPackId = '';
                if (typeof globalCache !== 'undefined') {
                    const rootFiles = globalCache.get('') || globalCache.get('root');
                    if (rootFiles) {
                        const realObj = rootFiles.find(f => f.kind === 'drive#folder' && f.name === CONF.SYSTEM_FOLDER_NAME);
                        if (realObj) realMyPackId = realObj.id;
                    }
                }

                if (CONF.SYSTEM_FOLDER_NAME.toLowerCase().includes(q)) {
                    const realObj = (globalCache.get('') || globalCache.get('root'))?.find(f => f.name === CONF.SYSTEM_FOLDER_NAME);
                    const sysRoot = {
                        id: realMyPackId,
                        kind: 'drive#folder',
                        name: CONF.SYSTEM_FOLDER_NAME,
                        parent_id: '',
                        size: 0,
                        modified_time: '',
                        starred: false,
                        tags: [],
                        _lineage: [],
                        _isSystemRoot: true,
                        icon_link: realObj ? realObj.icon_link : '',
                        thumbnail_link: realObj ? realObj.icon_link : ''
                    };
                    results.push(sysRoot);

                    if (realMyPackId) seenIds.add(realMyPackId);
                }

                if (typeof globalCache !== 'undefined') {
                    for (const [key, files] of globalCache) {
                        if (currentReqId !== S.sortId) return;
                        if (!Array.isArray(files)) continue;
                        if (key && (key.endsWith('_root') || key === 'root_trashed')) continue;
                        let parentLineage = (typeof globalLineageMap !== 'undefined') ? globalLineageMap.get(key) : undefined;

                        if (!parentLineage) {
                            if (key === '' || key === 'root') parentLineage = [];
                            else parentLineage = null;
                        }

                        for (let i = 0, len = files.length; i < len; i++) {
                            const f = files[i];

                            if (seenIds.has(f.id)) continue;

                            if (f && f.name && f.name.toLowerCase().includes(q)) {
                                let itemLineage = parentLineage;
                                const fObj = { ...f, _lineage: itemLineage };
                                const fName = fObj.name;
                                const fIdx = fName.toLowerCase().indexOf(q);
                                if (fIdx !== -1) {
                                    const start = Math.max(0, fIdx - 15);
                                    const end = Math.min(fName.length, fIdx + q.length + 30);
                                    let pre = start > 0 ? "..." : "";
                                    let suf = end < fName.length ? "..." : "";
                                    const b = fName.substring(start, fIdx);
                                    const m = fName.substring(fIdx, fIdx + q.length);
                                    const a = fName.substring(fIdx + q.length, end);
                                    fObj._hlNameHTML = `${pre}${esc(b)}<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">${esc(m)}</b>${esc(a)}${suf}`;
                                }
                                results.push(fObj);
                                seenIds.add(f.id);
                            }
                        }
                    }
                }
                S.display = results;
                S.items = results;
                S.lastGlobalResults = [...results];
                ensureItemMap();
            }
            else if (isInVirtualSearch && cur.id !== 'virtual_search_root') {
                let ancestors = (typeof globalLineageMap !== 'undefined' && globalLineageMap.get(cur.id)) || cur._lineage || [];
                const currentFolderAsAncestor = [...ancestors, { id: cur.id, name: cur.name }];

                S.display = S.items.map(item => {
                    const itemLineage = (item.kind === 'drive#folder' && typeof globalLineageMap !== 'undefined' && globalLineageMap.has(item.id))
                        ? globalLineageMap.get(item.id) : currentFolderAsAncestor;

                    return { ...item, _lineage: itemLineage };
                });
            }
            else {
                const query = S.search.toLowerCase();
                const includePath = UI.chkSearchPath && UI.chkSearchPath.checked;
                const isPathMode = S.isFlattened || (S.analyzeMode && cur.id === 'analyze_root');
                const rootPathStr = S.path.map(p => p.name).join('/');

                let filtered = S.items.filter(i => {
                    if (!i) return false;
                    if (i.name && i.name.toLowerCase().includes(query)) return true;
                    if (includePath && isPathMode) {
                        let pStr = "";
                        if (S.analyzeMode) {
                            pStr = i._pathStr || L.btn_nav_home;
                        } else if (i._lineage) {
                            let cleanLineage = i._lineage.map(x => x.name).filter(n => n && n !== 'Root' && n !== L.str_root_dir_cn);
                            if (cleanLineage[0] !== L.btn_nav_home) cleanLineage.unshift(L.btn_nav_home);
                            pStr = cleanLineage.join('/');
                        }
                        const fullItemPath = pStr.endsWith('/') ? (pStr + i.name) : (pStr + '/' + i.name);
                        if (fullItemPath.toLowerCase().includes(query)) return true;
                    }
                    return false;
                });

                if (S.offlineMode) {
                    const cfg = S.offlineFilters;
                    filtered = filtered.filter(i => {
                        const p = i.phase;
                        if (p === 'PHASE_TYPE_COMPLETE') return cfg.complete;
                        if (['PHASE_TYPE_RUNNING', 'PHASE_TYPE_PENDING', 'PHASE_TYPE_PAUSED'].includes(p)) return cfg.running;
                        return cfg.failed;
                    });
                }

                filtered.forEach(item => {
                    const name = item.name;
                    const idx = name.toLowerCase().indexOf(query);
                    if (idx !== -1) {
                        const len = query.length;
                        const start = Math.max(0, idx - 15);
                        const end = Math.min(name.length, idx + len + 30);
                        let prefix = start > 0 ? "..." : "";
                        let suffix = end < name.length ? "..." : "";
                        const partBefore = name.substring(start, idx);
                        const partMatch = name.substring(idx, idx + len);
                        const partAfter = name.substring(idx + len, end);

                        item._hlNameHTML = `${prefix}${esc(partBefore)}<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">${esc(partMatch)}</b>${esc(partAfter)}${suffix}`;
                    }
                });
                S.display = filtered;
            }
        }
        else {
            if (S.path.length > 0 && S.path[0].id === 'starred') {
                S.display = S.items.filter(i => (i.starred || (i.tags && i.tags.some(t => t.name === 'STAR'))) && !i.trashed);
            }
            else if (S.isFlattened && S.filterState && S.filterState.active && S.filterState.cat !== 'all') {
                const cat = S.filterState.cat;
                const ext = S.filterState.ext;
                const fExts = {
                    video: ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp','mpg','mpeg','rm','rmvb','asf','vob','dat','divx','f4v','m2ts','mts','tp','trp','ogv','mpe','m2v','m3u8'],
                    audio: ['mp3','wav','flac','aac','ogg','wma','ape','m4a','amr','opus','m4b','alac','aiff','mid','midi','ra','dts','ac3','dsf','dff'],
                    image: ['jpg','jpeg','png','gif','bmp','webp','svg','tif','tiff','ico','heic','heif','raw','cr2','nef','arw','dng','orf','avif','psd','ai','eps','jfif','jpe'],
                    document: ['txt','html','pdf','pptx','chm','docx','xlsx','htm','doc','dwg','mdb','ppt','xls','rtf','odt','ods','odp','epub','mobi','azw3','djvu','cbz','cbr','md','log','csv','xml','json'],
                    software: ['apk','exe','ipa','dmg','rpm','deb','msi','pkg','xapk','apks','aab','jar','bin','sh','bat','cmd'],
                    archive: ['zip','rar','7z','tar','gz','iso','cab','bz2','xz','tgz','wim','esd','img','zst','lzh'],
                    torrent: ['torrent']
                };

                let matchExts =[];
                if (cat === 'other') {
                    const definedExts = new Set([...fExts.video, ...fExts.audio, ...fExts.image, ...fExts.document, ...fExts.software, ...fExts.archive, ...fExts.torrent]);
                    S.display = S.items.filter(i => {
                        if (i.kind === 'drive#folder') return false;
                        const n = (i.name || '').toLowerCase();
                        const e = n.split('.').pop();
                        return !definedExts.has(e);
                    });
                } else {
                    if (ext === 'all') {
                        matchExts = fExts[cat] ||[];
                    } else {
                        matchExts = [ext];
                    }
                    S.display = S.items.filter(i => {
                        if (i.kind === 'drive#folder') return false;
                        const n = (i.name || '').toLowerCase();
                        const e = n.split('.').pop();
                        return matchExts.includes(e);
                    });
                }
            }
            else if (S.offlineMode) {
                const cfg = S.offlineFilters;
                S.display = S.items.filter(i => {
                    const p = i.phase;
                    if (p === 'PHASE_TYPE_COMPLETE') return cfg.complete;
                    if (['PHASE_TYPE_RUNNING', 'PHASE_TYPE_PENDING', 'PHASE_TYPE_PAUSED'].includes(p)) return cfg.running;
                    return cfg.failed;
                });
            }
            else if (S.uploadMode) {
                const cfg = S.uploadFilters;
                S.display = S.items.filter(i => {
                    const s = i.status;
                    if (s === 'DONE') return cfg.complete;
                    if (s === 'PAUSED' || s === 'ERROR') return cfg.paused;
                    return cfg.running;
                });
            }
            else if (S.analyzeMode && cur.id === 'analyze_root' && S.analyzeSimGroups) {
                const newDisplay =[];
                S.analyzeSimGroups.forEach((g, gIdx) => {
                    newDisplay.push({
                        id: `grp_${gIdx}`,
                        isHeader: true,
                        name: g.type,
                        count: g.ids.length,
                        type: g.type || L.str_group
                    });
                    const groupItems = g.ids.map(id => S.itemMap.get(id)).filter(Boolean);
                    groupItems.sort((a, b) => {
                        const pA = a._pathStr || "";
                        const pB = b._pathStr || "";
                        if (pA.length !== pB.length) return pA.length - pB.length;
                        return pA.localeCompare(pB);
                    });

                    let lastPathInGroup = null;
                    groupItems.forEach(it => {
                        const currentPath = it._pathStr || "";
                        if (currentPath === lastPathInGroup && currentPath !== "") {
                            it._isSameFolder = true;
                        } else {
                            it._isSameFolder = false;
                            lastPathInGroup = currentPath;
                        }
                        newDisplay.push(it);
                    });
                });
                S.display = newDisplay;
            }
            else {
                S.display = [...S.items];
            }
        }

        if (currentReqId !== S.sortId) return;

        const visibleSet = new Set(S.display.map(i => i.id));
        if (S.selMode === 'all') {
            const nextExcluded = new Set();
            for (const id of S.selEx) {
                if (visibleSet.has(id)) nextExcluded.add(id);
            }
            S.selEx = nextExcluded;
        } else {
            const nextSelected = S.getSelectedIds().filter(id => visibleSet.has(id));
            S.setExplicitSelection(nextSelected);
        }

        S.dupReasons.clear();
        S.dupGroups.clear();
        if (!S.dupMode) S.pinnedDupPath = null;

        const isStandardView = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.historyMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!cur.id.startsWith('virtual_') || cur.id === 'virtual_search_root');
        const isSortIndep = gmGet('pk_sort_independent', false);
        const folderFirstViewKey = S.starredMode ? '__starred__' : (S.recentMode ? '__recent__' : '');

        if (isStandardView) {
            const folderId = cur.id || 'root';

            if (S._viewAppliedForId !== folderId) {
                S.viewMode = resolvePreferredViewMode(folderId);
                S._viewAppliedForId = folderId;
            }

            if (S._sortAppliedForId !== folderId) {
                if (isSortIndep) {
                    try {
                        const prefStore = JSON.parse(gmGet('pk_folder_sort_prefs', '{}'));
                        if (prefStore[folderId]) {
                            S.sort = prefStore[folderId].sort;
                            S.dir = prefStore[folderId].dir;
                        } else {
                            S.sort = 'modified_time';
                            S.dir = 1;
                        }
                    } catch(e) {
                        S.sort = 'modified_time';
                        S.dir = 1;
                    }
                } else {
                    const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                    S.sort = globalPref.sort;
                    S.dir = globalPref.dir;
                }

                try {
                    const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                    if (globalPref.folderFirst !== undefined) S.folderFirst = globalPref.folderFirst === true;
                    else S.folderFirst = gmGet('pk_folder_first', false);
                } catch(e) {
                    S.folderFirst = gmGet('pk_folder_first', false);
                }

                S._sortAppliedForId = folderId;
                S._comicApplied = false;

                if (S.renderFolderFirst) S.renderFolderFirst();
            }

            if (gmGet('pk_comic_mode', true) && !S._comicApplied) {

                const hasSubFolders = S.items.some(i => i.kind === 'drive#folder');

                if (!hasSubFolders && S.items.length > 0) {
                    const isAllImages = S.items.every(i => i.mime_type && i.mime_type.startsWith('image/'));
                    const isAllVideos = S.items.every(i => i.mime_type && i.mime_type.startsWith('video/'));

                    if (isAllImages || isAllVideos) {
                        S.sort = 'name';
                        S.dir = 1;
                    }
                    S._comicApplied = true;
                }
            }
        } else if (folderFirstViewKey) {
            if (S._sortAppliedForId !== folderFirstViewKey) {
                try {
                    const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                    if (globalPref.folderFirst !== undefined) S.folderFirst = globalPref.folderFirst === true;
                    else S.folderFirst = gmGet('pk_folder_first', false);
                } catch(e) {
                    S.folderFirst = gmGet('pk_folder_first', false);
                }

                S._sortAppliedForId = folderFirstViewKey;

                if (S.renderFolderFirst) S.renderFolderFirst();
            }
        }

        const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
        const isGlobalSearchRoot = cur.id === 'virtual_search_root' && UI.chkGlobal && UI.chkGlobal.checked;
        const supportsPathSort = S.dupMode || isAnalyzeRoot || S.isFlattened || isGlobalSearchRoot;

        if (S.sort === 'path' && !supportsPathSort) {
            S.sort = 'modified_time';
            S.dir = 1;
        }

        if (S.dupMode) {
            setLoad(true);
            S.dupRunning = true;
            UI.stopBtn.onclick = () => { S.dupRunning = false; };
            updateLoadTxt(L.loading_dup.replace('{p}', 0));
            await sleep(20);

            const cfg = S.dupConfig || { video: true, image: false, other: false };
            let candidates = S.display.filter(i => {
                if (!i.mime_type) return false;
                const isVideo = i.mime_type.startsWith('video');
                const isImage = i.mime_type.startsWith('image');
                const isOther = !isVideo && !isImage;
                if (isVideo && cfg.video) return true;
                if (isImage && cfg.image) return true;
                if (isOther && cfg.other) return true;
                return false;
            });

            const groups = await computeDuplicateGroups(candidates, cfg, () => S.dupRunning);

            groups.sort((a, b) => {
                const getPriority = (t) => { if (t === L.tag_hash) return 3; if (t === L.tag_sim) return 2; if (t === L.tag_name) return 1; return 0; };
                const pA = getPriority(a.type); const pB = getPriority(b.type);
                if (pA !== pB) return pB - pA;
                return b.ids.length - a.ids.length;
            });

            const dupItemMap = new Map();
            for (let i = 0, len = S.items.length; i < len; i++) {
                dupItemMap.set(S.items[i].id, S.items[i]);
            }
            S.dupItemMap = dupItemMap;

            const dupBuckets = { all: [], hash: [], sim: [], name: [] };
            for (let i = 0, len = groups.length; i < len; i++) {
                const g = groups[i];
                const validIds = [];
                for (let k = 0, idsLen = g.ids.length; k < idsLen; k++) {
                    const id = g.ids[k];
                    if (dupItemMap.has(id)) validIds.push(id);
                }
                if (validIds.length <= 1) continue;

                const normalizedGroup = { ...g, ids: validIds };
                dupBuckets.all.push(normalizedGroup);

                if (g.type === L.tag_hash) dupBuckets.hash.push(normalizedGroup);
                else if (g.type === L.tag_sim) dupBuckets.sim.push(normalizedGroup);
                else if (g.type === L.tag_name) dupBuckets.name.push(normalizedGroup);
            }

            if (dupBuckets.all.length === 0) {
                S.dupRunning = false;
                setLoad(false);
                showToast(L.msg_dup_none);
                if (UI.btnExit) UI.btnExit.click();
                return;
            }

            S.dupBuckets = dupBuckets;
            S.dupRawGroups = dupBuckets.all;

            if (UI.chkName) UI.chkName.checked = true;
            if (UI.chkSim) UI.chkSim.checked = true;
            if (UI.chkHash) UI.chkHash.checked = true;

            const applyDupSelection = () => {
                const targetPath = UI.selDupFolder.value;
                const invertChk = document.getElementById('pk-dup-invert');

                if (!targetPath || targetPath === "__RESET__") {
                    S.pinnedDupPath = null;
                    S.sel.clear();

                    if (invertChk) {
                        invertChk.checked = false;
                        invertChk.disabled = true;
                        invertChk.parentNode.style.opacity = '0.5';
                    }

                    renderDupView();
                    updateStat();
                    if(targetPath) UI.selDupFolder.value = "";
                    return;
                }

                if (invertChk) {
                    invertChk.disabled = false;
                    invertChk.parentNode.style.opacity = '1';
                }
                const isInvert = invertChk ? invertChk.checked : false;

                S.pinnedDupPath = targetPath;
                const itemMap = new Map();
                for(const item of S.items) itemMap.set(item.id, item);

                S.sel.clear();

                S.dupRawGroups.forEach(g => {
                    const filesInTarget = [];
                    const filesInOther = [];
                    g.ids.forEach(id => {
                        const item = itemMap.get(id);
                        if (item) {
                            let path = "";
                            if (item._cachedPath !== undefined) {
                                path = item._cachedPath;
                            } else {
                                if (item._lineage && item._lineage.length > 0) {
                                    path = item._lineage.map(p => p.name).join('/');
                                } else {
                                    const curFolder = S.path[S.path.length - 1];
                                    path = curFolder ? curFolder.name : L.current_dir;
                                }
                                item._cachedPath = path;
                            }

                            if (path === targetPath) filesInTarget.push(item);
                            else filesInOther.push(item);
                        }
                    });

                    if (filesInOther.length > 0) {
                        if (isInvert) {
                            filesInOther.forEach(f => S.sel.add(f.id));
                        } else {
                            filesInTarget.forEach(f => S.sel.add(f.id));
                        }
                    } else if (filesInTarget.length > 0) {
                        if (!isInvert) {
                            filesInTarget.forEach(f => S.sel.add(f.id));
                        }
                    }
                });

                renderDupView();
                updateStat();

                if (S._dupFolderPickedScrollTop) {
                    S._dupFolderPickedScrollTop = false;
                    if (S._dupFolderPickedTopRaf) cancelAnimationFrame(S._dupFolderPickedTopRaf);
                    S._dupFolderPickedTopRaf = requestAnimationFrame(() => {
                        S._dupFolderPickedTopRaf = 0;
                        if (UI.vp && UI.vp.scrollTop !== 0) UI.vp.scrollTop = 0;
                    });
                }
            };

            UI.selDupFolder.onchange = applyDupSelection;

            setTimeout(() => {
                const invertChk = document.getElementById('pk-dup-invert');
                if (invertChk) {
                    invertChk.onchange = () => {
                        if (UI.selDupFolder.value && UI.selDupFolder.value !== "__RESET__") {
                            applyDupSelection();
                        }
                    };
                }

                const smartBtn = document.getElementById('pk-dup-smart-btn');
                if (smartBtn) {
                    smartBtn.addEventListener('click', () => {
                        if (S.pinnedDupPath) {
                            S.pinnedDupPath = null;
                            UI.selDupFolder.value = "";

                            if (invertChk) {
                                invertChk.checked = false;
                                invertChk.disabled = true;
                                invertChk.parentNode.style.opacity = '0.5';
                            }

                            renderDupView();
                        }
                    }, { capture: true });
                }
            }, 0);

            S.dupRunning = false;
            setLoad(false);
            if (UI.dupTools) UI.dupTools.style.display = 'flex';
            if (UI.dupFilters) {
                UI.dupFilters.style.display = 'flex';
                const simChkWrapper = UI.dupFilters.querySelector('#pk-chk-sim')?.closest('.pk-dup-chk');
                if (simChkWrapper) {
                    simChkWrapper.style.display = S.dupConfig.video ? 'flex' : 'none';
                }
            }
            renderDupView();

        } else {
            if (UI.dupTools) UI.dupTools.style.display = 'none';
            if (UI.dupFilters) UI.dupFilters.style.display = 'none';
            if (S.uploadMode || (S.analyzeMode && S.analyzeSimGroups && cur.id === 'analyze_root')) {
                renderList();
                if (gmGet('pk_keep_pos', true) && S.latestChildId) {
                    const targetIdx = S.display.findIndex(x => x.id === S.latestChildId);
                    if (targetIdx !== -1) {
                        const targetTop = getItemScrollTopByIndex(targetIdx);
                        UI.vp.scrollTop = Math.max(0, targetTop - (UI.vp.clientHeight / 2) + (CONF.rowHeight / 2));
                        S.clearSelection();
                        S.activeId = S.latestChildId;
                        renderVisible();
                    }
                    S.latestChildId = null;
                }
                updateStat();
                return;
            }

            if (S.display.length > 5000 && !S.offlineMode) {
                setLoad(true);
                updateLoadTxt(L.str_sorting);
                await sleep(10);
            }

            const sortedList = await new Promise(resolve => {

                const isRoot = S.path.length === 1 && S.path[0].id === '';

                const proxyList = new Array(S.display.length);
                const len = S.display.length;
                for (let i = 0; i < len; i++) {
                    const item = S.display[i];

                    const isStarred = S.starredSet.has(item.id) || !!(item.starred || (item.tags && item.tags.some(t => t.name === 'STAR')));

                    const isMyPack = item._isSystemRoot || (!S.trashMode && item.kind === 'drive#folder' && (
                        (item.id === '' || item.id === 'root') ||
                        (isRoot && item.name === CONF.SYSTEM_FOLDER_NAME)
                    ));

                    const ext = item.name.split('.').pop().toLowerCase();
                    const isVid = ['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'ts', 'm4v', '3gp'].includes(ext);
                    const isImg = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'tiff', 'ico'].includes(ext);
                    const mimeGroup = isVid ? 1 : (isImg ? 2 : 3);

                    let finalDur = S.durationMap.get(item.id) || parseFloat(item.params?.duration || 0);
                    if (finalDur > 0 && item.params) item.params.duration = finalDur;

                    let pathStr = "";
                    if (S.analyzeMode) pathStr = item._pathStr || "";
                    else if (item._lineage) pathStr = item._lineage.map(x=>x.name).join('/');

                    proxyList[i] = {
                        i: i,
                        n: item.name,
                        k: item.kind === 'drive#folder' ? 1 : 0,
                        s: item.size || 0,
                        t: item.modified_time,
                        d: finalDur,
                        st: isStarred ? 1 : 0,
                        mp: isMyPack ? 1 : 0,
                        m: mimeGroup,
                        p: pathStr,
                        ct: item.created_time,
                        pt: item._history_ts || 0,
                        pg: finalDur > 0 ? Math.min(100, Math.max(0, ((item._history_progress || 0) / finalDur) * 100)) : -1,
                        v: parseInt(item.view_count || 0),
                        sc: parseInt(item.save_count || 0),
                        ss: item.share_status || 'OK',
                        ed: parseInt(item.expiration_days || -1),
                        lc: parseInt(item.limit_count || 0)
                    };
                }

                const workerCode = `
                    self.onmessage = function(e) {
                        const { proxy, sort, dir, reqId, folderFirst, locale } = e.data;

                        const parseSize = n => parseInt(n || 0);
                        const parseTime = t => t ? new Date(t).getTime() : 0;

                        const collator = new Intl.Collator(locale, {
                            numeric: true,
                            sensitivity: 'base',
                            caseFirst: 'lower'
                        });

                        const getCharWeight = (str) => {
                            if (!str) return 0;
                            const c = str.charAt(0);

                            if (/[0-9]/.test(c)) return 20;

                            if (/[\\u4e00-\\u9fa5]/.test(c)) return 30;

                            if (/[a-zA-Z]/.test(c)) return 40;

                            return 10;
                        };

                        const compareNames = (nameA, nameB) => {
                            const rankA = getCharWeight(nameA);
                            const rankB = getCharWeight(nameB);

                            if (rankA !== rankB) {
                                return rankA - rankB;
                            }

                            return collator.compare(nameA, nameB);
                        };

                        proxy.sort((a, b) => {
                            if (a.mp !== b.mp) return b.mp - a.mp;

                            if (folderFirst && a.k !== b.k) {
                                return b.k - a.k;
                            }

                            if (sort === 'view_count' || sort === 'save_count') {
                                const valA = sort === 'view_count' ? (a.v || 0) : (a.sc || 0);
                                const valB = sort === 'view_count' ? (b.v || 0) : (b.sc || 0);
                                if (valA !== valB) return (valB - valA) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'share_status') {
                                const getStatusRank = (item) => {
                                    if (item.lc > 0 && item.sc >= item.lc) return 5;
                                    if (item.ss === 'DELETED') return 4;
                                    if (item.ss === 'EXPIRED') return 3;
                                    if (item.ed === -1) return 1;
                                    return 2;
                                };
                                const rA = getStatusRank(a), rB = getStatusRank(b);
                                if (rA !== rB) return (rA - rB) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'modified_time') {
                                const tA = parseTime(a.t), tB = parseTime(b.t);
                                if (tA !== tB) return (tB - tA) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'created_time') {
                                const tA = parseTime(a.ct), tB = parseTime(b.ct);
                                if (tA !== tB) return (tB - tA) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'progress') {
                                const pctA = typeof a.pg === 'number' ? a.pg : -1;
                                const pctB = typeof b.pg === 'number' ? b.pg : -1;
                                if (pctA !== pctB) return (pctB - pctA) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'play_time') {
                                if (a.pt !== b.pt) return (b.pt - a.pt) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'path') {
                                const pathCmp = compareNames(a.p, b.p);
                                if (pathCmp !== 0) return pathCmp * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'size') {
                                if (a.k !== b.k) return folderFirst ? (b.k - a.k) : (a.k - b.k);
                                const sizeA = parseSize(a.s), sizeB = parseSize(b.s);
                                if (sizeA !== sizeB) return (sizeB - sizeA) * dir;
                                return compareNames(a.n, b.n) * dir;
                            }

                            if (sort === 'duration') {
                                if (a.k !== b.k) return folderFirst ? (b.k - a.k) : (a.k - b.k);

                                if (a.k) return compareNames(a.n, b.n) * dir;

                                const hasDurA = a.d > 0;
                                const hasDurB = b.d > 0;

                                if (hasDurA !== hasDurB) return hasDurA ? -1 : 1;

                                if (hasDurA) {
                                    if (a.d !== b.d) return (b.d - a.d) * dir;
                                    return compareNames(a.n, b.n) * dir;
                                }

                                const isVidA = (a.m === 1);
                                const isVidB = (b.m === 1);

                                if (isVidA !== isVidB) return isVidA ? -1 : 1;

                                if (isVidA) {
                                    return compareNames(a.n, b.n);
                                } else {
                                    const getExt = s => { const i = s.lastIndexOf('.'); return i > 0 ? s.slice(i).toLowerCase() : ''; };
                                    const extA = getExt(a.n);
                                    const extB = getExt(b.n);

                                    const extCmp = compareNames(extA, extB);
                                    if (extCmp !== 0) {
                                        return extCmp * dir;
                                    }

                                    return compareNames(a.n, b.n) * dir;
                                }
                            }

                            if (sort === 'starred') {
                                if (a.st !== b.st) return b.st - a.st;
                                return (parseTime(b.t) - parseTime(a.t)) * dir;
                            }

                            return compareNames(a.n, b.n) * dir;
                        });

                        const sortedIndices = new Uint32Array(proxy.length);
                        for (let i = 0; i < proxy.length; i++) sortedIndices[i] = proxy[i].i;
                        self.postMessage({ indices: sortedIndices, reqId: reqId }, [sortedIndices.buffer]);
                    };
                `;
                const blob = new Blob([workerCode], { type: 'application/javascript' });
                const workerUrl = URL.createObjectURL(blob);
                const sortWorker = new Worker(workerUrl);

                sortWorker.onmessage = (e) => {
                    const { indices, reqId } = e.data;
                    URL.revokeObjectURL(workerUrl);
                    sortWorker.terminate();
                    if (reqId === S.sortId) {
                        resolve(Array.from(indices).map(idx => S.display[idx]));
                    } else resolve(null);
                };
                const sortLocale = ({'zh':'zh-CN','tc':'zh-TW','ja':'ja','ko':'ko','en':'en','id':'id','ms':'ms'})[getLang()] || 'en';
                sortWorker.postMessage({ proxy: proxyList, sort: S.sort, dir: S.dir, reqId: currentReqId, folderFirst: S.folderFirst, locale: sortLocale });
            });

            if (S.display.length > 5000 && !S.offlineMode) setLoad(false);
            if (sortedList === null) return;
            S.display = sortedList;
        }

        if (currentReqId !== S.sortId) return;

        const currentIds = new Set(S.display.map(i => i.id));
        if (S.selMode === 'all') {
            const nextExcluded = new Set();
            for (const id of S.selEx) {
                if (currentIds.has(id)) nextExcluded.add(id);
            }
            S.selEx = nextExcluded;
        } else {
            const nextSelected = S.getSelectedIds().filter(id => currentIds.has(id));
            S.setExplicitSelection(nextSelected);
        }

        renderList();

        if (gmGet('pk_keep_pos', true) && S.latestChildId) {
            const targetIdx = S.display.findIndex(x => x.id === S.latestChildId);
            if (targetIdx !== -1) {
                const rowTop = getItemScrollTopByIndex(targetIdx);
                const vpHeight = UI.vp.clientHeight;
                const centerScroll = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));
                UI.vp.scrollTop = centerScroll;
                S.activeId = S.latestChildId;
                S.clearSelection();
                S.activeId = S.latestChildId;
                renderVisible();
            }
            S.latestChildId = null;
        }

        updateStat();
    }

    const getCommonPathPrefix = (pathStrings) => {
        if (!pathStrings || pathStrings.length < 2) return "";

        const splitPaths = pathStrings.map(p => p.split('/'));
        const firstPath = splitPaths[0];
        let commonLen = 0;

        for (let i = 0; i < firstPath.length; i++) {
            const segment = firstPath[i];
            const isAllSame = splitPaths.every(p => p[i] === segment);
            if (isAllSame) {
                commonLen++;
            } else {
                break;
            }
        }

        if (commonLen <= 1) return "";

        return firstPath.slice(0, commonLen).join('/');
    };

    function renderDupView() {
        if (!S.dupMode) return;
        const L = getStrings();
        const showName = UI.chkName.checked;
        const showSim = UI.chkSim.checked;
        const showHash = UI.chkHash.checked;
        const newDisplay = [];

        if (!S.dupItemMap || S.dupItemMap.size !== S.items.length) {
            S.dupItemMap = new Map();
            const len = S.items.length;
            for (let i = 0; i < len; i++) {
                S.dupItemMap.set(S.items[i].id, S.items[i]);
            }
        }
        const itemMap = S.dupItemMap;

        const getCachedPath = (item) => {
            if (item._cachedPath !== undefined) return item._cachedPath;
            let pStr = "";
            if (item._lineage && item._lineage.length > 0) {
                pStr = item._lineage.map(node => node.name).join('/');
            } else {
                const curFolder = S.path[S.path.length - 1];
                pStr = curFolder ? curFolder.name : L.current_dir;
            }
            item._cachedPath = pStr;
            return pStr;
        };

        S.dupGroups.clear();

        let dupBuckets = S.dupBuckets;
        if (!dupBuckets || !Array.isArray(dupBuckets.all)) {
            dupBuckets = { all: Array.isArray(S.dupRawGroups) ? S.dupRawGroups : [], hash: [], sim: [], name: [] };
            const allLen = dupBuckets.all.length;
            for (let i = 0; i < allLen; i++) {
                const g = dupBuckets.all[i];
                if (g.type === L.tag_hash) dupBuckets.hash.push(g);
                else if (g.type === L.tag_sim) dupBuckets.sim.push(g);
                else if (g.type === L.tag_name) dupBuckets.name.push(g);
            }
            S.dupBuckets = dupBuckets;
        }

        if (dupBuckets.all.length === 0 && !S.dupRunning) {
            if (UI.btnExit) {
                UI.btnExit.click();
            } else {
                S.dupMode = false;
                S.isFlattened = false;
                refresh();
            }
            return;
        }

        let groupsToRender = [];
        if (showHash) groupsToRender.push(...dupBuckets.hash);
        if (showSim) groupsToRender.push(...dupBuckets.sim);
        if (showName) groupsToRender.push(...dupBuckets.name);

        if (S.pinnedDupPath) {
            const pinnedGroups = [];
            const normalGroups = [];

            for (const g of groupsToRender) {
                let isPinned = false;
                for (const id of g.ids) {
                    const item = itemMap.get(id);
                    if (item) {
                        const path = getCachedPath(item);
                        if (path === S.pinnedDupPath) {
                            isPinned = true;
                            break;
                        }
                    }
                }
                if (isPinned) pinnedGroups.push(g);
                else normalGroups.push(g);
            }
            groupsToRender = [...pinnedGroups, ...normalGroups];
        }

        const dynamicFolderStats = new Map();
        const groupsLen = groupsToRender.length;

        const searchKey = S.search ? S.search.toLowerCase().trim() : null;
        const includePath = S.search && UI.chkSearchPath ? UI.chkSearchPath.checked : false;

        for (let gIdx = 0; gIdx < groupsLen; gIdx++) {
            const g = groupsToRender[gIdx];

            if (g.type === L.tag_name && !showName) continue;
            if (g.type === L.tag_sim && !showSim) continue;
            if (g.type === L.tag_hash && !showHash) continue;

            const ids = g.ids;
            const idsLen = ids.length;

            for (let k = 0; k < idsLen; k++) {
                S.dupGroups.set(ids[k], gIdx);
            }

            const groupItems = [];
            const groupPaths = [];

            for (let k = 0; k < idsLen; k++) {
                const item = itemMap.get(ids[k]);
                if (item) {
                    groupItems.push(item);
                    groupPaths.push(getCachedPath(item));
                }
            }

            if (searchKey) {
                const isMatch = groupItems.some((item, idx) => {
                    const nameHit = item.name.toLowerCase().includes(searchKey);
                    let pathHit = false;
                    if (includePath) {
                        const rawPath = groupPaths[idx] || "";
                        const homeText = L.btn_nav_home;
                        const parentPath = (rawPath === homeText || rawPath.startsWith(homeText + '/')) ? rawPath : (homeText + '/' + rawPath);
                        const fullItemPath = parentPath.endsWith('/') ? (parentPath + (item.name || "")) : (parentPath + '/' + (item.name || ""));
                        pathHit = fullItemPath.toLowerCase().includes(searchKey);
                    }
                    return nameHit || pathHit;
                });

                if (!isMatch) continue;
            }
            const pathsLen = groupPaths.length;
            for (let p = 0; p < pathsLen; p++) {
                const pStr = groupPaths[p];
                dynamicFolderStats.set(pStr, (dynamicFolderStats.get(pStr) || 0) + 1);
            }

            const firstItem = itemMap.get(ids[0]);
            let featureStr = "";

            if (firstItem) {
                if (g.type === L.tag_hash) {
                    featureStr = `[ ${fmtSize(firstItem.size)} ] `;
                } else if (g.type === L.tag_sim) {
                    const dur = firstItem.params?.duration || 0;
                    featureStr = `[ ${dur > 0 ? fmtDur(dur) : '--:--'} ] `;
                } else if (g.type === L.tag_name) {
                    featureStr = `[ ${L.tag_name_short} ] `;
                }
            }

            const baseName = firstItem ? (g.type === L.tag_name ? firstItem.name.replace(/\.[^/.]+$/, "") : firstItem.name) : `${L.str_group} ${gIdx}`;

            const countStr = `${idsLen} ${L.str_items}`;
            const smartTitle = `${countStr} | ${featureStr}${baseName}`;

            newDisplay.push({
                id: `grp_${gIdx}`,
                isHeader: true,
                name: smartTitle,
                count: idsLen,
                type: g.type || L.str_group,
                groupIds: ids.slice()
            });

            const selectedDupPath = S.pinnedDupPath || "";

            const compareDupGroupData = (a, b) => {
                const op = S.groupSortOp || 'path';
                const dir = S.groupSortDir || 1;
                let res = 0;

                if (op === 'time') {
                    res = new Date(b.it.modified_time) - new Date(a.it.modified_time);
                } else if (op === 'size') {
                    const sa = BigInt(a.it.size || 0), sb = BigInt(b.it.size || 0);
                    res = sa < sb ? 1 : (sa > sb ? -1 : 0);
                } else if (op === 'path') {
                    if (a.path.length !== b.path.length) res = a.path.length - b.path.length;
                    else res = a.path.localeCompare(b.path);
                } else if (op === 'name') {
                    res = a.it.name.localeCompare(b.it.name);
                }

                return res * dir;
            };

            const sortDupGroupChunk = arr => arr.slice().sort(compareDupGroupData);

            const groupData = groupItems.map((it, idx) => ({
                it,
                path: groupPaths[idx]
            }));

            let sortedGroupData;

            if (selectedDupPath) {
                const groupA = [];
                const groupB = [];

                for (let i = 0; i < groupData.length; i++) {
                    const row = groupData[i];
                    if (row.path === selectedDupPath) groupA.push(row);
                    else groupB.push(row);
                }

                if (groupA.length > 0) {
                    sortedGroupData = sortDupGroupChunk(groupA).concat(sortDupGroupChunk(groupB));
                } else {
                    sortedGroupData = sortDupGroupChunk(groupData);
                }
            } else {
                sortedGroupData = sortDupGroupChunk(groupData);
            }

            let lastFullPath = null;

            for (let idx = 0; idx < sortedGroupData.length; idx++) {
                const { it: item, path: fullPath } = sortedGroupData[idx];

                item._dupFullPath = fullPath;
                if (idx > 0 && fullPath === lastFullPath) {
                    item._isSameFolder = true;
                }
                else {
                    item._isSameFolder = false;
                }

                lastFullPath = fullPath;
                newDisplay.push(item);
            }
        }

        const dupFolderWrap = document.getElementById('pk-dup-folder-sel-wrap');
        if (dupFolderWrap) {
            dupFolderWrap.style.width = "220px";
            dupFolderWrap.style.flexShrink = "0";
        }
        UI.selDupFolder.style.width = "220px";
        UI.selDupFolder.style.flexShrink = "0";
        const currentSelection = UI.selDupFolder.value;
        const invertChk = document.getElementById('pk-dup-invert');
        const invertLabel = invertChk ? invertChk.parentNode : null;

        if (dynamicFolderStats.size <= 1) {
            if (dupFolderWrap) dupFolderWrap.style.display = 'none';
            UI.selDupFolder.style.display = 'none';
            if (invertLabel) invertLabel.style.display = 'none';
        } else {
            if (dupFolderWrap) dupFolderWrap.style.display = 'inline-flex';
            UI.selDupFolder.style.display = 'inline-block';
            if (invertLabel) invertLabel.style.display = 'inline-flex';
        }

        let dropdownHtml = `<option value="" disabled selected style="display:none">${L.lbl_dup_select_folder}</option>`;

        if (dynamicFolderStats.size > 0) {
            dropdownHtml += `<option value="__RESET__" style="font-weight:bold;color:var(--pk-pri);">${L.lbl_dup_reset}</option>`;
        }

        const truncateMiddle = (str, len = 30) => {
            if (!str || str.length <= len * 2 + 3) return str;
            return str.substring(0, len) + ' ... ' + str.substring(str.length - len);
        };

        const sortLocale = ({'zh':'zh-CN','tc':'zh-TW','ja':'ja','ko':'ko','en':'en','id':'id','ms':'ms'})[getLang()] || 'en';
        const collator = new Intl.Collator(sortLocale, { numeric: true });

        const sortedFolders = Array.from(dynamicFolderStats.entries())
            .sort((a, b) => {
                const arrA = a[0].split('/');
                const arrB = b[0].split('/');
                const len = Math.min(arrA.length, arrB.length);
                for (let i = 0; i < len; i++) {
                    const cmp = collator.compare(arrA[i], arrB[i]);
                    if (cmp !== 0) return cmp;
                }
                return arrA.length - arrB.length;
            });

        const optionsArr = sortedFolders.map(([path, count]) => {
            return `<option value="${esc(path)}" title="${esc(path)}">${esc(truncateMiddle(path, 35))} ${L.fmt_dup_count.replace('{n}', count)}</option>`;
        });

        dropdownHtml += optionsArr.join('');
        UI.selDupFolder.innerHTML = dropdownHtml;

        if (currentSelection && dynamicFolderStats.has(currentSelection)) {
            UI.selDupFolder.value = currentSelection;
        } else if (currentSelection === "__RESET__") {
            UI.selDupFolder.value = "__RESET__";
        } else {
            if (S.pinnedDupPath && !dynamicFolderStats.has(S.pinnedDupPath)) {
                S.pinnedDupPath = null;
            }
            UI.selDupFolder.value = "";
        }

        syncDupFolderButtonText();

        S.display = newDisplay;

        const visibleIds = new Set(newDisplay.map(d => d.id));
        for(let id of S.sel) {
            if (!visibleIds.has(id)) S.sel.delete(id);
        }

        if (UI.chkAll) UI.chkAll.checked = false;

        renderList();
        updateStat();
    }

    function renderList() {
        syncLayoutMetrics();
        UI.win.classList.toggle('pk-grid-view', isGridView());
        renderViewSwitch();
        if (isGridView()) {
            const gridLayout = getGridLayout();
            S._gridLayoutKey = getGridLayoutKey();
            if (isGroupedGridView()) {
                const dupGridMeta = getGroupedGridMeta(true);
                UI.in.style.height = `${Math.max(0, dupGridMeta ? dupGridMeta.totalHeight : 0)}px`;
            } else {
                S.dupGridMeta = null;
                S.dupGridMetaKey = '';
                UI.in.style.height = `${Math.ceil((S.display.length || 0) / gridLayout.cols) * CONF.rowHeight}px`;
            }
            scheduleGridRelayout();
        } else {
            S._gridLayoutKey = '';
            S.dupGridMeta = null;
            S.dupGridMetaKey = '';
            if (UI.win) UI.win.classList.remove('pk-grid-view', 'pk-grid-resizing', 'pk-grid-scrolling');
            UI.in.style.height = `${S.display.length * CONF.rowHeight}px`;
        }
        let colDef;

        const cur = S.path[S.path.length - 1];
        const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
        const isGlobalSearchRoot = cur.id === 'virtual_search_root' && UI.chkGlobal && UI.chkGlobal.checked;
        const showPathCol = S.dupMode || isAnalyzeRoot || S.isFlattened || isGlobalSearchRoot;

        const isMax = UI.win.classList.contains('pk-maximized');

        if (S.offlineMode) {
            colDef = "36px 1fr 120px 240px 180px";
        } else if (S.uploadMode) {
            colDef = "36px 1fr 100px 120px 180px";
        } else if (S.shareMode) {
            colDef = isMax ? "36px 1fr 80px 80px 120px 130px" : "36px 1fr 80px 80px 80px 130px";
        } else if (S.historyMode) {
            colDef = "36px 30px 1fr 80px 80px 120px 160px";
        } else if (S.trashMode) {
            colDef = "36px 30px 1fr 80px 105px 130px";
        } else if (S.recentMode) {
            colDef = "36px 30px 1fr 80px 105px 130px";
        } else if (isAnalyzeRoot) {
            colDef = "36px 30px 2fr 1fr 80px 130px";
        } else if (showPathCol) {
            colDef = "36px 30px 2fr 1fr 80px 105px 130px";
        } else {
            colDef = "36px 30px 1fr 80px 105px 130px";
        }

        const hd = UI.win.querySelector('.pk-grid-hd');
        if (hd) {
            if (isGridView()) {
                const gridMeta = getGridSortMeta();
                const hideGroupedRootGridHeadTools = S.dupMode || (isAnalyzeRoot && S.analyzeSimGroups);
                const hideGridFolderFirst = S.isFlattened || S.dupMode || isAnalyzeRoot;
                hd.classList.add('pk-grid-view-hd');
                hd.style.gridTemplateColumns = '';
                hd.innerHTML = `
                    <div class="pk-grid-check-tools">
                        <input type="checkbox" id="pk-all">
                        <button class="pk-btn" id="pk-grid-folder-first" type="button" data-pk-tip="${L.lbl_folder_first}" style="cursor:pointer; display:${hideGridFolderFirst ? 'none' : 'flex'}; align-items:center; color:${S.folderFirst ? 'var(--pk-pri)' : '#666'}; font-size:12px; flex-shrink:0; padding:0;">
                            ${CONF.icons.folderFirst}
                            <span style="margin-left:2px; white-space:nowrap;">${L.lbl_folder_first}</span>
                        </button>
                        <div id="pk-grid-invert" class="pk-btn" data-pk-tip="${L.btn_invert}" style="margin-left:12px; cursor:pointer; display:${(!S.dupMode && S.getSelectedCount() > 0) ? 'flex' : 'none'}; align-items:center; color:var(--pk-fg); padding:0; pointer-events:auto; flex-shrink:0;">
                            ${CONF.icons.invert}
                            <span style="margin-left:2px; white-space:nowrap;">${L.btn_invert}</span>
                        </div>
                    </div>
                    <div class="pk-grid-sort-wrap" style="display:${hideGroupedRootGridHeadTools ? 'none' : 'flex'};">
                        <button class="pk-grid-sort-trigger" id="pk-grid-sort-trigger" type="button">
                            ${gridMeta.icon}
                            <span>${gridMeta.label}</span>
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
                        </button>
                        <div class="pk-grid-sort-menu">
                             ${getGridSortOptions().map(opt => {
                                 const state = resolveGridSortOptionState(opt);
                                 return `<div class="pk-grid-sort-opt ${state.active ? 'active' : ''}" data-sort="${opt.sort}">${state.icon}<span>${state.label}</span></div>`;
                             }).join('')}
                        </div>
                    </div>
                `;
                UI.chkAll = hd.querySelector('#pk-all');
                if (UI.chkAll) UI.chkAll.onclick = S.handleSelectAll;

                UI.btnGridFolderFirst = hd.querySelector('#pk-grid-folder-first');
                if (UI.btnGridFolderFirst) {
                    UI.btnGridFolderFirst.onclick = (e) => {
                        e.stopPropagation();
                        toggleFolderFirst();
                    };
                }

                const btnGridInv = hd.querySelector('#pk-grid-invert');
                if (btnGridInv) {
                    btnGridInv.onclick = (e) => {
                        e.stopPropagation();
                        if (S.loading || S.display.length === 0) return;
                        S.invertSelection();
                        renderVisible();
                        updateStat();
                    };
                    btnGridInv.onmouseenter = () => btnGridInv.style.color = 'var(--pk-pri)';
                    btnGridInv.onmouseleave = () => btnGridInv.style.color = 'var(--pk-fg)';
                }

                UI.gridSortWrap = hd.querySelector('.pk-grid-sort-wrap');
                if (UI.gridSortWrap && S.gridSortMenuOpen) UI.gridSortWrap.classList.add('open');

                const sortTrigger = hd.querySelector('#pk-grid-sort-trigger');
                if (sortTrigger) {
                    sortTrigger.onclick = (e) => {
                        e.stopPropagation();
                        if (!UI.gridSortWrap) return;
                        const willOpen = !UI.gridSortWrap.classList.contains('open');
                        S.gridSortMenuOpen = willOpen;
                        UI.gridSortWrap.classList.toggle('open', willOpen);
                    };
                }

                hd.querySelectorAll('.pk-grid-sort-opt').forEach(opt => {
                    opt.onclick = (e) => {
                        e.stopPropagation();
                        applySortSelection(opt.dataset.sort);
                    };
                });

                if (!S.gridSortDismissBound) {
                    document.addEventListener('mousedown', closeGridSortMenu, true);
                    document.addEventListener('scroll', closeGridSortMenu, true);
                    window.addEventListener('resize', closeGridSortMenu);
                    S.gridSortDismissBound = true;
                }
            } else {
                hd.classList.remove('pk-grid-view-hd');
                hd.style.gridTemplateColumns = colDef;
                UI.gridSortWrap = null;
                closeGridSortMenu();
            }

            if (!isGridView() && S.offlineMode) {
                if (!hd.querySelector('[data-k="offline_status"]')) {
                    hd.innerHTML = `
                        <div><input type="checkbox" id="pk-all"></div>
                        <div class="pk-col" data-k="name" style="justify-content:flex-start;">${L.col_name}<span></span></div>
                        <div class="pk-col" data-k="size" style="justify-content:flex-start;">${L.col_size}<span></span></div>
                        <div class="pk-col" data-k="offline_status" style="justify-content:flex-start;">${L.col_task_status}<span></span></div>
                        <div class="pk-col" data-k="offline_progress" style="justify-content:flex-start;">${L.col_task_progress}<span></span></div>
                    `;
                    const chk = hd.querySelector('#pk-all');
                    chk.onclick = S.handleSelectAll;
                    UI.chkAll = chk;
                }
            }
            else if (!isGridView() && S.historyMode) {
                if (!hd.querySelector('[data-k="play_time"]')) {
                    hd.innerHTML = `
                        <div><input type="checkbox" id="pk-all"></div>
                        <div class="pk-col" data-k="starred" style="justify-content:center;">
                            <svg width="16" height="16" viewBox="0 0 1024 1024" style="margin-top:-2px;"><path d="M953.107692 425.353846c3.938462-19.692308-11.815385-39.384615-31.507692-43.323077l-259.938462-39.384615-118.153846-244.184616c-3.938462-7.876923-7.876923-11.815385-15.753846-15.753846-7.876923-3.938462-15.753846-3.938462-19.692308-3.938461h-3.938461c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462-3.938462 3.938462-3.938462 7.876923-7.876923 11.815384v3.938462l-118.153846 244.184615-259.938462 39.384616c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462 0 0-3.938462 0-3.938461 3.938461v3.938462c0 3.938462-3.938462 3.938462-3.938461 7.876923 0 0 0 3.938462-3.938462 3.938462v15.753846c0 3.938462 0 3.938462 3.938462 7.876923 0 3.938462 3.938462 7.876923 7.876923 11.815384L275.692308 638.030769 232.369231 905.846154V917.661538c0 3.938462 0 3.938462 3.938461 7.876924v3.938461c0 3.938462 3.938462 3.938462 7.876923 7.876923l3.938462 3.938462c3.938462 0 3.938462 3.938462 7.876923 3.938461H275.692308c3.938462 0 7.876923 0 11.815384-3.938461l78.769231-43.323077 149.661539-78.769231 3.938461-3.938462 232.369231 126.03077c7.876923 3.938462 15.753846 3.938462 23.630769 3.938461 19.692308-3.938462 31.507692-23.630769 27.569231-43.323077l-43.323077-267.815384 189.046154-189.046154c0-7.876923 0-11.815385 3.938461-19.692308z" fill="#FFC107"></path></svg>
                            <span></span>
                        </div>
                        <div class="pk-col" data-k="name" style="justify-content:flex-start;">
                            <div id="pk-name-text-wrap" style="display:flex;align-items:center;">${L.col_name}<span></span></div>
                        </div>
                        <div class="pk-col" data-k="size">${L.col_size}<span></span></div>
                        <div class="pk-col" data-k="duration">${L.col_duration_only}<span></span></div>
                        <div class="pk-col" data-k="progress" style="justify-content:flex-start;">${L.col_progress}<span></span></div>
                        <div class="pk-col" data-k="play_time" style="justify-content:flex-start;">${L.col_play_time}<span></span></div>
                    `;
                    const chk = hd.querySelector('#pk-all');
                    chk.onclick = S.handleSelectAll;
                    UI.chkAll = chk;
                }
            }
            else if (!isGridView() && S.uploadMode) {
                if (!hd.querySelector('[data-k="upload_speed"]')) {
                    hd.innerHTML = `
                        <div><input type="checkbox" id="pk-all"></div>
                        <div class="pk-col" data-k="name" style="justify-content:flex-start;">${L.col_name}<span></span></div>
                        <div class="pk-col" data-k="size" style="justify-content:flex-start;">${L.col_size}<span></span></div>
                        <div class="pk-col" data-k="upload_speed" style="justify-content:flex-start;">${L.col_up_speed}<span></span></div>
                        <div class="pk-col" data-k="upload_status" style="justify-content:flex-start;">${L.col_up_status}<span></span></div>
                    `;
                    const chk = hd.querySelector('#pk-all');
                    chk.onclick = S.handleSelectAll;
                    UI.chkAll = chk;
                }
            }
            else if (!isGridView() && S.shareMode) {
                if (!hd.querySelector('[data-k="share_status"]')) {
                    hd.innerHTML = `
                        <div><input type="checkbox" id="pk-all"></div>
                        <div class="pk-col" data-k="name" style="justify-content:flex-start;">
                            <div id="pk-name-text-wrap" style="display:flex;align-items:center;">${L.col_name}<span style="display:inline-block; min-width:18px; text-align:center;"></span></div>
                            <div id="pk-btn-invert" class="pk-btn" data-pk-tip="${L.btn_invert}" style="margin-left:12px; cursor:pointer; display:none; align-items:center; color:var(--pk-fg); padding:0; pointer-events:auto;">
                                ${CONF.icons.invert}
                                <span style="margin-left:2px;">${L.btn_invert}</span>
                            </div>
                        </div>
                        <div class="pk-col" data-k="view_count" style="justify-content:flex-start;">${L.col_view}<span></span></div>
                        <div class="pk-col" data-k="save_count" style="justify-content:flex-start;">${L.col_save}<span></span></div>
                        <div class="pk-col" data-k="share_status" style="justify-content:center;">${L.col_share_status}<span></span></div>
                        <div class="pk-col" data-k="modified_time" style="justify-content:flex-end;">${L.col_share_time}<span></span></div>
                    `;
                    const chk = hd.querySelector('#pk-all');
                    chk.onclick = S.handleSelectAll;
                    UI.chkAll = chk;

                    const btnInv = hd.querySelector('#pk-btn-invert');
                    if(btnInv) {
                        btnInv.onclick = (e) => {
                            e.stopPropagation();
                            if (S.loading || S.display.length === 0) return;
                            const newSel = [];
                            for (let i = 0; i < S.display.length; i++) {
                                const item = S.display[i];
                                if (item && !item.isHeader && !S.movingIds.has(item.id) && !S.isSelected(item.id)) newSel.push(item.id);
                            }
                            S.setExplicitSelection(newSel); S.lastSelIdx = -1; S.activeId = null;
                            renderVisible(); updateStat();
                        };
                        btnInv.onmouseenter = () => btnInv.style.color = 'var(--pk-pri)';
                        btnInv.onmouseleave = () => btnInv.style.color = 'var(--pk-fg)';
                    }
                }
            } else if (!isGridView()) {
                const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
                const hasDurationCol = !!hd.querySelector('[data-k="duration"]');
                const hasHistoryCol = !!hd.querySelector('[data-k="play_time"]');
                const hasOfflineCol = !!hd.querySelector('[data-k="offline_status"]');

                if (hasDurationCol === isAnalyzeRoot || !hd.querySelector('[data-k="name"]') || hasHistoryCol || hasOfflineCol) {
                     hd.innerHTML = `
                        <div><input type="checkbox" id="pk-all"></div>
                        <div class="pk-col" data-k="starred" style="justify-content:center;">
                            <svg width="16" height="16" viewBox="0 0 1024 1024" style="margin-top:-2px;"><path d="M953.107692 425.353846c3.938462-19.692308-11.815385-39.384615-31.507692-43.323077l-259.938462-39.384615-118.153846-244.184616c-3.938462-7.876923-7.876923-11.815385-15.753846-15.753846-7.876923-3.938462-15.753846-3.938462-19.692308-3.938461h-3.938461c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462-3.938462 3.938462-3.938462 7.876923-7.876923 11.815384v3.938462l-118.153846 244.184615-259.938462 39.384616c-3.938462 0-7.876923 3.938462-11.815385 3.938461 0 0-3.938462 0-3.938461 3.938462 0 0-3.938462 0-3.938462 3.938461v3.938462c0 3.938462-3.938462 3.938462-3.938461 7.876923 0 0 0 3.938462-3.938462 3.938462v15.753846c0 3.938462 0 3.938462 3.938462 7.876923 0 3.938462 3.938462 7.876923 7.876923 11.815384L275.692308 638.030769 232.369231 905.846154V917.661538c0 3.938462 0 3.938462 3.938461 7.876924v3.938461c0 3.938462 3.938462 3.938462 7.876923 7.876923l3.938462 3.938462c3.938462 0 3.938462 3.938462 7.876923 3.938461H275.692308c3.938462 0 7.876923 0 11.815384-3.938461l78.769231-43.323077 149.661539-78.769231 3.938461-3.938462 232.369231 126.03077c7.876923 3.938462 15.753846 3.938462 23.630769 3.938461 19.692308-3.938462 31.507692-23.630769 27.569231-43.323077l-43.323077-267.815384 189.046154-189.046154c0-7.876923 0-11.815385 3.938461-19.692308z" fill="#FFC107"></path></svg>
                            <span></span>
                        </div>
                         <div class="pk-col" data-k="name" style="justify-content:flex-start;">
                              <div id="pk-name-text-wrap" style="display:flex;align-items:center;">${L.col_name}<span></span></div>
                         ${(!isAnalyzeRoot && !S.isFlattened && !S.dupMode) ? `
                         <div id="pk-btn-folder-first" class="pk-btn" data-pk-tip="${L.lbl_folder_first}" style="margin-left:8px; cursor:pointer; display:flex; align-items:center; color:#666; font-size:12px; flex-shrink:0; padding:0;">
                           ${CONF.icons.folderFirst}
                           <span style="margin-left:2px; white-space:nowrap;">${L.lbl_folder_first}</span>
                         </div>` : ''}
                             <div id="pk-btn-invert" class="pk-btn" data-pk-tip="${L.btn_invert}" style="margin-left:12px; cursor:pointer; display:none; align-items:center; color:var(--pk-fg); padding:0; pointer-events:auto;">
                                ${CONF.icons.invert}
                                <span style="margin-left:2px; white-space:nowrap;">${L.btn_invert}</span>
                             </div>
                        </div>
                        <div class="pk-col" data-k="path" style="display:none;color:#888;">${L.col_path} <span></span></div>
                        <div class="pk-col" data-k="size">${L.col_size}<span></span></div>
                        ${!isAnalyzeRoot ? `<div class="pk-col" data-k="duration">${L.col_dur}<span></span></div>` : ''}
                        <div class="pk-col" data-k="modified_time">${L.col_date}<span></span></div>
                    `;

                    const chk = hd.querySelector('#pk-all');
                    chk.onclick = S.handleSelectAll;
                    UI.chkAll = chk;

                    const btnFF = hd.querySelector('#pk-btn-folder-first');
                    if (btnFF) {
                        UI.btnFolderFirst = btnFF;
                        btnFF.onclick = (e) => {
                            e.stopPropagation();
                            S.folderFirst = !S.folderFirst;
                            gmSet('pk_folder_first', S.folderFirst);
                            if(S.renderFolderFirst) S.renderFolderFirst();
                            refresh();
                        };
                        if(S.renderFolderFirst) S.renderFolderFirst();
                    } else {
                        UI.btnFolderFirst = null;
                        S.renderFolderFirst = () => {};
                    }

                    const btnInv = hd.querySelector('#pk-btn-invert');
                    if(btnInv) {
                        btnInv.onclick = (e) => {
                            e.stopPropagation();
                            if (S.loading || S.display.length === 0) return;
                            const newSel = [];
                            for (let i = 0; i < S.display.length; i++) {
                                const item = S.display[i];
                                if (item && !item.isHeader && !S.movingIds.has(item.id) && !S.isSelected(item.id)) newSel.push(item.id);
                            }
                            S.setExplicitSelection(newSel); S.lastSelIdx = -1; S.activeId = null;
                            renderVisible(); updateStat();
                        };
                        btnInv.onmouseenter = () => btnInv.style.color = 'var(--pk-pri)';
                        btnInv.onmouseleave = () => btnInv.style.color = 'var(--pk-fg)';
                    }
                }

                const colPaths = hd.querySelector('[data-k="path"]');
                const colStar = hd.querySelector('[data-k="starred"]');
                const colDur = hd.querySelector('[data-k="duration"]');
                const colSize = hd.querySelector('[data-k="size"]');

                if (colPaths) colPaths.style.display = showPathCol ? 'flex' : 'none';

                if (colStar) {
                    colStar.style.display = 'flex';
                    colStar.style.opacity = '1';
                    colStar.style.pointerEvents = 'auto';
                }
                if (colDur) {
                    colDur.style.display = isAnalyzeRoot ? 'none' : 'flex';
                    colDur.style.opacity = '1';
                    colDur.style.pointerEvents = 'auto';
                }
                if (colSize) colSize.style.display = 'flex';

                const dateHeader = hd.querySelector('[data-k="modified_time"]');
                if (dateHeader) {
                    dateHeader.childNodes[0].textContent = S.trashMode ? L.col_remaining : L.col_date;
                }
            }
        }

        const currentCols = isGridView() ? [] : hd.querySelectorAll('.pk-col');

        currentCols.forEach(c => {
            const span = c.querySelector('span');
            const isSimFolderView = (isAnalyzeRoot && S.analyzeSimGroups);

            if (S.dupMode || S.offlineMode || S.uploadMode || isSimFolderView) {
                c.style.cursor = 'default';
                c.style.pointerEvents = 'none';
                c.style.color = 'var(--pk-fg)';
                if(span) span.textContent = '';

                if (c.dataset.k === 'name') {
                    const nameWrap = c.querySelector('#pk-name-text-wrap');
                    if (nameWrap) nameWrap.style.color = 'var(--pk-fg)';
                }
            } else {
                c.style.cursor = 'pointer';
                c.style.pointerEvents = 'auto';

                c.onclick = () => {
                    const k = c.dataset.k;
                    if (S.sort === k) S.dir *= -1; else { S.sort = k; S.dir = 1; }

                    const curNode = S.path[S.path.length - 1];
                    const isStandard = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && (!curNode.id.startsWith('virtual_') || curNode.id === 'virtual_search_root');

                    if (isStandard) {
                        if (gmGet('pk_sort_independent', false)) {
                            const folderId = curNode.id || 'root';
                            try {
                                const prefStore = JSON.parse(gmGet('pk_folder_sort_prefs', '{}'));
                                prefStore[folderId] = { sort: S.sort, dir: S.dir, folderFirst: S.folderFirst };
                                gmSet('pk_folder_sort_prefs', JSON.stringify(prefStore));
                            } catch(e) {}
                        } else {
                            gmSet('pk_global_sort_pref', JSON.stringify({ sort: S.sort, dir: S.dir, folderFirst: S.folderFirst }));
                        }
                    }

                    refresh();
                };

                if (span) span.textContent = (c.dataset.k === S.sort) ? (S.dir === 1 ? ' ▼' : ' ▲') : '';

                if (c.dataset.k === 'name') {
                    c.style.color = '';
                    const nameWrap = c.querySelector('#pk-name-text-wrap');
                    if (nameWrap) {
                        nameWrap.style.color = (S.sort === 'name') ? 'var(--pk-pri)' : '#666';
                    }
                } else {
                    c.style.color = (c.dataset.k === S.sort) ? 'var(--pk-pri)' : '';
                }
            }
        });

        UI.chkAll = hd.querySelector('#pk-all');
        if (UI.chkAll) {
            UI.chkAll.onclick = S.handleSelectAll;

            const totalSelectable = S.getSelectableCount();
            const selectedCount = S.getSelectedCount();
            UI.chkAll.checked = totalSelectable > 0 && selectedCount === totalSelectable;
            UI.chkAll.indeterminate = selectedCount > 0 && selectedCount < totalSelectable;
        }

        requestAnimationFrame(() => {
            renderVisible();
            if (!isGridView()) {
                requestAnimationFrame(() => {
                    endFolderViewSync();
                    if (UI.win) UI.win.classList.remove('pk-view-switching');
                });
            }
        });
    }

    const getStarIcon = (isStarred) => {
        const color = isStarred ? '#FFC107' : '#ccc';
        const fill = isStarred ? '#FFC107' : 'none';
        return `<svg width="18" height="18" viewBox="0 0 24 24" fill="${fill}" stroke="${color}" stroke-width="2" style="cursor:pointer;" class="pk-star-toggle">
        <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path></svg>`;
    };

    function renderVisible() {
        const L = getStrings();
        const isGridMode = isGridView();
        const gridLayout = isGridMode ? getGridLayout() : null;

        const winEl = el.querySelector('.pk-win');
        const listIn = el.querySelector('#pk-in');
        if (winEl && listIn) {
            const isMax = winEl.classList.contains('pk-maximized');
            const cur = S.path[S.path.length - 1];
            const isGroupedView = S.dupMode || (S.analyzeMode && cur.id === 'analyze_root' && S.analyzeSimGroups);
            const gap = isGroupedView ? (isMax ? 10 : 4) : 0;
            listIn.style.transform = gap > 0 ? `translateY(-${gap}px)` : 'none';
        }

        if (S.display.length === 0) {
            if (S.loading) {
                UI.in.style.height = '100%';
                UI.in.innerHTML = `
                    <div style="position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; color: var(--pk-fg); opacity: 0.7; z-index: 10; pointer-events: none;">
                        <div class="pk-spin-lg" style="width: 36px; height: 36px; border-width: 3px; border-color: rgba(136, 136, 136, 0.2); border-top-color: var(--pk-pri);"></div>
                        <div style="font-size: 13px; font-weight: 500; letter-spacing: 0.5px;">${L.loading_detail}</div>
                    </div>
                `;
                UI.pop.style.display = 'none';
                UI.pop.innerHTML = '';
                if (UI.ctx) UI.ctx.style.display = 'none';
                return;
            }

            if (S.items.length > 0 && !S.search && !S.dupMode && !S.trashMode && !S.shareMode && !S.offlineMode && !S.uploadMode && !S.isFlattened) {
                return;
            }

            if (UI.in.querySelector('.pk-empty')) {
                if (UI.in.style.height !== '100%') UI.in.style.height = '100%';
                return;
            }

            UI.in.style.height = '100%';
            UI.in.innerHTML = `<div class="pk-empty">${CONF.emptySVG}<div class="pk-empty-txt">${L.str_no_files}</div></div>`;

            UI.pop.style.display = 'none';
            UI.pop.innerHTML = '';
            if (UI.ctx) UI.ctx.style.display = 'none';
            return;
        }

        const dupGridMeta = isGroupedGridView() ? getGroupedGridMeta() : null;

        if (isGridMode && gridLayout) {
            UI.in.style.height = `${Math.max(0, dupGridMeta ? dupGridMeta.totalHeight : Math.ceil(S.display.length / gridLayout.cols) * CONF.rowHeight + gridLayout.gapY)}px`;
        } else {
            UI.in.style.height = `${S.display.length * CONF.rowHeight}px`;
        }

        const top = UI.vp.scrollTop;
        const h = UI.vp.clientHeight;
        const buffer = CONF.buffer || 20;

        let start = 0;
        let end = 0;
        let visibleIndices = null;

        if (dupGridMeta) {
            const sectionBuffer = Math.max(CONF.rowHeight * 2, buffer * CONF.rowHeight);
            visibleIndices = [];

            for (const section of dupGridMeta.sections) {
                if (section.bottom < top - sectionBuffer) continue;
                if (section.top > top + h + sectionBuffer) break;

                if (section.headerIndex !== null) visibleIndices.push(section.headerIndex);
                const itemIndices = section.itemIndices || [];
                for (let k = 0; k < itemIndices.length; k++) visibleIndices.push(itemIndices[k]);
            }

            end = visibleIndices.length;
        } else {
            start = isGridMode && gridLayout
                ? Math.max(0, (Math.floor(top / CONF.rowHeight) - Math.ceil(buffer / 2)) * gridLayout.cols)
            : Math.max(0, Math.floor(top / CONF.rowHeight) - buffer);
            end = isGridMode && gridLayout
                ? Math.min(S.display.length, (Math.ceil((top + h) / CONF.rowHeight) + Math.ceil(buffer / 2)) * gridLayout.cols)
            : Math.min(S.display.length, Math.ceil((top + h) / CONF.rowHeight) + buffer);
        }

        const isBlur = isBlurEnabledForView(isGridMode ? 'grid' : 'list');
        const rootPathStr = S.path.map(p => p.name).join('/');

        const lang = getLang();

        let colDef;
        const cur = S.path[S.path.length - 1];
        const isAnalyzeRoot = S.analyzeMode && cur.id === 'analyze_root';
        const isGlobalSearchRoot = cur.id === 'virtual_search_root' && UI.chkGlobal && UI.chkGlobal.checked;

        const showPathCol = S.dupMode || isAnalyzeRoot || S.isFlattened || isGlobalSearchRoot;

        const isMax = UI.win.classList.contains('pk-maximized');

        if (S.offlineMode) {
            colDef = "36px 1fr 120px 240px 180px";
        } else if (S.uploadMode) {
            colDef = "36px 1fr 100px 120px 180px";
        } else if (S.shareMode) {
            colDef = isMax ? "36px 1fr 80px 80px 120px 130px" : "36px 1fr 80px 80px 80px 130px";
        } else if (S.historyMode) {
            colDef = "36px 30px 1fr 80px 80px 120px 160px";
        } else if (S.trashMode) {
            colDef = "36px 30px 1fr 80px 105px 130px";
        } else if (S.recentMode) {
            colDef = "36px 30px 1fr 80px 105px 130px";
        } else if (isAnalyzeRoot) {
            colDef = "36px 30px 2fr 1fr 80px 130px";
        } else if (showPathCol) {
            colDef = "36px 30px 2fr 1fr 80px 105px 130px";
        } else {
            colDef = "36px 30px 1fr 80px 105px 130px";
        }

        UI.pop.style.display = 'none';
        UI.pop.innerHTML = '';

        cleanupNonPooledChildren(UI.in);

        const visibleCount = dupGridMeta ? visibleIndices.length : Math.max(0, end - start);
        const rowPool = ensureVisibleRowPool(UI.in, visibleCount);
        let poolPtr = 0;

        let nameColWidth = 400;
        let charCapacity = 50;

        const pathIds = S.path.map(p => p.id);
        const isAtGlobalSearchRoot = pathIds[pathIds.length - 1] === 'virtual_search_root';
        const isGlobalSearchHistoryPresent = pathIds.includes('virtual_search_root');

        const shouldShowHl = (!!S.search && (!isGlobalSearchHistoryPresent || isAtGlobalSearchRoot));

        let pathColWidth = 200, pathCharCapacity = 30;
        if (UI.win) {
            const charWidth = isMax ? 9.2 : 8;
            const nameColEl = UI.win.querySelector('.pk-grid-hd .pk-col[data-k="name"]');
            if (nameColEl) {
                nameColWidth = nameColEl.offsetWidth;
                charCapacity = Math.floor((nameColWidth - 80) / charWidth);
            }
            const pathColEl = UI.win.querySelector('.pk-grid-hd .pk-col[data-k="path"]');
            if (pathColEl) {
                pathColWidth = pathColEl.offsetWidth;
                pathCharCapacity = Math.floor((pathColWidth - 20) / charWidth);
            }
        }

        const getTooltipHlHTML = (text, query) => {
            if (!query || !shouldShowHl) return esc(text);
            const lowText = text.toLowerCase();
            const q = query.toLowerCase();
            const idx = lowText.indexOf(q);
            if (idx === -1) return esc(text);
            return esc(text.substring(0, idx)) + `<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">${esc(text.substring(idx, idx + q.length))}</b>` + getTooltipHlHTML(text.substring(idx + q.length), query);
        };

        const getSearchHlHTML = (name, query, capacity) => {
            const q = query.toLowerCase();
            const idx = name.toLowerCase().indexOf(q);
            if (idx === -1) return esc(name);

            let start = 0, end = name.length, prefix = "", suffix = "";
            if (name.length > capacity) {
                const preLimit = Math.floor(capacity * 0.3);
                start = Math.max(0, idx - preLimit);
                end = Math.min(name.length, start + capacity);
                if (start > 0) prefix = "...";
                if (end < name.length) suffix = "...";
            }

            const targetSlice = name.substring(start, end);
            return prefix + getTooltipHlHTML(targetSlice, query) + suffix;
        };

        const _internalGetStarIcon = (isStarred) => {
            const color = isStarred ? '#FFC107' : '#ccc';
            const fill = isStarred ? '#FFC107' : 'none';
            return `<svg class="pk-star-toggle" width="16" height="16" viewBox="0 0 24 24" fill="${fill}" stroke="${color}" stroke-width="2" style="cursor:pointer; transition: all 0.2s;">
                <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
            </svg>`;
        };

        const getRemainingDays = (dateStr) => {
            if (!dateStr) return "-";
            const deleteTime = new Date(dateStr).getTime();
            const now = getServerNow();
            const diffMs = now - deleteTime;
            const daysPassed = diffMs / (1000 * 60 * 60 * 24);
            let left = Math.max(0, Math.min(15, Math.ceil(15 - daysPassed)));
            return left + " " + L.unit_days;
        };

        const renderIndices = dupGridMeta ? visibleIndices : null;
        const renderCount = dupGridMeta ? renderIndices.length : Math.max(0, end - start);

        for (let seqIdx = 0; seqIdx < renderCount; seqIdx++) {
            const i = dupGridMeta ? renderIndices[seqIdx] : (start + seqIdx);
            const d = S.display[i];
            if (!d) continue;

            const row = rowPool[poolPtr++];
            const prevBoundId = row.dataset.pkBoundId || '';
            const prevBoundKind = row.dataset.pkBoundKind || '';
            const prevGridMediaMount = isGridMode ? row.querySelector('.pk-gv-media-mount') : null;
            const prevGridMediaNode = prevGridMediaMount ? prevGridMediaMount.firstElementChild : null;
            const prevGridMediaKey = prevGridMediaMount ? (prevGridMediaMount.dataset.pkMediaKey || '') : '';

            if (isGridMode && prevBoundId && prevGridMediaNode && typeof stashFrozenGridMediaNode === 'function') {
                stashFrozenGridMediaNode(prevBoundId, prevGridMediaKey, prevGridMediaNode, prevBoundKind);
            }

            resetPooledRow(row);

            const dupLayout = dupGridMeta ? dupGridMeta.indexLayout.get(i) : null;

            if (dupLayout) {
                row.style.top = `${dupLayout.top}px`;
                row.style.left = `${dupLayout.left}px`;
                row.style.width = `${dupLayout.width}px`;
                row.style.height = `${dupLayout.height}px`;

                if (dupLayout.kind === 'header') {
                    row.style.padding = isMax ? '0 20px' : '0 16px';
                    row.style.display = 'grid';
                    row.style.gridTemplateColumns = colDef;
                    row.style.zIndex = '2';
                    row.style.boxSizing = 'border-box';
                } else {
                    row.style.padding = '0';
                    row.style.display = 'block';
                    row.style.gridTemplateColumns = '';
                    row.style.zIndex = '1';
                }
            } else if (isGridMode && gridLayout) {
                const rowIdx = Math.floor(i / gridLayout.cols);
                const colIdx = i % gridLayout.cols;
                row.style.top = `${gridLayout.gapY + rowIdx * CONF.rowHeight}px`;
                row.style.left = `${gridLayout.padX + colIdx * (gridLayout.cardWidth + gridLayout.gapX)}px`;
                row.style.width = `${gridLayout.cardWidth}px`;
                row.style.height = `${gridLayout.cardHeight}px`;
                row.style.padding = '0';
                row.style.display = 'block';
            } else {
                row.style.top = `${i * CONF.rowHeight}px`;
                row.style.left = '0';
                row.style.width = '100%';
                row.style.gridTemplateColumns = colDef;
                row.style.height = '';
                row.style.padding = '';
                row.style.display = 'grid';
            }

            if (d.isHeader) {
                row.className = 'pk-group-hd';

                if (i === 0) {
                    row.style.setProperty('border-top', 'none', 'important');
                    const borderW = isMax ? 10 : 4;
                    row.style.setProperty('padding-top', borderW + 'px', 'important');
                } else {
                    row.style.removeProperty('border-top');
                    row.style.removeProperty('padding-top');
                }

                if (dupGridMeta) row.style.gridColumn = "";
                else if (S.trashMode) row.style.gridColumn = "1 / 7";
                else if (S.dupMode || S.analyzeMode) row.style.gridColumn = "1 / 8";
                else row.style.gridColumn = "1 / 7";

                const gIdx = parseInt(d.id.replace('grp_', ''));
                const groupData = S.dupMode ? S.dupRawGroups[gIdx] : (S.analyzeMode ? S.analyzeSimGroups[gIdx] : null);
                const groupItemIds = Array.isArray(d.groupIds) ? d.groupIds.slice() : (groupData ? groupData.ids : []);
                row._pkGroupIds = groupItemIds.slice();

                let selectedInGroup = 0;
                groupItemIds.forEach(id => { if (S.isSelected(id)) selectedInGroup++; });

                const isAllSelected = groupItemIds.length > 0 && selectedInGroup === groupItemIds.length;
                const isIndeterminate = selectedInGroup > 0 && selectedInGroup < groupItemIds.length;

                let groupIcon = CONF.dupHashSVG;
                const isContain = d.name.includes(L.lbl_containment);
                const isSimLike = d.type === L.tag_sim || d.name.includes(L.lbl_sim_score);
                const isNameLike = d.type === L.tag_name || (S.analyzeMode && d.name.includes(' | ') && !isSimLike && !isContain);

                if (isContain) {
                    groupIcon = CONF.dupContainSVG;
                } else if (isSimLike) {
                    groupIcon = CONF.dupSimSVG;
                } else if (isNameLike) {
                    groupIcon = CONF.dupNameSVG;
                }

                groupIcon = groupIcon.replace(/width:\s*\d+px;?/, 'width:18px;').replace(/height:\s*\d+px;?/, 'height:18px;').replace(/margin-right:\s*\d+px;?/, 'margin:0;');

                let headerName = d.name;
                const headerTip = getTooltipHlHTML(d.name, S.search).replace(/"/g, '&quot;');

                row.style.display = 'flex';
                row.innerHTML = `
                <div style="width:36px; flex-shrink:0; display:flex; justify-content:center; align-items:center; margin-right:10px;">
                            <input type="checkbox" class="pk-grp-chk" ${isAllSelected ? 'checked' : ''} style="width:18px; height:18px; cursor:pointer; margin:0;">
                        </div>
                        <div style="width:30px; flex-shrink:0; display:flex; justify-content:flex-start; align-items:center; margin-right:10px;">
                            <div style="display:flex; color:var(--pk-pri); opacity:0.8; margin-left:-1px;">${groupIcon}</div>
                        </div>
                        <div class="pk-name" style="display:flex; align-items:center; overflow:hidden; white-space:nowrap; flex:1; min-width:0;" data-pk-tip="${headerTip}">
                    <span style="font-weight:600; color:var(--pk-fg); opacity:0.9; overflow:hidden; text-overflow:ellipsis;">${esc(headerName)}</span>
                </div>
                <div style="margin-left:auto; display:flex; align-items:center; flex-shrink:0; gap:8px;"></div>
                </div>
                `;

                const grpChk = row.querySelector('.pk-grp-chk');
                if (grpChk) {
                    grpChk.indeterminate = isIndeterminate;
                    grpChk.onclick = (e) => {
                        e.stopPropagation();
                        const targetState = grpChk.checked;
                        groupItemIds.forEach(id => {
                            S.setSelected(id, targetState);
                        });
                        renderVisible();
                        updateStat();
                    };
                }
            } else {
                const isSel = S.isSelected(d.id);
                const isFocused = S.activeId === d.id;

                const isMoving = S.movingIds && S.movingIds.has(d.id);

                row.className = getRowClassName(isSel, isFocused, isMoving);
                row.ondragstart = (e) => e.preventDefault();

                if (isFocused && !isSel) {
                    row.style.backgroundColor = 'var(--pk-sel-bg)';
                    row.style.border = '1px solid var(--pk-pri)';
                    row.style.borderRadius = isGridMode ? `${getGridCardRadius()}px` : '4px';
                }

                if (isMoving) {
                    row.style.opacity = '0.4';
                    row.style.filter = 'grayscale(100%)';
                    row.style.pointerEvents = 'none';
                    row.style.cursor = 'wait';
                } else {
                    row.style.opacity = '';
                    row.style.filter = '';
                    row.style.pointerEvents = '';
                    row.style.cursor = '';
                }

                row.dataset.id = d.id;

                const isProtected = !S.trashMode && isSystemItem(d);
                const isMax = UI.win.classList.contains('pk-maximized');
                const nameTip = getTooltipHlHTML(d.name, S.search).replace(/"/g, '&quot;');

                const getDynamicIcon = (item) => {
                    let isBlacklisted = false;
                    const cleanName = (item.name || "").replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim().toLowerCase();
                    if (item.kind === 'drive#folder') {
                        isBlacklisted = S.blFolderSet && S.blFolderSet.has(cleanName);
                    } else {
                        isBlacklisted = S.blSet && S.blSet.has(cleanName);
                    }
                    const blHtml = isBlacklisted ? `<div class="pk-bl-marker" data-bl="active">${CONF.icons.blMarker}</div>` : '';

                    if (S.uploadMode) {
                        if (item.status === 'DONE' && item.file && item.mime_type && item.mime_type.startsWith('image/')) {
                            if (!item._localThumbUrl) {
                                try { item._localThumbUrl = URL.createObjectURL(item.file); } catch(e) {}
                            }
                            if (item._localThumbUrl) item.thumbnail_link = item._localThumbUrl;
                        }

                        const hasReadyThumb = item.status === 'DONE' && item.thumbnail_link && item.thumbnail_link !== item.icon_link;
                        if (!hasReadyThumb) {
                            const scriptIcon = getIcon(item);
                            if (isMax) {
                                const boxStyle = "width:54px; min-width:54px; height:100%; display:flex; align-items:center; justify-content:flex-start !important; margin-right:12px; position:relative;";
                                return `<div class="pk-max-icon-box" style="${boxStyle}"><div style="transform: translateX(-6px); display:flex;">${scriptIcon}</div>${blHtml}</div>`;
                            }
                            return `<div class="pk-min-icon" style="width:24px; height:24px; display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; margin-right:12px; flex-shrink:0; position:relative;">
                                <div style="display:flex; transform: translateX(4px) scale(0.96);">${scriptIcon}</div>
                                ${blHtml}
                            </div>`;
                        }
                    }

                    if (!window.pkGlobalThumbCache) window.pkGlobalThumbCache = new Set();
                    const isFolder = item.kind === 'drive#folder';
                    const isTask = item.kind === 'drive#task';
                    const isUploadTask = item.kind === 'pk#upload';
                    const lookupId = (isTask || isUploadTask) ? item.file_id : item.id;
                    let hasValidCover = !!(item.thumbnail_link && item.thumbnail_link !== item.icon_link);

                    if (!window.pkRecentMetaCache) window.pkRecentMetaCache = new Map();
                    if (S.recentMode && !isFolder) {
                        if (window.pkRecentMetaCache.has(item.id)) {
                            const meta = window.pkRecentMetaCache.get(item.id);
                            item.thumbnail_link = meta.thumbnail_link || item.thumbnail_link;
                            item.icon_link = meta.icon_link || item.icon_link;
                            item.mime_type = meta.mime_type || item.mime_type;
                            if (meta.medias) item.medias = meta.medias;
                            item.params = Object.assign(item.params || {}, meta.params || {});
                            hasValidCover = !!(item.thumbnail_link && item.thumbnail_link !== item.icon_link);
                        } else if (!hasValidCover && !item._metaFetching) {
                            item._metaFetching = true;
                            const ext = (item.name || '').split('.').pop().toLowerCase();
                            const mime = (item.mime_type || '').toLowerCase();
                            const isLikelyMedia = mime.startsWith('video/') || mime.startsWith('image/') ||['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp','jpg','jpeg','png','gif','bmp','webp','heic','svg','tif','tiff','ico'].includes(ext);

                            if (isLikelyMedia) {
                                apiGet(item.id).then(meta => {
                                    if (meta) {
                                        window.pkRecentMetaCache.set(item.id, meta);
                                        item.thumbnail_link = meta.thumbnail_link || item.thumbnail_link;
                                        item.icon_link = meta.icon_link || item.icon_link;
                                        item.mime_type = meta.mime_type || item.mime_type;
                                        if (meta.medias) item.medias = meta.medias;
                                        item.params = Object.assign(item.params || {}, meta.params || {});
                                        requestAnimationFrame(() => {
                                            if (typeof renderVisible === 'function') renderVisible();
                                        });
                                    }
                                }).catch(()=>{});
                            }
                        }
                    }

                    let forceDeepScan = false;
                    if (item._coverResolved && !hasValidCover) {
                        forceDeepScan = true;
                    }

                    if ((isFolder || isTask || isUploadTask) && lookupId && (!hasValidCover || forceDeepScan) && typeof globalCache !== 'undefined') {
                        const normalize = (data) => (data && !Array.isArray(data) && data.items) ? data.items : data;

                        const scanDeepCover = (targetId, depth) => {
                            if (depth > 5) return null;
                            const raw = globalCache.get(targetId);
                            if (!raw) return null;

                            const files = normalize(raw);
                            if (!files || files.length === 0) return null;

                            const vid = files.find(f => f.mime_type?.startsWith('video/') && f.thumbnail_link);
                            if (vid) return vid.thumbnail_link;

                            const img = files.find(f => f.mime_type?.startsWith('image/') && f.thumbnail_link);
                            if (img) return img.thumbnail_link;

                            const subFolders = files.filter(f => f.kind === 'drive#folder');
                            for (const sub of subFolders) {
                                if (globalCache.has(sub.id)) {
                                    const childThumb = scanDeepCover(sub.id, depth + 1);
                                    if (childThumb) return childThumb;
                                    continue;
                                }
                                if (sub.thumbnail_link && sub.thumbnail_link !== sub.icon_link && !sub._coverResolved) {
                                    return sub.thumbnail_link;
                                }
                                const childThumb = scanDeepCover(sub.id, depth + 1);
                                if (childThumb) return childThumb;
                            }
                            return null;
                        };

                        const foundThumb = scanDeepCover(lookupId, 0);

                        if (foundThumb) {
                            item.thumbnail_link = foundThumb;
                            item._coverResolved = true;
                            item._isFolderLike = true;
                            hasValidCover = true;

                            if (typeof window.markStableFolderMediaState === 'function') {
                                window.markStableFolderMediaState(item.id || lookupId, 'ok', foundThumb);
                            } else {
                                if (!window.pkThumbTriState) window.pkThumbTriState = { folder: Object.create(null) };
                                if (!window.pkGridMediaStore) window.pkGridMediaStore = { folder: Object.create(null) };
                                if (!window.pkGlobalThumbCache) window.pkGlobalThumbCache = new Set();
                                window.pkThumbTriState.folder[item.id || lookupId] = 'ok';
                                window.pkGridMediaStore.folder[item.id || lookupId] = foundThumb;
                                window.pkGlobalThumbCache.add(item.id || lookupId);
                            }
                        } else if (item._coverResolved || (isFolder && globalCache.has(lookupId))) {
                            item.thumbnail_link = null;
                            item._coverResolved = false;
                            item._isFolderLike = false;
                            hasValidCover = false;

                            if (typeof window.markStableFolderMediaState === 'function') {
                                window.markStableFolderMediaState(item.id || lookupId, 'fail');
                            } else if (window.pkThumbTriState && window.pkThumbTriState.folder) {
                                window.pkThumbTriState.folder[item.id || lookupId] = 'fail';
                                if (window.pkGridMediaStore && window.pkGridMediaStore.folder) delete window.pkGridMediaStore.folder[item.id || lookupId];
                            }
                        }
                        else if ((isFolder || isTask || isUploadTask) && typeof scannedFolderIds !== 'undefined' && !scannedFolderIds.has(lookupId)) {
                            if (!S.loading && !S.scanning) {
                                scannedFolderIds.add(lookupId);
                                backgroundQueue.push({ id: lookupId, name: 'DeepCoverProbe', retryCount: 0 });
                                if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
                            }
                        }
                    }

                    const iconHtml = getIcon(item);
                    const boxStyle = "width:54px; min-width:54px; height:100%; display:flex; align-items:center; justify-content:flex-start !important; margin-right:12px; position:relative;";
                    const placeholderStyle = `position: absolute; left: 0; top: 50%; transform: translateY(-50%); z-index: 1; width: 100%; display: flex; align-items: center; transition: opacity 0.3s; pointer-events: none;`;
                    const imgStyle = "width: 48px; height: 48px; object-fit: cover; border-radius: 4px; margin: 0 !important; background: transparent; position: relative; z-index: 2; transition: opacity 0.3s;";
                    const isDarkTheme = document.body.classList.contains('dark') || document.documentElement.getAttribute('data-theme') === 'dark';
                    const badgeBg = isDarkTheme ? '#303134' : '#FFFFFF';
                    const badgeBorder = isDarkTheme ? '1px solid rgba(255,255,255,0.1)' : '1px solid rgba(0,0,0,0.08)';
                    const badgeShadow = isDarkTheme ? '0 2px 6px rgba(0,0,0,0.4)' : '0 2px 6px rgba(0,0,0,0.15)';
                    const badgeStyle = `position: absolute; bottom: -5px; right: -5px; z-index: 3; width: 24px; height: 24px; border-radius: 50%; background-color: ${badgeBg}; border: ${badgeBorder}; box-shadow: ${badgeShadow}; box-sizing: border-box; transition: opacity 0.3s; pointer-events: none; display: grid; place-items: center; line-height: 0;`;
                    const isBlur = isBlurEnabledForView(isGridView() ? 'grid' : 'list');
                    if (isMax && isBlur) {
                        const fallbackSvg = iconHtml.replace(/"/g, "&quot;").replace(/\n/g, "");
                        return `<div class="pk-max-icon-box" style="${boxStyle}">
                            <img src="${item.icon_link}" style="${imgStyle} opacity:1; object-fit:contain;" draggable="false"
                                 onerror="this.outerHTML='<div class=&quot;pk-placeholder-layer&quot; style=&quot;${placeholderStyle} opacity:1;&quot;>${fallbackSvg}</div>'">
                            ${blHtml}
                        </div>`;
                    }

                    if (isMax && item.thumbnail_link && !item.isHeader && item.thumbnail_link !== item.icon_link) {
                        const isCached = window.pkGlobalThumbCache.has(item.id);
                        const phOp = isCached ? '0' : '1';
                        const imgOp = isCached ? '1' : '0';

                        const placeholderContent = `<img src="${item.icon_link}" style="width:44px; height:44px; object-fit:contain;">`;

                        const isFolderLike = isFolder || item._isFolderLike || (item.icon_link && item.icon_link.includes('folder'));
                        const badgeOp = (isFolderLike && isCached) ? '1' : '0';

                        let badgeHtml = '';
                        if (isFolderLike) {
                            const innerContent = `<img src="${item.icon_link}" style="width:16px; height:16px; object-fit:contain; display:block;">`;
                            badgeHtml = `<div class="pk-folder-badge" style="${badgeStyle} opacity: ${badgeOp};">${innerContent}</div>`;
                        }

                        const isVideo = !isFolder && (typeof isGridVideoItem === 'function' ? isGridVideoItem(item) : (item.mime_type && item.mime_type.startsWith('video/')));
                        let videoOvHtml = '';
                        if (isVideo) {
                            const vOp = isCached ? '1' : '0';
                            videoOvHtml = `
                                <div class="pk-video-ov" style="position:absolute; top:0; bottom:0; left:0; right:0; z-index:4; pointer-events:none; opacity:${vOp}; transition:opacity 0.3s; display:flex !important; align-items:center !important; justify-content:center !important;">
                                    <svg viewBox="0 0 24 24" fill="rgba(255, 255, 255, 0.6)" style="width:24px !important; height:24px !important; flex-shrink:0; display:block; filter:drop-shadow(0 1px 3px rgba(0,0,0,0.5)); transform:translateX(-1.5px); margin:0 !important; padding:0 !important;">
                                        <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/>
                                    </svg>
                                </div>`;
                        }

                        return `
                                <div class="pk-max-icon-box" style="${boxStyle}">
                                    <div class="pk-placeholder-layer" style="${placeholderStyle} opacity: ${phOp};"><img src="${item.icon_link}" style="width:50px; height:50px; object-fit:contain;"></div>
                                    <img src="${item.thumbnail_link}"
                                         class="pk-max-thumb"
                                         style="${imgStyle} opacity: ${imgOp};"
                                         draggable="false">
                                    ${badgeHtml}
                                    ${videoOvHtml}
                                    ${blHtml}
                                </div>
                            `;
                    }

                    if (isMax) {
                        if (hasValidCover) {
                            const showBadge = isFolder || item._isFolderLike || (item.icon_link && item.icon_link.includes('folder'));
                            let badgeHtml = '';
                            if (showBadge) {
                                const innerIcon = `<img src="${item.icon_link}" style="width:16px; height:16px; object-fit:contain; display:block;">`;
                                badgeHtml = `<div class="pk-folder-badge" style="${badgeStyle} opacity:1;">${innerIcon}</div>`;
                            }

                            return `
                                <div class="pk-max-icon-box" style="${boxStyle}">
                                    <div class="pk-placeholder-layer" style="${placeholderStyle} opacity:0;"><img src="${item.icon_link}" style="width:50px;height:50px;object-fit:contain;"></div>
                                    <img src="${item.thumbnail_link}" class="pk-max-thumb" style="${imgStyle} opacity:1;" draggable="false">
                                    ${badgeHtml}
                                    ${blHtml}
                                </div>
                            `;
                        }
                        const finalIconSrc = item.icon_link || item.thumbnail_link;
                        if (finalIconSrc) {
                            return `<div class="pk-max-icon-box" style="${boxStyle}"><img src="${finalIconSrc}" style="width:50px; height:50px; object-fit:contain;" onerror="this.outerHTML='<div style=&quot;transform:translateX(-6px);display:flex;&quot;>${iconHtml.replace(/"/g, "&quot;").replace(/\n/g, "")}</div>'">${blHtml}</div>`;
                        }
                        return `<div class="pk-max-icon-box" style="${boxStyle}"><div style="transform: translateX(-6px); display:flex;">${iconHtml}</div>${blHtml}</div>`;
                    }

                    const mime = (item.mime_type || '').toLowerCase();
                    const isMedia = item.kind !== 'drive#folder' && (mime.startsWith('image/') || mime.startsWith('video/') || (item.params && item.params.duration > 0));
                    const hasRealThumb = isMedia && item.thumbnail_link && item.thumbnail_link !== item.icon_link;

                    if (hasRealThumb) {
                        if (!window.pkGlobalThumbCache) window.pkGlobalThumbCache = new Set();
                        const isCached = window.pkGlobalThumbCache.has(item.id);
                        const phOp = isCached ? '0' : '1';
                        const imgOp = isCached ? '1' : '0';

                        const placeholder = item.icon_link ? `<img src="${item.icon_link}" style="width:100%;height:100%;object-fit:contain;border-radius:4px;pointer-events:none;">` : iconHtml;
                        return `<div class="pk-min-media-box" style="width:24px;height:24px;margin-right:12px;position:relative;flex-shrink:0;display:inline-flex;vertical-align:middle;overflow:visible !important;border-radius:4px;">
                            <div style="position:absolute;inset:0;overflow:hidden;border-radius:4px;z-index:1;">
                                <div class="pk-min-ph" style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:1;transition:opacity 0.2s;opacity:${phOp};">${placeholder}</div>
                                <img src="${item.thumbnail_link}" class="pk-min-thumb" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:${imgOp};z-index:2;transition:opacity 0.2s;"
                                     onload="if(window.pkGlobalThumbCache){window.pkGlobalThumbCache.add('${item.id}');}this.style.opacity='1';this.previousElementSibling.style.opacity='0';"
                                     onerror="this.previousElementSibling.style.opacity='1';this.remove();">
                            </div>
                            ${blHtml}
                        </div>`;
                    }

                    if (item.icon_link) {
                        return `<div class="pk-min-icon" style="position:relative; display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; margin-right:12px; flex-shrink:0; overflow:visible;"><img draggable="false" src="${item.icon_link}" style="width:24px; height:24px; object-fit:contain; border-radius:4px;" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.style.display='inline-flex';"><span style="display:none; align-items:center; justify-content:center;">${iconHtml}</span>${blHtml}</div>`;
                    }
                    return `<div class="pk-min-icon" style="position:relative; display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; margin-right:12px; flex-shrink:0; overflow:visible;">${iconHtml}${blHtml}</div>`;

                };

                const checkboxHtml = `<input type="checkbox" ${isSel ? 'checked' : ''}>`;
                let html = `<div>${checkboxHtml}</div>`;

                if (isGridMode && gridLayout) {
                    getDynamicIcon(d);
                    const isFolder = d.kind === 'drive#folder';
                    const displayDate = fmtDate(d.modified_time);
                    const isVideo = !isFolder && (((d.mime_type || '').toLowerCase().startsWith('video/')) || (d.params && d.params.duration > 0));
                    const displayDur = isVideo ? (d._history_duration || d.params?.duration || 0) : 0;

                    let folderItemCount = null;
                    if (isFolder && !S.trashMode) {
                        let count = d.file_count;

                        if (count === undefined || count === null) count = d.usage?.file_count;
                        if (count === undefined || count === null) count = d.params?.file_count;
                        if (count === undefined || count === null) count = d.audit?.file_count;

                        if ((count === undefined || count === null) && typeof globalCache !== 'undefined') {
                            const cachedData = globalCache.get(d.id);
                            if (cachedData) {
                                const list = Array.isArray(cachedData) ? cachedData : (cachedData.items || []);
                                count = list.length;
                            }
                        }

                        if (count !== undefined && count !== null && count !== '') {
                            const parsed = parseInt(count, 10);
                            if (!Number.isNaN(parsed)) folderItemCount = Math.max(0, parsed);
                        }
                    }

                    const displayFolderCount = folderItemCount === null ? '' : L.grid_folder_count.replace('{n}', folderItemCount);
                    const hasThumb = !!(d.thumbnail_link && d.thumbnail_link !== d.icon_link);


                    const isStarred = S.starredSet.has(d.id) || !!(d.starred || (d.tags && d.tags.some(t => t.name === 'STAR')));
                    const nameDisplay = (S.search && shouldShowHl) ? getSearchHlHTML(d.name, S.search, 160) : esc(d.name);
                    const coverCls = `pk-gv-cover ${isFolder ? 'pk-gv-folder' : 'pk-gv-file'}${hasThumb ? ' pk-gv-has-thumb' : ''}${isBlur && hasThumb ? ' pk-gv-blur' : ''}`;
                    const iconFallback = getIcon(d).replace(/width="\d+"/g, 'width="108"').replace(/height="\d+"/g, 'height="108"');
                    const menuIcon = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="5.5" r="1.8"></circle><circle cx="12" cy="12" r="1.8"></circle><circle cx="12" cy="18.5" r="1.8"></circle></svg>`;
                    ensureGridMediaStore();

                    const gridFileFallbackHtml = `<div class="pk-gv-icon">${iconFallback}</div>`.replace(/"/g, '&quot;').replace(/\n/g, '');

                    const stableFolderMedia = hasThumb ? getStableFolderMediaState(d) : { state: 'none', src: '' };
                    let folderThumbState = stableFolderMedia.state;
                    let folderResolvedSrc = stableFolderMedia.src || d.thumbnail_link || '';

                    if (hasThumb && d.kind === 'drive#folder' && folderResolvedSrc && d._coverResolved && folderThumbState !== 'ok') {
                        if (typeof window.markStableFolderMediaState === 'function') {
                            window.markStableFolderMediaState(d.id, 'ok', folderResolvedSrc);
                        } else {
                            if (!window.pkThumbTriState) window.pkThumbTriState = { folder: Object.create(null) };
                            if (!window.pkGridMediaStore) window.pkGridMediaStore = { folder: Object.create(null) };
                            if (!window.pkGlobalThumbCache) window.pkGlobalThumbCache = new Set();
                            window.pkThumbTriState.folder[d.id] = 'ok';
                            window.pkGridMediaStore.folder[d.id] = folderResolvedSrc;
                            window.pkGlobalThumbCache.add(d.id);
                        }
                        folderThumbState = 'ok';
                    }

                    const folderHasCoverThumb = hasThumb && folderThumbState === 'ok' && !!folderResolvedSrc;

                    if (hasThumb && folderThumbState === 'unknown' && !folderResolvedSrc) {
                        window.pkThumbTriState.folder[d.id] = 'probing';
                    }

                    const makeFolderFallbackHtml = (display = 'flex', opacity = '1') => d.icon_link
                    ? `<span class="pk-gv-folder-base" style="display:${display};align-items:center;justify-content:center;width:100%;height:100%;opacity:${opacity};transition:opacity .18s ease;"><img src="${d.icon_link}" class="pk-gv-folder-fallback" draggable="false" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.style.display='inline-flex';"><span class="pk-gv-folder-fallback" style="display:none;">${iconFallback}</span></span>`
                    : `<span class="pk-gv-folder-base" style="display:${display};align-items:center;justify-content:center;width:100%;height:100%;opacity:${opacity};transition:opacity .18s ease;"><span class="pk-gv-folder-fallback">${iconFallback}</span></span>`;

                    const folderPreviewHtml = folderHasCoverThumb
                    ? `<img src="${folderResolvedSrc}" class="pk-gv-folder-fallback" draggable="false" data-pk-id="${d.id}" data-pk-src="${folderResolvedSrc}" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:1;" onload="if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='ok';}" onerror="if(this.dataset.pkId&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'fail');}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='fail';}this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='flex';">${makeFolderFallbackHtml('none','1')}`
                    : (hasThumb
                       ? `${makeFolderFallbackHtml('flex','1')}<img src="${d.thumbnail_link}" class="pk-gv-folder-fallback" draggable="false" data-pk-id="${d.id}" data-pk-src="${d.thumbnail_link}" style="position:absolute;inset:0;width:100% !important;height:100% !important;max-width:none !important;max-height:none !important;object-fit:cover !important;border-radius:0 !important;z-index:2;opacity:0;transition:opacity .18s ease;" onload="if(this.dataset.pkId&&this.dataset.pkSrc&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'ok',this.dataset.pkSrc);}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='ok';if(window.pkGlobalThumbCache)window.pkGlobalThumbCache.add(this.dataset.pkId);}this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.opacity='0';" onerror="if(this.dataset.pkId&&window.markStableFolderMediaState){window.markStableFolderMediaState(this.dataset.pkId,'fail');}else if(window.pkThumbTriState&&window.pkThumbTriState.folder&&this.dataset.pkId){window.pkThumbTriState.folder[this.dataset.pkId]='fail';}this.remove();">`
                       : makeFolderFallbackHtml('flex','1'));

                    const gridIconFallbackHtml = iconFallback.replace(/"/g, '&quot;').replace(/\n/g, '');

                    const showFolderAnalyzeSize = isFolder && S.analyzeMode && !S.analyzeSimGroups && d.size !== undefined && d.size !== null && d.size !== '';
                    const showFolderDedupeSize = isFolder && S.analyzeMode && !!S.analyzeSimGroups && d.size !== undefined && d.size !== null && d.size !== '';
                    const folderSizeHtml = (showFolderAnalyzeSize || showFolderDedupeSize) ? `<span class="pk-gv-meta-item pk-gv-size">${fmtSize(d.size)}</span><span class="pk-gv-meta-sep"></span>` : '';
                    const metaHtml = isFolder
                    ? `<div class="pk-gv-meta"><span class="pk-gv-date-wrap">${folderSizeHtml}<span class="pk-gv-meta-item pk-gv-date">${displayDate}</span>${displayFolderCount ? `<span class="pk-gv-meta-sep"></span><span class="pk-gv-meta-item pk-gv-fcount">${displayFolderCount}</span>` : ''}</span></div>`
                    : `<div class="pk-gv-meta"><span class="pk-gv-meta-item pk-gv-size">${fmtSize(d.size)}</span><span class="pk-gv-meta-sep"></span><span class="pk-gv-date-wrap"><span class="pk-gv-meta-item pk-gv-date">${displayDate}</span>${displayDur > 0 ? `<span class="pk-gv-meta-sep"></span><span class="pk-gv-meta-item pk-gv-dur">${fmtDur(displayDur)}</span>` : ''}</span></div>`;
                    const cleanName = (d.name || "").replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim().toLowerCase();
                    const isBlMarked = isFolder ? (S.blFolderSet && S.blFolderSet.has(cleanName)) : (S.blSet && S.blSet.has(cleanName));
                    const gridBlHtml = isBlMarked ? `<span class="pk-gv-bl" data-bl="active">${CONF.icons.blMarker}</span>` : '';
                    const videoPlayDisplay = 'none';
                    html = `
                    <div class="pk-grid-card-body">
                            <label class="pk-gv-check">${checkboxHtml}</label>
                            <button class="pk-gv-more" type="button">${menuIcon}</button>
                            <div class="${coverCls}">
                                <div class="pk-gv-media-mount" data-pk-id="${d.id}" data-pk-media-key="" data-pk-icon-fallback="${gridIconFallbackHtml}" data-pk-file-fallback="${gridFileFallbackHtml}"></div>
                                ${isVideo && hasThumb ? `<div class="pk-gv-play" style="display:${videoPlayDisplay};"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 6.5v11l9-5.5-9-5.5z"></path></svg></div>` : ''}
                                ${isStarred ? `<span class="pk-gv-fav${isFolder ? ' pk-gv-fav-folder' : ''}">${_internalGetStarIcon(true).replace('pk-star-toggle', 'pk-star-icon')}</span>` : ''}
                                ${gridBlHtml}
                            </div>
                            <div class="pk-gv-info">
                                <div class="pk-name">
                                    <div class="pk-name-main">
                                        <span class="pk-name-txt">${nameDisplay}</span>
                                        ${isProtected ? `<span class="pk-tag-default">${L.tag_default}</span>` : ''}
                                    </div>
                                </div>
                                ${metaHtml}
                            </div>
                        </div>
                    `;
                } else if (S.historyMode) {
                    const isStarred = S.starredSet.has(d.id) || !!(d.starred || (d.tags && d.tags.some(t => t.name === 'STAR')));
                    const starColor = isStarred ? '#FFC107' : '#ccc';
                    const starFill = isStarred ? '#FFC107' : 'none';
                    html += `<div style="display:flex; align-items:center; justify-content:center;"><svg class="pk-star-icon" width="16" height="16" viewBox="0 0 24 24" fill="${starFill}" stroke="${starColor}" stroke-width="2" style="cursor:default; opacity:0.8;"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path></svg></div>`;
                    let iconImg = getDynamicIcon(d);
                    if (isMax) {
                        if (iconImg.includes('margin-right:12px')) iconImg = iconImg.replace('margin-right:12px', 'margin-right:20px');
                        else if (!iconImg.includes('margin-right')) iconImg = iconImg.replace(/style=['"]/, '$&margin-right:20px; ');
                    }
                    const nameDisplay = (S.search && shouldShowHl) ? getSearchHlHTML(d.name, S.search, charCapacity) : esc(d.name);
                    html += `<div class="pk-name" ${d.thumbnail_link ? `data-pk-thumb="${d.thumbnail_link}"` : ''} data-pk-tip="${nameTip}">
                        ${iconImg}
                        <span class="pk-name-txt">${nameDisplay}</span>
                    </div>`;

                    html += `<div>${fmtSize(d.size)}</div>`;

                    const displayDur = d._history_duration || d.params?.duration || 0;
                    html += `<div>${displayDur > 0 ? fmtDur(displayDur) : '-'}</div>`;

                    let progressHtml = '-';
                    const curT = d._history_progress || 0;
                    const totalT = displayDur;
                    if (totalT > 0) {
                        const pct = Math.min(100, Math.max(0, (curT / totalT) * 100)).toFixed(1);
                        progressHtml = `
                            <div style="width:100%; display:flex; flex-direction:column; justify-content:center; gap:2px;">
                                <div style="display:flex; justify-content:space-between; font-size:13px; color:var(--pk-fg); line-height:1.2; font-weight:normal;">
                                    <span>${fmtDur(curT)}</span>
                                    <span>${parseInt(pct)}%</span>
                                </div>
                                <div style="width:100%; height:4px; background:var(--pk-bd); border-radius:2px; overflow:hidden;">
                                    <div style="width:${pct}%; height:100%; background:var(--pk-pri);"></div>
                                </div>
                            </div>
                        `;
                    }
                    html += `<div style="padding-right:10px;">${progressHtml}</div>`;

                    let playTimeStr = '-';
                    if (d._history_ts > 0) {
                        playTimeStr = fmtDate(new Date(d._history_ts).toISOString());
                    } else {
                        playTimeStr = "-";
                    }
                    html += `<div style="color:var(--pk-fg); font-weight:normal;">${playTimeStr}</div>`;

                } else if (S.offlineMode) {
                    let iconImg = getDynamicIcon(d);
                    if (isMax) {
                        if (iconImg.includes('margin-right:12px')) {
                            iconImg = iconImg.replace('margin-right:12px', 'margin-right:20px');
                        } else if (!iconImg.includes('margin-right')) {
                            iconImg = iconImg.replace(/style=['"]/, '$&margin-right:20px; ');
                        }
                    }
                    const isFailed = d.phase === 'PHASE_TYPE_ERROR';
                    const isNavigable = !!d.file_id && !isFailed;
                    const isTaskDone = d.phase === 'PHASE_TYPE_COMPLETE';
                    const nameStyle = isNavigable ? 'cursor:pointer; opacity:1;' : 'cursor:default; pointer-events:none; color:inherit; opacity:0.6;';
                    const isMedia = d.mime_type && (d.mime_type.startsWith('video/') || d.mime_type.startsWith('image/'));
                    const hasResolvedCover = d._coverResolved && d.thumbnail_link;
                    const thumbAttr = (isTaskDone && (isMedia || hasResolvedCover)) ? `data-pk-thumb="${d.thumbnail_link}"` : '';
                    const nameDisplay = S.search ? getSearchHlHTML(d.name, S.search, charCapacity) : esc(d.name);
                    html += `<div class="pk-name" ${thumbAttr} data-pk-tip="${nameTip}">
                        ${iconImg}
                        <span class="pk-name-txt" style="${nameStyle}">${nameDisplay}</span>
                    </div>`;

                    html += `<div>${fmtSize(d.size)}</div>`;

                    let statusHtml = '';
                    let statusColor = 'var(--pk-fg)';
                    let statusText = d.phase;

                    if (d.phase === 'PHASE_TYPE_COMPLETE') {
                        statusColor = '#52c41a';
                        statusText = L.lbl_up_done;
                    } else if (d.phase === 'PHASE_TYPE_RUNNING') {
                        statusColor = '#1890ff';
                        statusText = L.lbl_up_downloading;
                    } else if (d.phase === 'PHASE_TYPE_ERROR') {
                        statusColor = '#ff4d4f';
                        statusText = L.str_failed;
                    } else if (d.phase === 'PHASE_TYPE_PENDING') {
                        statusText = L.msg_task_waiting;
                    } else if (d.phase === 'PHASE_TYPE_PAUSED') {
                        statusText = L.msg_task_paused;
                    }

                    if (d.phase === 'PHASE_TYPE_ERROR' && d.message) {
                        statusText = d.message;
                    }

                    html += `<div style="color:${statusColor}; font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${esc(statusText)}">${statusText}</div>`;

                    let progressHtml = '';
                    if (d.phase === 'PHASE_TYPE_COMPLETE') {
                        progressHtml = `<div style="color:#52c41a; font-weight:bold;">100%</div>`;
                    } else if (d.phase === 'PHASE_TYPE_ERROR') {
                        progressHtml = `<div style="color:#ff4d4f;">-</div>`;
                    } else {
                        const pct = d.progress || 0;
                        progressHtml = `
                            <div style="width:100px; height:6px; background:var(--pk-bd); border-radius:3px; overflow:hidden; margin-right:8px;">
                                <div style="width:${pct}%; height:100%; background:var(--pk-pri); transition:width 0.3s;"></div>
                            </div>
                            <div style="font-size:12px; font-variant-numeric:tabular-nums;">${pct}%</div>
                        `;
                    }
                    html += `<div style="display:flex; align-items:center;">${progressHtml}</div>`;

                }
                else if (S.uploadMode) {
                    if (!d.mime_type && d.file) d.mime_type = d.file.type;

                    const nameDisplay = shouldShowHl ? getSearchHlHTML(d.name, S.search, charCapacity) : esc(d.name);
                    const isDone = d.status === 'DONE';
                    const nameStyle = isDone ? '' : 'cursor:default; pointer-events:none; color:inherit;';

                    if (isDone && d.file && d.mime_type && d.mime_type.startsWith('image/')) {
                        if (!d._localThumbUrl) {
                            try { d._localThumbUrl = URL.createObjectURL(d.file); } catch(e) {}
                        }
                        if (d._localThumbUrl) d.thumbnail_link = d._localThumbUrl;
                    }

                    const hasResolvedCover = isDone && d.thumbnail_link && d.thumbnail_link !== d.icon_link;
                    const thumbAttr = hasResolvedCover ? `data-pk-thumb="${d.thumbnail_link}"` : '';

                    html += `<div class="pk-name" ${thumbAttr} data-pk-tip="${nameTip}" style="display:flex; align-items:center; height:100%; overflow:visible;">
                        ${getDynamicIcon(d)}
                        <span class="pk-name-txt" style="margin:0!important; padding:0!important; line-height:1.5; ${nameStyle}">${nameDisplay}</span>
                    </div>`;

                    html += `<div>${fmtSize(d.size)}</div>`;
                    html += `<div class="pk-up-spd">${(d.status === 'UPLOADING' && S.upMng) ? S.upMng.fmtSpeed(d.speed) : '-'}</div>`;

                    let statusColor = '#888';
                    const activeStatus = ['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'];
                    if (activeStatus.includes(d.status)) statusColor = 'var(--pk-pri)';
                    else if (d.status === 'DONE') statusColor = '#52c41a';
                    else if (d.status === 'ERROR') statusColor = '#d93025';
                    else if (d.status === 'PAUSED') statusColor = '#faad14';

                    html += `
                        <div style="display:flex; flex-direction:column; justify-content:center; gap:4px; width:100%; min-width:0;">
                            <div style="display:flex; justify-content:space-between; font-size:12px; min-width:0;">
                                <span class="pk-force-tip" style="color:${statusColor}; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:8px; display:block;" data-pk-tip="${esc(d.message)}">${esc(d.message)}</span>
                                <span class="pk-up-prog-txt" style="flex-shrink:0;">${Math.floor(d.progress)}%</span>
                            </div>
                            <div style="width:100%; height:4px; background:var(--pk-bd); border-radius:2px; overflow:hidden; flex-shrink:0;">
                                <div class="pk-up-prog-bar" style="width:${d.progress}%; height:100%; background:${statusColor}; transition:width 0.2s;"></div>
                            </div>
                        </div>
                    `;
                }
                else if (S.shareMode) {
                    const iconUrl = d.icon_link;
                    let iconImg = '';

                    const isShareDisabled = (d.share_status === 'DELETED') || (d.limit_count > 0 && d.save_count >= d.limit_count);
                    const lockHtml = d.pass_code ? `<div class="pk-share-lock">${CONF.icons.lock}</div>` : '';

                    if (isShareDisabled) {
                        iconImg = `<div class="pk-share-icon-wrap pk-share-icon-disabled" draggable="false" style="opacity:0.6;">${CONF.typeIcons.file}</div>`;
                    } else {
                        const currentIcon = getDynamicIcon(d);
                        const isUsingThumb = currentIcon.includes('pk-max-thumb');

                        if (isUsingThumb) {
                            iconImg = `<div class="pk-share-icon-wrap" style="margin-right:20px !important;">${currentIcon}${lockHtml}</div>`;
                        } else if (iconUrl) {
                            iconImg = `<div class="pk-share-icon-wrap" draggable="false" style="display:flex;align-items:center;justify-content:center;"><img draggable="false" src="${iconUrl}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;">${lockHtml}</div>`;
                        } else {
                            const isFolder = d.kind === 'drive#folder';
                            iconImg = `<div class="pk-share-icon-wrap" draggable="false"><div style="width:100%;height:100%;${isFolder?'':'transform:scale(1.08);'}">${getIcon(d)}</div>${lockHtml}</div>`;
                        }
                    }

                    const nameDisplay = shouldShowHl ? getSearchHlHTML(d.name, S.search, charCapacity) : esc(d.name);
                    html += `<div class="pk-name" data-pk-tip="${nameTip}">
                        ${iconImg}
                        <span class="pk-name-txt" style="${isShareDisabled ? 'cursor:default; pointer-events:none;' : ''}">${nameDisplay}</span>
                    </div>`;

                    html += `<div style="text-align:left; padding-left:2px; font-variant-numeric:tabular-nums;">${d.view_count || 0}</div>`;

                    const saveVal = d.limit_count > 0 ? `${d.save_count || 0}/${d.limit_count}` : (d.save_count || 0);
                    const saveTip = d.limit_count > 0 ? `${L.lbl_limit_tip}: ${d.limit_count}` : '';
                    const saveClass = d.limit_count > 0 ? "pk-force-tip" : "";
                    html += `<div class="${saveClass}" style="text-align:left; padding-left:2px; font-variant-numeric:tabular-nums; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${saveTip}">${saveVal}</div>`;

                    let statusColor = 'inherit';
                    let statusText = '';
                    const timeLeft = d.expiration_left || "";

                    if (d.share_status !== 'OK') {
                        statusColor = '#ff4d4f';
                        statusText = (d.save_count >= d.limit_count && d.limit_count > 0) ? (L.lbl_limit_reached) : (d.share_status_text || d.share_status);
                    } else if (d.expiration_days === "-1" || timeLeft === "-1") {
                        statusColor = '#52c41a';
                        statusText = L.share_perm;
                    } else {
                        statusText = timeLeft + (L.str_expire_suffix);
                        const isUrgent = timeLeft.includes('小时') || timeLeft.includes('hour') || timeLeft.includes('时间') || timeLeft.includes('时间');
                        statusColor = isUrgent ? '#ff4d4f' : 'inherit';
                    }
                    html += `<div style="text-align:center;color:${statusColor};font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${esc(statusText)}">${statusText}</div>`;

                    const shareDate = fmtDate(d.modified_time);
                    html += `<div style="text-align:right;font-size:12px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${shareDate}">${shareDate}</div>`;

                } else {
                    const isRoot = S.path.length === 1 && S.path[0].id === '';
                    const isStarred = S.starredSet.has(d.id) || !!(d.starred || (d.tags && d.tags.some(t => t.name === 'STAR')));
                    const starColor = isStarred ? '#FFC107' : '#ccc';
                    const starFill = isStarred ? '#FFC107' : 'none';

                    let displayPath = rootPathStr;
                    let shortPath = "";

                    if (S.trashMode) {
                        if (d._lineage && Array.isArray(d._lineage)) {
                            shortPath = d._lineage.map(p => p.name).join('/');
                            displayPath = shortPath;
                        }
                    } else if (d._lineage && Array.isArray(d._lineage)) {
                        const relativePath = d._lineage.map(p => p.name).join('/');
                        shortPath = relativePath;

                        if (isGlobalSearchRoot) {
                            displayPath = L.btn_nav_home + (relativePath ? '/' + relativePath : '');
                        } else if (S.analyzeMode) {
                            displayPath = relativePath || L.btn_nav_home;
                        } else if (relativePath) {
                            displayPath = rootPathStr + '/' + relativePath;
                        } else {
                            displayPath = rootPathStr;
                        }
                    } else if (isGlobalSearchRoot) {
                        displayPath = L.btn_nav_home;
                        shortPath = "";
                    }

                    if (S.trashMode) {
                        html += `<div style="display:flex; align-items:center; justify-content:center;"><svg class="pk-star-icon" width="16" height="16" viewBox="0 0 24 24" fill="${starFill}" stroke="${starColor}" stroke-width="2" style="cursor:default; opacity:0.8;"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path></svg></div>`;
                    } else {
                        if (isProtected) html += `<div></div>`;
                        else html += `<div style="display:flex; align-items:center; justify-content:center;"><svg class="pk-star-icon" width="16" height="16" viewBox="0 0 24 24" fill="${starFill}" stroke="${starColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="cursor:default; opacity:0.8;"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg></div>`;
                    }

                    const isRealThumb = d.thumbnail_link && d.thumbnail_link !== d.icon_link;
                    const thumbAttr = isRealThumb ? `data-pk-thumb="${d.thumbnail_link}"` : '';
                    const nameDisplay = (S.search && shouldShowHl) ? getSearchHlHTML(d.name, S.search, charCapacity) : esc(d.name);

                    html += `<div class="pk-name" ${thumbAttr} data-pk-tip="${nameTip}" style="${isProtected ? 'opacity:1;' : ''}">${getDynamicIcon(d)}<span class="pk-name-txt">${nameDisplay}</span>${isProtected ? `<span class="pk-tag-default">${L.tag_default}</span>` : ''}</div>`;

                    if (showPathCol) {
                        let pathHtml = '';
                        const homeIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:4px;flex-shrink:0;vertical-align:-4px;"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>`;
                        const homeText = L.btn_nav_home;
                        let homeQuery = S.search;
                        let restQuery = S.search;
                        let isSlashMatched = false;
                        if (S.search) {
                            const qLower = S.search.toLowerCase();
                            const hLower = homeText.toLowerCase();
                            if (qLower.startsWith(hLower + '/')) {
                                homeQuery = S.search.substring(0, homeText.length);
                                restQuery = S.search.substring(homeText.length + 1);
                                isSlashMatched = true;
                            }
                        }
                        const isHomeMatched = shouldShowHl && homeQuery && homeText.toLowerCase().includes(homeQuery.toLowerCase());
                        const homeDisplay = isHomeMatched ? getSearchHlHTML(homeText, homeQuery, 20) : esc(homeText);
                        const prefix = homeText + '/';
                        const rootStyle = isMax? "display:inline-flex;align-items:baseline;flex-shrink:0;margin-right:2px;": "display:inline-flex;align-items:baseline;flex-shrink:0;line-height:1.2;padding-bottom:0;";
                        const contentStyle = isMax ? '' : 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5;padding-bottom:2px;';
                        const containerStyle = isMax? "display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-all;line-height:1.4;white-space:normal;color:var(--pk-fg);": "display:flex;align-items:center;overflow:hidden;white-space:nowrap;color:var(--pk-fg);line-height:1.5;padding-bottom:2px;";

                        if (isAnalyzeRoot) {
                            const pStr = d._pathStr || d.path || "";
                            displayPath = pStr;
                            let isSameAsPrev = d._isSameFolder || false;
                            if (i > 0) {
                                const prevItem = S.display[i - 1];
                                if (!prevItem.isHeader) {
                                    const prevPStr = prevItem._pathStr || prevItem.path || "";
                                    if (prevPStr === pStr) isSameAsPrev = true;
                                    else isSameAsPrev = false;
                                } else {
                                    isSameAsPrev = false;
                                }
                            } else {
                                isSameAsPrev = false;
                            }
                            if (isSameAsPrev) {
                                pathHtml = `<span style="color:var(--pk-fg);">${L.str_same_folder}</span>`;
                            } else {
                                let content = pStr;
                                if (pStr === homeText) content = "";
                                else if (pStr.startsWith(prefix)) content = pStr.substring(prefix.length);

                                let hq = restQuery;
                                let sm = isSlashMatched;
                                if (hq.endsWith('/')) hq = hq.slice(0, -1);
                                if (hq.startsWith('/') && content.toLowerCase().startsWith(hq.substring(1).toLowerCase())) {
                                    sm = true;
                                    hq = hq.substring(1);
                                }
                                const sDisp = (shouldShowHl && sm) ? `<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">/</b>` : '/';
                                const sHtml = `<span style="margin:0 1px;line-height:1.5;padding-bottom:2px;">${sDisp}</span>`;

                                const includePath = UI.chkSearchPath && UI.chkSearchPath.checked;
                                const pathDisplay = (shouldShowHl && includePath && content)
                                    ? getSearchHlHTML(content, hq, pathCharCapacity)
                                    : esc(content);

                                let innerHtml = `<span style="${rootStyle}">${homeIcon}<span style="${isMax ? '' : 'transform:translateY(-2px);'}">${homeDisplay}</span></span>`;
                                if (content) {
                                    innerHtml += `${sHtml}<span style="${contentStyle}">${pathDisplay}</span>`;
                                }
                                pathHtml = `<div style="${containerStyle}">${innerHtml}</div>`;
                            }
                        }
                        else if (S.dupMode) {
                            let isSameAsPrev = d._isSameFolder || false;
                            const rawPath = d._dupFullPath || "";
                            if (i > 0) {
                                const prevItem = S.display[i - 1];
                                if (!prevItem.isHeader) {
                                    const prevRawPath = prevItem._dupFullPath || "";
                                    if (prevRawPath === rawPath) isSameAsPrev = true;
                                    else isSameAsPrev = false;
                                } else {
                                    isSameAsPrev = false;
                                }
                            } else {
                                isSameAsPrev = false;
                            }

                            if (isSameAsPrev) {
                                pathHtml = `<span style="color:var(--pk-fg);">${L.str_same_folder}</span>`;
                            } else {
                                let realPath = rawPath;

                                if (rawPath === L.current_dir) {
                                    realPath = rootPathStr;
                                } else {
                                    if (!rawPath.startsWith(rootPathStr)) {
                                        realPath = rootPathStr + '/' + rawPath;
                                    }
                                }

                                let contentStr = realPath;
                                if (realPath === homeText) {
                                    contentStr = "";
                                } else if (realPath.startsWith(prefix)) {
                                    contentStr = realPath.substring(prefix.length);
                                }

                                let hq = restQuery;
                                let sm = isSlashMatched;
                                if (hq.endsWith('/')) hq = hq.slice(0, -1);
                                if (hq.startsWith('/') && contentStr.toLowerCase().startsWith(hq.substring(1).toLowerCase())) {
                                    sm = true;
                                    hq = hq.substring(1);
                                }
                                const sDisp = (shouldShowHl && sm) ? `<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">/</b>` : '/';
                                const sHtml = `<span style="margin:0 1px;line-height:1.5;padding-bottom:2px;">${sDisp}</span>`;

                                const includePath = UI.chkSearchPath && UI.chkSearchPath.checked;
                                const pathDisplay = (shouldShowHl && includePath && contentStr)
                                    ? getSearchHlHTML(contentStr, hq, pathCharCapacity)
                                    : esc(contentStr);

                                let innerHtml = `<span style="${rootStyle}">${homeIcon}<span style="${isMax ? '' : 'transform:translateY(-2px);'}">${homeDisplay}</span></span>`;
                                if (contentStr) {
                                    innerHtml += `${sHtml}<span style="${contentStyle}">${pathDisplay}</span>`;
                                }
                                pathHtml = `<div style="${containerStyle}">${innerHtml}</div>`;
                            }
                        } else {
                            let fullPathStr = "";

                            if (d._lineage === null && d.parent_id && d.parent_id !== 'root') {
                                pathHtml = `<span style="color:#ccc; font-size:12px;">...</span>`;
                                displayPath = null;
                            }
                            else if (isGlobalSearchRoot) {
                                fullPathStr = shortPath;
                            } else {
                                if (displayPath && displayPath.startsWith(prefix)) {
                                    fullPathStr = displayPath.substring(prefix.length);
                                } else {
                                    fullPathStr = shortPath;
                                }
                            }
                            let isSameAsPrev = false;
                            if (i > 0) {
                                const prevItem = S.display[i - 1];
                                if (!prevItem.isHeader) {
                                    let prevPathStr = "";
                                    if (S.analyzeMode) {
                                        prevPathStr = prevItem._pathStr || "";
                                    } else {
                                        if (prevItem._lineage) prevPathStr = prevItem._lineage.map(x=>x.name).join('/');
                                    }
                                    if (prevPathStr === shortPath) isSameAsPrev = true;
                                }
                            }
                            if (displayPath !== null) {
                                if (isSameAsPrev) {
                                    pathHtml = `<span style="color:var(--pk-fg);">${L.str_same_folder}</span>`;
                                } else {
                                    let hq = restQuery;
                                    let sm = isSlashMatched;
                                    if (hq.endsWith('/')) hq = hq.slice(0, -1);
                                    if (hq.startsWith('/') && fullPathStr.toLowerCase().startsWith(hq.substring(1).toLowerCase())) {
                                        sm = true;
                                        hq = hq.substring(1);
                                    }
                                    const sDisp = (shouldShowHl && sm) ? `<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">/</b>` : '/';
                                    const sHtml = `<span style="margin:0 1px;line-height:1.5;padding-bottom:2px;">${sDisp}</span>`;

                                    const includePath = UI.chkSearchPath && UI.chkSearchPath.checked;
                                    const pathDisplay = (shouldShowHl && includePath && fullPathStr)
                                        ? getSearchHlHTML(fullPathStr, hq, pathCharCapacity)
                                        : esc(fullPathStr);

                                    let innerHtml = `<span style="${rootStyle}">${homeIcon}<span style="${isMax ? '' : 'transform:translateY(-2px);'}">${homeDisplay}</span></span>`;
                                    if (fullPathStr) {
                                        innerHtml += `${sHtml}<span style="${contentStyle}">${pathDisplay}</span>`;
                                    }
                                    pathHtml = `<div style="${containerStyle}">${innerHtml}</div>`;
                                }
                            }
                        }

                        let pathTipHtml = getTooltipHlHTML(displayPath || "", S.search);
                        if (displayPath && displayPath.startsWith(homeText)) {
                            const rest = displayPath.substring(homeText.length);
                            const homeDisplayTip = getTooltipHlHTML(homeText, homeQuery);

                            let restDisplayTip = '';
                            if (rest.startsWith('/')) {
                                const pureRest = rest.substring(1);
                                let hq = restQuery;
                                let sm = isSlashMatched;
                                if (hq.endsWith('/')) hq = hq.slice(0, -1);
                                if (hq.startsWith('/') && pureRest.toLowerCase().startsWith(hq.substring(1).toLowerCase())) {
                                    sm = true;
                                    hq = hq.substring(1);
                                }
                                const slashTip = (shouldShowHl && sm) ? `<b style="color:var(--pk-match-fg); background:var(--pk-match-bg); border-radius:2px; padding:0 2px;">/</b>` : '/';
                                restDisplayTip = slashTip + getTooltipHlHTML(pureRest, hq);
                            } else {
                                restDisplayTip = getTooltipHlHTML(rest, restQuery);
                            }

                            const homeGroup = `<span style="display:inline-flex;align-items:center;vertical-align:bottom;">${homeIcon}${homeDisplayTip}</span>`;
                            pathTipHtml = `<div style="line-height:1.6;word-break:break-all;">${homeGroup}${restDisplayTip}</div>`;
                        }

                        html += `<div class="pk-path" style="font-size:12px; overflow:hidden; text-overflow:ellipsis; ${isMax ? '' : 'white-space:nowrap;'} padding-right:10px; padding-bottom:2px;" data-pk-tip="${pathTipHtml.replace(/"/g, '&quot;')}">${pathHtml}</div>`;
                    }

                    const displaySize = (d.kind === 'drive#folder' && !S.analyzeMode) ? '-' : fmtSize(d.size);
                    html += `<div>${displaySize}</div>`;

                    if (!isAnalyzeRoot) {
                        const isFolder = d.kind === 'drive#folder';
                        const displayDur = S.durationMap.get(d.id) || d.params?.duration || 0;

                        const mime = (d.mime_type || '').toLowerCase();
                        const ext = (d.name || '').split('.').pop().toLowerCase();
                        const isVideoFormat =['mp4','mkv','avi','mov','wmv','flv','webm','ts'].includes(ext);
                        const isVideo = !isFolder && (mime.startsWith('video/') || isVideoFormat || displayDur > 0);

                        let durHtml = "-";

                        if (isFolder) {
                            if (!S.trashMode) {
                                let count = d.file_count;

                                if ((count === undefined || count === null) && typeof globalCache !== 'undefined') {
                                    const cachedData = globalCache.get(d.id);
                                    if (cachedData) {
                                        const list = Array.isArray(cachedData) ? cachedData : (cachedData.items ||[]);
                                        count = list.length;
                                    }
                                }

                                const hasCount = count !== undefined && count !== null;

                                if (hasCount) {
                                    durHtml = `${count} ${L.str_items}`;
                                }
                            }
                        } else if (isVideo && displayDur > 0) {
                            durHtml = fmtDur(displayDur);
                        } else {
                            const rawName = d.name || "";
                            const lastDot = rawName.lastIndexOf('.');
                            if (lastDot > 0) {
                                const extUpper = rawName.substring(lastDot + 1).toUpperCase();

                                const SETS = {
                                    vid: new Set(['MP4','MKV','AVI','MOV','WMV','FLV','WEBM','TS','M4V','3GP','MPG','MPEG','RM','RMVB','ASF','VOB','DAT','DIVX','F4V','M2TS','MTS','TP','TRP','OGV','MPE','M2V','M3U8']),
                                    aud: new Set(['MP3','WAV','FLAC','AAC','OGG','WMA','APE','M4A','AMR','OPUS','M4B','ALAC','AIFF','MID','MIDI','RA','DTS','AC3','DSF','DFF']),
                                    img: new Set(['JPG','JPEG','PNG','GIF','BMP','WEBP','SVG','TIF','TIFF','ICO','HEIC','HEIF','RAW','CR2','NEF','ARW','DNG','ORF','AVIF','PSD','AI','EPS','JFIF','JPE']),
                                    doc: new Set(['TXT','HTML','PDF','PPTX','CHM','DOCX','XLSX','HTM','DOC','DWG','MDB','PPT','XLS','RTF','ODT','ODS','ODP','EPUB','MOBI','AZW3','DJVU','CBZ','CBR','MD','LOG','CSV','XML','JSON']),
                                    app: new Set(['APK','EXE','IPA','DMG','RPM','DEB','MSI','PKG','XAPK','APKS','AAB','JAR','BIN','SH','BAT','CMD']),
                                    arc: new Set(['ZIP','RAR','7Z','TAR','GZ','ISO','CAB','BZ2','XZ','TGZ','WIM','ESD','IMG','ZST','LZH']),
                                    sub: new Set(['SRT','ASS','SSA','VTT','SMI','SUB','IDX','SUP','LRC'])
                                };

                                if (SETS.img.has(extUpper)) durHtml = `${extUpper} ${L.type_img}`;
                                else if (SETS.arc.has(extUpper) || /^(PART\d+|\d{3}|[RZ]\d{2})$/.test(extUpper)) durHtml = `${extUpper} ${L.type_archive}`;
                                else if (SETS.doc.has(extUpper)) durHtml = `${extUpper} ${L.type_doc}`;
                                else if (SETS.sub.has(extUpper)) durHtml = `${extUpper} ${L.type_sub}`;
                                else if (SETS.app.has(extUpper)) durHtml = `${extUpper} ${L.type_app}`;
                                else if (SETS.aud.has(extUpper)) durHtml = `${extUpper} ${L.cat_audio}`;
                                else if (SETS.vid.has(extUpper)) durHtml = `${extUpper} ${L.cat_video}`;
                                else durHtml = `${extUpper} ${L.type_suffix}`;
                            } else {
                                durHtml = L.type_suffix;
                            }
                        }

                        html += `<div data-pk-tip="${durHtml}">${durHtml}</div>`;
                    }

                    const dateTxt = S.trashMode ? getRemainingDays(d.modified_time) : fmtDate(d.modified_time);
                    html += `<div data-pk-tip="${dateTxt}">${dateTxt}</div>`;
                }


                row.innerHTML = html;

                row.dataset.pkBoundId = d.id || '';
                row.dataset.pkBoundKind = d.kind || '';

                if (isGridMode && typeof patchFrozenGridMedia === 'function') {
                    patchFrozenGridMedia(row, d, prevGridMediaNode, prevGridMediaKey);
                }

                const thumbImg = row.querySelector('.pk-max-thumb, .pk-min-thumb');
                if (thumbImg) {
                    const toggleThumb = () => {
                        if(window.pkGlobalThumbCache) window.pkGlobalThumbCache.add(d.id);
                        thumbImg.style.opacity = '1';

                        const placeholder = thumbImg.previousElementSibling;
                        if (placeholder && (placeholder.classList.contains('pk-placeholder-layer') || placeholder.classList.contains('pk-min-ph'))) {
                            placeholder.style.opacity = '0';
                        }

                        const parent = thumbImg.parentElement;
                        const badge = parent ? parent.querySelector('.pk-folder-badge') : null;
                        if (badge) {
                            badge.style.opacity = '1';
                        }
                        const vidOv = parent ? parent.querySelector('.pk-video-ov') : null;
                        if (vidOv) {
                            vidOv.style.opacity = '1';
                        }

                        if (typeof syncGridVideoPlayState === 'function') {
                            syncGridVideoPlayState(row, d);
                        }

                        if (S.sel.has(d.id) && typeof updateStat === 'function') updateStat();
                    };

                    const handleThumbError = () => {
                        thumbImg.style.display = 'none';
                        if (window.pkGlobalThumbCache) window.pkGlobalThumbCache.delete(d.id);
                        const placeholder = thumbImg.previousElementSibling;
                        if (placeholder && (placeholder.classList.contains('pk-placeholder-layer') || placeholder.classList.contains('pk-min-ph'))) {
                            placeholder.style.opacity = '1';
                        }
                        const parent = thumbImg.parentElement;
                        const badge = parent ? parent.querySelector('.pk-folder-badge') : null;
                        if (badge) {
                            badge.style.opacity = '0';
                        }

                        if (typeof syncGridVideoPlayState === 'function') {
                            syncGridVideoPlayState(row, d);
                        }

                        if (S.sel.has(d.id) && typeof updateStat === 'function') updateStat();
                    };

                    if (thumbImg.complete) {
                        if (thumbImg.naturalWidth > 0) toggleThumb();
                        else handleThumbError();
                    } else {
                        thumbImg.onload = toggleThumb;
                        thumbImg.onerror = handleThumbError;
                    }
                }

                if (!d.isHeader && d.thumbnail_link) {
                    const minIcon = row.querySelector('.pk-min-icon');
                    if (minIcon && !minIcon.querySelector('.pk-proxy-thumb')) {
                        const proxyImg = document.createElement('img');
                        proxyImg.className = 'pk-proxy-thumb';
                        proxyImg.src = d.thumbnail_link;
                        proxyImg.style.cssText = 'position: absolute; top: 0; left: 0; width: 0; height: 0; opacity: 0; pointer-events: none; visibility: hidden;';
                        minIcon.appendChild(proxyImg);
                    }
                }

                const chk = row.querySelector('input');
                const gridMoreBtn = row.querySelector('.pk-gv-more');
                if (gridMoreBtn) {
                    gridMoreBtn.onclick = (evt) => {
                        evt.preventDefault();
                        evt.stopPropagation();
                        const openMenu = () => {
                            const liveRow = UI.in.querySelector(`.pk-row[data-id="${d.id}"]`) || row;
                            const liveBtn = liveRow.querySelector('.pk-gv-more') || gridMoreBtn;
                            const btnRect = liveBtn.getBoundingClientRect();
                            liveRow.dispatchEvent(new MouseEvent('contextmenu', {
                                bubbles: true,
                                cancelable: true,
                                clientX: Math.max(btnRect.left + 8, btnRect.right - 8),
                                clientY: btnRect.bottom + 6,
                                button: 2
                            }));
                        };
                        if (!S.isSelected(d.id) || S.activeId !== d.id) {
                            S.setAllSelection(false);
                            S.setSelected(d.id, true);
                            S.activeId = d.id;
                            S.lastSelIdx = i;
                            renderVisible();
                            updateStat();
                            requestAnimationFrame(openMenu);
                            return;
                        }
                        openMenu();
                    };
                }

                const triggerOpen = () => {
                    if (S.movingIds.has(d.id)) return;

                    if (S.trashMode) return;

                    const checkType = (it) => {
                        const m = (it.mime_type || '').toLowerCase();
                        const n = (it.name || '').toLowerCase();
                        const dur = (it.params && it.params.duration) || 0;
                        const vExts = ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp'];
                        const aExts = ['zip','rar','7z','tar','gz','bz2','xz'];
                        return {
                            isVideo: m.startsWith('video/') || dur > 0 || vExts.some(e => n.endsWith('.' + e)),
                            isImage: m.startsWith('image/'),
                            isTorrent: n.endsWith('.torrent'),
                            isArchive: m.includes('zip') || m.includes('rar') || m.includes('archive') || aExts.some(e => n.endsWith('.' + e))
                        };
                    };

                    if (S.uploadMode) {
                        if (d.status !== 'DONE') {
                            if (d.file_id) {
                                S.setAllSelection(false);
                                S.setSelected(d.id, true);
                                S.activeId = d.id;
                                const locateBtn = document.getElementById('ctx-locate');
                                if (locateBtn) locateBtn.click();
                            }
                            return;
                        }
                        const t = checkType(d);
                        if (t.isTorrent) handleTorrentFile(d);
                        else if (t.isVideo || t.isImage) {
                            if (t.isVideo) playVideo(d); else showImage(d);
                        } else if (d.file_id) {
                            S.setAllSelection(false);
                            S.setSelected(d.id, true);
                            S.activeId = d.id;
                            const locateBtn = document.getElementById('ctx-locate');
                            if (locateBtn) locateBtn.click();
                        }
                        return;
                    }

                    if (S.offlineMode) {
                        if (!d.file_id || d.phase === 'PHASE_TYPE_ERROR') return;

                        if (d.phase !== 'PHASE_TYPE_COMPLETE') {
                            S.setAllSelection(false);
                            S.setSelected(d.id, true);
                            S.activeId = d.id;
                            const locateBtn = document.getElementById('ctx-locate');
                            if (locateBtn) locateBtn.click();
                            return;
                        }

                        const t = checkType(d);

                        if (t.isTorrent) handleTorrentFile(d);
                        else if (t.isVideo || t.isImage) {
                            if (t.isVideo) playVideo(d);
                            else showImage(d);
                        } else {
                            S.setAllSelection(false);
                            S.setSelected(d.id, true);
                            S.activeId = d.id;

                            const locateBtn = document.getElementById('ctx-locate');
                            if (locateBtn) {
                                locateBtn.click();
                            }
                        }
                        return;
                    }

                    if (S.shareMode) {
                        const isShareDisabled = (d.share_status === 'DELETED') || (d.limit_count > 0 && d.save_count >= d.limit_count);
                        if (isShareDisabled) return;
                        showShareDetail(d);
                        return;
                    }

                    if (d.kind === 'drive#folder') {
                        if (!S.trashMode) {
                            const isExitMode = S.isFlattened || S.dupMode;
                            if (isExitMode) {
                                S.isFlattened = false; S.dupMode = false;
                                UI.scan.style.display = 'flex'; UI.btnExit.style.display = 'none';
                                if(UI.dupTools) UI.dupTools.style.display = 'none';
                                if(UI.btnFolderFirst) UI.btnFolderFirst.style.display = 'flex';
                                if(UI.btnNewFolder) UI.btnNewFolder.style.display = 'flex';
                                if(UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                                if(UI.chkGlobal) UI.chkGlobal.checked = false;
                                if (d._lineage && d._lineage.length > 0) {
                                    const newPath = [{ id: '', name: L.btn_nav_home }];
                                    d._lineage.forEach(n => { if (n.id && n.id !== 'root') newPath.push({ id: n.id, name: n.name }); });
                                    if (newPath.length === 0 || newPath[newPath.length-1].id !== d.id) newPath.push({ id: d.id, name: d.name });
                                    S.path = newPath;
                                } else { S.path = [{ id: '', name: L.btn_nav_home }, { id: d.id, name: d.name }]; }
                                load();
                            } else {
                                const lastNode = S.path[S.path.length - 1];
                                if (lastNode && lastNode.id === d.id) return;

                                if (S.analyzeMode && S.strictFolderFirstSnapshot !== null) {
                                    S.folderFirst = S.strictFolderFirstSnapshot === true;
                                } else {
                                    S.folderFirst = false;
                                }
                                if (S.renderFolderFirst) S.renderFolderFirst();
                                S.saveNavScrollTop(S.getNavBucketKey(), S.path);
                                S.path.push(d); load();
                            }
                        }
                    } else {
                        const t = checkType(d);
                        if (t.isTorrent) handleTorrentFile(d);
                        else if (t.isArchive) handleOpenArchive(d);
                        else if (t.isVideo) playVideo(d);
                        else if (t.isImage) showImage(d);
                    }
                };

                row.onclick = async (e) => {
                    if (UI.ctx && UI.ctx.style.display !== 'none') {
                        UI.ctx.style.display = 'none';
                    }

                    const nameText = e.target.closest('.pk-name .pk-name-txt');
                    const isAnalyzeRoot = S.analyzeMode && S.path[S.path.length - 1].id === 'analyze_root';
                    const isProtectedView = S.dupMode || (isAnalyzeRoot && S.analyzeSimGroups);

                    if (!S.trashMode && nameText) {
                        const selectedCount = S.getSelectedCount();
                        const isCurrentSelected = S.isSelected(d.id);

                        if (isProtectedView && selectedCount > 5) {
                            if (selectedCount > 1 || !isCurrentSelected) {
                                if (!await confirmSelectionClear()) return;
                            }
                        }

                        S.setAllSelection(false);
                        S.setSelected(d.id, true);
                        S.activeId = d.id;

                        triggerOpen();

                        renderVisible();
                        updateStat();
                        return;
                    }

                    S.activeId = d.id;

                    if (e.target === chk) {
                        if (e.shiftKey && S.lastSelIdx !== -1) {
                            const startIdx = Math.min(S.lastSelIdx, i);
                            const endIdx = Math.max(S.lastSelIdx, i);
                            const targetState = chk.checked;
                            for (let k = startIdx; k <= endIdx; k++) {
                                const item = S.display[k];
                                if (item && !item.isHeader && !S.movingIds.has(item.id)) {
                                    S.setSelected(item.id, targetState);
                                }
                            }
                        } else {
                            S.setSelected(d.id, chk.checked);
                        }
                    } else {
                        if (e.shiftKey && S.lastSelIdx !== -1) {
                            const startIdx = Math.min(S.lastSelIdx, i);
                            const endIdx = Math.max(S.lastSelIdx, i);
                            for (let k = startIdx; k <= endIdx; k++) {
                                const item = S.display[k];
                                if (item && !item.isHeader && !S.movingIds.has(item.id)) S.setSelected(item.id, true);
                            }
                        } else if (e.ctrlKey || e.metaKey) {
                            S.toggleSelected(d.id);
                        } else {
                            const selectedCount = S.getSelectedCount();
                            const isCurrentSelected = S.isSelected(d.id);

                            if (isProtectedView && selectedCount > 5) {
                                if (selectedCount > 1 || !isCurrentSelected) {
                                    if (!await confirmSelectionClear()) {
                                        renderVisible();
                                        return;
                                    }
                                }
                            }
                            S.setAllSelection(false);
                            S.setSelected(d.id, true);
                        }
                    }

                    S.lastSelIdx = i;

                    const rows = UI.in.children;
                    for (let k = 0; k < rows.length; k++) {
                        const r = rows[k];

                        if (r.dataset.id) {
                            const rid = r.dataset.id;
                            const isSelected = S.isSelected(rid);
                            const isActiveRow = (S.activeId === rid);
                            const isFocused = isSelected && isActiveRow;
                            if (!(isActiveRow && !isSelected)) {
                                r.style.backgroundColor = '';
                                r.style.border = '';
                                r.style.borderRadius = '';
                            }

                            if (isActiveRow && !isSelected) {
                                r.style.backgroundColor = 'var(--pk-sel-bg)';
                                r.style.border = '1px solid var(--pk-pri)';
                                r.style.borderRadius = isGridView() ? `${getGridCardRadius()}px` : '4px';
                            }

                            const cls = getRowClassName(isSelected, isFocused, r.classList.contains('pk-moving'));
                            if (r.className !== cls) r.className = cls;
                            const cb = r.querySelector('input[type="checkbox"]');
                            if (cb && cb.checked !== isSelected) cb.checked = isSelected;
                        }
                        else if (r.classList.contains('pk-group-hd')) {
                            const grpChk = r.querySelector('.pk-grp-chk');
                            if (grpChk) {
                                const gNode = S.display[start + k];
                                if (gNode && gNode.isHeader) {
                                    const gIdx = parseInt(gNode.id.replace('grp_', ''));
                                    const gIds = Array.isArray(gNode.groupIds)
                                    ? gNode.groupIds
                                    : (S.dupMode ? (S.dupRawGroups[gIdx]?.ids || []) : (S.analyzeMode ? (S.analyzeSimGroups[gIdx]?.ids || []) : []));
                                    let selCount = 0;
                                    gIds.forEach(id => { if (S.isSelected(id)) selCount++; });

                                    const isAll = gIds.length > 0 && selCount === gIds.length;
                                    const isInd = selCount > 0 && selCount < gIds.length;

                                    if (grpChk.checked !== isAll) grpChk.checked = isAll;
                                    if (grpChk.indeterminate !== isInd) grpChk.indeterminate = isInd;
                                }
                            }
                        }
                    }
                    updateStat();
                };

                row.ondblclick = (e) => {
                    e.preventDefault();
                    if (S.trashMode) return;

                    if (S.uploadMode) {
                        if (d.status !== 'DONE') return;
                        triggerOpen();
                        return;
                    }

                    if (S.shareMode) {
                        const isShareDisabled = (d.share_status === 'DELETED') || (d.limit_count > 0 && d.save_count >= d.limit_count);

                        if (!isShareDisabled) {
                            showShareDetail(d);
                        }
                        return;
                    }

                    const isNameTxt = e.target.closest('.pk-name .pk-name-txt');
                    if (e.target === chk || isNameTxt) return;

                    if (S.offlineMode) {
                        triggerOpen();
                        return;
                    }

                    if (d.kind === 'drive#folder') {
                        const isExitMode = S.isFlattened || S.dupMode;

                        if (isExitMode) {
                            S.isFlattened = false;
                            S.dupMode = false;
                            if (S.filterState) S.filterState = { active: false, cat: 'all', ext: 'all' };

                            UI.scan.style.display = 'flex';
                            UI.btnExit.style.display = 'none';
                            if(UI.dupTools) UI.dupTools.style.display = 'none';
                            if(UI.dupFilters) UI.dupFilters.style.display = 'none';

                            if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';
                            if(UI.btnNewFolder) UI.btnNewFolder.style.display = 'flex';
                            if(UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                            if(UI.chkGlobal) UI.chkGlobal.checked = false;

                            if (d._lineage && d._lineage.length > 0) {
                                const newPath = [{ id: '', name: L.btn_nav_home }];
                                d._lineage.forEach(n => {
                                    if (n.id && n.id !== 'root') newPath.push({ id: n.id, name: n.name });
                                });
                                if (newPath.length === 0 || newPath[newPath.length-1].id !== d.id) {
                                    newPath.push({ id: d.id, name: d.name });
                                }
                                S.path = newPath;
                            } else {
                                S.path = [{ id: '', name: L.btn_nav_home }, { id: d.id, name: d.name }];
                            }

                            load();

                        } else {
                            S.folderFirst = false;
                            if (S.renderFolderFirst) S.renderFolderFirst();

                            S.saveNavScrollTop(S.getNavBucketKey(), S.path);
                            S.path.push(d);
                            load();
                        }
                    } else {
                        const mime = (d.mime_type || "").toLowerCase();
                        const name = (d.name || "").toLowerCase();

                        if (name.endsWith('.torrent')) {
                            handleTorrentFile(d);
                        }
                        else if (mime.includes('zip') || mime.includes('rar') || mime.includes('7z') ||
                            mime.includes('compressed') || mime.includes('archive') ||
                            name.endsWith('.zip') || name.endsWith('.rar') || name.endsWith('.7z') || name.endsWith('.tar') || name.endsWith('.gz')) {
                            handleOpenArchive(d);
                        }
                        else if (mime.startsWith('video')) {
                            playVideo(d);
                        }
                        else if (mime.startsWith('image')) {
                            showImage(d);
                        }
                    }
                };

                row.oncontextmenu = (e) => {
                    e.preventDefault();

                    if (!S.isSelected(d.id)) {
                        S.setAllSelection(false);
                        S.setSelected(d.id, true);
                        S.activeId = d.id;
                        S.lastSelIdx = i;
                        renderVisible();
                        updateStat();
                    }

                    const itms = {
                        share: UI.ctx.querySelector('#ctx-share'),
                        open: UI.ctx.querySelector('#ctx-open'),
                        extPlay: UI.ctx.querySelector('#ctx-ext-play'),
                        star: UI.ctx.querySelector('#ctx-star'),
                        prop: UI.ctx.querySelector('#ctx-property'),
                        locate: UI.ctx.querySelector('#ctx-locate'),
                        down: UI.ctx.querySelector('#ctx-down'),
                        cpName: UI.ctx.querySelector('#ctx-copy-name'),
                        cut: UI.ctx.querySelector('#ctx-cut'),
                        copy: UI.ctx.querySelector('#ctx-copy'),
                        rename: UI.ctx.querySelector('#ctx-rename'),
                        prune: UI.ctx.querySelector('#ctx-prune'),
                        addBl: UI.ctx.querySelector('#ctx-add-bl'),
                        del: UI.ctx.querySelector('#ctx-del'),
                        restore: UI.ctx.querySelector('#ctx-restore'),
                        delF: UI.ctx.querySelector('#ctx-del-forever'),
                        shCancel: UI.ctx.querySelector('#ctx-share-cancel'),
                        shDetail: UI.ctx.querySelector('#ctx-share-detail'),
                        shCopy: UI.ctx.querySelector('#ctx-share-copy'),
                        taskRetry: UI.ctx.querySelector('#ctx-task-retry')
                    };
                    const seps = Array.from(UI.ctx.querySelectorAll('.pk-ctx-sep'));

                    if (!itms.taskRetry) {
                        const retryDiv = document.createElement('div');
                        retryDiv.className = 'pk-ctx-item';
                        retryDiv.id = 'ctx-task-retry';
                        retryDiv.innerHTML = `${CONF.icons.refresh} ${L.btn_retry_task}`;
                        const ref = document.getElementById('ctx-down');
                        if (ref) ref.parentNode.insertBefore(retryDiv, ref);
                        itms.taskRetry = retryDiv;
                    }

                    if (!UI.ctx.querySelector('#ctx-up-pause')) {
                        const pBtn = document.createElement('div');
                        pBtn.className = 'pk-ctx-item'; pBtn.id = 'ctx-up-pause';
                        pBtn.innerHTML = `${CONF.icons.taskPause} ${L.btn_up_pause}`;
                        const delRef = document.getElementById('ctx-del');
                        if (delRef) delRef.parentNode.insertBefore(pBtn, delRef);
                    }
                    if (!UI.ctx.querySelector('#ctx-up-start')) {
                        const sBtn = document.createElement('div');
                        sBtn.className = 'pk-ctx-item'; sBtn.id = 'ctx-up-start';
                        sBtn.innerHTML = `${CONF.icons.taskStart} ${L.btn_up_start}`;
                        const delRef = document.getElementById('ctx-del');
                        if (delRef) delRef.parentNode.insertBefore(sBtn, delRef);
                    }

                    const allCtxItems = UI.ctx.querySelectorAll('.pk-ctx-item');
                    const allCtxSeps = UI.ctx.querySelectorAll('.pk-ctx-sep');
                    allCtxItems.forEach(el => el.style.display = 'none');
                    allCtxSeps.forEach(el => el.style.display = 'none');

                    const sepShareExtra = UI.ctx.querySelector('#sep-share-extra');

                    if (S.shareMode) {
                        const sh = {
                            name: UI.ctx.querySelector('#ctx-copy-name'),
                            cancel: UI.ctx.querySelector('#ctx-sh-cancel'),
                            bl: UI.ctx.querySelector('#ctx-add-bl'),
                            detail: UI.ctx.querySelector('#ctx-sh-detail'),
                            copy: UI.ctx.querySelector('#ctx-sh-copy')
                        };

                        const selectedIds = S.getSelectedIds();
                        const isSingle = (selectedIds.length === 1);
                        const isShareDisabled = (d.share_status === 'DELETED') || (d.limit_count > 0 && d.save_count >= d.limit_count);

                        if(seps[0]) seps[0].style.display = 'none';
                        if(seps[1]) seps[1].style.display = 'none';
                        if(sh.name) sh.name.style.display = 'flex';
                        if(seps[2]) seps[2].style.display = 'block';

                        if(sh.cancel) sh.cancel.style.display = 'flex';
                        if(sh.bl) {
                            sh.bl.style.display = 'flex';
                            let isRemoveMode = false;
                            for (const id of selectedIds) {
                                const target = S.itemMap.get(id);
                                if (target && S.blSet.has((target.name || target.title || "").toLowerCase().trim())) { isRemoveMode = true; break; }
                            }
                            sh.bl.innerHTML = isRemoveMode ? `${ctxIcons.blRem} ${L.ctx_remove_bl}` : `${ctxIcons.blAdd} ${L.ctx_add_bl}`;
                            sh.bl.setAttribute('data-action', isRemoveMode ? 'remove' : 'add');
                        }
                        const showDetails = isSingle && !isShareDisabled;
                        if (showDetails) {
                            if(sepShareExtra) sepShareExtra.style.display = 'block';
                            if(sh.detail) sh.detail.style.display = 'flex';
                            if(sh.copy) sh.copy.style.display = 'flex';
                        }

                        if(seps[3]) seps[3].style.display = 'none';

                    } else if (S.uploadMode) {
                        const upEls = {
                            open: UI.ctx.querySelector('#ctx-open'),
                            ext: UI.ctx.querySelector('#ctx-ext-play'),
                            loc: UI.ctx.querySelector('#ctx-locate'),
                            name: UI.ctx.querySelector('#ctx-copy-name'),
                            del: UI.ctx.querySelector('#ctx-del'),
                            pause: UI.ctx.querySelector('#ctx-up-pause'),
                            start: UI.ctx.querySelector('#ctx-up-start'),
                            sep1: UI.ctx.querySelector('#sep-1'),
                            sep2: UI.ctx.querySelector('#sep-2'),
                            sep3: UI.ctx.querySelector('#sep-3')
                        };

                        const uploadSelectedIds = S.getSelectedIds();

                        if (uploadSelectedIds.length > 1) {
                            const items = uploadSelectedIds.map(id => S.itemMap.get(id)).filter(Boolean);
                            let hasActive = false, hasPaused = false, hasDone = false;

                            items.forEach(t => {
                                if (['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'].includes(t.status)) hasActive = true;
                                else if (['PAUSED', 'ERROR'].includes(t.status)) hasPaused = true;
                                else if (t.status === 'DONE') hasDone = true;
                            });

                            upEls.name.style.order = '10';
                            upEls.name.style.display = 'flex';

                            if (hasActive && !hasPaused && !hasDone) {
                                if (upEls.sep1) { upEls.sep1.style.order = '15'; upEls.sep1.style.display = 'block'; }
                                if (upEls.pause) {
                                    upEls.pause.style.order = '20';
                                    upEls.pause.style.display = 'flex';
                                    upEls.pause.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpPause) UI.btnUpPause.click(); };
                                }
                            } else if (!hasActive && hasPaused && !hasDone) {
                                if (upEls.sep1) { upEls.sep1.style.order = '15'; upEls.sep1.style.display = 'block'; }
                                if (upEls.start) {
                                    upEls.start.style.order = '20';
                                    upEls.start.style.display = 'flex';
                                    upEls.start.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpStart) UI.btnUpStart.click(); };
                                }
                            } else {
                            }

                            upEls.sep2.style.order = '25';
                            upEls.sep2.style.display = 'block';

                            upEls.del.style.order = '30';
                            upEls.del.style.display = 'flex';
                            upEls.del.innerHTML = `${CONF.icons.del} ${L.btn_up_del}`;
                            upEls.del.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpDel) UI.btnUpDel.click(); };

                        } else {
                            const mime = (d.mime_type || '').toLowerCase();
                            const isImg = mime.startsWith('image/');
                            const isVideo = mime.startsWith('video/') || ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp'].some(e => (d.name||'').toLowerCase().endsWith('.'+e));

                            const isActive = ['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'].includes(d.status);
                            const isDone = d.status === 'DONE';

                            if (isDone) {
                                upEls.open.style.order = '10';
                                upEls.open.style.display = (isVideo || isImg) ? 'flex' : 'none';

                                upEls.ext.style.order = '11';
                                upEls.ext.style.display = isVideo ? 'flex' : 'none';

                                upEls.loc.style.order = '12';
                                upEls.loc.style.display = d.file_id ? 'flex' : 'none';

                                upEls.sep1.style.order = '15';
                                upEls.sep1.style.display = 'block';
                                upEls.name.style.order = '20';
                                upEls.name.style.display = 'flex';

                                upEls.sep2.style.order = '25';
                                upEls.sep2.style.display = 'block';

                                upEls.del.style.order = '30';
                                upEls.del.style.display = 'flex';
                                upEls.del.innerHTML = `${CONF.icons.del} ${L.btn_up_del}`;
                                upEls.del.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpDel) UI.btnUpDel.click(); };
                            }
                            else if (isActive) {
                                upEls.name.style.order = '10';
                                upEls.name.style.display = 'flex';

                                upEls.sep1.style.order = '15';
                                upEls.sep1.style.display = 'block';

                                if (upEls.pause) {
                                    upEls.pause.style.order = '20';
                                    upEls.pause.style.display = 'flex';
                                    upEls.pause.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpPause) UI.btnUpPause.click(); };
                                }

                                upEls.sep2.style.order = '25';
                                upEls.sep2.style.display = 'block';

                                upEls.del.style.order = '30';
                                upEls.del.style.display = 'flex';
                                upEls.del.innerHTML = `${CONF.icons.del} ${L.btn_up_del}`;
                                upEls.del.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpDel) UI.btnUpDel.click(); };
                            }
                            else {
                                upEls.name.style.order = '10';
                                upEls.name.style.display = 'flex';
                                upEls.sep1.style.order = '15';
                                upEls.sep1.style.display = 'block';
                                if (upEls.start) {
                                    upEls.start.style.order = '20';
                                    upEls.start.style.display = 'flex';
                                    upEls.start.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpStart) UI.btnUpStart.click(); };
                                }

                                upEls.sep2.style.order = '25';
                                upEls.sep2.style.display = 'block';

                                upEls.del.style.order = '30';
                                upEls.del.style.display = 'flex';
                                upEls.del.innerHTML = `${CONF.icons.del} ${L.btn_up_del}`;
                                upEls.del.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if(UI.btnUpDel) UI.btnUpDel.click(); };
                            }
                        }

                    } else if (S.offlineMode) {
                        const els = {
                            open: UI.ctx.querySelector('#ctx-open'),
                            ext: UI.ctx.querySelector('#ctx-ext-play'),
                            loc: UI.ctx.querySelector('#ctx-locate'),
                            prop: UI.ctx.querySelector('#ctx-property'),
                            name: UI.ctx.querySelector('#ctx-copy-name'),
                            retry: UI.ctx.querySelector('#ctx-task-retry'),
                            link: UI.ctx.querySelector('#ctx-copy-link'),
                            del: UI.ctx.querySelector('#ctx-del'),
                            bl: UI.ctx.querySelector('#ctx-add-bl'),
                            sep1: UI.ctx.querySelector('#sep-1'),
                            sep2: UI.ctx.querySelector('#sep-2'),
                            sep3: UI.ctx.querySelector('#sep-3')
                        };

                        const ids = S.getSelectedIds();
                        const isSingle = ids.length === 1;
                        const firstItem = S.itemMap.get(ids[0]);
                        const hasFailed = ids.some(id => S.itemMap.get(id)?.phase === 'PHASE_TYPE_ERROR');

                        let isVid = false, isImg = false;
                        if (firstItem && firstItem.file_id) {
                            const m = (firstItem.mime_type || '').toLowerCase();
                            const n = (firstItem.name || '').toLowerCase();
                            isVid = m.startsWith('video/') || ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp'].some(e => n.endsWith('.'+e));
                            isImg = m.startsWith('image/');
                        }

                        let g1 = 0;
                        if (els.open) {
                            els.open.style.order = '10';
                            const isFolderLike = (firstItem.mime_type && (firstItem.mime_type.includes('folder') || firstItem.mime_type.includes('directory'))) || (firstItem.icon_link && firstItem.icon_link.includes('folder'));
                            const isReadyMedia = firstItem.phase === 'PHASE_TYPE_COMPLETE' && (isVid || isImg);
                            const canOpen = isSingle && firstItem?.file_id && firstItem.phase !== 'PHASE_TYPE_ERROR' && (isReadyMedia || isFolderLike);
                            els.open.style.display = canOpen ? 'flex' : 'none';
                            if(canOpen) g1++;
                        }
                        if (els.ext) {
                            els.ext.style.order = '11';
                            const canExt = isSingle && firstItem?.file_id && firstItem.phase === 'PHASE_TYPE_COMPLETE' && isVid;
                            els.ext.style.display = canExt ? 'flex' : 'none';
                            if(canExt) g1++;
                        }
                        if (els.prop) {
                            els.prop.style.order = '12';
                            els.prop.style.display = isSingle ? 'flex' : 'none';
                            if(isSingle) g1++;
                        }
                        if (els.loc) {
                            els.loc.style.order = '13';
                            const canLocate = isSingle && firstItem?.file_id && firstItem.phase !== 'PHASE_TYPE_ERROR';
                            els.loc.style.display = canLocate ? 'flex' : 'none';
                            if(canLocate) g1++;
                        }

                        let g2 = 0;
                        if (els.name) {
                            els.name.style.order = '20';
                            els.name.style.display = 'flex';
                            g2++;
                        }

                        let g3 = 0;
                        if (els.retry) {
                            els.retry.style.order = '30';
                            els.retry.innerHTML = `${CONF.icons.retry} ${L.btn_retry_task}`;
                            els.retry.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; if (UI.btnRetryTask) UI.btnRetryTask.click(); };
                            els.retry.style.display = hasFailed ? 'flex' : 'none';
                            if(hasFailed) g3++;
                        }
                        if (els.link) {
                            els.link.style.order = '31';
                            els.link.style.display = 'flex';
                            els.link.onclick = (e) => {
                                e.stopPropagation(); UI.ctx.style.display = 'none';
                                const tasks = ids.map(id => S.itemMap.get(id)).filter(t => t && (t.source_url || t.params?.url));
                                if (tasks.length) {
                                    GM_setClipboard(tasks.map(t => t.source_url || t.params.url).join('\n'));
                                    showToast(L.msg_copy_success);
                                }
                            };
                            g3++;
                        }

                        let g4 = 0;
                        if (els.del) {
                            els.del.style.order = '40';
                            els.del.style.display = 'flex';
                            els.del.innerHTML = `${CONF.icons.del} ${L.btn_del}`;
                            els.del.onclick = (e) => { e.stopPropagation(); UI.ctx.style.display = 'none'; UI.btnDel.click(); };
                            g4++;
                        }
                        if (els.bl) {
                            els.bl.style.order = '41';
                            els.bl.style.display = 'flex';
                            let isRemove = false;
                            for (const id of ids) {
                                const t = S.itemMap.get(id);
                                if (t && S.blSet.has((t.name||'').toLowerCase().trim())) { isRemove = true; break; }
                            }
                            els.bl.innerHTML = isRemove ? `${ctxIcons.blRem} ${L.ctx_remove_bl}` : `${ctxIcons.blAdd} ${L.ctx_add_bl}`;
                            els.bl.setAttribute('data-action', isRemove ? 'remove' : 'add');
                            g4++;
                        }

                        if (els.sep1) {
                            els.sep1.style.order = '15';
                            els.sep1.style.display = (g1 > 0 && (g2 > 0 || g3 > 0 || g4 > 0)) ? 'block' : 'none';
                        }
                        if (els.sep2) {
                            els.sep2.style.order = '25';
                            els.sep2.style.display = (g2 > 0 && (g3 > 0 || g4 > 0)) ? 'block' : 'none';
                        }
                        if (els.sep3) {
                            els.sep3.style.order = '35';
                            els.sep3.style.display = (g3 > 0 && g4 > 0) ? 'block' : 'none';
                        }

                    } else {
                        if(sepShareExtra) sepShareExtra.style.display = 'none';

                        if (S.trashMode) {
                            const tr = {
                                cpName: UI.ctx.querySelector('#ctx-copy-name'),
                                restore: UI.ctx.querySelector('#ctx-restore'),
                                delF: UI.ctx.querySelector('#ctx-del-forever'),
                                addBl: UI.ctx.querySelector('#ctx-add-bl'),
                                sep1: UI.ctx.querySelector('#sep-trash-1'),
                                sep2: UI.ctx.querySelector('#sep-trash-2')
                            };

                            if(tr.cpName) tr.cpName.style.display = 'flex';
                            if(tr.sep1) tr.sep1.style.display = 'block';

                            if(tr.restore) tr.restore.style.display = 'flex';
                            if(tr.sep2) tr.sep2.style.display = 'block';

                            if(tr.delF) tr.delF.style.display = 'flex';

                            if(tr.addBl) {
                                tr.addBl.style.display = 'flex';
                                let isRemoveMode = false;
                                const selectedIdsForBl = S.getSelectedIds();
                                for (const id of selectedIdsForBl) {
                                    const item = S.itemMap.get(id);
                                    if (item && (item.kind === 'drive#folder' ? S.blFolderSet.has(item.name.toLowerCase().trim()) : S.blSet.has(item.name.toLowerCase().trim()))) { isRemoveMode = true; break; }
                                }
                                tr.addBl.innerHTML = isRemoveMode ? `${ctxIcons.blRem} ${L.ctx_remove_bl}` : `${ctxIcons.blAdd} ${L.ctx_add_bl}`;
                                tr.addBl.setAttribute('data-action', isRemoveMode ? 'remove' : 'add');
                            }
                        }else {
                            if(itms.share) itms.share.style.display = 'flex';
                            if(seps[0]) seps[0].style.display = 'block';

                            let hasGroup2 = false;
                            const ctxSelectedIds = S.getSelectedIds();
                            const ctxSelectedCount = ctxSelectedIds.length;

                            if (ctxSelectedCount === 1) {
                                [itms.open, itms.prop].forEach(el => { if(el) el.style.display = 'flex'; });
                                const isVideo = (d.mime_type || '').startsWith('video/') || (d.params && d.params.duration > 0);
                                if (itms.extPlay) itms.extPlay.style.display = isVideo ? 'flex' : 'none';
                                hasGroup2 = true;
                            }
                            if (itms.star) {
                                let hasUnstarred = false;
                                let hasValidItem = false;
                                for (const id of ctxSelectedIds) {
                                    const it = S.itemMap.get(id);
                                    if (it && !isSystemItem(it)) {
                                        hasValidItem = true;
                                        if (!(it.starred || (it.tags && it.tags.some(t => t.name === 'STAR')))) {
                                            hasUnstarred = true;
                                            break;
                                        }
                                    }
                                }
                                itms.star.style.display = hasValidItem ? 'flex' : 'none';
                                if (hasValidItem) {
                                    hasGroup2 = true;
                                    itms.star.innerHTML = hasUnstarred ? `${ctxIcons.star} ${L.ctx_star}` : `${ctxIcons.unstar} ${L.ctx_unstar}`;
                                    itms.star.setAttribute('data-action', hasUnstarred ? 'star' : 'unstar');
                                }
                            }
                            const isSearchRoot = S.path.length > 0 && S.path[S.path.length - 1].id === 'virtual_search_root';
                            const canLocateUpload = S.uploadMode && d.status === 'DONE' && d.file_id;
                            const selectedCount = S.getSelectedCount();

                            if((S.starredMode || S.recentMode || S.historyMode || isSearchRoot || S.dupMode || S.isFlattened || S.analyzeMode || canLocateUpload) && selectedCount === 1) {
                                itms.locate.style.display = 'flex';
                                hasGroup2 = true;
                            }
                            if(seps[1]) seps[1].style.display = hasGroup2 ? 'block' : 'none';

                            [itms.down, itms.cpName].forEach(el => { if(el) el.style.display = 'flex'; });

                            if (S.historyMode) {
                                if(seps[2]) seps[2].style.display = 'none';
                                [itms.cut, itms.copy, itms.rename].forEach(el => { if(el) el.style.display = 'none'; });
                            } else {
                                if(seps[2]) seps[2].style.display = 'block';
                            }

                            if (S.historyMode) {
                                if(seps[3]) seps[3].style.display = 'block';
                            } else {
                                if (!isProtected) {
                                    if(itms.cut) itms.cut.style.display = 'flex';
                                    if(itms.rename) {
                                        itms.rename.style.display = 'flex';
                                        itms.rename.innerHTML = (selectedCount > 1) ? `${ctxIcons.renameBulk} ${L.btn_bulkrename}` : `${ctxIcons.rename} ${L.ctx_rename}`;
                                    }
                                }

                                if(itms.copy) itms.copy.style.display = 'flex';

                                let hasFolder = false;
                                const selectedIds = S.getSelectedIds();
                                for (const id of selectedIds) { if (S.itemMap.get(id)?.kind === 'drive#folder') { hasFolder = true; break; } }
                                if(itms.prune && hasFolder) itms.prune.style.display = 'flex';

                                if(seps[3]) seps[3].style.display = 'block';
                            }

                            if (itms.addBl) {
                                itms.addBl.style.display = 'flex';
                                S.updateBlCache();

                                let isRemoveMode = false;
                                const selectedIdsForBl = S.getSelectedIds();
                                for (const id of selectedIdsForBl) {
                                    const item = S.itemMap.get(id);
                                    if (!item) continue;
                                    const cleanName = (item.name || "").replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim().toLowerCase();

                                    if (item.kind === 'drive#folder') {
                                        if (S.blFolderSet.has(cleanName)) { isRemoveMode = true; break; }
                                    } else {
                                        if (S.blSet.has(cleanName)) { isRemoveMode = true; break; }
                                    }
                                }

                                itms.addBl.innerHTML = isRemoveMode ? `${ctxIcons.blRem} ${L.ctx_remove_bl}` : `${ctxIcons.blAdd} ${L.ctx_add_bl}`;
                                itms.addBl.setAttribute('data-action', isRemoveMode ? 'remove' : 'add');
                            }
                            if (itms.del) itms.del.style.display = 'flex';
                        }
                    }
                    const useFlexOrder = S.offlineMode || S.uploadMode;
                    UI.ctx.style.display = useFlexOrder ? 'flex' : 'block';
                    if (useFlexOrder) UI.ctx.style.flexDirection = 'column';

                    const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                    let x = e.clientX / scale, y = e.clientY / scale, w = 160, h = UI.ctx.offsetHeight || 280;
                    let winW = window.innerWidth / scale, winH = window.innerHeight / scale;
                    if (x + w > winW) x = winW - w - 10;
                    if (y + h > winH) y = winH - h - 10;
                    UI.ctx.style.left = x + 'px';
                    UI.ctx.style.top = y + 'px';
                };
            }
        }

        flushVisibleRowPool(UI.in, poolPtr);

        if (window.pkRefreshTooltip) {
            requestAnimationFrame(() => {
                window.pkRefreshTooltip();
            });
        }
    }

    let isMarquee = false, mqStartX = 0, mqStartY = 0, startScroll = 0, lastMouseX = 0, lastMouseY = 0, scrollSpeed = 0, scrollRaf = null, marqueeRenderRaf = null, marqueeAutoScrollTs = 0;
    let lastRngS = -1, lastRngE = -1, cachedVpRect = null;
    const mqBox = document.createElement('div');
    mqBox.className = 'pk-selection-box';
    el.appendChild(mqBox);

    const scheduleMarqueeRefresh = () => {
        if (marqueeRenderRaf) return;
        marqueeRenderRaf = requestAnimationFrame(() => {
            marqueeRenderRaf = null;
            refreshVisibleSelectionState();
            updateStat();
        });
    };

    const updateMarqueeUIAndSelection = (targetX, targetY) => {
        if (!cachedVpRect) return;
        const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;

        const cssTargetX = targetX / scale;
        const cssTargetY = targetY / scale;
        const cssRectTop = cachedVpRect.top;
        const cssRectBottom = cachedVpRect.bottom;
        const cssRectLeft = cachedVpRect.left;
        const cssMqStartX = mqStartX / scale;
        const cssMqStartY = mqStartY / scale;

        const curScroll = UI.vp.scrollTop;
        const clampedY = Math.max(cssRectTop, Math.min(cssRectBottom, cssTargetY));

        const logicA = cssMqStartY - cssRectTop + startScroll;
        const logicB = clampedY - cssRectTop + curScroll;
        const logTop = Math.max(0, Math.min(logicA, logicB));
        const logBot = Math.max(logicA, logicB);

        const visTop = logTop - curScroll + cssRectTop;
        const visBot = logBot - curScroll + cssRectTop;
        const clipT = Math.max(cssRectTop, visTop), clipB = Math.min(cssRectBottom, visBot);
        const drawH = Math.max(0, clipB - clipT);

        const safeRight = cssRectLeft + UI.vp.clientWidth;
        const clampedX = Math.max(cssRectLeft, Math.min(safeRight, cssTargetX));
        const drawL = Math.min(cssMqStartX, clampedX);
        const drawW = Math.abs(clampedX - cssMqStartX);

        mqBox.style.borderTopWidth = (visTop < cssRectTop) ? '0' : '1px';
        mqBox.style.borderBottomWidth = (visBot > cssRectBottom) ? '0' : '1px';
        mqBox.style.borderLeftWidth = (cssTargetX < cssRectLeft) ? '0' : '1px';
        mqBox.style.borderRightWidth = (cssTargetX > safeRight) ? '0' : '1px';

        Object.assign(mqBox.style, { display: drawH > 0 ? 'block' : 'none', width: drawW + 'px', height: drawH + 'px', transform: `translate3d(${drawL}px, ${clipT}px, 0)` });

        const drawR = drawL + drawW;
        const logicLeft = Math.max(0, drawL - cssRectLeft);
        const logicRight = Math.max(logicLeft, drawR - cssRectLeft);
        const sIdx = Math.floor(logTop / CONF.rowHeight), eIdx = Math.min(S.display.length - 1, Math.floor(logBot / CONF.rowHeight));
        if (sIdx !== lastRngS || eIdx !== lastRngE || isGridView()) {
            lastRngS = sIdx; lastRngE = eIdx;
            if (!window.event?.ctrlKey && !window.event?.metaKey) {
                S.selMode = 'explicit';
                S.sel.clear();
                S.selEx.clear();
            }
            if (isGridView()) {
                const gridLayout = getGridLayout();
                const dupGridMeta = isGroupedGridView() ? getGroupedGridMeta() : null;

                if (dupGridMeta && dupGridMeta.indexLayout) {
                    dupGridMeta.indexLayout.forEach((layout, itemIdx) => {
                        if (!layout || layout.kind !== 'item') return;
                        const item = S.display[itemIdx];
                        if (!item || item.isHeader || S.movingIds.has(item.id)) return;

                        const cardLeft = layout.left;
                        const cardRight = cardLeft + layout.width;
                        const cardTop = layout.top;
                        const cardBottom = cardTop + layout.height;

                        if (cardLeft < logicRight && cardRight > logicLeft && cardTop < logBot && cardBottom > logTop) {
                            S.setSelected(item.id, true);
                        }
                    });
                } else {
                    const adjustedTop = Math.max(0, logTop - gridLayout.gapY);
                    const adjustedBot = Math.max(adjustedTop, logBot - gridLayout.gapY);
                    const startRow = Math.max(0, Math.floor(adjustedTop / CONF.rowHeight));
                    const endRow = Math.max(startRow, Math.floor(adjustedBot / CONF.rowHeight));
                    for (let rowIdx = startRow; rowIdx <= endRow; rowIdx++) {
                        for (let colIdx = 0; colIdx < gridLayout.cols; colIdx++) {
                            const itemIdx = rowIdx * gridLayout.cols + colIdx;
                            if (itemIdx >= S.display.length) break;
                            const item = S.display[itemIdx];
                            if (!item || item.isHeader || S.movingIds.has(item.id)) continue;
                            const cardLeft = gridLayout.padX + colIdx * (gridLayout.cardWidth + gridLayout.gapX);
                            const cardRight = cardLeft + gridLayout.cardWidth;
                            const cardTop = gridLayout.gapY + rowIdx * CONF.rowHeight;
                            const cardBottom = cardTop + gridLayout.cardHeight;
                            if (cardLeft < logicRight && cardRight > logicLeft && cardTop < logBot && cardBottom > logTop) {
                                S.setSelected(item.id, true);
                            }
                        }
                    }
                }
            } else {
                for (let k = sIdx; k <= eIdx; k++) {
                    const item = S.display[k];
                    if (item && !item.isHeader && !S.movingIds.has(item.id)) S.setSelected(item.id, true);
                }
            }
            scheduleMarqueeRefresh();
        }
    };

    const runAutoScroll = (ts = 0) => {
        if (!isMarquee || scrollSpeed === 0) {
            scrollRaf = null;
            marqueeAutoScrollTs = 0;
            return;
        }

        const dt = marqueeAutoScrollTs ? Math.min(32, ts - marqueeAutoScrollTs) : 16;
        marqueeAutoScrollTs = ts || performance.now();

        const prevTop = UI.vp.scrollTop;
        const maxTop = Math.max(0, UI.vp.scrollHeight - UI.vp.clientHeight);
        const nextTop = Math.max(0, Math.min(maxTop, prevTop + (scrollSpeed * (dt / 16))));

        if (nextTop === prevTop) {
            scrollSpeed = 0;
            scrollRaf = null;
            marqueeAutoScrollTs = 0;
            return;
        }

        UI.vp.scrollTop = nextTop;
        updateMarqueeUIAndSelection(lastMouseX, lastMouseY);
        scrollRaf = requestAnimationFrame(runAutoScroll);
    };

    const handleMarqueeMove = (e) => {
        if (!cachedVpRect) return;
        if (!isMarquee) {
            const moveX = Math.abs(e.clientX - mqStartX);
            const moveY = Math.abs(e.clientY - mqStartY);
            if (moveX > 5 || moveY > 5) {
                isMarquee = true;
                S.activeId = null;
                UI.win.classList.add('pk-is-seeking');
                refreshVisibleSelectionState();
            } else {
                return;
            }
        }

        lastMouseX = e.clientX; lastMouseY = e.clientY;

        const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
        const logicalClientY = e.clientY / scale;
        const edgeZone = 28;

        if (logicalClientY > cachedVpRect.bottom - edgeZone) scrollSpeed = Math.min(26, 2 + Math.pow((logicalClientY - (cachedVpRect.bottom - edgeZone)) / 6, 1.2));
        else if (logicalClientY < cachedVpRect.top + edgeZone) scrollSpeed = -Math.min(26, 2 + Math.pow(((cachedVpRect.top + edgeZone) - logicalClientY) / 6, 1.2));
        else scrollSpeed = 0;

        if (scrollSpeed !== 0 && !scrollRaf) scrollRaf = requestAnimationFrame(runAutoScroll);
        updateMarqueeUIAndSelection(e.clientX, e.clientY);
    };

    const stopMarquee = () => {
        isMarquee = false;
        scrollSpeed = 0;
        marqueeAutoScrollTs = 0;
        if (UI.win) UI.win.classList.remove('pk-is-seeking');
        lastRngS = -1;
        lastRngE = -1;
        cachedVpRect = null;

        if (mqBox) {
            mqBox.style.display = 'none';
            mqBox.style.transform = 'none';
            mqBox.style.width = '0px';
            mqBox.style.height = '0px';
        }

        if (scrollRaf) cancelAnimationFrame(scrollRaf);
        if (marqueeRenderRaf) cancelAnimationFrame(marqueeRenderRaf);
        scrollRaf = null;
        marqueeRenderRaf = null;
        window.removeEventListener('mousemove', handleMarqueeMove);
        window.removeEventListener('mouseup', stopMarquee);
    };

    let isFileDragging = false;
    let fileDragGhost = null;
    let dropTargetId = null;
    let dropTargetType = null;
    let autoOpenTimer = null;
    let lastHoverSep = null;

    const handleFileDragMove = (e) => {
        if (!isFileDragging) {
            const moveX = Math.abs(e.clientX - mqStartX);
            const moveY = Math.abs(e.clientY - mqStartY);
            if (moveX > 5 || moveY > 5) {
                isFileDragging = true;
                document.body.classList.add('pk-dragging');

                fileDragGhost = document.createElement('div');
                fileDragGhost.className = 'pk-drag-ghost';
                if (el.classList.contains('pk-dark')) fileDragGhost.classList.add('pk-dark');
                const selectedIds = S.getSelectedIds();
                const count = selectedIds.length;
                const firstId = selectedIds[0];
                const firstItem = S.itemMap.get(firstId);
                let text = firstItem ? firstItem.name : 'Selected Items';
                if (count > 1) {
                    text += L.str_drag_files.replace('{n}', count);
                } else if (!firstItem) {
                    text = L.str_selected_items || 'Selected Items';
                }

                let dragIcon = CONF.typeIcons.folder.replace('30','20').replace('30','20');
                if (firstItem && firstItem.icon_link) {
                    dragIcon = `<img src="${firstItem.icon_link}" style="width:24px; height:24px; object-fit:contain; margin-right:8px; pointer-events:none;">`;
                }

                fileDragGhost.innerHTML = `${dragIcon}<span>${esc(text)}</span>`;
                document.body.appendChild(fileDragGhost);
            } else {
                return;
            }
        }

        if (fileDragGhost) {
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            fileDragGhost.style.zoom = 'var(--pk-zoom, 1)';
            fileDragGhost.style.left = (e.clientX / scale) + 'px';
            fileDragGhost.style.top = (e.clientY / scale) + 'px';
        }

        const prevTargets = document.querySelectorAll('.pk-drop-target');
        prevTargets.forEach(el => el.classList.remove('pk-drop-target'));
        dropTargetId = null;
        dropTargetType = null;

        if (fileDragGhost) fileDragGhost.style.display = 'none';
        const elUnder = document.elementFromPoint(e.clientX, e.clientY);
        if (fileDragGhost) fileDragGhost.style.display = 'flex';

        if (!elUnder) return;

        const menuItem = elUnder.closest('.pk-crumb-item');
        if (menuItem && menuItem.dataset.id) {
            const tid = menuItem.dataset.id;
            const currentFolderId = S.path[S.path.length - 1]?.id || '';
            if (tid !== currentFolderId && tid !== 'loading' && tid !== 'error') {
                dropTargetId = tid;
                dropTargetType = 'menu_item';
                menuItem.classList.add('pk-drop-target');
                return;
            }
        }

        const sep = elUnder.closest('.pk-crumb-sep');
        if (sep) {
            if (lastHoverSep !== sep) {
                lastHoverSep = sep;
                if (autoOpenTimer) clearTimeout(autoOpenTimer);
                autoOpenTimer = setTimeout(() => {
                    if (isFileDragging) sep.click();
                }, 500);
            }
        } else {
            lastHoverSep = null;
            if (autoOpenTimer) { clearTimeout(autoOpenTimer); autoOpenTimer = null; }
        }

        const row = elUnder.closest('.pk-row');
        if (row && row.dataset.id) {
            const tid = row.dataset.id;
            const item = S.itemMap.get(tid);
            if (item && item.kind === 'drive#folder' && !S.isSelected(tid)) {
                dropTargetId = tid;
                dropTargetType = 'row';
                row.classList.add('pk-drop-target');
                return;
            }
        }

        const crumbItem = elUnder.closest('#pk-crumb span');
        if (crumbItem && !crumbItem.classList.contains('pk-crumb-sep') && crumbItem.dataset.id) {
            const tid = crumbItem.dataset.id === 'root' ? '' : crumbItem.dataset.id;
            const currentFolderId = S.path[S.path.length - 1]?.id || '';
            const normalize = (id) => (!id || id === 'root') ? 'root' : id;
            if (normalize(tid) !== normalize(currentFolderId)) {
                dropTargetId = tid;
                dropTargetType = 'crumb';
                crumbItem.classList.add('pk-drop-target');
            }
        }
    };

    const executeFileTransfer = async (items, opType, sourcePid, targetPid, targetNameForLog) => {
        if (!items || items.length === 0) return;

        if (sourcePid !== '__VIRTUAL__' && sourcePid === targetPid) {
            showToast(L.err_paste_descendant, 'error');
            return;
        }

        const foldersToMoveIds = items.filter(it => it.kind === 'drive#folder').map(it => it.id);
        if (foldersToMoveIds.length > 0) {
            const isDescendant = (targetId, ancestorIds) => {
                let currentId = targetId;
                let safety = 100;
                while (currentId && currentId !== 'root' && safety > 0) {
                    if (ancestorIds.includes(currentId)) return true;
                    const parent = globalParentIndex.get(currentId);
                    currentId = parent ? parent.id : null;
                    safety--;
                }
                return false;
            };
            if (isDescendant(targetPid, foldersToMoveIds)) {
                showToast(L.err_paste_descendant, 'error');
                return;
            }
        }

        S.movingSourceId = sourcePid || 'root';
        S.movingDestId = targetPid || 'root';
        const allIds = items.map(it => it.id);

        allIds.forEach(id => S.movingIds.add(id));
        if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_ADD', ids: allIds, src: S.movingSourceId, dst: S.movingDestId });
        if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();

        isGUISensitive = true;

        const progressTask = FloatBarManager.create(`${opType === 'move' ? L.str_moving : L.str_copying}...`);
        const updateFloat = progressTask.update;

        const BATCH_SIZE = 500;
        let processedCount = 0;
        const total = items.length;
        const endpoint = opType === 'move' ? 'files:batchMove' : 'files:batchCopy';
        const actionText = opType === 'move' ? L.str_moving : L.str_copying;

        refresh();

        try {
            for (let i = 0; i < total; i += BATCH_SIZE) {
                const chunk = items.slice(i, i + BATCH_SIZE);
                const chunkIds = chunk.map(it => it.id);

                updateFloat(`${actionText} ${processedCount}/${total} -> ${esc(targetNameForLog || L.str_target_folder)}`);

                const apiTargetPid = (targetPid === 'root') ? '' : targetPid;

                const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/${endpoint}`, {
                    method: 'POST',
                    headers: getHeaders(),
                    body: JSON.stringify({ ids: chunkIds, to: { parent_id: apiTargetPid } })
                });

                const data = await res.json().catch(() => ({}));
                if (!res.ok) throw new Error(data.error_description || `API ${res.status}`);

                if (data.task_id) {
                    await new Promise(resolve => {
                        const checkTask = async () => {
                            try {
                                const tRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/tasks/${data.task_id}`, { headers: getHeaders() });
                                const tData = await tRes.json();
                                if (tData.phase === 'PHASE_TYPE_COMPLETE' || tData.phase === 'PHASE_TYPE_ERROR') resolve();
                                else setTimeout(checkTask, 800);
                            } catch { resolve(); }
                        };
                        checkTask();
                    });
                }
                else if (opType === 'move' && chunkIds.length > 0) {
                    const lastId = chunkIds[chunkIds.length - 1];
                    let retry = 0;
                    while (retry < 20) {
                        try {
                            const meta = await apiGet(lastId);
                            if (meta.parent_id === targetPid || meta.trashed) break;
                        } catch(e) { break; }
                        await sleep(500); retry++;
                    }
                }

                chunkIds.forEach(id => S.movingIds.delete(id));
                if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_REM', ids: chunkIds });

                if (opType === 'move') {
                    const movedSet = new Set(chunkIds);
                    S.items = S.items.filter(it => !movedSet.has(it.id));

                    if (typeof globalCache !== 'undefined') {
                        const cleanListChunk = (raw) => {
                            if (Array.isArray(raw)) return raw.filter(f => !movedSet.has(f.id));
                            if (raw && Array.isArray(raw.items)) {
                                raw.items = raw.items.filter(f => !movedSet.has(f.id));
                                return raw;
                            }
                            return raw;
                        };
                        for (const key of globalCache.keys()) {
                            globalCache.set(key, cleanListChunk(globalCache.get(key)));
                        }
                        for (const key of S.cache.keys()) {
                            S.cache.set(key, cleanListChunk(S.cache.get(key)));
                        }
                    }
                    if (typeof pkState !== 'undefined' && pkState && pkState.lastGlobalResults) {
                        pkState.lastGlobalResults = pkState.lastGlobalResults.filter(x => !movedSet.has(x.id));
                    }
                } else {
                    if (targetPid === (S.path[S.path.length-1].id||'')) {
                        const newItems = (data.files || []).map(f => minifyFile(f, false));
                        S.items.unshift(...newItems);
                    }
                }

                if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();
                refresh();

                processedCount += chunk.length;
            }

            if (targetPid) gmSet('pk_fmod_' + targetPid, new Date(getServerNow()).toISOString());

            if (S.analyzeMap) {
                const deltaSize = items.reduce((acc, it) => acc + parseInt(it.size || 0), 0);
                if (deltaSize > 0) {
                    const updateAnalyzeChain = (startId, isAdd) => {
                        let currId = startId;
                        let safety = 50;
                        while (currId && S.analyzeMap.has(currId) && safety > 0) {
                            const node = S.analyzeMap.get(currId);
                            const oldSize = parseInt(node.size || 0);
                            node.size = isAdd ? (oldSize + deltaSize) : Math.max(0, oldSize - deltaSize);
                            currId = node.parentId;
                            safety--;
                        }
                    };
                    if (S.analyzeMap.has(targetPid)) updateAnalyzeChain(targetPid, true);
                    if (opType === 'move' && S.analyzeMap.has(sourcePid)) updateAnalyzeChain(sourcePid, false);
                    if (S.analyzeResultItems) {
                        S.analyzeResultItems.forEach(resItem => {
                            if (S.analyzeMap.has(resItem.id)) {
                                resItem.size = S.analyzeMap.get(resItem.id).size.toString();
                            }
                        });
                    }
                }
            }

            const keysToClear = [sourcePid || 'root', targetPid || 'root'];
            keysToClear.forEach(k => {
                const normalizedK = (k === '' || k === 'root') ? 'root' : k;
                const altK = (normalizedK === 'root') ? '' : normalizedK;

                S.cache.delete(normalizedK);
                S.cache.delete(altK);

                if (typeof globalCache !== 'undefined') {
                    globalCache.delete(normalizedK);
                    globalCache.delete(altK);
                    if (typeof scannedFolderIds !== 'undefined') {
                        scannedFolderIds.delete(altK);
                    }
                }

                if (typeof globalDirtyFolders !== 'undefined') {
                    globalDirtyFolders.add(altK);
                }
            });

            if (opType === 'move') {
                const purgeMovedDescendants = (fid) => {
                    if (typeof globalLineageMap !== 'undefined') globalLineageMap.delete(fid);
                    if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.delete(fid);
                    const data = (typeof globalCache !== 'undefined' ? globalCache.get(fid) : null) || (S.cache ? S.cache.get(fid) : null);
                    if (data) {
                        const list = Array.isArray(data) ? data : (data.items || []);
                        list.forEach(child => {
                            if (child.kind === 'drive#folder') {
                                purgeMovedDescendants(child.id);
                            }
                        });
                        if (typeof globalCache !== 'undefined') globalCache.delete(fid);
                        if (S.cache) S.cache.delete(fid);
                    }
                };
                items.forEach(it => {
                    if (it.kind === 'drive#folder') purgeMovedDescendants(it.id);
                });
            }

            if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;

            if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();

            if (window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger(true);
            showToast(opType === 'move' ? L.msg_move_done : L.msg_copy_success);

        } catch (e) {
            showToast(`${L.str_error}: ${e.message}`, 'error');
            load(false, true);
        } finally {
            S.movingIds.clear();
            S.movingSourceId = null;
            S.movingDestId = null;
            if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_CLR', ids: [] });
            if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();

            isGUISensitive = false;

            if (progressTask) progressTask.destroy();

            updateStat();
        }
    };

    const stopFileDrag = async () => {
        if (autoOpenTimer) { clearTimeout(autoOpenTimer); autoOpenTimer = null; }
        lastHoverSep = null;
        window.removeEventListener('mousemove', handleFileDragMove);
        window.removeEventListener('mouseup', stopFileDrag);
        document.body.classList.remove('pk-dragging');
        if (fileDragGhost) fileDragGhost.remove();
        fileDragGhost = null;

        let targetName = L.lbl_type_folder;
        if (dropTargetType === 'menu_item') {
            const activeItem = document.querySelector('.pk-crumb-item.pk-drop-target span');
            if (activeItem) targetName = activeItem.textContent;
        } else if (dropTargetType === 'row') {
            targetName = S.itemMap.get(dropTargetId)?.name || L.lbl_type_folder;
        } else if (dropTargetType === 'crumb') {
            const pathNode = S.path.find(p => (p.id || 'root') === (dropTargetId || 'root'));
            targetName = pathNode ? pathNode.name : L.str_target_folder;
        }

        const targets = document.querySelectorAll('.pk-drop-target');
        targets.forEach(t => t.classList.remove('pk-drop-target'));

        document.querySelectorAll('.pk-crumb-pop').forEach(pop => {
            if (typeof pop._cleanup === 'function') pop._cleanup();
            else pop.remove();
        });
        UI.crumb.querySelectorAll('.pk-crumb-sep').forEach(sep => {
            sep.classList.remove('pk-active');
            sep.innerHTML = CONF.crumbIcons.right;
        });

        if (!isFileDragging || dropTargetId === null) {
            isFileDragging = false;
            return;
        }

        isFileDragging = false;

        const currentFolderId = S.path[S.path.length - 1]?.id || '';
        const normalize = (id) => (!id || id === 'root') ? 'root' : id;

        if (normalize(dropTargetId) === normalize(currentFolderId)) return;

        let hasSystem = false;
        const dragSelectedIds = S.getSelectedIds();
        for (const id of dragSelectedIds) {
            const item = S.itemMap.get(id);
            if (item && isSystemItem(item)) { hasSystem = true; break; }
        }
        if (hasSystem) { showToast(`${L.msg_sys_error}`, 'error'); return; }

        const rawItems = [];
        const validItems = [];
        let skipLocked = 0;
        let skipSelf = 0;
        let skipConflict = 0;

        const isDescendantConflict = (candidateId) => {
            if (!S.movingIds || S.movingIds.size === 0) return false;

            const hotspots = [S.movingSourceId, S.movingDestId].filter(x => x && x !== 'root');
            if (hotspots.length === 0) return false;

            for (const startPoint of hotspots) {
                let curr = startPoint;
                let safety = 50;
                while (curr && curr !== 'root' && safety > 0) {
                    if (curr === candidateId) return true;

                    const parentInfo = globalParentIndex.get(curr);
                    curr = parentInfo ? parentInfo.id : null;
                    safety--;
                }
            }
            return false;
        };

        dragSelectedIds.forEach(id => {
            const item = S.itemMap.get(id);
            if (!item) return;

            rawItems.push(item);

            if (S.movingIds.has(id)) {
                skipLocked++;
                return;
            }

            const targetId = normalize(dropTargetId);
            const selfId = normalize(item.id);
            const parentId = normalize(item.parent_id);

            if (targetId === selfId || targetId === parentId) {
                skipSelf++;
                return;
            }

            if (item.kind === 'drive#folder' && isDescendantConflict(item.id)) {
                skipConflict++;
                return;
            }

            validItems.push(item);
        });

        if (skipLocked > 0 || skipSelf > 0 || skipConflict > 0) {
            const reasons = [];
            if (skipLocked) reasons.push(L.msg_skip_locked.replace('{n}', skipLocked));
            if (skipSelf) reasons.push(L.msg_skip_self.replace('{n}', skipSelf));
            if (skipConflict) reasons.push(L.msg_skip_conflict.replace('{n}', skipConflict));
            showToast(`${L.msg_skip_invalid} ${reasons.join(', ')}`, 'warning');
        }

        S.clearSelection();
        refresh();

        if (validItems.length > 0) {
            await executeFileTransfer(
                validItems,
                'move',
                normalize(currentFolderId),
                normalize(dropTargetId),
                targetName
            );
        }
    };

    UI.vp.addEventListener('mousedown', (e) => {
        if (e.button !== 0 || e.detail > 1) return;

        if (e.target.closest('.pk-btn, .pk-star-icon, input')) return;

        const row = e.target.closest('.pk-row');
        const isClickingSelected = row && row.dataset.id && S.isSelected(row.dataset.id);

        if (isClickingSelected && !S.trashMode && !S.shareMode && !S.offlineMode && !S.historyMode && !S.starredMode && !S.recentMode && !S.isFlattened && !S.dupMode && !S.analyzeMode) {
            isFileDragging = false;
            mqStartX = e.clientX;
            mqStartY = e.clientY;

            window.addEventListener('mousemove', handleFileDragMove);
            window.addEventListener('mouseup', stopFileDrag);
            return;
        }
                cachedVpRect = getLogicalRect(UI.vp);
        const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
        const safeRight = cachedVpRect.left + UI.vp.clientWidth;
        const safeBottom = cachedVpRect.top + UI.vp.clientHeight;

        const logicalClientX = e.clientX / scale;
        const logicalClientY = e.clientY / scale;

        if (logicalClientX > safeRight || logicalClientY > safeBottom) return;

        let startItem = null;

        if (isGridView()) {
            const gridLayout = getGridLayout();
            const logicalOffsetX = logicalClientX - cachedVpRect.left;
            const logicalOffsetY = logicalClientY - cachedVpRect.top + UI.vp.scrollTop;

            let startIdx = -1;

            if (isGroupedGridView()) {
                startIdx = findDupGridHitIndex(logicalOffsetX, logicalOffsetY);
            } else {
                const totalRows = Math.max(1, Math.ceil(S.display.length / Math.max(1, gridLayout.cols)));

                if (logicalOffsetX >= gridLayout.padX && logicalOffsetY >= 0 && logicalOffsetY < totalRows * CONF.rowHeight) {
                    const colStride = gridLayout.cardWidth + gridLayout.gapX;
                    const relX = logicalOffsetX - gridLayout.padX;
                    const rowIdx = Math.floor(logicalOffsetY / CONF.rowHeight);
                    const colIdx = Math.floor(relX / Math.max(1, colStride));
                    const withinColX = relX - colIdx * colStride;
                    const withinRowY = logicalOffsetY - rowIdx * CONF.rowHeight;

                    if (colIdx >= 0 && colIdx < gridLayout.cols && withinColX >= 0 && withinColX <= gridLayout.cardWidth && withinRowY >= 0 && withinRowY <= gridLayout.cardHeight) {
                        startIdx = rowIdx * gridLayout.cols + colIdx;
                    }
                }
            }

            if (startIdx >= 0 && startIdx < S.display.length) {
                const hitItem = S.display[startIdx];
                startItem = (hitItem && !hitItem.isHeader) ? hitItem : null;
            }
        } else {
            const startOffsetY = logicalClientY - cachedVpRect.top + UI.vp.scrollTop;
            const startIdx = Math.floor(startOffsetY / CONF.rowHeight);
            if (startIdx >= 0 && startIdx < S.display.length) {
                const hitItem = S.display[startIdx];
                startItem = (hitItem && !hitItem.isHeader) ? hitItem : null;
            }
        }

        mqStartX = e.clientX;
        mqStartY = e.clientY;
        const prevActiveId = S.activeId;
        S.activeId = startItem ? startItem.id : null;

        if (!startItem && prevActiveId && S.getSelectedCount() === 0) {
            renderVisible();
            requestAnimationFrame(() => { if (UI.chkAll) UI.chkAll.checked = false; });
        }

        isMarquee = false;
        mqStartX = lastMouseX = e.clientX;
        mqStartY = lastMouseY = e.clientY;
        startScroll = UI.vp.scrollTop;
        window.addEventListener('mousemove', handleMarqueeMove);
        window.addEventListener('mouseup', stopMarquee);
    });

    let isScrollScheduled = false;
    let gridScrollEndTimer = 0;
    const markGridScrolling = () => {
        if (!isGridView() || !UI.win) return;
        UI.win.classList.add('pk-grid-scrolling');
        if (gridScrollEndTimer) clearTimeout(gridScrollEndTimer);
        gridScrollEndTimer = setTimeout(() => {
            gridScrollEndTimer = 0;
            if (UI.win) UI.win.classList.remove('pk-grid-scrolling');
        }, 90);
    };

    UI.vp.onscroll = () => {
        if (UI.ctx && UI.ctx.style.display !== 'none') UI.ctx.style.display = 'none';
        markGridScrolling();

        if (!isScrollScheduled) {
            requestAnimationFrame(() => {
                renderVisible();
                if (typeof isMarquee !== 'undefined' && isMarquee && !scrollRaf) {
                    updateMarqueeUIAndSelection(lastMouseX, lastMouseY);
                }
                isScrollScheduled = false;
            });
            isScrollScheduled = true;
        }
    };

    UI.vp.addEventListener('wheel', () => {
        if (UI.ctx && UI.ctx.style.display !== 'none') {
            UI.ctx.style.display = 'none';
        }
        markGridScrolling();
    }, { capture: true, passive: true });

    const handleBlankClick = async (e) => {
        if (UI.ctx && UI.ctx.style.display !== 'none') {
            UI.ctx.style.display = 'none';
        }

        if (e.target.closest('.pk-nav-btn') || e.target.closest('.pk-btn') || e.target.closest('.pk-btn-toggle') || e.target.closest('input') || e.target.closest('textarea') || e.target.closest('select') || e.target.closest('.pk-nav span') || e.target.closest('.pk-crumb-sep') || e.target.closest('.pk-search') || e.target.closest('label') || e.target.closest('.pk-dropdown-wrap') || e.target.closest('.pk-sort-opt') || e.target.closest('a')
           ) return;

        const selectedCount = S.getSelectedCount();
        if (selectedCount > 0 || S.activeId) {
            const isAnalyzeRoot = S.analyzeMode && S.path[S.path.length - 1].id === 'analyze_root';
            if ((S.dupMode || (isAnalyzeRoot && S.analyzeSimGroups)) && selectedCount > 5) {
                if (!await confirmSelectionClear()) return;
            }
            S.clearSelection();
            S.activeId = null;
            renderVisible();
            requestAnimationFrame(() => { if (UI.chkAll) UI.chkAll.checked = false; });
        }
    };

    const sidebarArea = el.querySelector('.pk-sidebar');
    if (sidebarArea) sidebarArea.onclick = handleBlankClick;
    const footerArea = el.querySelector('.pk-ft');
    if (footerArea) footerArea.onclick = handleBlankClick;
    const headerArea = el.querySelector('.pk-hd');
    if (headerArea) headerArea.onclick = handleBlankClick;
    const topBarArea = el.querySelector('#pk-top-bar');
    if (topBarArea) topBarArea.onclick = handleBlankClick;
    const actionArea = el.querySelector('#pk-actionbar');
    if (actionArea) actionArea.onclick = handleBlankClick;
    const trashArea = el.querySelector('#pk-trash-bar');
    if (trashArea) trashArea.onclick = handleBlankClick;

    el.addEventListener('click', (e) => {
        if (!S.activeId) return;
        if (e.target.closest('.pk-row')) return;
        if (e.target.closest('#pk-theme')) return;
        if (e.target.closest('#pk-view-switch') || e.target.closest('.pk-view-switch') || e.target.closest('#pk-view-list') || e.target.closest('#pk-view-grid') || e.target.closest('.pk-view-btn')) return;
        S.activeId = null;
        renderVisible();
    }, { capture: true });

    if (UI.vp) {
        UI.vp.addEventListener('click', (e) => {
            if (e.target.closest('.pk-row')) return;

            const rect = getLogicalRect(UI.vp);
            if (e.clientX > rect.left + UI.vp.clientWidth || e.clientY > rect.top + UI.vp.clientHeight) return;

            if (Math.abs(e.clientX - mqStartX) > 5 || Math.abs(e.clientY - mqStartY) > 5) return;

            handleBlankClick(e);
        });
    }

    const showCrumbDropdown = async (e, parentId, triggerEl) => {
        const getLiveTriggerEl = () => {
            if (triggerEl && triggerEl.isConnected) return triggerEl;
            if (!UI.crumb) return triggerEl;
            const idx = S.path.findIndex(p => p.id === parentId);
            const seps = UI.crumb.querySelectorAll('.pk-crumb-sep');
            return (idx >= 0 && seps[idx]) ? seps[idx] : triggerEl;
        };

        const activeTriggerEl = getLiveTriggerEl();

        if (activeTriggerEl && activeTriggerEl.classList.contains('pk-active')) {
            const oldPop = document.getElementById('pk-main-crumb-pop');
            if (oldPop) oldPop.remove();
            activeTriggerEl.classList.remove('pk-active');
            activeTriggerEl.innerHTML = CONF.crumbIcons.right;
            return;
        }
        let targetId = parentId || 'root';
        if (targetId === 'root' || targetId === '') {
            if (S.shareMode) targetId = 'share_root';
            else if (S.starredMode) targetId = 'starred_root';
            else targetId = 'root';
        }

        let folders = [];

        if (targetId === 'virtual_search_root' || targetId === 'analyze_root' || targetId === 'recent_root') {
            let sourceItems = [];
            const isAnalyzeSimMode = targetId === 'analyze_root' && S.analyzeSimGroups;

            if (isAnalyzeSimMode) {
                sourceItems = S.display.filter(item => !item.isHeader);
            } else if (targetId === 'analyze_root') {
                sourceItems = (S.analyzeResultItems && S.analyzeResultItems.length > 0) ? S.analyzeResultItems : S.items;
            } else if (targetId === 'recent_root') {
                if (S.recentResultItems && S.recentResultItems.length > 0) {
                    sourceItems = S.recentResultItems;
                } else if (typeof globalCache !== 'undefined' && globalCache.has('recent_root')) {
                    const cached = globalCache.get('recent_root');
                    sourceItems = Array.isArray(cached) ? cached : (cached.items || []);
                } else {
                    sourceItems = S.items || [];
                }
            } else {
                sourceItems = (S.lastGlobalResults && S.lastGlobalResults.length > 0) ? S.lastGlobalResults : S.items;
            }
            folders = sourceItems.filter(f => f.kind === 'drive#folder');

            if (folders.length === 0) {
                const pop = document.createElement('div');
                pop.className = 'pk-crumb-pop';
                pop.innerHTML = `<div class="pk-crumb-item" style="color:#888;justify-content:center;">${L.str_no_files}</div>`;
                document.body.appendChild(pop);

                const liveTriggerEl = getLiveTriggerEl();
                if (!liveTriggerEl || !liveTriggerEl.isConnected) {
                    pop.remove();
                    return;
                }

                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                pop.style.zoom = scale;
                const rect = getLogicalRect(liveTriggerEl);

                pop.style.left = (rect.left - 4) + 'px';
                pop.style.top = (rect.bottom + 6) + 'px';
                pop.classList.add('pk-show');
                setTimeout(() => {
                    const closer = (evt) => {
                        pop.remove();
                        document.removeEventListener('mousedown', closer);
                    };
                    document.addEventListener('mousedown', closer);
                }, 10);
                return;
            }
        } else {
            const listId = targetId === 'root' ? '' : targetId;
            const cacheKey = targetId === 'root' ? 'root' : targetId;
            const isDirty = typeof globalDirtyFolders !== 'undefined' && globalDirtyFolders.has(listId);

            const rawCached = !isDirty
            ? ((typeof globalCache !== 'undefined' && globalCache.has(cacheKey)) ? globalCache.get(cacheKey) : S.cache.get(cacheKey))
            : null;
            const cachedItems = (rawCached && !Array.isArray(rawCached) && rawCached.items) ? rawCached.items : (Array.isArray(rawCached) ? rawCached : null);
            const cachedFolders = cachedItems ? cachedItems.filter(f => f.kind === 'drive#folder') : null;

            if (cachedFolders && cachedFolders.length > 0) {
                folders = cachedFolders;
            } else {
                folders = [{ id: 'loading', name: L.loading, kind: 'drive#folder' }];
                apiList(listId).then(res => {
                    if (typeof globalCache !== 'undefined') globalCache.set(cacheKey, res);
                    if (S.cache) S.cache.set(cacheKey, res);
                    if (typeof globalDirtyFolders !== 'undefined') globalDirtyFolders.delete(listId);

                    const freshFolders = Array.isArray(res) ? res.filter(f => f.kind === 'drive#folder') : [];
                    const pop = document.getElementById('pk-main-crumb-pop');
                    if (!pop || pop.dataset.targetId !== targetId) return;

                    renderMenu(freshFolders.length > 0 ? freshFolders : [{ id: 'error', name: L.str_no_files, kind: 'drive#folder' }]);
                }).catch(() => {
                    const pop = document.getElementById('pk-main-crumb-pop');
                    if (pop && pop.dataset.targetId === targetId) {
                        renderMenu([{ id: 'error', name: L.str_load_failed, kind: 'drive#folder' }]);
                    }
                });
            }
        }

        const renderMenu = (list) => {
            let s = S.sort, d = S.dir;

            const isStandard = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.historyMode && !S.isFlattened && !S.dupMode && !S.analyzeMode && !S.path.some(p => p.id === 'virtual_search_root');

            if (isStandard) {
                const isNormalFolder = targetId && !targetId.includes('_root');
                if (isNormalFolder) {
                    if (gmGet('pk_sort_independent', false)) {
                        const prefs = JSON.parse(gmGet('pk_folder_sort_prefs', '{}'));
                        if (prefs[targetId]) { s = prefs[targetId].sort; d = prefs[targetId].dir; }
                        else { s = 'modified_time'; d = 1; }
                    } else {
                        const g = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                        s = g.sort; d = g.dir;
                    }
                }
            }

            const isAnalyzeSimMode = S.analyzeMode && targetId === 'analyze_root' && S.analyzeSimGroups;

            if (!isAnalyzeSimMode) {
                const collator = new Intl.Collator(({ 'zh': 'zh-CN', 'tc': 'zh-TW', 'ja': 'ja', 'ko': 'ko' })[getLang()] || 'en', { numeric: true, sensitivity: 'base' });
                const parseSize = n => parseInt(n || 0);
                const parseTime = t => t ? new Date(t).getTime() : 0;

                const getCharWeight = (str) => {
                    if (!str) return 0;
                    const c = str.charAt(0);
                    if (/[0-9]/.test(c)) return 20;
                    if (/[\u4e00-\u9fa5]/.test(c)) return 30;
                    if (/[a-zA-Z]/.test(c)) return 40;
                    return 10;
                };

                const compareNames = (nameA, nameB) => {
                    const rankA = getCharWeight(nameA);
                    const rankB = getCharWeight(nameB);
                    if (rankA !== rankB) return rankA - rankB;
                    return collator.compare(nameA, nameB);
                };

                list.sort((a, b) => {
                    const isSysA = a.name === CONF.SYSTEM_FOLDER_NAME && (!a.parent_id || a.parent_id === '' || a.parent_id === 'root');
                    const isSysB = b.name === CONF.SYSTEM_FOLDER_NAME && (!b.parent_id || b.parent_id === '' || b.parent_id === 'root');
                    if (isSysA !== isSysB) return isSysA ? -1 : 1;

                    if (s === 'play_time') {
                        const tA = a._history_ts || 0;
                        const tB = b._history_ts || 0;
                        if (tA !== tB) return (tB - tA) * d;
                    } else if (s === 'modified_time') {
                        const tA = parseTime(a.modified_time);
                        const tB = parseTime(b.modified_time);
                        if (tA !== tB) return (tB - tA) * d;
                    } else if (s === 'created_time') {
                        const tA = parseTime(a.created_time);
                        const tB = parseTime(b.created_time);
                        if (tA !== tB) return (tB - tA) * d;
                    } else if (s === 'size') {
                        const sizeA = parseSize(a.size);
                        const sizeB = parseSize(b.size);
                        if (sizeA !== sizeB) return (sizeB - sizeA) * d;
                    } else if (s === 'play_time') {
                        const ptA = a._history_ts || 0;
                        const ptB = b._history_ts || 0;
                        if (ptA !== ptB) return (ptB - ptA) * d;
                    } else if (s === 'view_count' || s === 'save_count') {
                        const valA = s === 'view_count' ? parseSize(a.view_count) : parseSize(a.save_count);
                        const valB = s === 'view_count' ? parseSize(b.view_count) : parseSize(b.save_count);
                        if (valA !== valB) return (valB - valA) * d;
                    } else if (s === 'starred') {
                        const isItemStarred = (item) => S.starredSet.has(item.id) || !!(item.starred || (item.tags && item.tags.some(t => t.name === 'STAR')));
                        const stA = isItemStarred(a) ? 1 : 0;
                        const stB = isItemStarred(b) ? 1 : 0;
                        if (stA !== stB) return stB - stA;
                        const tA = parseTime(a.modified_time);
                        const tB = parseTime(b.modified_time);
                        return (tB - tA) * d;
                    } else if (s === 'path') {
                        const getPath = (item) => {
                            if (S.analyzeMode) return item._pathStr || "";
                            if (item._lineage) return item._lineage.map(x => x.name).join('/');
                            return "";
                        };
                        const pA = getPath(a);
                        const pB = getPath(b);
                        const pathCmp = compareNames(pA, pB);
                        if (pathCmp !== 0) return pathCmp * d;
                    }

                    return compareNames(a.name, b.name) * d;
                });
            }

            let pop = document.getElementById('pk-main-crumb-pop');
            if (!pop) {
                pop = document.createElement('div');
                pop.id = 'pk-main-crumb-pop';
                pop.className = 'pk-crumb-pop pk-scroll';
                if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) pop.classList.add('pk-dark');
                pop.style.top = '-9999px';
                document.body.appendChild(pop);
            }
            pop.dataset.targetId = targetId;
            pop.innerHTML = '';

            const movingIds = (typeof pkState !== 'undefined') ? pkState.movingIds : new Set();
            list.forEach(f => {
                const item = document.createElement('div');
                const isMoving = movingIds.has(f.id);
                item.className = 'pk-crumb-item' + (isMoving ? ' pk-moving' : '');
                item.dataset.id = f.id;

                const iconSrc = f.icon_link || '';
                const fallbackSvg = CONF.typeIcons.folder.replace(/width="\d+"/, 'width="18"').replace(/height="\d+"/, 'height="18"');

                const iconHtml = iconSrc
                ? `<img src="${iconSrc}" style="width:18px;height:18px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;flex-shrink:0;">${fallbackSvg}</span>`
                : fallbackSvg;

                const isInPath = S.path.some(pathNode => pathNode.id === f.id);
                const textStyle = isInPath ? 'font-weight:bold; color:var(--pk-pri);' : '';
                const isProtected = typeof isSystemItem === 'function' && isSystemItem(f);
                const tagHtml = isProtected ? `<span class="pk-tag-default">${L.tag_default}</span>` : '';

                item.innerHTML = `${iconHtml}<span class="pk-crumb-name-wrap"><span class="pk-crumb-name" style="${textStyle}">${esc(f.name)}</span>${tagHtml}</span>`;

                if (f.id !== 'loading' && f.id !== 'error') {
                    item.onclick = (ev) => {
                        ev.stopPropagation();
                        closeMenu();

                        if (targetId === 'virtual_search_root' || targetId === 'analyze_root' || targetId === 'recent_root') {
                            let rootName = L.str_search_results;
                            if (targetId === 'analyze_root') rootName = L.str_analyze_results;
                            else if (targetId === 'recent_root') rootName = L.btn_nav_recent;

                            const newPath = [];

                            if (targetId === 'virtual_search_root') {
                                newPath.push({ id: '', name: L.btn_nav_home });
                            }

                            newPath.push({ id: targetId, name: rootName });
                            newPath.push({ id: f.id, name: f.name });

                            S.path = newPath;
                        } else {
                            const idx = S.path.findIndex(p => p.id === parentId);
                            if (idx !== -1) {
                                S.path = S.path.slice(0, idx + 1);
                                S.path.push({ id: f.id, name: f.name });
                            }
                        }
                        load();
                    };
                }
                pop.appendChild(item);
            });

            requestAnimationFrame(() => {
                const liveTriggerEl = getLiveTriggerEl();
                if (!liveTriggerEl || !liveTriggerEl.isConnected) return;

                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                pop.style.zoom = scale;
                const rect = getLogicalRect(liveTriggerEl);
                const popRect = pop.getBoundingClientRect();

                let left = rect.left - 4;
                const popW = popRect.width / scale;
                const winW = window.innerWidth / scale;

                if (left + popW > winW) left = winW - popW - 15;
                pop.style.left = Math.max(10, left) + 'px';
                pop.style.top = (rect.bottom + 6) + 'px';

                pop.classList.add('pk-show');
                liveTriggerEl.classList.add('pk-active');
                liveTriggerEl.innerHTML = CONF.crumbIcons.down;
            });

            const closeMenu = () => {
                if (pop.parentNode) pop.remove();

                const liveTriggerEl = getLiveTriggerEl();
                if (liveTriggerEl) {
                    liveTriggerEl.classList.remove('pk-active');
                    liveTriggerEl.innerHTML = CONF.crumbIcons.right;
                    const svg = liveTriggerEl.querySelector('svg');
                    if (svg) {
                        svg.style.width = '14px'; svg.style.height = '14px';
                        svg.style.display = 'block'; svg.style.opacity = '0.6';
                    }
                }
                document.removeEventListener('mousedown', onOutsideClick);
                window.removeEventListener('resize', closeMenu);
                UI.vp.removeEventListener('scroll', closeMenu);
            };
            const onOutsideClick = (evt) => {
                const liveTriggerEl = getLiveTriggerEl();
                if (!pop.contains(evt.target) && !(liveTriggerEl && liveTriggerEl.contains(evt.target))) closeMenu();
            };
            window.addEventListener('resize', closeMenu);
            UI.vp.addEventListener('scroll', closeMenu, { passive: true });
            setTimeout(() => document.addEventListener('mousedown', onOutsideClick), 10);
        };

        renderMenu(folders || []);
    };

    function renderCrumb() {
        UI.crumb.innerHTML = '';

        if (S.offlineMode) {
            UI.crumb.style.pointerEvents = 'none';
            UI.crumb.innerHTML = `
                <div style="display:flex; align-items:center; color:var(--pk-fg); margin-left: -6px;">
                    <span style="font-weight:bold; font-size:15px; cursor:default;">${L.title_offline}</span>
                </div>
            `;
            return;
        }

        if (S.uploadMode) {
            UI.crumb.style.pointerEvents = 'none';
            UI.crumb.innerHTML = `
                <div style="display:flex; align-items:center; color:var(--pk-fg); margin-left: -6px;">
                    <span style="font-weight:bold; font-size:15px; cursor:default;">${L.btn_nav_upload}</span>
                </div>
            `;
            return;
        }

        if (S.shareMode) {
            UI.crumb.style.pointerEvents = 'none';
            UI.crumb.innerHTML = `
                <div style="display:flex; align-items:center; color:var(--pk-fg); margin-left: -6px;">
                    <span style="font-weight:bold; font-size:15px; cursor:default;">${L.btn_nav_share}</span>
                </div>
            `;
            return;
        }

        if (S.historyMode) {
            UI.crumb.style.pointerEvents = 'none';
            UI.crumb.innerHTML = `
                <div style="display:flex; align-items:baseline; color:var(--pk-fg); margin-left: -6px;">
                    <span style="font-weight:bold; font-size:15px; cursor:default;">${L.btn_nav_history}</span>
                    <span style="color:#888; font-size:11px; margin-left:10px; cursor:default; font-weight:normal; opacity:0.8;">${L.history_notice}</span>
                </div>
            `;
            return;
        }

        if (S.trashMode) {
            UI.crumb.style.pointerEvents = 'none';
            UI.crumb.innerHTML = `
                <div style="display:flex; align-items:baseline; color:var(--pk-fg); margin-left: -6px;">
                    <span style="font-weight:bold; font-size:15px; cursor:default;">${L.trash_title}</span>
                    <span style="color:#888; font-size:11px; margin-left:10px; cursor:default; font-weight:normal; opacity:0.8;">${L.trash_notice}</span>
                </div>
            `;
            return;
        }
        UI.crumb.style.pointerEvents = 'auto';

        const svgStyle = 'width:14px;height:14px;vertical-align:-4px;margin-right:4px;display:inline-block;';
        const ICON_HOME = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="${svgStyle}"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>`;
        const ICON_SEARCH = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="${svgStyle}"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>`;
        const ICON_TRASH = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="${svgStyle}"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>`;
        const ICON_STAR = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="${svgStyle}"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>`;

        S.path.forEach((p, i) => {
            const s = document.createElement('span');

            const isHome = (p.id === '' || p.id === 'root');
            const isSearch = (p.id === 'virtual_search_root');
            const isAnalyze = (p.id === 'analyze_root');
            const isRecentRoot = (p.id === 'recent_root');

            if (isHome) {
                let icon = ICON_HOME;
                if (S.trashMode) icon = ICON_TRASH;
                else if (S.starredMode) icon = ICON_STAR;

                s.innerHTML = `${icon}${p.name}`;
            }
            else if (isSearch || isAnalyze) {
                s.innerHTML = `${ICON_SEARCH}${p.name}`;
            }
            else if (isRecentRoot) {
                const ICON_RECENT = CONF.icons.recent.replace('<svg', `<svg style="${svgStyle}"`);
                s.innerHTML = `${ICON_RECENT}${p.name}`;
            }
            else {
                s.textContent = p.name;
            }

            s.dataset.id = (p.id === '' || !p.id) ? 'root' : p.id;
            s.className = i === S.path.length - 1 ? 'act' : '';

            s.style.cssText = "display:inline-flex; align-items:center; padding:2px 6px; border-radius:4px; flex-shrink:0; transition:background 0.2s; white-space:nowrap; margin: auto 2px;";

            s.onclick = (e) => {
                e.stopPropagation();
                if (i !== S.path.length - 1) {
                    if (i < S.path.length - 1) {
                        S.latestChildId = S.path[i + 1].id;
                    }

                    S.folderFirst = false;
                    if (S.renderFolderFirst) S.renderFolderFirst();

                    S.path = S.path.slice(0, i + 1);
                    load();
                } else {
                    if (UI.btnRefresh && !UI.btnRefresh.disabled) UI.btnRefresh.click();
                }
            };

            UI.crumb.appendChild(s);

            const isLast = i === S.path.length - 1;
            let showArrow = !isLast;

            if (isLast) {
                const isUnsupportedVirtual = p.id === 'history_root' || p.id === 'offline_root' || p.id === 'upload_root';
                if (!S.isFlattened && !S.dupMode && !isUnsupportedVirtual) {
                    if (S.items && S.items.some(item => item.kind === 'drive#folder')) {
                        showArrow = true;
                    }
                }
            }

            if (showArrow) {
                const sep = document.createElement('span');
                sep.className = 'pk-crumb-sep';
                sep.innerHTML = CONF.crumbIcons.right;
                sep.onclick = (e) => {
                    e.stopPropagation();
                    showCrumbDropdown(e, p.id, sep);
                };
                UI.crumb.appendChild(sep);
            }
        });

        S._crumbIdx = S.path.length - 1;

        UI.crumb.onwheel = (e) => {
            e.preventDefault();

            const existingPop = document.getElementById('pk-main-crumb-pop');
            if (existingPop) {
                existingPop.remove();
                UI.crumb.querySelectorAll('.pk-crumb-sep').forEach(sep => {
                    sep.classList.remove('pk-active');
                    sep.innerHTML = CONF.crumbIcons.right;
                });
            }

            const nodes = [...UI.crumb.querySelectorAll('span:not(.pk-crumb-sep)')];
            if (!nodes.length) return;

            const now = Date.now();
            if (now - (S._lastCrumbScroll || 0) < 120) return;
            S._lastCrumbScroll = now;

            if (e.deltaY < 0) S._crumbIdx = Math.max(0, S._crumbIdx - 1);
            else S._crumbIdx = Math.min(nodes.length - 1, S._crumbIdx + 1);

            const targetNode = nodes[S._crumbIdx];
            const containerWidth = UI.crumb.offsetWidth;

            const centerOffset = targetNode.offsetLeft + (targetNode.offsetWidth / 2) - (containerWidth / 2);

            UI.crumb.scrollTo({
                left: centerOffset,
                behavior: 'smooth'
            });
        };

        setTimeout(() => {
            if (UI.crumb) {
                UI.crumb.scrollTo({ left: UI.crumb.scrollWidth, behavior: 'smooth' });
            }
        }, 0);
    }

    el.tabIndex = 0; el.focus();

    const keyHandler = (e) => {
        const win = document.querySelector('.pk-ov');
        if (!win || win.style.display === 'none') return;

        const tag = e.target.tagName;

        if (tag === 'TEXTAREA' || (tag === 'INPUT' && e.target.type !== 'checkbox' && e.target.type !== 'button' && e.target.type !== 'submit')) return;

        if (e.key === 'Escape') {
            if (typeof isSearchHistOpen === 'function' && isSearchHistOpen()) {
                closeSearchHist();
                return;
            }

            const pTip = document.getElementById('pk_p_plist_tip_global');
            if (pTip) pTip.style.display = 'none';

            if (S.closePlayerOverlay()) return;
            if (S.closeImageOverlay()) return;
            if (S.closeTopModalOverlay()) return;
            if (UI.ctx.style.display === 'block') { UI.ctx.style.display = 'none'; return; }

            if (S.getSelectedCount() > 0) {
                S.clearSelection();
                refresh();
            } else if (UI.win.classList.contains('pk-maximized')) {
                if (!isTurbo && btnMax) btnMax.click();
            } else {
                if (!isTurbo) UI.btnClose.click();
            }
            return;
        }

        const hasMediaOverlay = document.querySelector('.pk-img-ov, #pk-player-ov');
        if (hasMediaOverlay) return;

        if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
            if (e.key === 'f' || e.key === 'F') {
                if (UI.btnImgSearch && !UI.btnImgSearch.disabled && UI.btnImgSearch.style.display !== 'none') {
                    e.preventDefault();
                    UI.btnImgSearch.click();
                    return;
                }
            }
        }

        if (e.key === 'Delete' && e.altKey) {
            e.preventDefault();
            if (S.uploadMode) return;

            const existingModal = Array.from(document.querySelectorAll('.pk-modal-ov')).find(m => {
                const title = m.querySelector('h3');
                return title && title.textContent.includes(L.title_blacklist);
            });

            if (existingModal) {
                existingModal.remove();
            } else {
                showBlacklistModal();
            }
        }

        if (e.altKey && (e.key === 'h' || e.key === 'H')) {
            e.preventDefault();
            const existingHelp = Array.from(document.querySelectorAll('.pk-modal-ov')).find(m => {
                const title = m.querySelector('h3');
                return title && title.textContent === L.modal_help_title;
            });
            if (existingHelp) {
                existingHelp.remove();
            } else if (UI.btnHelp) {
                UI.btnHelp.click();
            }
            return;
        }

        const hasActiveOverlay = document.querySelector('.pk-modal-ov');
        if (hasActiveOverlay) return;

        if (e.key === 'F2') {
            e.preventDefault();
            if (S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode) return;

            const selectedCount = S.getSelectedCount();
            if (selectedCount === 1) {
                if (UI.btnRename.disabled) return;
                UI.btnRename.click();
            }
            else if (selectedCount > 1) {
                if (UI.btnBulkRename.disabled) return;
                UI.btnBulkRename.click();
            }
        }

        if (e.key === 'F5') {
            e.preventDefault();
            if (S.uploadMode) load(false, true);
            else if (S.trashMode) load(false, true);
            else UI.btnRefresh.click();
        }

        if (e.key === 'F8') {
            e.preventDefault();

            const cur = S.path[S.path.length - 1];
            const isStarredRoot = S.starredMode && S.path.length === 1;
            const isRecentRoot = S.recentMode && S.path.length === 1;
            const isHistoryRoot = S.historyMode && S.path.length === 1;
            const isBlocked = S.isFlattened || S.dupMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || isStarredRoot || isRecentRoot || isHistoryRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';
            if (isBlocked) return;
            UI.btnNewFolder.click();
        }

        if (e.key === 'r' || e.key === 'R') {
            if (S.trashMode && !e.ctrlKey && !e.altKey && !e.metaKey) {
                e.preventDefault();
                if (UI.btnRestore && !UI.btnRestore.disabled) UI.btnRestore.click();
                return;
            }
            if (S.offlineMode && !e.ctrlKey && !e.altKey && !e.metaKey) {
                e.preventDefault();
                if (UI.btnRetryTask && !UI.btnRetryTask.disabled) UI.btnRetryTask.click();
                return;
            }
        }

        if (e.altKey && (e.key === 'm' || e.key === 'M')) {
            e.preventDefault();
            if (UI.btnMigrate && !UI.btnMigrate.disabled && UI.btnMigrate.style.display !== 'none') {
                UI.btnMigrate.click();
            }
            return;
        }

        if ((e.key === 'm' || e.key === 'M') && !e.altKey && !e.ctrlKey && !e.metaKey) {
            const imgPlayer = document.querySelector('.pk-img-ov');
            const vidPlayer = document.getElementById('pk-player-ov');
            if (!imgPlayer && !vidPlayer) {
                const btnMax = document.querySelector('#pk-maximize');
                if (!isTurbo && btnMax) btnMax.click();
            }
        }

        if (e.key === 'Delete' && e.shiftKey) {
            if (S.trashMode && UI.btnEmptyTrash) {
                e.preventDefault();
                UI.btnEmptyTrash.click();
                return;
            }
            if (S.uploadMode && UI.btnUpClearAll) {
                e.preventDefault();
                UI.btnUpClearAll.click();
                return;
            }
        }

        if (e.key === 'Delete' && !e.ctrlKey && !e.altKey && !e.shiftKey) {
            if (S.shareMode) {
                if (UI.btnCancelShare && !UI.btnCancelShare.disabled) UI.btnCancelShare.click();
                return;
            }

            if (S.uploadMode) {
                if (UI.btnUpDel && !UI.btnUpDel.disabled) UI.btnUpDel.click();
                return;
            }

            if (S.trashMode) {
                if(UI.btnDelForever && !UI.btnDelForever.disabled) UI.btnDelForever.click();
            } else {
                if (!UI.btnDel.disabled) UI.btnDel.click();
            }
        }

        if (e.key === 'Delete' && e.ctrlKey) {
            e.preventDefault();
            if (S.trashMode) return;
            if (S.isFlattened || S.dupMode || S.uploadMode) return;
            if (!UI.btnPrune.disabled) UI.btnPrune.click();
        }

        if (e.ctrlKey || e.metaKey) {

            if (e.key === 'a' || e.key === 'A') {
                e.preventDefault();
                if (S.handleSelectAll) S.handleSelectAll();
            }

            if (e.key === 'c' || e.key === 'C') {
                if (window.getSelection().toString()) return;
                e.preventDefault();
                if (S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode) return;
                if (!UI.btnCopy.disabled) UI.btnCopy.click();
            }

            if (e.key === 'x' || e.key === 'X') {
                e.preventDefault();
                if (S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode) return;
                if (!UI.btnCut.disabled) UI.btnCut.click();
            }

            if (e.key === 'v' || e.key === 'V') {
                e.preventDefault();
                if (S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode) return;

                const cur = S.path[S.path.length - 1];
                const isStarredRoot = S.starredMode && S.path.length === 1;
                const isRecentRoot = S.recentMode && S.path.length === 1;
                const isHistoryRoot = S.historyMode && S.path.length === 1;
                const isPasteBlocked = S.isFlattened || S.dupMode || S.offlineMode || S.historyMode || isStarredRoot || isRecentRoot || isHistoryRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';
                if (isPasteBlocked) return;

                if (!UI.btnPaste.disabled) UI.btnPaste.click();
            }
        }

        if (e.altKey) {
            if (S.offlineMode && (e.key === 'c' || e.key === 'C')) {
                e.preventDefault();
                if (UI.btnCopyLinkOffline && !UI.btnCopyLinkOffline.disabled) UI.btnCopyLinkOffline.click();
                return;
            }
            if (e.key === 's' || e.key === 'S') {
                e.preventDefault();
                openSettingsModal();
            }
            if (e.key === 'g' || e.key === 'G') {
                e.preventDefault();
                if (S.uploadMode) {
                    if (UI.btnUpStart && !UI.btnUpStart.disabled) UI.btnUpStart.click();
                }
            }
            if (e.key === 'p' || e.key === 'P') {
                e.preventDefault();
                if (S.uploadMode) {
                    if (UI.btnUpPause && !UI.btnUpPause.disabled) UI.btnUpPause.click();
                }
            }
            if (e.key === 'u' || e.key === 'U') {
                e.preventDefault();
                const cur = S.path[S.path.length - 1];
                const isRoot = S.path.length === 1;
                const isUnzipBlocked = S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || S.isFlattened || S.dupMode || (S.analyzeMode && cur.id === 'analyze_root') || (cur.id === 'virtual_search_root') || (S.recentMode && isRoot) || (S.starredMode && isRoot);
                if (!isUnzipBlocked && !UI.btnUnzip.disabled) UI.btnUnzip.click();
            }

            if (e.key === 'd' || e.key === 'D') {
                e.preventDefault();
                if (S.shareMode || S.uploadMode || S.trashMode || S.offlineMode) return;
                if (UI.btnDown && !UI.btnDown.disabled) UI.btnDown.click();
            }
            if (e.key === 'a' || e.key === 'A') {
                e.preventDefault();
                if (S.shareMode || S.uploadMode || S.trashMode || S.offlineMode) return;
                if (UI.btnAria2 && !UI.btnAria2.disabled) UI.btnAria2.click();
            }
            if (e.key === 'e' || e.key === 'E') {
                e.preventDefault();
                if (S.shareMode || S.trashMode) return;
                if (UI.btnExt && !UI.btnExt.disabled) UI.btnExt.click();
            }
            if (e.key === 't' || e.key === 'T') {
                e.preventDefault();
                if (UI.btnTheme) UI.btnTheme.click();
            }
        }
    };

    document.addEventListener('keydown', keyHandler);

    if (document._pkPlayerCloseClickHandler) {
        document.removeEventListener('click', document._pkPlayerCloseClickHandler, true);
    }
    document._pkPlayerCloseClickHandler = (e) => {
        const btn = e.target && e.target.closest ? e.target.closest('#pk_p_close') : null;
        if (!btn) return;
        e.preventDefault();
        e.stopPropagation();
        if (e.stopImmediatePropagation) e.stopImmediatePropagation();
        S.closePlayerOverlay();
    };
    document.addEventListener('click', document._pkPlayerCloseClickHandler, true);

    if (document._pkImageCloseClickHandler) {
        document.removeEventListener('click', document._pkImageCloseClickHandler, true);
    }
    document._pkImageCloseClickHandler = (e) => {
        const btn = e.target && e.target.closest ? e.target.closest('#pk_img_close') : null;
        if (!btn) return;
        e.preventDefault();
        e.stopPropagation();
        if (e.stopImmediatePropagation) e.stopImmediatePropagation();
        S.closeImageOverlay();
    };
    document.addEventListener('click', document._pkImageCloseClickHandler, true);

    let sideMouseBusy = false;
    let lastSideMouseBtn = -1;
    let lastSideMouseTs = 0;

    const mouseSideNavHandler = async (e) => {
        const btn = e.button;
        if (btn !== 3 && btn !== 4) return;

        const win = document.querySelector('.pk-ov');
        if (!win || win.style.display === 'none') return;

        S.sideNavLog('event', { type: e.type, button: btn, target: e.target });

        const now = Date.now();
        if (lastSideMouseBtn === btn && (now - lastSideMouseTs) < 250) {
            S.sideNavLog('blocked: duplicate');
            e.preventDefault();
            e.stopPropagation();
            if (e.stopImmediatePropagation) e.stopImmediatePropagation();
            return;
        }
        lastSideMouseBtn = btn;
        lastSideMouseTs = now;

        const hasFrontOverlay = !!document.querySelector('.pk-modal-ov, .pk-img-ov, #pk-player-ov');
        if (!hasFrontOverlay) {
            const t = e.target;
            if (t && (t.closest('textarea') || t.closest('input:not([type="checkbox"]):not([type="button"]):not([type="submit"])') || t.closest('[contenteditable="true"]'))) {
                S.sideNavLog('pass-through: editing target');
                return;
            }
        }

        e.preventDefault();
        e.stopPropagation();
        if (e.stopImmediatePropagation) e.stopImmediatePropagation();

        if (sideMouseBusy) {
            S.sideNavLog('blocked: busy');
            return;
        }
        sideMouseBusy = true;

        try {
            if (btn === 3) {
                S.sideNavLog('dispatch: back');
                await S.handleMouseSideBack();
            } else {
                S.sideNavLog('dispatch: forward');
                await S.handleMouseSideForward();
            }
        } catch (err) {
            S.sideNavLog('error', err);
            throw err;
        } finally {
            setTimeout(() => { sideMouseBusy = false; }, 0);
        }
    };

    if (window._pkMouseSideNavHandler) {
        window.removeEventListener('mousedown', window._pkMouseSideNavHandler, true);
        window.removeEventListener('mouseup', window._pkMouseSideNavHandler, true);
        window.removeEventListener('auxclick', window._pkMouseSideNavHandler, true);
        window.removeEventListener('click', window._pkMouseSideNavHandler, true);
    }
    window._pkMouseSideNavHandler = mouseSideNavHandler;
    window.addEventListener('mousedown', mouseSideNavHandler, { capture: true, passive: false });
    window.addEventListener('mouseup', mouseSideNavHandler, { capture: true, passive: false });
    window.addEventListener('auxclick', mouseSideNavHandler, { capture: true, passive: false });
    window.addEventListener('click', mouseSideNavHandler, { capture: true, passive: false });

    const showProperty = (item) => {
        const L = getStrings();
        let rows = [];

        rows.push({ k: L.lbl_prop_name, v: item.name });
        rows.push({ k: L.lbl_prop_size, v: fmtSize(item.size) });
        rows.push({ k: L.lbl_prop_ctime, v: fmtDate(item.created_time) });
        if (item.modified_time) rows.push({ k: L.lbl_prop_mtime, v: fmtDate(item.modified_time) });

        let source = L.str_prop_unknown;
        if (item.kind === 'drive#task') source = L.str_prop_offline;
        else if (item.kind === 'drive#folder') source = L.lbl_type_folder;
        else if (item.from === 'share') source = L.str_prop_share;
        else source = L.str_prop_user;
        rows.push({ k: L.lbl_prop_source, v: source });

        let link = item.source_url || (item.params && item.params.url) || item.web_content_link || item.url;

        if (!link && item.params && typeof item.params === 'object') {
             for (const key in item.params) {
                 if (key.toLowerCase() === 'url') {
                     link = item.params[key];
                     break;
                 }
             }
        }

        if (link) {
            rows.push({ k: L.lbl_prop_link, v: link, isLink: true });
        }

        let pathStr = "Root";
        if (S.offlineMode) pathStr = L.title_offline;
        else if (S.shareMode) pathStr = L.btn_nav_share;
        else if (item._lineage) pathStr = item._lineage.map(x => x.name).join('/');
        rows.push({ k: L.lbl_prop_path, v: pathStr });

        let html = `<div style="display:flex; flex-direction:column; gap:16px; padding-top:10px;">`;
        rows.forEach(r => {
            let valHtml = `<div style="flex:1; word-break:break-all; color:var(--pk-fg); user-select:text; line-height:1.5;">${esc(r.v)}</div>`;

            if (r.isLink) {
                valHtml = `
                    <div style="flex:1; display:flex; gap:8px; align-items:flex-start; min-width:0;">
                        <div style="flex:1; word-break:break-all; color:var(--pk-pri); cursor:pointer; overflow:hidden; text-overflow:ellipsis; display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical;"
                             title="${esc(r.v)}" onclick="navigator.clipboard.writeText(this.title); showToast('${L.msg_copy_success}')">
                             ${esc(r.v)}
                        </div>
                        <button class="pk-btn" style="padding:0 10px; height:26px; font-size:12px; flex-shrink:0; margin-top:-2px;"
                                onclick="navigator.clipboard.writeText('${esc(r.v)}'); showToast('${L.msg_copy_success}')">
                            ${L.btn_copy_text}
                        </button>
                    </div>
                `;
            }

            html += `
                <div style="display:flex; align-items:baseline; font-size:13px;">
                    <div style="width:70px; color:#888; flex-shrink:0; text-align:right; margin-right:15px;">${r.k}:</div>
                    ${valHtml}
                </div>
            `;
        });
        html += `</div>`;

        const m = showModal(html);
        m.querySelector('.pk-modal h3').textContent = L.title_property;
        m.querySelector('.pk-modal').style.width = "520px";
    };

    const btnProp = document.getElementById('ctx-property');
    if (btnProp) {
        btnProp.onclick = () => {
            const item = S.itemMap.get(S.activeId);
            if (item) showProperty(item);
            UI.ctx.style.display = 'none';
        };
    }

    const mouseHandler = (e) => {
        if (!document.querySelector('.pk-ov') || !UI || !UI.ctx) return;

        if (UI.ctx.style.display !== 'none' && !UI.ctx.contains(e.target)) {
            UI.ctx.style.display = 'none';
            UI.ctx.style.flexDirection = '';

            const isRoot = S.path.length === 1 && S.path[0].id === '';
            if (!S.trashMode && isRoot && S.getSelectedCount() === 1) {
                const id = S.getSelectedIds()[0];
                const item = S.itemMap.get(id);
                if (item && item.kind === 'drive#folder' && item.name === CONF.SYSTEM_FOLDER_NAME) {
                    S.clearSelection();
                    renderVisible();
                    updateStat();
                }
            }
        }
    };
    document.addEventListener('mouseup', mouseHandler);

    function updateStat() {
        let total = 0;
        const visibleIdSet = new Set();
        const len = S.display.length;

        for (let i = 0; i < len; i++) {
            const item = S.display[i];
            if (item && !item.isHeader && !(S.movingIds && S.movingIds.has(item.id))) {
                total++;
                visibleIdSet.add(item.id);
            }
        }

        if (S.selMode === 'all') {
            const validExcludedIds = new Set();
            for (const id of S.selEx) {
                if (visibleIdSet.has(id)) validExcludedIds.add(id);
            }
            if (validExcludedIds.size !== S.selEx.size) {
                S.selEx = validExcludedIds;
            }
        } else {
            const validSelectedIds = S.getSelectedIds().filter(id => visibleIdSet.has(id));
            const hadShrink = validSelectedIds.length !== S.getSelectedCount();
            if (hadShrink) {
                S.setExplicitSelection(validSelectedIds);
            }
        }

        const selectedIds = S.getSelectedIds();
        let n = selectedIds.length;

        const btnInvert = document.getElementById('pk-btn-invert');
        if (btnInvert) {
            btnInvert.style.display = (!S.dupMode && n > 0) ? 'flex' : 'none';
            btnInvert.style.color = 'var(--pk-fg)';
        }

        const btnGridInvert = document.getElementById('pk-grid-invert');
        if (btnGridInvert) {
            btnGridInvert.style.display = (!S.dupMode && n > 0) ? 'flex' : 'none';
            btnGridInvert.style.color = 'var(--pk-fg)';
        }

        let hasProtectedItem = false;
        let hasSelFolder = false;
        let selSize = 0;

        if (n > 0) {
            for (const id of selectedIds) {
                const item = S.itemMap.get(id);
                if (!item) continue;
                const sz = parseInt(item.size || 0);

                if (item.kind === 'drive#folder') {
                    hasSelFolder = true;
                    if (S.analyzeMode) {
                        selSize += sz;
                    }
                } else {
                    selSize += sz;
                }
                if (!S.trashMode && isSystemItem(item)) hasProtectedItem = true;
            }
        }

        let statText = L.status_ready.replace('{n}', total);
        if (n > 0) {
            statText += `\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0${L.sel_count.replace('{n}', n)}`;
            if (!S.shareMode && (S.analyzeMode || !hasSelFolder)) {
                statText += `\u00A0\u00A0${fmtSize(selSize)}`;
            }
        }
        UI.stat.textContent = statText;

        const hasSel = n > 0;

        if (UI.btnAria2) UI.btnAria2.disabled = S.trashMode || !hasSel;
        if (UI.btnDown) UI.btnDown.disabled = S.trashMode || !hasSel;

        if (UI.chkAll) {
            let isAll = false;
            let isInd = false;

            if (n > 0 && total > 0) {
                isAll = (n === total);
                isInd = (n < total);
            } else {
                isAll = false;
                isInd = false;
            }

            UI.chkAll.checked = isAll;
            UI.chkAll.indeterminate = isInd;
        }

        const isShare = S.shareMode;
        const isOffline = S.offlineMode;
        const isHistory = S.historyMode;
        const isUpload = S.uploadMode;

        let isSingleVideo = false;
        let isSingleMediaWithCover = false;
        if (n === 1) {
            const id = selectedIds[0];
            const item = S.itemMap.get(id);
            if (item && item.kind !== 'drive#folder') {
                const mime = (item.mime_type || '').toLowerCase();
                const ext = (item.name || '').split('.').pop().toLowerCase();
                const isVid = mime.startsWith('video/') || ['mp4','mkv','avi','mov','wmv','flv','webm','ts'].includes(ext);
                const isImg = mime.startsWith('image/') || ['jpg','jpeg','png','gif','bmp','webp','heic'].includes(ext);
                let isTaskReady = true;
                if (S.offlineMode) isTaskReady = item.phase === 'PHASE_TYPE_COMPLETE';
                if (S.uploadMode) isTaskReady = item.status === 'DONE';

                if (isVid && isTaskReady) isSingleVideo = true;
                if ((isVid || isImg) && isTaskReady) {
                    if (item.thumbnail_link && item.thumbnail_link !== item.icon_link) {
                        const isMax = UI.win.classList.contains('pk-maximized');
                        const isBlur = typeof isBlurEnabledForView === 'function'
                        ? isBlurEnabledForView(isGridView() ? 'grid' : 'list')
                        : (typeof gmGet !== 'undefined' ? gmGet('pk_blur_thumb', false) : false);
                        if (isMax && isBlur) {
                            isSingleMediaWithCover = true;
                        } else if (window.pkGlobalThumbCache && window.pkGlobalThumbCache.has(item.id)) {
                            isSingleMediaWithCover = true;
                        }
                    }
                }
            }
        }

        if (UI.btnExt) UI.btnExt.disabled = S.trashMode || !isSingleVideo;
        if (UI.btnImgSearch) UI.btnImgSearch.disabled = S.trashMode || !isSingleMediaWithCover;

        if (isUpload) {
            if (hasSel) {
                let hasRunning = false;
                let hasStopped = false;

                for (const id of selectedIds) {
                    const task = S.itemMap.get(id);
                    if (task) {
                        const s = task.status;
                        if (['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'].includes(s)) hasRunning = true;
                        if (['PAUSED', 'ERROR'].includes(s)) hasStopped = true;
                    }
                    if (hasRunning && hasStopped) break;
                }

                if (UI.btnUpPause) UI.btnUpPause.disabled = !hasRunning;
                if (UI.btnUpStart) UI.btnUpStart.disabled = !hasStopped;
                if (UI.btnUpDel) UI.btnUpDel.disabled = false;
            } else {
                if (UI.btnUpPause) UI.btnUpPause.disabled = true;
                if (UI.btnUpStart) UI.btnUpStart.disabled = true;
                if (UI.btnUpDel) UI.btnUpDel.disabled = true;
            }
            if (UI.btnUpClearAll) UI.btnUpClearAll.disabled = (S.uploadTasks.length === 0);
        }

        if (UI.btnRetryTask) {
            UI.btnRetryTask.style.display = isOffline ? 'inline-flex' : 'none';
            const hasFailedTask = selectedIds.some(id => S.itemMap.get(id)?.phase === 'PHASE_TYPE_ERROR');
            UI.btnRetryTask.disabled = !hasFailedTask;
        }

        if (UI.btnCopyLinkOffline) {
            UI.btnCopyLinkOffline.style.display = isOffline ? 'inline-flex' : 'none';
            UI.btnCopyLinkOffline.disabled = !hasSel;
        }

        UI.btnCopy.style.display = (isShare || isOffline || isHistory || isUpload) ? 'none' : 'inline-flex';
        UI.btnCut.style.display = (isShare || isOffline || isHistory || isUpload) ? 'none' : 'inline-flex';
        UI.btnRename.style.display = (isShare || isOffline || isHistory || isUpload) ? 'none' : 'inline-flex';
        UI.btnBulkRename.style.display = (isShare || isOffline || isHistory || isUpload) ? 'none' : 'inline-flex';

        UI.btnDel.style.display = (isShare || isUpload) ? 'none' : 'inline-flex';
        const delBtnSpan = UI.btnDel.querySelector('span');
        if (delBtnSpan) {
            delBtnSpan.textContent = isHistory ? L.btn_clear_history : L.btn_del;
            UI.btnDel.setAttribute('data-pk-tip', isHistory ? L.tip_clear_history : L.tip_del);
        }

        if (isShare && UI.btnCancelShare) {
            UI.btnCancelShare.disabled = !hasSel;
        }

        if (UI.btnMigrate) {
            UI.btnMigrate.disabled = !hasSel;
            UI.btnMigrate.style.cursor = UI.btnMigrate.disabled ? 'not-allowed' : 'pointer';
            UI.btnMigrate.style.opacity = UI.btnMigrate.disabled ? '0.4' : '1';
        }

        const hasClipData = S.clipItems && S.clipItems.length > 0;
        const isPasteLocked = S.movingIds && S.movingIds.size > 0;

        if (UI.btnPaste) {
            UI.btnPaste.disabled = !hasClipData || isPasteLocked;
            const cur = S.path[S.path.length - 1];
            const isStarredRoot = S.starredMode && S.path.length === 1;
            const isRecentRoot = S.recentMode && S.path.length === 1;
            const isHistoryRoot = S.historyMode && S.path.length === 1;
            const shouldHidePaste = S.trashMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || S.isFlattened || S.dupMode || isStarredRoot || isRecentRoot || isHistoryRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';

            UI.btnPaste.style.display = shouldHidePaste ? 'none' : 'inline-flex';
            UI.btnPaste.style.cursor = isPasteLocked ? 'wait' : (UI.btnPaste.disabled ? 'not-allowed' : 'pointer');
        }

        UI.btnCopy.disabled = !hasSel || isPasteLocked ;
        UI.btnCut.disabled = !hasSel || isPasteLocked || hasProtectedItem;

        if (UI.btnNewFolder) {
            UI.btnNewFolder.disabled = isPasteLocked;
            if (isPasteLocked) UI.btnNewFolder.style.cursor = 'wait';
            else UI.btnNewFolder.style.cursor = '';
        }
        UI.btnDel.disabled = !hasSel || (n === 1 && hasProtectedItem);

        const isStandardRenameView = !(isShare || isOffline || isHistory || isUpload);
        if (isStandardRenameView) {
            if (n <= 1) {
                UI.btnRename.style.display = 'inline-flex';
                UI.btnBulkRename.style.display = 'none';
                UI.btnRename.disabled = (n === 0) || hasProtectedItem;
            } else {
                UI.btnRename.style.display = 'none';
                UI.btnBulkRename.style.display = 'inline-flex';
                UI.btnBulkRename.disabled = false;
            }
        }

        if (UI.btnRestore) UI.btnRestore.disabled = !hasSel;
        if (UI.btnDelForever) UI.btnDelForever.disabled = !hasSel;

        if (UI.btnPrune) {
            const isHiddenMode = S.isFlattened || S.dupMode || S.shareMode || S.offlineMode || S.uploadMode || S.historyMode;
            UI.btnPrune.style.display = isHiddenMode ? 'none' : 'inline-flex';
            UI.btnPrune.disabled = isHiddenMode || !hasSelFolder;
        }

        if (UI.btnUnzip) {
            const cur = S.path[S.path.length - 1];
            const isRoot = S.path.length === 1;
            const isUnzipHidden = S.shareMode || S.offlineMode || S.uploadMode || S.historyMode || S.isFlattened || S.dupMode || (S.analyzeMode && cur.id === 'analyze_root') || (cur.id === 'virtual_search_root') || (S.recentMode && isRoot) || (S.starredMode && isRoot);
            UI.btnUnzip.style.display = isUnzipHidden ? 'none' : 'inline-flex';
            const isArchive = (it) => {
                if (!it || it.kind === 'drive#folder') return false;
                const n = (it.name || '').toLowerCase();
                const m = (it.mime_type || '').toLowerCase();
                return m.includes('zip') || m.includes('rar') || m.includes('7z') || n.endsWith('.zip') || n.endsWith('.rar') || n.endsWith('.7z');
            };
            const hasArchive = selectedIds.some(id => isArchive(S.itemMap.get(id)));
            UI.btnUnzip.disabled = S.trashMode || !hasArchive;
        }
    }

    async function getLinks() { const res = []; for (const id of S.getSelectedIds()) { let item = S.items.find(x => x.id === id); if (item && !item.web_content_link) { try { item = await apiGet(id); } catch { } } if (item?.web_content_link) res.push(item); } return res; }

    const generateQualityList = (data) => {
        const list =[];
        const seenNames = new Set();

        if (data.web_content_link) {
            list.push({ name: L.str_original, url: data.web_content_link, isOriginal: true });
            seenNames.add(L.str_original);
        }

        const transcodeList =[];
        if (data.medias && Array.isArray(data.medias)) {
            data.medias.forEach(m => {
                if (!m.link || !m.link.url) return;

                let name = m.resolution_name || m.video_stream_id || 'Unknown';
                let streamUrl = m.link.url;

                if (m.video && m.video.video_type === 'mpegts' && !streamUrl.includes('.m3u8')) {
                    streamUrl += '&ext=.m3u8';
                }

                if (name.toLowerCase() === 'unknown' || !name) {
                    name = L.str_original_fast;
                }

                if (name === 'Unknown') { }
                else if (name.includes('1080') || name === 'FHD') name = '1080P';
                else if (name.includes('720') || name === 'HD') name = '720P';
                else if (name.includes('480') || name === 'SD') name = '480P';

                if (seenNames.has(name)) return;
                seenNames.add(name);

                transcodeList.push({ name: name, url: streamUrl, isOriginal: false, rawName: name });
            });
        }

        if (transcodeList.length > 0) {
            const getRank = (s) => {
                const n = s.toUpperCase();
                if (n.includes('4K')) return 100;
                if (n.includes('2K')) return 90;
                if (n.includes('1080')) return 80;
                if (n.includes('720')) return 70;
                if (n.includes('480')) return 60;
                return 10;
            };

            transcodeList.sort((a, b) => getRank(b.rawName) - getRank(a.rawName));

            const highestStream = transcodeList[0];
            if (!highestStream.name.includes('速') && !highestStream.name.includes('Fast') && highestStream.name !== L.str_original_fast) {
                const speedSuffix = (L.str_speed && L.str_speed.includes('速')) ? "(高速)" : "(Fast)";
                highestStream.name = `${highestStream.name} ${speedSuffix}`;
            }
        }

        transcodeList.forEach(t => {
            list.push({ name: t.name, url: t.url, isOriginal: false });
        });

        list.sort((a, b) => {
            if (a.isOriginal) return -1;
            if (b.isOriginal) return 1;

            const getRank = (s) => {
                const n = s.toUpperCase();
                if (n.includes('4K')) return 100;
                if (n.includes('2K')) return 90;
                if (n.includes('1080')) return 80;
                if (n.includes('720')) return 70;
                if (n.includes('480')) return 60;
                return 0;
            };

            const rA = getRank(a.name);
            const rB = getRank(b.name);

            if (rA !== rB) return rB - rA;
            return b.name.localeCompare(a.name, undefined, { numeric: true });
        });

        return list;
    };

    const getBestSource = (data) => {
        const fileName = (data.name || "").toLowerCase();
        const mime = (data.mime_type || '').toLowerCase();
        const generatedList = generateQualityList(data);
        const list = generatedList.map(item => ({
            name: item.name,
            link: item.url,
            active: false,
            weight: 0,
            isOriginal: item.isOriginal
        }));
        const isLegacyFormat = fileName.endsWith('.avi') || fileName.endsWith('.wmv') || fileName.endsWith('.rmvb') || fileName.endsWith('.divx') || fileName.endsWith('.flv') || fileName.endsWith('.vob') || mime.includes('mpeg4') || mime.includes('avi') || mime.includes('x-ms-wmv');
        const isMKV = fileName.endsWith('.mkv');

        list.forEach(item => {
            const n = item.name;
            if (item.isOriginal) {
                item.weight = (isLegacyFormat || isMKV) ? -9999 : 50;
            } else {
                if (n.includes('高速') || n.includes('Fast')) item.weight = 500;
                else if (n === 'Unknown') item.weight = 400;
                else if (n.includes('1080P')) item.weight = 300;
                else if (n.includes('720P')) item.weight = 200;
                else if (n.includes('480P')) item.weight = 100;
                else item.weight = 10;
            }
        });

        let bestMatch = null;
        if (list.length > 0) {
            const sortedByWeight = [...list].sort((a, b) => b.weight - a.weight);
            bestMatch = sortedByWeight[0];
        } else {
            bestMatch = { name: L.str_original, link: data.web_content_link, active: true };
            list.push(bestMatch);
        }

        bestMatch.active = true;
        return { src: bestMatch.link, name: bestMatch.name, list: list };
    };

    async function playVideo(item, startFullscreen = false) {
        if (S.trashMode) return;
        const getPhysicalId = (it) => {
            if ((S.offlineMode && it.kind === 'drive#task') || (S.uploadMode && it.file_id)) {
                return it.file_id || it.id;
            }
            return it.id;
        };

        let pkHls = null;
        let isPlayerDestroyed = false;
        const L = getStrings();

        const isVideoItem = (it) => {
            if (!it || it.isHeader || it.kind === 'drive#folder') return false;
            if (S.offlineMode && it.phase !== 'PHASE_TYPE_COMPLETE') return false;
            if (S.uploadMode && it.status !== 'DONE') return false;
            const m = (it.mime_type || '').toLowerCase();
            const n = (it.name || '').toLowerCase();
            const dur = (it.params && it.params.duration) || 0;
            const vExts = ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp'];
            return m.startsWith('video/') || dur > 0 || vExts.some(e => n.endsWith('.' + e));
        };

        const videoPlaylist = S.display.filter(i => isVideoItem(i));
        let curListIdx = videoPlaylist.findIndex(v => v.id === item.id);
        const totalInList = videoPlaylist.length;
        let isSwitching = false;
        let switchReqId = 0;
        const showSadBox = (codecName) => {
            if (box.querySelector('.pk-err-dialog')) return;
            if (posterEl) {
                posterEl.style.transition = 'none';
                posterEl.style.display = 'flex';
                posterEl.style.opacity = '1';
                posterEl.style.pointerEvents = 'auto';
            }

            const savedPlayer = gmGet('pk_ext_player', 'potplayer');
            const sadBoxSVG = `<svg width="85" height="85" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20 45l15-15h30l15 15H20z" fill="#777"/><path d="M20 45L5 30h20l15 15H20z" fill="#999"/><path d="M80 45l15-15H75L60 45h20z" fill="#999"/><path fill-rule="evenodd" d="M20 45h60v35c0 5-5 5-5 5H25c-5 0-5-5-5-5V45zm16.5 14c0 3.3 1.5 6 3.5 6s3.5-2.7 3.5-6-1.5-6-3.5-6-3.5 2.7-3.5 6zm20 0c0 3.3 1.5 6 3.5 6s3.5-2.7 3.5-6-1.5-6-3.5-6-3.5 2.7-3.5 6z" fill="#aaa"/><path d="M38 16a12 8 0 1 0 24 0a12 8 0 1 0-24 0M48 24l2 3.5l2-3.5h-4z" fill="#aaa"/><path d="M47 13l6 6M53 13l-6 6" stroke="#181818" stroke-width="1.8" stroke-linecap="round"/></svg>`;
            const recommended = qualityList.find(q => !q.isOriginal) || qualityList[0];
            const recommendedUrl = recommended.link || recommended.url;
            const resOptions = qualityList.map(q => {
                const qUrl = q.link || q.url;
                const isSelected = qUrl === recommendedUrl;
                return `<option value="${qUrl}" ${isSelected ? 'selected' : ''}>${q.name}</option>`;
            }).join('');
            const lblRes = L.lbl_resolution;
            const dialog = document.createElement('div');
            dialog.className = 'pk-err-dialog';
            dialog.style.cssText = "position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(30, 30, 30, 0.85);backdrop-filter:blur(12px);border-radius:12px;padding:40px 50px;display:flex;flex-direction:column;align-items:center;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.8);z-index:999;min-width:400px;border:1px solid rgba(255,255,255,0.1);";
            dialog.innerHTML = `
                <div class="pk-err-close" style="position:absolute;top:15px;right:15px;cursor:pointer;color:#fff;padding:5px;display:flex;align-items:center;justify-content:center;opacity:0.7;transition:opacity 0.2s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.7"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg></div>
                <div style="margin-bottom:20px;">${sadBoxSVG}</div>
                <div style="font-size:16px;font-weight:bold;color:#fff;margin-bottom:10px;max-width:400px;line-height:1.4;">${L.err_codec_t1.replace('{c}', codecName)}</div>
                <div style="font-size:12px;color:#999;margin-bottom:30px;">${L.err_codec_t2}</div>

                <div style="display:grid;grid-template-columns:80px 1fr;gap:15px;align-items:center;width:100%;margin-bottom:15px;">
                    <div style="color:#ccc;font-size:14px;text-align:right;">${lblRes}</div>
                    <div style="position:relative;">
                        <select id="pk_err_res_sel" style="width:100%;padding:8px 12px;border-radius:6px;background:rgba(0,0,0,0.3);color:#fff;border:1px solid #444;outline:none;cursor:pointer;font-size:14px;">
                            ${resOptions}
                        </select>
                    </div>
                </div>

                <div style="display:grid;grid-template-columns:80px 1fr;gap:15px;align-items:center;width:100%;margin-bottom:25px;">
                    <div style="color:#ccc;font-size:14px;text-align:right;">${L.lbl_player}</div>
                    <div style="position:relative;">
                        <select id="pk_err_player_sel" style="width:100%;padding:8px 12px;border-radius:6px;background:rgba(0,0,0,0.3);color:#fff;border:1px solid #444;outline:none;cursor:pointer;font-size:14px;">
                            <option value="potplayer" selected>PotPlayer</option>
                            <option value="other">${L.opt_player_other}</option>
                        </select>
                    </div>
                </div>
                <button id="pk_err_launch_btn" style="background:#fff;color:#000;border:none;padding:10px 40px;border-radius:6px;font-size:14px;font-weight:bold;cursor:pointer;transition:background 0.2s;">${L.btn_start_play}</button>
            `;

            box.appendChild(dialog);

            const showLinkModal = (url) => {
                const old = d.querySelector('.pk-link-export-ov');
                if (old) old.remove();

                const ov = document.createElement('div');
                ov.className = 'pk-link-export-ov';
                ov.style.cssText = "position:absolute; inset:0; background:rgba(0,0,0,0.9); backdrop-filter:blur(5px); z-index:2147483647; display:flex; align-items:center; justify-content:center; border-radius:inherit;";

                const m = document.createElement('div');
                m.className = 'pk-modal';
                m.style.cssText = "position:relative;background:#222;padding:30px;border-radius:12px;width:500px;border:1px solid #444;box-shadow:0 20px 50px rgba(0,0,0,0.8);display:flex;flex-direction:column;gap:20px;text-align:left;";

                m.innerHTML = `
                    <div class="pk-modal-close" style="position:absolute;top:15px;right:15px;cursor:pointer;color:#aaa;">${mkSvg(icons.close)}</div>
                    <h3 style="margin:0;font-size:16px;color:#fff;">${L.export_link_title}</h3>
                    <div class="pk-field">
                        <textarea readonly style="width:100%;height:100px;background:#111;color:#4caf50;border:1px solid #333;border-radius:6px;padding:10px;font-family:monospace;font-size:12px;resize:none;word-break:break-all;outline:none;">${url}</textarea>
                    </div>
                    <div class="pk-modal-act" style="justify-content:flex-end;display:flex;gap:10px;">
                        <button class="pk-btn" id="pk_link_cancel" style="border:1px solid #444;background:transparent;color:#ddd;">${L.btn_close}</button>
                        <button class="pk-btn pri" id="pk_copy_link_btn" style="background:var(--pk-pri);color:#fff;border:none;padding:8px 20px;border-radius:6px;font-weight:bold;cursor:pointer;">${L.btn_copy_link}</button>
                    </div>
                `;

                ov.appendChild(m);
                d.appendChild(ov);

                const doClose = () => ov.remove();
                m.querySelector('.pk-modal-close').onclick = doClose;
                m.querySelector('#pk_link_cancel').onclick = doClose;

                const cpBtn = m.querySelector('#pk_copy_link_btn');
                const txt = m.querySelector('textarea');
                setTimeout(() => txt.select(), 50);

                cpBtn.onclick = () => {
                    GM_setClipboard(url);
                    cpBtn.textContent = L.msg_copy_success;
                    setTimeout(() => { cpBtn.textContent = L.btn_copy_link; }, 2000);
                };

                ov.onclick = (evt) => { if(evt.target === ov) doClose(); };
                m.onclick = (ev) => ev.stopPropagation();
            };

            dialog.querySelector('.pk-err-close').onclick = (e) => {
                e.stopPropagation();
                dialog.remove();
            };

            dialog.onclick = (e) => e.stopPropagation();

            const playerSel = dialog.querySelector('#pk_err_player_sel');
            const launchBtn = dialog.querySelector('#pk_err_launch_btn');

            playerSel.onchange = () => {
                launchBtn.textContent = (playerSel.value === 'other') ? L.btn_copy_link : L.btn_start_play;
            };
            launchBtn.textContent = L.btn_start_play;

            launchBtn.onclick = (e) => {
                e.stopPropagation();
                const selPlayer = playerSel.value;
                const selResLink = dialog.querySelector('#pk_err_res_sel').value;
                const selResName = dialog.querySelector('#pk_err_res_sel').options[dialog.querySelector('#pk_err_res_sel').selectedIndex].text;

                gmSet('pk_ext_player', selPlayer);

                let cleanLink = selResLink.replace('&ext=.m3u8', '');
                if (cleanLink.includes('ts_downloader') && cleanLink.includes('url=')) {
                    const urlParam = new URL(cleanLink).searchParams.get('url');
                    if (urlParam) cleanLink = decodeURIComponent(urlParam);
                }

                if (selPlayer === 'other') {
                    GM_setClipboard(cleanLink);
                    launchBtn.textContent = L.msg_copy_success;
                    launchBtn.style.background = "#52c41a";
                    launchBtn.style.color = "#fff";
                    setTimeout(() => {
                        if (typeof destroyPlayer === 'function') destroyPlayer();
                    }, 800);
                }
                else if (selPlayer === 'potplayer') {
                    const ua = navigator.userAgent.replace(/"/g, '');
                    const cmd = `${cleanLink} /user_agent="${ua}" /referer="https://mypikpak.com/"`;
                    window.location.href = `potplayer://${cmd}`;
                    destroyPlayer();
                }
                else {
                    const curT = v.currentTime;
                    currentLink = selResLink;
                    currentResName = selResName;

                    box.classList.add('buffering');
                    if (posterEl) {
                        if (v.readyState >= 2 && v.currentTime > 0) {
                            try {
                                const cvs = document.createElement('canvas');
                                cvs.width = v.videoWidth; cvs.height = v.videoHeight;
                                cvs.getContext('2d').drawImage(v, 0, 0);
                                posterEl.querySelector('img').src = cvs.toDataURL('image/jpeg', 0.7);
                            } catch (err) {}
                        }
                        posterEl.style.display = 'flex';
                        posterEl.style.opacity = '1';
                    }

                    if(resTxt) resTxt.textContent = currentResName;
                    if(resList) {
                        resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                        bindResEvents();
                    }

                    dialog.remove();
                    shutterTargetTime = curT > 0.1 ? curT : 0;
                    loadSource(currentLink, curT);
                    v.play().catch(()=>{});
                }
            };
        };

        const renderPlaylistItems = () => {
            const RANGE = 150;
            const start = Math.max(0, curListIdx - RANGE);
            const end = Math.min(videoPlaylist.length, curListIdx + RANGE + 1);

            return videoPlaylist.slice(start, end).map((v, i) => {
                const absIdx = start + i;
                const coverSrc = (v.thumbnail_link && v.thumbnail_link !== v.icon_link) ? v.thumbnail_link : '';
                const phSrc = v.icon_link || v.thumbnail_link || '';
                const phSvg = getIcon(v);
                return `
            <div class="pk-p-plist-item ${absIdx === curListIdx ? 'active' : ''}"
                 data-idx="${absIdx}"
                 data-name="${esc(v.name)}"
                 data-size="${fmtSize(v.size)}">
                <div class="pk-p-plist-ph">
                    ${phSrc ? `<img src="${phSrc}" draggable="false">` : phSvg}
                </div>
                ${coverSrc ? `<img src="${coverSrc}" style="filter:none !important;opacity:0;" draggable="false" onload="this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.display='none';" onerror="this.remove();if(this.previousElementSibling)this.previousElementSibling.style.display='flex';" loading="lazy">` : ``}
            </div>
        `;
            }).join('');
        };

        const triggerResume = (targetVideo, targetItem) => {
            if (targetVideo._hasShownResumeToast || hasUserSeeked || targetVideo._isRestarting) return;
            const savedData = gmGet('pk_progress_' + getPhysicalId(targetItem), 0);
            let savedT = 0;
            if (typeof savedData === 'number') {
                savedT = savedData;
            } else if (savedData && typeof savedData === 'object') {
                savedT = parseFloat(savedData.t || 0);
            }

            const skipOp = parseInt(gmGet('pk_skip_intro', 0)) || 0;

            if (savedT > 5 || skipOp > 0) {
                targetVideo._hasShownResumeToast = true;
            } else {
                return;
            }

            const showToast = (text) => {
                const oldToast = d.querySelector('.pk-p-resume-toast');
                if (oldToast) oldToast.remove();
                const toast = document.createElement('div');
                toast.className = 'pk-p-resume-toast';
                toast.style.zIndex = "150";
                toast.innerHTML = `<span>${text}</span><span class="pk-p-resume-btn" id="pk_re_start">${L.btn_restart}</span><div class="pk-p-resume-close" id="pk_re_close">${mkSvg(icons.close)}</div>`;
                d.querySelector('#pk_p_box').appendChild(toast);

                toast.querySelector('#pk_re_start').onclick = (e) => {
                    e.stopPropagation();
                    if (tCur) tCur.textContent = "00:00:00";
                    if (progFilled) progFilled.style.setProperty('width', '0%', 'important');
                    shutterTargetTime = 0.1;
                    targetVideo._isRestarting = true;
                    targetVideo.currentTime = 0;
                    toast.remove();
                };
                toast.querySelector('#pk_re_close').onclick = (e) => { e.stopPropagation(); toast.remove(); };
                setTimeout(() => { if(toast.parentNode) toast.remove(); }, 8000);
            };

            if (savedT > 5) {
                showToast(L.msg_resume_hint.replace('{t}', fmtT(savedT)));
            } else if (skipOp > 0) {
                const dp = d.querySelector('.pk-player-box');
                if(dp && !dp.querySelector('.pk-skip-toast')) {
                    const tip = document.createElement('div');
                    tip.className = 'pk-skip-toast';
                    tip.style.cssText = "position:absolute;top:80px;left:20px;background:rgba(0,0,0,0.6);color:#ddd;padding:4px 8px;border-radius:4px;font-size:12px;pointer-events:none;animation:pkFadeIn 0.5s;";
                    tip.textContent = `${L.lbl_skip_op} ${skipOp}s`;
                    dp.appendChild(tip);
                    setTimeout(()=>tip.remove(), 3000);
                }
            }
        };

        const softSwitch = async (newIdx, scrollMode = 'smooth') => {
            if (!videoPlaylist[newIdx]) return;

            switchReqId++;
            const myReqId = switchReqId;
            isSwitching = true;
            const v = d.querySelector('#pk_video');
            const loader = d.querySelector('.pk-p-loading');
            const poster = d.querySelector('#pk_p_poster');
            if (v && v.currentTime > 5 && v.duration > 0) {
                gmSet('pk_progress_' + getPhysicalId(item), {
                    t: v.currentTime,
                    d: v.duration,
                    ts: Date.now()
                });
            }

            if (v) {
                v.pause();
                v._isRestarting = false;
                v._hasShownResumeToast = false;
                Array.from(v.querySelectorAll('track')).forEach(t => t.remove());

                if (subState.blobUrl) {
                    URL.revokeObjectURL(subState.blobUrl);
                    subState.blobUrl = null;
                }
                subState.hasSub = false;
                subState.track = null;

                const subNameLabel = d.querySelector('#pk_sub_name');
                if (subNameLabel) subNameLabel.textContent = L.str_no_sub;

                v.src = "";
                v.load();
            }

            isSwitching = false;
            if (progFilled) progFilled.style.setProperty('width', '0%', 'important');
            const newItem = videoPlaylist[newIdx];
            if (tCur) tCur.textContent = '00:00:00';
            if (tDur) {
                const newPId = getPhysicalId(newItem);
                const knownDur = (newItem.params && newItem.params.duration) ||
                                 S.durationMap.get(newPId) ||
                                 gmGet('pk_duration_' + newPId, 0);
                tDur.textContent = knownDur > 0 ? fmtT(knownDur) : '00:00:00';
            }

            transformState = { rotate: 0, flipH: 1, flipV: 1, ratio: 'default' };
            if (typeof applyTransform === 'function') applyTransform();

            const ratioBtns = d.querySelectorAll('#pk_ratio_opts .pk-size-btn');
            ratioBtns.forEach(btn => {
                if (btn.dataset.ratio === 'default') btn.classList.add('active');
                else btn.classList.remove('active');
            });

            if (loader) loader.style.display = 'block';
            hasCheckedProgress = false;
            const oldResumeToast = d.querySelector('.pk-p-resume-toast');
            if (oldResumeToast) oldResumeToast.remove();
            const errDialogs = d.querySelectorAll('.pk-err-dialog');
            errDialogs.forEach(el => el.remove());
            const linkOvs = d.querySelectorAll('.pk-link-export-ov');
            linkOvs.forEach(el => el.remove());
            const searchModal = d.querySelector('.pk-sub-search-modal');
            if (searchModal) searchModal.remove();
            const btnSearchS = d.querySelector('#pk_p_search');
            if (btnSearchS) btnSearchS.style.setProperty('display', 'none', 'important');
            const btnPipS = d.querySelector('#pk_p_pip');
            if (btnPipS) btnPipS.style.setProperty('display', 'none', 'important');

            curListIdx = newIdx;
            item = newItem;

            if (posterEl) {
                posterEl.style.transition = 'none';
                posterEl.style.display = 'flex';
                posterEl.style.opacity = '1';

                const img = posterEl.querySelector('img');
                if (img && newItem.thumbnail_link) {
                    img.style.display = 'block';
                    img.src = newItem.thumbnail_link.replace('SIZE_MEDIUM', 'SIZE_LARGE');
                    img.style.opacity = '0';
                    img.onload = () => {
                        img.style.opacity = '1';
                        if (btnSearchS) btnSearchS.style.setProperty('display', 'flex', 'important');
                    };
                    img.onerror = () => img.style.display = 'none';
                } else if (img) {
                    img.style.display = 'none';
                }

                setTimeout(() => { posterEl.style.transition = 'opacity 0.4s ease'; }, 50);
            }

            if (loaderEl) loaderEl.style.display = 'block';

            const pTitle = d.querySelector('.pk-player-title');
            if(pTitle) pTitle.textContent = `[${newIdx + 1}/${totalInList}] ${newItem.name}`;
            const pTab = d.querySelector('#pk_p_plist_tab');
            if(pTab) pTab.innerHTML = `${curListIdx + 1} / ${totalInList} <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>`;

            if (pScroll) {
                syncVideoPlistItems(
                    scrollMode === 'instant'
                    ? 'instant'
                    : (box.classList.contains('plist-active') ? 'smooth' : false)
                );
            }

            const btnL = d.querySelector('#pk_p_side_L');
            const btnR = d.querySelector('#pk_p_side_R');
            if (btnL) btnL.style.display = newIdx === 0 ? 'none' : 'flex';
            if (btnR) btnR.style.display = newIdx === totalInList - 1 ? 'none' : 'flex';

            if (typeof updatePlistNav === 'function') updatePlistNav();

            hasUserSeeked = false;

            try {
                const targetApiId = getPhysicalId(newItem);
                const newData = await apiGet(targetApiId);
                if (newData) {
                    if (newData.thumbnail_link) newItem.thumbnail_link = newData.thumbnail_link;
                    if (newData.icon_link) newItem.icon_link = newData.icon_link;
                }

                if (myReqId !== switchReqId) return;
                const freshData = getBestSource(newData);
                currentResName = freshData.name;
                currentLink = freshData.src;
                qualityList = freshData.list;
                const resTxt = d.querySelector('#pk_p_res_txt');
                if (resTxt) resTxt.textContent = currentResName;
                const resList = d.querySelector('#pk_p_res_list');
                if (resList) {
                    resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                    bindResEvents();
                }
                loadSource(freshData.src, null);
                if (typeof ThumbnailEngine !== 'undefined') {
                    ThumbnailEngine.resetSource(freshData.src);
                }
                if(poster) poster.style.display = 'block';
                if (d.querySelector('#pk_sub_name')) d.querySelector('#pk_sub_name').textContent = L.str_no_sub;
                autoMatchSubtitle(newItem);

                triggerResume(v, newItem);
                if (!isPlayerDestroyed) {
                    await v.play().catch(()=>{});
                }

                if (!v.paused && !isPlayerDestroyed) {
                    box.classList.add('pk-v-started');
                    stopSpinner();
                }
                updateState();

            } catch (err) {
                if (myReqId === switchReqId) console.error("[SoftSwitch] Critical Error:", err);
            } finally {
                if (myReqId === switchReqId) {
                    if(loader) loader.style.display = 'none';
                }
            }
        };

        const loadSource = (url, customStartTime = null) => {
            let startTime = 0;
            const skipOp = parseInt(gmGet('pk_skip_intro', 0)) || 0;
            const pId = getPhysicalId(item);

            if (customStartTime !== null) {
                startTime = customStartTime;
            } else {
                const savedData = gmGet('pk_progress_' + pId, 0);
                let historyTime = 0;

                if (typeof savedData === 'number') {
                    historyTime = savedData;
                } else if (savedData && typeof savedData === 'object') {
                    historyTime = parseFloat(savedData.t || 0);
                }

                if (historyTime > 5) {
                    startTime = historyTime;
                } else if (skipOp > 0) {
                    const knownDur = (item.params && item.params.duration) || S.durationMap.get(pId) || gmGet('pk_duration_' + pId, 0);
                    if (knownDur > 0 && skipOp >= knownDur) {
                        console.warn(`[AutoSkip] Intro skip (${skipOp}s) exceeds total duration (${knownDur}s), ignored.`);
                        startTime = 0;
                    } else {
                        startTime = skipOp;
                    }
                }
            }

            shutterTargetTime = startTime > 1 ? startTime : 0;
            if (tCur) tCur.textContent = fmtT(startTime);
            const totalDur = (item.params && item.params.duration) ||
                             S.durationMap.get(pId) ||
                             gmGet('pk_duration_' + pId, 0);

            if (progFilled && totalDur > 0) {
                const pct = Math.min(100, (startTime / totalDur) * 100);
                progFilled.style.setProperty('width', `${pct}%`, 'important');
            }

            if (pkHls) {
                pkHls.destroy();
                pkHls = null;
            }

            const isM3u8 = url.includes('.m3u8');

            if (isM3u8 && window.Hls && window.Hls.isSupported()) {
                let finalPlayUrl = url;
                if (url.includes('&ext=.m3u8')) {
                    const isHevc = (item.params?.video_codec === 'hevc') || (item.video_media_metadata?.video_codec === 'hevc');
                    if (isHevc) {
                        console.log("[Hls] HEVC stream detected. Bypassing JS check to force hardware decoding.");
                    }

                    const realUrl = url.replace('&ext=.m3u8', '');
                    const duration = (item.params && item.params.duration) ? parseInt(item.params.duration) : 36000;

                    const syntheticManifest =
                        `#EXTM3U\n` +
                        `#EXT-X-VERSION:3\n` +
                        `#EXT-X-TARGETDURATION:${duration + 10}\n` +
                        `#EXT-X-PLAYLIST-TYPE:VOD\n` +
                        `#EXTINF:${duration},\n` +
                        `${realUrl}\n` +
                        `#EXT-X-ENDLIST`;

                    const blob = new Blob([syntheticManifest], { type: 'application/x-mpegurl' });
                    finalPlayUrl = URL.createObjectURL(blob);
                    console.log("[Hls] Generated synthetic manifest for TS stream");
                }

                pkHls = new window.Hls({
                    debug: false,
                    enableWorker: true,
                    lowLatencyMode: true,
                    startPosition: startTime,
                    fragLoadingMaxRetry: 1,
                    manifestLoadingMaxRetry: 1,
                    levelLoadingMaxRetry: 1,
                    fragLoadingTimeOut: 15000,
                    manifestLoadingTimeOut: 10000,
                    maxBufferHole: 0.5,
                    maxFragLookUpTolerance: 0.1,
                    nudgeOffset: 0.1,
                    nudgeMaxRetry: 5,
                    maxBufferLength: 120,
                    maxMaxBufferLength: 600,
                    backBufferLength: 90,
                    maxBufferHole: 0.5,
                    startLevel: -1,
                    capLevelToPlayerSize: false
                });
                pkHls.loadSource(finalPlayUrl);
                pkHls.attachMedia(v);
                const healthTimer = setInterval(() => {
                    if (!pkHls || isPlayerDestroyed) { clearInterval(healthTimer); return; }

                    if (box.classList.contains('buffering')) {
                        if (!v._bufferingSince) v._bufferingSince = Date.now();
                        const isColdStart = v.currentTime < 1.0;
                        const timeoutThreshold = isColdStart ? 60000 : 20000;

                        if (Date.now() - v._bufferingSince > timeoutThreshold) {
                            console.warn(`[Watchdog] Buffering timeout (${isColdStart ? 'Cold Start' : 'Mid-Stream'}). Forcing error...`);
                            clearInterval(healthTimer);
                            handleVideoError({ force: true, target: v });
                        }
                    } else {
                        v._bufferingSince = null;
                    }

                    if (!v.paused && v.currentTime > 0.5) {
                        if (v.videoWidth === 0 || v.videoHeight === 0) {
                             v._blackScreenCount = (v._blackScreenCount || 0) + 1;
                        }
                        else {
                            const quality = v.getVideoPlaybackQuality ? v.getVideoPlaybackQuality() : null;
                            const decodedFrames = quality ? quality.totalVideoFrames : (v.webkitDecodedFrameCount || 0);

                            if (decodedFrames === 0) {
                                v._blackScreenCount = (v._blackScreenCount || 0) + 1;
                            } else {
                                v._blackScreenCount = 0;
                            }
                        }

                        if (v._blackScreenCount > 3) {
                            console.warn(`[Watchdog] Black screen detected (Time: ${v.currentTime.toFixed(1)}, Frames: 0). Forcing external player.`);
                            clearInterval(healthTimer);
                            handleVideoError({ force: true, target: v });
                        }
                    }
                }, 1000);

                pkHls.on(window.Hls.Events.LEVEL_LOADED, function (event, data) {
                    if (!data.details || !data.details.videoCodec) return;

                    const vCodec = data.details.videoCodec.toLowerCase();
                    const isBadVideo = vCodec.includes('mp4v') || vCodec.includes('hvc1') || vCodec.includes('hev1');

                    if (isBadVideo) {
                        const testMime = `video/mp4; codecs="${vCodec}"`;
                        if (window.MediaSource && !window.MediaSource.isTypeSupported(testMime)) {
                            console.warn(`[VideoCheck] Unsupported video codec detected: ${vCodec}`);
                            v.pause();
                            showSadBox(vCodec);

                            if (pkHls) { pkHls.destroy(); pkHls = null; }
                        }
                    }
                });

                pkHls.on(window.Hls.Events.AUDIO_TRACKS_UPDATED, function (event, data) {
                    const tracks = data.audioTracks || [];
                    let detectedBadCodec = null;

                    for (const track of tracks) {
                        const codec = (track.codec || '').toLowerCase();
                        const name = (track.name || '').toLowerCase();

                        const isSuspicious =
                            codec.includes('ac-3') || codec.includes('ec-3') || codec.includes('dts') ||
                            name.includes('dts') || name.includes('truehd') || name.includes('atmos');

                        if (isSuspicious) {
                            const testMime = `audio/mp4; codecs="${track.codec || 'ac-3'}"`;
                            if (window.MediaSource && !window.MediaSource.isTypeSupported(testMime)) {
                                detectedBadCodec = codec || name;
                                break;
                            }
                        }
                    }

                    if (detectedBadCodec) {
                        console.warn(`[AudioCheck] Unsupported codec detected: ${detectedBadCodec}`);
                        v.pause();
                        showSadBox(detectedBadCodec.toUpperCase());
                    }
                });

                pkHls.on(window.Hls.Events.ERROR, function (event, data) {
                    if (data.fatal) {
                        switch (data.type) {
                            case window.Hls.ErrorTypes.NETWORK_ERROR:
                                if (data.response && (data.response.code === 403 || data.response.code === 404 || data.response.code === 401)) {
                                    console.warn(`[Hls] Fatal Network Error (${data.response.code}). Triggering rollback.`);
                                    handleVideoError({ target: v });
                                } else {
                                    console.log("[Hls] Network error, trying to recover...");
                                    pkHls.startLoad();
                                }
                                break;
                            case window.Hls.ErrorTypes.MEDIA_ERROR:
                                pkHls.recoverMediaError();
                                setTimeout(() => {
                                    if (v.paused && !box.querySelector('.pk-err-dialog')) {
                                        handleVideoError({ target: v });
                                    }
                                }, 1500);
                                break;
                            default:
                                handleVideoError({ target: v });
                                break;
                        }
                    } else if (data.details === 'bufferStalledError' && v.currentTime < 1) {
                        handleVideoError({ target: v });
                    }
                });
            } else {
                v.src = url;
                if (startTime > 0) {
                    v.currentTime = startTime;
                }
            }
        };

        let initialData = getBestSource(item);
        let currentLink = initialData.src;
        let qualityList = initialData.list;
        let currentResName = initialData.name;
        let lastWorkingLink = null;
        let lastWorkingResName = null;
        const failedUrls = new Set();
        let posterUrl = item.thumbnail_link || '';
        if (posterUrl && posterUrl.includes('SIZE_MEDIUM')) {
            posterUrl = posterUrl.replace('SIZE_MEDIUM', 'SIZE_LARGE');
        }

        try {
            const linkUrl = new URL(currentLink);
            const linkDomain = linkUrl.origin;
            if (!document.head.querySelector(`link[href="${linkDomain}"]`)) {
                const pc = document.createElement('link');
                pc.rel = 'preconnect';
                pc.href = linkDomain;
                pc.crossOrigin = 'anonymous';
                document.head.appendChild(pc);
            }
        } catch(e) {}

        const d = document.createElement('div');
        d.id = 'pk-player-ov';
        d.className = 'pk-ov pk-dark';
        d.tabIndex = 0;
        d.style.cssText = "position:fixed;inset:0;z-index:2147483640;background:rgba(0,0,0,0.9);display:flex;justify-content:center;align-items:center;outline:none;will-change:transform;";

        const icons = {
            play: '<path d="M9.5,4.3c-1.1-0.7-2.6,0.1-2.6,1.4v12.6c0,1.3,1.5,2.1,2.6,1.4l10.3-6.3c1.1-0.7,1.1-2.2,0-2.9L9.5,4.3z"/>',
            playCenter: '<path d="M7.7,4.3c-1.1-0.7-2.6,0.1-2.6,1.4v12.6c0,1.3,1.5,2.1,2.6,1.4l10.3-6.3c1.1-0.7,1.1-2.2,0-2.9L7.7,4.3z"/>',
            pause: '<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>',
            vol: '<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>',
            mute: '<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73 4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>',
            full: '<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>',
            settings: '<path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"/>',
            exitFull: '<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>',
            close: '<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>',
            search: '<g transform="scale(0.0234375)"><path d="M107.739429 580.388571a365.860571 365.860571 0 0 0 648.630857 85.504L635.611429 532.48a36.571429 36.571429 0 0 0-56.612572 2.852571l-39.131428 53.101715a109.714286 109.714286 0 0 1-167.716572 10.605714L235.008 455.387429a36.571429 36.571429 0 0 0-58.002286 6.729142l-69.266285 118.272z m-19.894858-110.738285l26.038858-44.470857a109.714286 109.714286 0 0 1 174.08-20.333715l137.216 143.798857a36.571429 36.571429 0 0 0 55.881142-3.584l39.131429-53.101714a109.714286 109.714286 0 0 1 169.691429-8.484571l102.985142 113.810285A365.714286 365.714286 0 1 0 87.844571 469.577143z m658.139429 318.317714a438.857143 438.857143 0 1 1 50.029714-52.736c1.316571 1.024 2.56 2.194286 3.803429 3.437714l206.921143 206.848a36.571429 36.571429 0 0 1-51.712 51.712l-206.921143-206.848a37.083429 37.083429 0 0 1-2.194286-2.413714zM526.628571 314.514286a73.142857 73.142857 0 1 1 0-146.285715 73.142857 73.142857 0 0 1 0 146.285715z" fill="currentColor"></path></g>',
            pip: '<g transform="scale(0.0234375)"><path d="M768 213.333333H256a85.333333 85.333333 0 0 0-85.333333 85.333334v426.666666a85.333333 85.333333 0 0 0 85.333333 85.333334h170.666667a42.666667 42.666667 0 1 1 0 85.333333H256a170.666667 170.666667 0 0 1-170.666667-170.666667V298.666667a170.666667 170.666667 0 0 1 170.666667-170.666667h512a170.666667 170.666667 0 0 1 170.666667 170.666667v128a42.666667 42.666667 0 1 1-85.333334 0V298.666667a85.333333 85.333333 0 0 0-85.333333-85.333334z m-128 341.333334a128 128 0 0 0-128 128v85.333333a128 128 0 0 0 128 128h170.666667a128 128 0 0 0 128-128v-85.333333a128 128 0 0 0-128-128h-170.666667z" fill="currentColor"></path></g>',
            rotL: '<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 3v5h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
            rotR: '<path d="M21 12a9 9 0 1 1-9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 3v5h-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
            flipH: '<path d="M17 7l5 5-5 5M7 17l-5-5 5-5M2 12h20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
            flipV: '<path d="M7 7l5-5 5 5M17 17l-5 5-5-5M12 2v20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
        };

        const mkSvg = (path) => `<svg viewBox="0 0 24 24">${path}</svg>`;
        const renderQualityMenu = (list, activeName) => list.map(q => {
            const displayName = q.name.replace(/\s(\(|\uff08)/, '<br>$1');
            return `<div class="pk-p-item ${q.name === activeName ? 'active' : ''}" data-link="${q.link}" style="line-height:1.3; padding:8px 12px;">${displayName}</div>`;
        }).join('');

        const styleEl = document.createElement('style');
        styleEl.textContent = `
        .pk-player-box.ui-hidden .pk-player-top, .pk-player-box.ui-hidden .pk-player-controls, .pk-player-box.ui-hidden .pk-p-prog-wrap, .pk-player-box.ui-hidden .pk-p-side-nav { opacity: 0 !important; pointer-events: none !important; }
        .pk-player-box.ui-hidden:not(.plist-active) .pk-p-plist-tab { opacity: 0 !important; pointer-events: none !important; }
        .pk-player-box.ui-hidden .pk-p-center-play { opacity: 1 !important; pointer-events: none; }
        .pk-player-box.ui-hidden { cursor: none; }
        .pk-p-pop { flex-direction: column !important; }
        .pk-p-eye { transition: transform 1.0s cubic-bezier(0.2, 0, 0.2, 1); transform: translate3d(0,0,0); backface-visibility: hidden; image-rendering: -webkit-optimize-contrast; }
        .pk-look-r .pk-p-eye { transform: translate3d(24px,0,0); }
        .pk-look-l .pk-p-eye { transform: translate3d(-24px,0,0); }
        .pk-eye-closed { display: none; }
        .pk-player-progress-thumb.pk-blink-anim .pk-eye-open { display: none; }
        .pk-player-progress-thumb.pk-blink-anim .pk-eye-closed { display: block; }
        .pk-player-progress-thumb { position: absolute; top: 50% !important; transform: perspective(1px) translateY(-50%) scale(0) translateZ(0) !important; opacity: 0 !important; will-change: transform, opacity; transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s !important; image-rendering: -webkit-optimize-contrast; }
        .pk-player-progress-bg { transition: height 0.2s ease !important; transform: translateZ(0); backface-visibility: hidden; -webkit-perspective: 1000; }
        #pk_p_prog_area:hover .pk-player-progress-thumb, .pk-player-box.pk-is-seeking .pk-player-progress-thumb, .pk-player-progress-thumb.pk-blink-hold { transform: perspective(1px) translateY(-50%) scale(1) translateZ(0) !important; opacity: 1 !important; }
        #pk_p_prog_area:hover .pk-player-progress-bg, .pk-player-box.pk-is-seeking .pk-player-progress-bg, .pk-player-progress-bg:has(.pk-blink-hold) { height: 6px !important; }
        #pk_p_box:not(.pk-is-seeking) #pk_p_plist:hover ~ .pk-player-controls, #pk_p_box:not(.pk-is-seeking) #pk_p_plist:hover ~ .pk-p-prog-wrap { opacity: 0 !important; pointer-events: none !important; }
        .pk-p-side-nav { position: absolute; top: 50%; transform: translateY(-50%) translateZ(0); width: 60px; height: 60px; background: rgba(0, 0, 0, 0.3); display: flex; align-items: center; justify-content: center; color: #fff; cursor: pointer; z-index: 40; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); opacity: 0; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.18); pointer-events: auto !important; box-shadow: 0 4px 15px rgba(0,0,0,0.15); will-change: transform, opacity; }
        .pk-player-box:hover .pk-p-side-nav { opacity: 1; }
        .pk-p-side-nav:hover { background: rgba(0, 0, 0, 0.45); transform: translateY(-50%) scale(1.08) translateZ(0); border-color: rgba(255, 255, 255, 0.3); }
        .pk-p-side-nav.L { left: 30px; }
        .pk-p-side-nav.R { right: 30px; }
        .pk-p-side-nav svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 0 5px rgba(0,0,0,0.2)); }
        .pk-p-side-nav.L svg { margin-left: -2px; }
        .pk-p-side-nav.R svg { margin-left: 2px; }
        .pk-p-sub-pop { position: absolute; bottom: 48px; right: 0; background: #222; border-radius: 8px; padding: 16px; width: 340px; height: 380px; color: #eee; font-size: 12px; box-shadow: 0 12px 40px rgba(0,0,0,0.5); border: 1px solid #333; display: none; flex-direction: column; cursor: default; z-index: 30; font-family: sans-serif; box-sizing: border-box; }
        .pk-p-sub-pop::after { content: ""; position: absolute; top: 100%; left: 0; right: 0; height: 12px; background: transparent; }
        .pk-p-menu-con:hover .pk-p-sub-pop, .pk-p-sub-pop:hover { display: flex; }
        .pk-sub-tabs { display: flex; border-bottom: 1px solid #333; margin-bottom: 15px; flex-shrink: 0; }
        .pk-sub-tab { padding: 8px 12px; color: #aaa; cursor: pointer; font-size: 13px; position: relative; }
        .pk-sub-tab.active { color: #4aa1ff; font-weight: bold; border-bottom: 2px solid #4aa1ff; }
        .pk-sub-tab.active::after { content: ''; position: absolute; bottom: -1px; left: 0; right: 0; height: 2px; background: #4aa1ff; }
        .pk-sub-pane { display: none; flex-direction: column; gap: 12px; flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #444 transparent; }
        .pk-sub-pane.active { display: flex; }
        .pk-sub-radio-grp { display: flex; flex-direction: column; gap: 14px; margin-top: 5px; }
        .pk-sub-radio-item { display: flex; align-items: center; gap: 10px; cursor: pointer; color: #eee; font-size: 13px; user-select: none; }
        .pk-sub-radio-item input { width: 16px; height: 16px; accent-color: #4aa1ff; cursor: pointer; margin: 0; }
        .pk-sub-val-input { width: 50px; background: #333; border: 1px solid #444; color: #fff; border-radius: 4px; padding: 2px 5px; text-align: center; font-size: 12px; outline: none; }
        .pk-sub-val-input:focus { border-color: #4aa1ff; }
        .pk-more-sep { height: 1px; background: #333; margin: 15px 0; }
        .pk-size-row { display: flex; align-items: center; margin-bottom: 15px; }
        .pk-size-label { min-width: 40px; margin-right: 12px; white-space: nowrap; color: #888; font-size: 12px; flex-shrink: 0; }
        .pk-size-opts { display: flex; gap: 8px; flex: 1; }
        .pk-size-btn { flex: 1; background: #333; border: 1px solid #444; color: #ddd; padding: 6px 4px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.2s; display: flex; align-items: center; justify-content: center; gap: 0; text-align: center; }
        .pk-size-btn:hover { background: #444; border-color: #555; color: #fff; }
        .pk-size-btn.active { color: #4aa1ff; border-color: #4aa1ff; background: rgba(74, 161, 255, 0.1); }
        .pk-size-btn svg { width: 16px; height: 16px; margin-right: 4px; display: block; flex-shrink: 0; margin-bottom: 1px; }
        .pk-sub-pane::-webkit-scrollbar { width: 6px; }
        .pk-sub-pane::-webkit-scrollbar-track { background: transparent; }
        .pk-sub-pane::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
        .pk-sub-pane::-webkit-scrollbar-thumb:hover { background: #555; }
        .pk-sub-radio-group { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }
        .pk-sub-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
        .pk-sub-label { color: #aaa; white-space: nowrap; flex-shrink: 0; margin-right: 10px; }
        .pk-sub-btn-group { display: flex; gap: 8px; margin-top: 4px; }
        .pk-sub-btn { flex: 1; background: #333; border: 1px solid #444; color: #ddd; padding: 6px 4px; border-radius: 4px; cursor: pointer; text-align: center; transition: background 0.2s; font-size: 11.5px; line-height: 1.2; display: flex; align-items: center; justify-content: center; }
        .pk-sub-btn:hover { background: #444; }
        .pk-sub-btn:active { background: #555; }
        .pk-sub-ctrl { display: flex; align-items: center; background: #333; border-radius: 4px; border: 1px solid #444; }
        .pk-sub-ctrl-btn { width: 28px; height: 24px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #ddd; }
        .pk-sub-ctrl-btn:hover { background: #444; }
        .pk-sub-val { width: 60px; text-align: center; border-left: 1px solid #444; border-right: 1px solid #444; height: 24px; line-height: 24px; font-variant-numeric: tabular-nums; }
        .pk-sub-slider { -webkit-appearance: none; width: 100px; height: 4px; background: #444; border-radius: 2px; outline: none; }
        .pk-sub-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #fff; cursor: pointer; }
        .pk-sub-check { width: 14px; height: 14px; accent-color: #4aa1ff; cursor: pointer; }
        video::cue { background: rgba(0,0,0,0.5); color: white; font-family: sans-serif; }
    #pk_p_box { position: absolute !important; top: 10% !important; left: 50% !important; transform: translateX(-50%) !important; width: 90% !important; height: 80% !important; background: #000; border-radius: 0 !important; overflow: visible !important; transition: height 0.2s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 25px 50px rgba(0,0,0,0.5); }
    #pk_p_box.plist-active { height: calc(80% - 84px) !important; }
    #pk_p_box:fullscreen, #pk_p_box:-webkit-full-screen, #pk_p_box:-moz-full-screen { width: 100% !important; height: 100% !important; top: 0 !important; left: 0 !important; transform: none !important; margin: 0 !important; border-radius: 0 !important; overflow: hidden !important; }
    #pk_video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: contain; z-index: 10; transition: none; }
    .pk-player-controls { position: absolute; bottom: 0; left: 0; right: 0; z-index: 80 !important; transition: none; }
    .pk-p-prog-wrap { position: absolute; bottom: 64px; left: 20px; right: 20px; z-index: 60 !important; display: flex; align-items: center; gap: 16px; transition: none; pointer-events: none; }
    .pk-p-prog-wrap > * { pointer-events: auto; }
    #pk_p_box.pk-tab-hover:not(.pk-is-seeking) .pk-player-controls, #pk_p_box.pk-tab-hover:not(.pk-is-seeking) .pk-p-prog-wrap { opacity: 0 !important; pointer-events: none !important; transition: opacity 0.2s ease; }
    .pk-player-progress-container { position: relative !important; bottom: auto !important; left: auto !important; right: auto !important; width: auto !important; flex: 1; z-index: auto !important; }
    .pk-p-side-nav, .pk-p-center-play, .pk-p-seek-indicator { top: 50%; transition: top 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, transform 0.2s ease !important; }
    #pk_p_plist { position: absolute; top: 100%; left: 0; right: 0; z-index: 95 !important; height: 84px; opacity: 1; pointer-events: none; }
    .pk-p-plist-tab { pointer-events: auto !important; margin-bottom: -1px !important; z-index: 90 !important; }
    .pk-p-plist-strip { opacity: 0; pointer-events: none !important; transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
    #pk_p_box.plist-active .pk-p-plist-strip { opacity: 1; pointer-events: auto !important; }
    .pk-p-loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 35; pointer-events: none; }
    #pk_p_poster { z-index: 25 !important; transition: opacity 0.4s ease; width: 100% !important; height: 100% !important; background: #000; }
    .pk-player-box.pk-v-started #pk_p_poster { pointer-events: none; }
    .pk-transcode-mask { position: absolute; inset: 0; z-index: 30; background: #0c0c0c; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #ddd; font-size: 14px; }
    .pk-tc-icon { width: 50px; height: 50px; margin-bottom: 20px; border: 3px solid rgba(255,255,255,0.1); border-top-color: var(--pk-pri); border-radius: 50%; animation: spin 1s linear infinite; }
    .pk-tc-btn { margin-top: 20px; padding: 6px 16px; border: 1px solid #444; border-radius: 20px; cursor: pointer; font-size: 12px; transition: all 0.2s; }
    .pk-tc-btn:hover { background: #333; border-color: #666; color: #fff; }
    #pk_p_preview { position: absolute; bottom: 85px; left: 0; display: flex; flex-direction: column; align-items: center; z-index: 100; pointer-events: none; opacity: 0; transform: translateX(-50%) scale(0.95); transform-origin: bottom center; transition: opacity 0.15s ease, transform 0.15s ease; will-change: transform, opacity, left; }
    #pk_p_preview.show { opacity: 1; transform: translateX(-50%) scale(1); }
    .pk-prev-img-box { position: relative; background: #000; border: 2px solid #fff; border-radius: 4px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); overflow: hidden; display: none; min-width: 120px; min-height: 68px; transition: width 0.1s, height 0.1s; }
    .pk-prev-img-box img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.1s ease-out; }
    .pk-prev-img-box img.active { opacity: 1; }
    .pk-prev-time { margin-top: 6px; background: rgba(0,0,0,0.85); color: #fff; font-size: 12px; font-weight: 600; text-align: center; padding: 4px 8px; border-radius: 4px; font-family: "Segoe UI", Roboto, monospace; box-shadow: 0 2px 8px rgba(0,0,0,0.3); text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
    `;

    document.head.appendChild(styleEl);

    d.innerHTML = `
        <div class="pk-player-box" id="pk_p_box" style="width: 90%; max-width: calc(1600px / var(--pk-zoom, 1)); min-width: 480px; height: 80%; border-radius: 0; box-shadow: 0 25px 50px rgba(0,0,0,0.5);">
                <div id="pk_p_preview">
                    <div class="pk-prev-img-box" id="pk_p_img_box"></div>
                    <div class="pk-prev-time">00:00</div>
                </div>
                <div id="pk_p_poster" style="position:absolute; inset:0; z-index:1; background:#000; display:flex; align-items:center; justify-content:center; pointer-events:none;">
                    <img src="${posterUrl}" draggable="false" style="width:100%; height:100%; object-fit:contain; opacity:${posterUrl ? 1 : 0}; -webkit-user-drag:none; user-select:none;"
                        onerror="this.style.display='none';">
                </div>

                <div id="pk_sub_render_layer" style="position:absolute; inset:0; z-index:40; pointer-events:none; display:flex; flex-direction:column; justify-content:flex-end; align-items:center; padding-bottom:10%; text-align:center; overflow:hidden; transition: padding-bottom 0.1s linear;">
                    <span id="pk_sub_text" style="background:rgba(0,0,0,0.6); color:#fff; padding:4px 8px; border-radius:4px; font-size:24px; line-height:1.4; white-space:pre-wrap; display:none; text-shadow:0 1px 2px black; transition: font-size 0.1s linear, background-color 0.1s linear;"></span>
                </div>

                <video class="pk-player-video" id="pk_video"
                    type="video/mp4"
                    crossorigin="anonymous"
                    referrerpolicy="no-referrer"
                    preload="auto"
                    fetchpriority="high"
                    playsinline
                    draggable="false"
                    style="position:relative; z-index:0; transform: translateZ(0); backface-visibility: hidden; image-rendering: -webkit-optimize-contrast; -webkit-font-smoothing: antialiased; -webkit-user-drag:none; user-select:none;">
                </video>

                <div class="pk-p-loading"><div class="pk-spin-lg" style="border-color:rgba(255,255,255,0.3); border-top-color:#fff;"></div></div>

                <div class="pk-p-center-play" id="pk_p_center_btn">
                    ${mkSvg(icons.playCenter)}
                </div>

                <div class="pk-p-vol-indicator" id="pk_p_vol_indicator">
                    <div style="width:24px;height:24px;display:flex;align-items:center;">${mkSvg(icons.vol)}</div>
                    <span id="pk_p_vol_val">100%</span>
                </div>

                <div class="pk-p-seek-indicator" id="pk_p_seek_indicator">
                    00:00 / 00:00
                </div>

                <div class="pk-p-side-nav L" id="pk_p_side_L" data-pk-tip="${L.btn_prev_video}">${mkSvg('<path d="M15.5 19l-7-7 7-7"/>')}</div>
                <div class="pk-p-side-nav R" id="pk_p_side_R" data-pk-tip="${L.btn_next_video}">${mkSvg('<path d="M8.5 19l7-7-7-7"/>')}</div>

                <div class="pk-player-top">
                    <div class="pk-player-title">[${curListIdx + 1}/${totalInList}] ${esc(item.name)}</div>
                    <div style="display:flex; gap:10px;">
                        <div class="pk-p-btn" id="pk_p_pip" data-pk-tip="${L.tip_pip}" style="display:none !important;">${mkSvg(icons.pip)}</div>
                        <div class="pk-p-btn" id="pk_p_search" data-pk-tip="${L.tip_play_search}" style="display:none !important;">${mkSvg(icons.search)}</div>
                        <div class="pk-p-btn" id="pk_p_close" data-pk-tip="${L.tip_close}">${mkSvg(icons.close)}</div>
                    </div>
                </div>

                <div class="pk-p-plist-ov" id="pk_p_plist">
                    <div class="pk-p-plist-tab" id="pk_p_plist_tab" data-pk-tip="${L.tip_plist_open}">
                        ${curListIdx + 1} / ${totalInList}
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>
                    </div>
                    <div class="pk-p-plist-strip">
                        <div class="pk-p-plist-nav L" id="pk_p_plist_L">${mkSvg('<path d="M15 6 L9 12 L15 18" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>')}</div>
                        <div class="pk-p-plist-scroll" id="pk_p_plist_scroll">
                            ${renderPlaylistItems()}
                        </div>
                        <div class="pk-p-plist-nav R" id="pk_p_plist_R">${mkSvg('<path d="M9 6 L15 12 L9 18" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>')}</div>
                    </div>
                </div>

                <div class="pk-p-prog-wrap" style="position: absolute; bottom: 64px; left: 20px; right: 20px; z-index: 61; display: flex; align-items: center; gap: 16px;">
                    <div class="pk-p-time-side" id="pk_t_cur" style="font-size: 14px; font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-weight: 600; color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,0.9); font-variant-numeric: tabular-nums; min-width: 60px; text-align: right;">00:00:00</div>

                    <div class="pk-player-progress-container" id="pk_p_prog_area" style="position: relative; flex: 1; height: 16px; cursor: pointer; display: flex; align-items: center;">
                        <div class="pk-player-progress-bg" style="width: 100%; height: 4px; background: rgba(255,255,255,0.2); position: relative; border-radius: 2px; backdrop-filter: blur(2px);">
                            <div class="pk-player-progress-filled" id="pk_p_filled" style="height: 100%; background: var(--pk-pri); width: 0; position: relative; border-radius: 2px;">
                                <div class="pk-player-progress-thumb" style="position: absolute; right: -11px; width: 22px; height: 22px; background: transparent; display: flex; align-items: center; justify-content: center;">
                                    <svg viewBox="0 0 192 192" shape-rendering="geometricPrecision" style="width: 100%; height: 100%; overflow: visible; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));">
                                        <path d="M23.3 57.5 82.5 37" fill="none" stroke="#444" stroke-width="12" stroke-linecap="round" stroke-miterlimit="10"/>
                                        <path d="m168.7 57.5-59.2-20.5" fill="none" stroke="#444" stroke-width="12" stroke-linecap="round" stroke-miterlimit="10"/>
                                        <rect x="22" y="57.5" width="148" height="92.5" rx="18" ry="18" fill="#fff" stroke="#444" stroke-width="12" />

                                        <g class="pk-eye-open">
                                            <path class="pk-p-eye" d="M69.7 118.5c3.8 0 6.9-3.1 6.9-6.9V97.1c0-3.8-3.1-6.9-6.9-6.9-3.8 0-6.9 3.1-6.9 6.9v14.6c0 3.8 3.1 6.8 6.9 6.8z" fill="#444" stroke="#444" stroke-width="5"/>
                                            <path class="pk-p-eye" d="M122.3 118.5c-3.8 0-6.9-3.1-6.9-6.9V97.1c0-3.8 3.1-6.9 6.9-6.9 3.8 0 6.9 3.1 6.9 6.9v14.6c0 3.8-3.1 6.8-6.9 6.8z" fill="#444" stroke="#444" stroke-width="5"/>
                                        </g>

                                        <g class="pk-eye-closed">
                                            <rect class="pk-p-eye" x="57.7" y="100" width="24" height="8" rx="4" ry="4" fill="#444" />
                                            <rect class="pk-p-eye" x="110.3" y="100" width="24" height="8" rx="4" ry="4" fill="#444" />
                                        </g>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="pk-p-time-side" id="pk_t_dur" style="font-size: 14px; font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-weight: 600; color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,0.9); font-variant-numeric: tabular-nums; min-width: 60px; text-align: left;">00:00:00</div>
                </div>

                <div class="pk-player-controls">
                    <div class="pk-p-btn" id="pk_p_play">${mkSvg(icons.pause)}</div>

                    <div class="pk-p-vol-wrap" style="margin-left: 8px;">
                        <div class="pk-p-btn" id="pk_p_vol">${mkSvg(icons.vol)}</div>
                        <input type="range" class="pk-p-vol-slider" id="pk_p_vol_slide" min="0" max="1" step="0.05" value="1">
                    </div>

                    <div style="flex:1"></div>

                    <div class="pk-p-menu-con">
                        <span id="pk_p_res_txt">${currentResName}</span>
                        <div class="pk-p-pop" id="pk_p_res_list">${renderQualityMenu(qualityList, currentResName)}</div>
                    </div>

                    <div class="pk-p-menu-con">
                        <span id="pk_p_spd_txt">1.0x</span>
                        <div class="pk-p-pop" id="pk_p_spd_list">
                            <div class="pk-p-item" data-spd="3.0">3.0x</div>
                            <div class="pk-p-item" data-spd="2.0">2.0x</div>
                            <div class="pk-p-item" data-spd="1.5">1.5x</div>
                            <div class="pk-p-item" data-spd="1.25">1.25x</div>
                            <div class="pk-p-item active" data-spd="1.0">1.0x</div>
                            <div class="pk-p-item" data-spd="0.75">0.75x</div>
                            <div class="pk-p-item" data-spd="0.5">0.5x</div>
                        </div>
                    </div>

                    <div class="pk-p-menu-con" id="pk_sub_trigger">
                        <div class="pk-p-btn">${mkSvg(icons.settings)}</div>

                        <div class="pk-p-sub-pop" id="pk_sub_panel" onclick="event.stopPropagation()">
                            <div class="pk-sub-tabs">
                                <div class="pk-sub-tab active" data-target="pane-sub">${L.tab_sub}</div>
                                <div class="pk-sub-tab" data-target="pane-size">${L.tab_size}</div>
                                <div class="pk-sub-tab" data-target="pane-more">${L.tab_more}</div>
                            </div>

                            <div class="pk-sub-pane active" id="pane-sub">
                                <div class="pk-sub-row">
                                    <span class="pk-sub-label">${L.lbl_sub_sel}</span>
                                <label style="display:flex;align-items:center;gap:4px;cursor:pointer;">
                                    <input type="checkbox" id="pk_sub_toggle" class="pk-sub-check" checked>
                                    <span>${L.lbl_show_sub}</span>
                                </label>
                            </div>
                            <div style="background:#333; padding:6px; border-radius:4px; color:#888; font-size:11px;
                                margin-bottom:8px;" id="pk_sub_name">
                                ${L.str_no_sub}
                            </div>
                            <div class="pk-sub-btn-group">
                                <div class="pk-sub-btn" id="pk_sub_search_btn">${L.btn_sub_search}</div>
                                <div class="pk-sub-btn" id="pk_sub_cloud_btn">${L.btn_sub_cloud}</div>
                                <div class="pk-sub-btn" id="pk_sub_local_btn">${L.btn_sub_local}</div>
                                <input type="file" id="pk_sub_file" accept=".srt,.vtt,.ass" style="display:none;">
                            </div>

                            <div style="height:10px;"></div>

                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_sub_pos}</span>
                                <div style="display:flex;align-items:center;gap:8px;">
                                    <span style="font-size:11px;color:#888;white-space:nowrap;">${L.lbl_sub_bottom}</span>
                                    <input type="range" id="pk_sub_pos" class="pk-sub-slider" min="0" max="100" value="10">
                                    <span style="font-size:11px;color:#888;white-space:nowrap;">${L.lbl_sub_top}</span>
                                </div>
                            </div>

                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_sub_bg_op}</span>
                                <div style="display:flex;align-items:center;gap:8px;">
                                    <span style="font-size:11px;color:#888;white-space:nowrap;">0%</span>
                                    <input type="range" id="pk_sub_bg_opacity" class="pk-sub-slider" min="0" max="100" value="60">
                                    <span style="font-size:11px;color:#888;white-space:nowrap;">100%</span>
                                </div>
                            </div>

                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_sub_size}</span>
                                <div class="pk-sub-ctrl">
                                    <div class="pk-sub-ctrl-btn" id="pk_sub_size_dec">-</div>
                                    <div class="pk-sub-val" id="pk_sub_size_val">20</div>
                                    <div class="pk-sub-ctrl-btn" id="pk_sub_size_inc">+</div>
                                </div>
                            </div>

                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_sub_offset}</span>
                                <div class="pk-sub-ctrl">
                                    <div class="pk-sub-ctrl-btn" id="pk_sub_time_dec">-</div>
                                    <div class="pk-sub-val" id="pk_sub_time_val">0.0 ${L.unit_sec}</div>
                                    <div class="pk-sub-ctrl-btn" id="pk_sub_time_inc">+</div>
                                </div>
                            </div>
                        </div>

                        <div class="pk-sub-pane" id="pane-size">
                            <div class="pk-size-row">
                                <span class="pk-size-label">${L.lbl_ratio}</span>
                                <div class="pk-size-opts" id="pk_ratio_opts">
                                    <div class="pk-size-btn active" data-ratio="default">${L.opt_ratio_def}</div>
                                    <div class="pk-size-btn" data-ratio="16/9">16:9</div>
                                    <div class="pk-size-btn" data-ratio="4/3">4:3</div>
                                </div>
                            </div>
                            <div class="pk-size-row" style="align-items: flex-start;">
                                <span class="pk-size-label" style="margin-top:8px;">${L.lbl_direction}</span>
                                <div class="pk-size-opts" style="display:grid; grid-template-columns: 1fr 1fr; gap:10px; width:100%;">
                                    <div class="pk-size-btn" id="pk_btn_rot_l">${mkSvg(icons.rotL)} ${L.btn_rot_l}</div>
                                    <div class="pk-size-btn" id="pk_btn_rot_r">${mkSvg(icons.rotR)} ${L.btn_rot_r}</div>
                                    <div class="pk-size-btn" id="pk_btn_flip_h">${mkSvg(icons.flipH)} ${L.btn_flip_h}</div>
                                    <div class="pk-size-btn" id="pk_btn_flip_v">${mkSvg(icons.flipV)} ${L.btn_flip_v}</div>
                                </div>
                            </div>
                        </div>

                        <div class="pk-sub-pane" id="pane-more">
                            <div style="font-weight:bold; margin-bottom:5px; color:#fff;">${L.lbl_play_end}</div>
                            <div class="pk-sub-radio-grp">
                                <label class="pk-sub-radio-item"><input type="radio" name="pk_pmode" value="list_loop"> ${L.opt_list_loop}</label>
                                <label class="pk-sub-radio-item"><input type="radio" name="pk_pmode" value="single_loop"> ${L.opt_single_loop}</label>
                                <label class="pk-sub-radio-item"><input type="radio" name="pk_pmode" value="stop"> ${L.opt_play_stop}</label>
                            </div>

                            <div class="pk-more-sep"></div>

                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_skip_op}</span>
                                <div style="display:flex; align-items:center; gap:8px;">
                                    <div class="pk-sub-btn" id="pk_op_mark" style="padding:2px 8px; font-size:11px;">${L.btn_mark}</div>
                                    <div class="pk-sub-ctrl">
                                        <div class="pk-sub-ctrl-btn" id="pk_op_dec">-</div>
                                        <div class="pk-sub-val" id="pk_op_val">0 ${L.unit_sec}</div>
                                        <div class="pk-sub-ctrl-btn" id="pk_op_inc">+</div>
                                    </div>
                                </div>
                            </div>
                            <div class="pk-sub-row">
                                <span class="pk-sub-label">${L.lbl_skip_ed}</span>
                                <div style="display:flex; align-items:center; gap:8px;">
                                    <div class="pk-sub-btn" id="pk_ed_mark" style="padding:2px 8px; font-size:11px;">${L.btn_mark}</div>
                                    <div class="pk-sub-ctrl">
                                        <div class="pk-sub-ctrl-btn" id="pk_ed_dec">-</div>
                                        <div class="pk-sub-val" id="pk_ed_val">0 ${L.unit_sec}</div>
                                        <div class="pk-sub-ctrl-btn" id="pk_ed_inc">+</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="pk-p-btn" id="pk_p_full" data-pk-tip="${L.tip_full_screen}">${mkSvg(icons.full)}</div>
                </div>
            </div>
        `;
        const v = d.querySelector('#pk_video');
        const box = d.querySelector('#pk_p_box');
        const btnPlay = d.querySelector('#pk_p_play');
        const btnVol = d.querySelector('#pk_p_vol');
        const slideVol = d.querySelector('#pk_p_vol_slide');
        const btnFull = d.querySelector('#pk_p_full');
        const btnClose = d.querySelector('#pk_p_close');
        const btnSearch = d.querySelector('#pk_p_search');
        const tCur = d.querySelector('#pk_t_cur');
        const tDur = d.querySelector('#pk_t_dur');
        const progArea = d.querySelector('#pk_p_prog_area');
        const progFilled = d.querySelector('#pk_p_filled');
        const resList = d.querySelector('#pk_p_res_list');
        const resTxt = d.querySelector('#pk_p_res_txt');
        const spdList = d.querySelector('#pk_p_spd_list');
        const plist = d.querySelector('#pk_p_plist');
        const pTab = d.querySelector('#pk_p_plist_tab');
        const pScroll = d.querySelector('#pk_p_plist_scroll');
        pScroll.onwheel = (e) => {
            e.preventDefault();
            pScroll.scrollBy({ left: e.deltaY > 0 ? 300 : -300, behavior: 'smooth' });
        };
        let pTip = document.getElementById('pk_p_plist_tip_global');
        if (!pTip) {
            pTip = document.createElement('div');
            pTip.id = 'pk_p_plist_tip_global';
            pTip.className = 'pk-p-plist-tip';
            document.body.appendChild(pTip);
        }

        d.querySelector('#pk_p_side_L').onclick = (e) => {
            e.stopPropagation();
            const prevIdx = (curListIdx - 1 + totalInList) % totalInList;
            softSwitch(prevIdx);
        };
        d.querySelector('#pk_p_side_R').onclick = (e) => {
            e.stopPropagation();
            const nextIdx = (curListIdx + 1) % totalInList;
            softSwitch(nextIdx);
        };

        pTab.onmouseenter = () => box.classList.add('pk-tab-hover');
        pTab.onmouseleave = () => box.classList.remove('pk-tab-hover');

        const strip = d.querySelector('.pk-p-plist-strip');
        if (strip) {
            strip.onmouseenter = () => box.classList.add('pk-tab-hover');
            strip.onmouseleave = () => box.classList.remove('pk-tab-hover');
        }

        pTab.onclick = (e) => {
            e.stopPropagation();
            plist.classList.toggle('open');
            box.classList.toggle('plist-active');

            pTab.setAttribute('data-pk-tip', plist.classList.contains('open') ? L.tip_plist_close : L.tip_plist_open);

            if (plist.classList.contains('open')) {
                const activeItem = pScroll.querySelector('.active');
                if (activeItem) activeItem.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'center' });
            }
        };

        const bindVideoPlistItems = () => {
            pScroll.querySelectorAll('.pk-p-plist-item').forEach(el => {
                el.onmouseenter = (e) => {
                    if (!plist.classList.contains('open')) return;
                    pTip.innerHTML = `<strong>${e.currentTarget.dataset.name}</strong><br>${e.currentTarget.dataset.size}`;
                    pTip.style.display = 'block';
                };
                el.onmousemove = (e) => {
                    if (pTip.style.display === 'block') {
                        const tW = pTip.offsetWidth || 150;
                        pTip.style.left = (e.clientX - (tW / 2)) + 'px';
                        pTip.style.top = (e.clientY - 60) + 'px';
                    }
                };
                el.onmouseleave = () => pTip.style.display = 'none';

                el.onclick = (e) => {
                    e.stopPropagation();
                    if (pTip) pTip.style.display = 'none';
                    const idx = parseInt(e.currentTarget.dataset.idx, 10);
                    if (idx === curListIdx) return;
                    softSwitch(idx);
                };
            });
        };

        const syncVideoPlistItems = (scrollMode = 'smooth') => {
            const RANGE = 150;
            const desiredStart = Math.max(0, curListIdx - RANGE);
            const desiredEnd = Math.min(videoPlaylist.length, curListIdx + RANGE + 1);

            const prevStart = parseInt(pScroll.dataset.pkStart || '-1', 10);
            const prevEnd = parseInt(pScroll.dataset.pkEnd || '-1', 10);

            const needRebuild =
                !Number.isFinite(prevStart) ||
                !Number.isFinite(prevEnd) ||
                curListIdx < prevStart ||
                curListIdx >= prevEnd ||
                pScroll.childElementCount === 0 ||
                pScroll.dataset.pkTotal !== String(videoPlaylist.length);

            if (needRebuild) {
                pScroll.innerHTML = renderPlaylistItems();
                pScroll.dataset.pkStart = String(desiredStart);
                pScroll.dataset.pkEnd = String(desiredEnd);
                pScroll.dataset.pkTotal = String(videoPlaylist.length);
                bindVideoPlistItems();
            }

            pScroll.querySelectorAll('.pk-p-plist-item').forEach(el => {
                const idx = parseInt(el.dataset.idx, 10);
                el.classList.toggle('active', idx === curListIdx);
            });

            const activeItem = pScroll.querySelector('.pk-p-plist-item.active');
            if (activeItem && plist.classList.contains('open') && scrollMode !== false) {
                if (scrollMode === 'instant') {
                    pScroll.style.scrollBehavior = 'auto';
                } else {
                    pScroll.style.scrollBehavior = 'smooth';
                }

                activeItem.scrollIntoView({
                    behavior: scrollMode === 'instant' ? 'auto' : 'smooth',
                    block: 'nearest',
                    inline: 'center'
                });

                if (scrollMode === 'instant') {
                    setTimeout(() => { pScroll.style.scrollBehavior = 'smooth'; }, 50);
                }
            }

            if (typeof updatePlistNav === 'function') updatePlistNav();
        };

        bindVideoPlistItems();

        const updatePlistNav = () => {
            const sl = pScroll.scrollLeft;
            const sw = pScroll.scrollWidth;
            const cw = pScroll.clientWidth;
            d.querySelector('#pk_p_plist_L').style.display = sl <= 5 ? 'none' : 'flex';
            d.querySelector('#pk_p_plist_R').style.display = (sl + cw >= sw - 5) ? 'none' : 'flex';
        };

        d.querySelector('#pk_p_plist_L').onclick = (e) => {
            e.stopPropagation();
            pScroll.scrollBy({ left: -400, behavior: 'smooth' });
            setTimeout(updatePlistNav, 300);
        };
        d.querySelector('#pk_p_plist_R').onclick = (e) => {
            e.stopPropagation();
            pScroll.scrollBy({ left: 400, behavior: 'smooth' });
            setTimeout(updatePlistNav, 300);
        };

        pScroll.addEventListener('scroll', updatePlistNav, { passive: true });

        setTimeout(() => syncVideoPlistItems(false), 50);

        document.body.appendChild(d);

        const initL = d.querySelector('#pk_p_side_L');
        const initR = d.querySelector('#pk_p_side_R');
        if (initL) initL.style.display = curListIdx === 0 ? 'none' : 'flex';
        if (initR) initR.style.display = curListIdx === totalInList - 1 ? 'none' : 'flex';

        let hideTimer = null;
        let isMouseOverUI = false;
        const resetHideTimer = () => {
            box.classList.remove('ui-hidden');
            if (hideTimer) clearTimeout(hideTimer);
            if (isMouseOverUI) return;
            hideTimer = setTimeout(() => {
                if (!v.paused) box.classList.add('ui-hidden');
            }, 3000);
        };

        const protectedUIs = [
            d.querySelector('.pk-player-top'),
            d.querySelector('.pk-player-controls'),
            d.querySelector('.pk-p-prog-wrap')
        ];
        protectedUIs.forEach(ui => {
            if (!ui) return;
            ui.addEventListener('mouseenter', () => { isMouseOverUI = true; if (hideTimer) clearTimeout(hideTimer); });
            ui.addEventListener('mouseleave', () => { isMouseOverUI = false; resetHideTimer(); });
        });

        box.addEventListener('mouseleave', () => { if (!isMouseOverUI) box.classList.add('ui-hidden'); });
        box.addEventListener('mouseenter', resetHideTimer);
        box.addEventListener('mousemove', resetHideTimer);
        box.addEventListener('click', resetHideTimer);
        box.addEventListener('keydown', resetHideTimer);
        resetHideTimer();

        const handleVideoError = (e) => {
            if (isSwitching) return;

            if (!v.getAttribute('src') && !pkHls) return;
            if (v.networkState === 2 && !v.error && !e.force) return;

            const errCode = v.error ? v.error.code : (e.force ? 4 : 0);
            const errMsg = v.error ? v.error.message : "";
            console.warn(`[VideoError] Code: ${errCode}, Msg: ${errMsg}, Src: ${v.src || 'HLS'}`);

            if (currentLink) failedUrls.add(currentLink);

            if (lastWorkingLink && lastWorkingLink !== currentLink && !failedUrls.has(lastWorkingLink)) {
                console.log(`[Compatibility] Target stream failed, rolling back to: ${lastWorkingResName}`);

                if (resTxt) resTxt.textContent = `${L.str_compat_mode} (${lastWorkingResName})`;

                const toast = document.createElement('div');
                toast.style.cssText = "position:absolute;top:20px;left:50%;transform:translateX(-50%);background:rgba(217, 48, 37, 0.9);color:#fff;padding:6px 12px;border-radius:20px;font-size:12px;z-index:100;animation:pkFadeIn 0.5s;";
                toast.textContent = L.str_switch_compat.replace('{n}', lastWorkingResName);
                box.appendChild(toast);
                setTimeout(() => toast.remove(), 4000);

                currentResName = lastWorkingResName;
                currentLink = lastWorkingLink;

                const savedTime = v.currentTime;
                loadSource(currentLink, savedTime);
                v.play().catch(()=>{});

                const resList = d.querySelector('#pk_p_res_list');
                if (resList) {
                    resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                    bindResEvents();
                }
                return;
            }

            const nextCandidate = qualityList.find(q => {
                const url = q.link || q.url;
                return url !== currentLink && !failedUrls.has(url);
            });

            if (nextCandidate) {
                console.log(`[Compatibility] Auto-switching to next available source: ${nextCandidate.name}`);

                if (resTxt) resTxt.textContent = `${L.str_compat_mode} (${nextCandidate.name})`;

                const toast = document.createElement('div');
                toast.style.cssText = "position:absolute;top:20px;left:50%;transform:translateX(-50%);background:rgba(33, 150, 243, 0.9);color:#fff;padding:6px 12px;border-radius:20px;font-size:12px;z-index:100;animation:pkFadeIn 0.5s;";
                toast.textContent = L.msg_fallback_report.replace('{n}', nextCandidate.name);
                box.appendChild(toast);
                setTimeout(()=>toast.remove(), 4000);

                currentResName = nextCandidate.name;
                currentLink = nextCandidate.link || nextCandidate.url;

                const savedTime = v.currentTime;
                loadSource(currentLink, savedTime);
                v.play().catch(()=>{});

                const resList = d.querySelector('#pk_p_res_list');
                if (resList) {
                    resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                    bindResEvents();
                }
                return;
            }

            if (errCode !== 4 && !e.force) {
                console.log("[Video] Recoverable error detected, staying in loader.");
                return;
            }

            const loader = d.querySelector('.pk-p-loading');
            if (loader) loader.style.display = 'none';

            showSadBox(currentResName);
        };
        v.addEventListener('error', handleVideoError);

        v.addEventListener('loadstart', () => {
            const errOv = box.querySelector('.pk-err-ov');
            if (errOv) errOv.remove();

            const loader = d.querySelector('.pk-p-loading');
            if (loader) loader.style.display = 'block';
        });

        (async () => {
            try {
                const targetApiId = getPhysicalId(item);
                const newData = await apiGet(targetApiId);
                const freshData = getBestSource(newData);
                qualityList = freshData.list;
                resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                bindResEvents();

                if (v.error || !currentLink || (currentResName === L.str_original && freshData.name !== L.str_original)) {
                    const savedTime = v.currentTime;
                    currentLink = freshData.src;
                    currentResName = freshData.name;
                    resTxt.textContent = currentResName;

                    loadSource(currentLink);

                    v.currentTime = savedTime;
                    v.play().catch(()=>{});

                    resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                    bindResEvents();
                }
            } catch (e) { }
        })();

        const fmtT = (s) => {
            s = Math.max(0, s || 0);
            const h = Math.floor(s / 3600);
            const m = Math.floor((s % 3600) / 60);
            const sc = Math.floor(s % 60);
            return String(h).padStart(2, '0') + ":" + String(m).padStart(2, '0') + ":" + String(sc).padStart(2, '0');
        };
        const fmtFullT = fmtT;

        const togglePlay = () => { if (v.paused) v.play(); else v.pause(); };

        const ThumbnailEngine = (() => {
            let shadowV = null;
            let canvas = null;
            let ctx = null;
            let isInit = false;
            let cacheStore = null;
            let currentReqId = 0;
            const BASE_HEIGHT = 180;
            const DISPLAY_HEIGHT = 120;
            const previewBox = d.querySelector('#pk_p_preview');
            const imgBox = d.querySelector('#pk_p_img_box');
            const previewTime = previewBox.querySelector('.pk-prev-time');

            const init = async () => {
                if (isInit) return;

                if (window.localforage) {
                    cacheStore = window.localforage.createInstance({ name: 'pk_thumbs', storeName: 'snapshots' });
                }

                shadowV = document.createElement('video');
                shadowV.muted = true;
                shadowV.crossOrigin = 'anonymous';
                shadowV.style.display = 'none';
                shadowV.preload = 'auto';
                shadowV.src = currentLink;

                canvas = document.createElement('canvas');
                canvas.width = 160; canvas.height = 90;
                ctx = canvas.getContext('2d');

                shadowV.onerror = () => console.warn("[Thumb] Shadow player error");

                isInit = true;
            };

            const getCacheKey = (time) => `${getPhysicalId(item)}_${Math.floor(time)}`;

            const generate = async (time) => {
                if (!isInit) await init();

                if (cacheStore) {
                    const cachedBlob = await cacheStore.getItem(getCacheKey(time));
                    if (cachedBlob) return URL.createObjectURL(cachedBlob);
                }

                return new Promise((resolve, reject) => {
                    const seekHandler = () => {
                        try {
                            const vw = shadowV.videoWidth || 160;
                            const vh = shadowV.videoHeight || 90;
                            const ratio = vw / vh;

                            const renderH = BASE_HEIGHT;
                            const renderW = Math.floor(renderH * ratio);

                            if (canvas.width !== renderW || canvas.height !== renderH) {
                                canvas.width = renderW;
                                canvas.height = renderH;
                            }

                            ctx.drawImage(shadowV, 0, 0, renderW, renderH);
                            canvas.toBlob((blob) => {
                                if (cacheStore) cacheStore.setItem(getCacheKey(time), blob).catch(()=>{});
                                resolve(URL.createObjectURL(blob));
                            }, 'image/jpeg', 0.8);
                        } catch (e) {
                            reject(e);
                        } finally {
                            shadowV.removeEventListener('seeked', seekHandler);
                        }
                    };

                    shadowV.addEventListener('seeked', seekHandler);
                    shadowV.currentTime = time;
                });
            };

            const show = async (clientX, rect) => {
                const boxRect = getLogicalRect(box);
                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                const logicalX = clientX / scale;
                const pos = Math.max(0, Math.min(1, (logicalX - rect.left) / rect.width));
                const targetTime = pos * v.duration;
                if (!isFinite(targetTime)) return;

                let left = logicalX - boxRect.left;
                const halfWidth = (imgBox.offsetWidth / 2) || 80;
                const minX = halfWidth + 10;
                const maxX = boxRect.width - halfWidth - 10;

                left = Math.max(minX, Math.min(maxX, left));
                previewBox.style.left = `${left}px`;
                previewTime.textContent = fmtT(targetTime);
                previewBox.classList.add('show');

                const myId = ++currentReqId;
                try {
                    await sleep(50);
                    if (myId !== currentReqId) return;

                    const url = await generate(targetTime);
                    if (myId !== currentReqId) return;

                    const img = document.createElement('img');
                    img.src = url;

                    const onImgReady = () => {
                        if (myId !== currentReqId) return;

                        if (img.naturalWidth && img.naturalHeight) {
                            const ratio = img.naturalWidth / img.naturalHeight;
                            imgBox.style.width = `${DISPLAY_HEIGHT * ratio}px`;
                            imgBox.style.height = `${DISPLAY_HEIGHT}px`;
                        }

                        imgBox.style.display = 'flex';

                        img.classList.add('active');
                        imgBox.appendChild(img);

                        const oldImages = imgBox.querySelectorAll('img');
                        if (oldImages.length > 1) {
                            setTimeout(() => {
                                for (let i = 0; i < oldImages.length - 1; i++) {
                                    oldImages[i].remove();
                                }
                            }, 150);
                        }
                    };

                    if (img.complete) onImgReady();
                    else img.onload = onImgReady;

                } catch (e) {
                }
            };

            const hide = () => {
                previewBox.classList.remove('show');
                imgBox.style.display = 'none';
                currentReqId++;
                setTimeout(() => {
                    if (!previewBox.classList.contains('show')) {
                        const imgs = imgBox.querySelectorAll('img');
                        imgs.forEach(i => i.remove());
                    }
                }, 500);
            };

            const resetSource = (newUrl) => {
                if (shadowV) shadowV.src = newUrl;
            };

            return { show, hide, resetSource };
        })();


        const updateState = () => {
            if (v.paused) {
                box.classList.add('paused');
                btnPlay.innerHTML = mkSvg(icons.play);
                box.classList.remove('ui-hidden');
                if (hideTimer) clearTimeout(hideTimer);
            }
            else {
                box.classList.remove('paused');
                btnPlay.innerHTML = mkSvg(icons.pause);
                resetHideTimer();
            }
        };
        const updateVolUI = () => {
            slideVol.value = v.muted ? 0 : v.volume;
            btnVol.innerHTML = mkSvg((v.muted || v.volume === 0) ? icons.mute : icons.vol);
        };

        let transcodeTimer = null;

        const destroyPlayer = () => {
            if (isPlayerDestroyed) return;
            isPlayerDestroyed = true;

            const pTip = document.getElementById('pk_p_plist_tip_global');
            if (pTip) pTip.style.display = 'none';

            document.removeEventListener('keydown', playerKeyHandler);

            if (document.pictureInPictureElement) {
                document.exitPictureInPicture().catch(() => {});
            }

            window.removeEventListener('resize', onResizeTransform);

            if (transcodeTimer) { clearInterval(transcodeTimer); transcodeTimer = null; }

            if (v.duration > 0 && v.currentTime > 5 && v.duration - v.currentTime > 5) {
                gmSet('pk_progress_' + getPhysicalId(item), {
                    t: v.currentTime,
                    d: v.duration,
                    ts: Date.now()
                });
            }

            v.pause();
            v.muted = true;

            if (pkHls) {
                pkHls.stopLoad();
                pkHls.detachMedia();
                pkHls.destroy();
                pkHls = null;
            }

            const targetId = item.id;
            const targetIdx = S.display.findIndex(x => x.id === targetId);
            if (targetIdx !== -1) {
                S.sel.clear();
                S.sel.add(targetId);
                S.activeId = targetId;
                const rowTop = getItemScrollTopByIndex(targetIdx);
                const vpHeight = UI.vp.clientHeight;
                UI.vp.scrollTop = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));
                renderVisible();
                updateStat();
            }

            v.src = "";
            v.load();

            if (styleEl) styleEl.remove();
            d._pkDestroyPlayer = null;
            d.remove();
        };
        d._pkDestroyPlayer = destroyPlayer;

        v.addEventListener('play', () => {
            S.navVideoBackArmed = false;
            updateState();
        });
        v.addEventListener('pause', updateState);

        const markStarted = () => {
            if (isPlayerDestroyed) return;
            if (box) {
                box.classList.add('pk-v-started');
                stopSpinner();
                updateState();
                lastWorkingLink = currentLink;
                lastWorkingResName = currentResName;
            }
        };

        v.addEventListener('playing', markStarted);
        v.addEventListener('seeked', () => { if(v.currentTime > 0.1) markStarted(); });

        v.addEventListener('click', () => {
            markStarted();
            togglePlay();
        });

        v.addEventListener('dblclick', (e) => {
            e.stopPropagation();
            btnFull.click();
        });

        const blockMediaDragWhenErr = (e) => {
            if (!box.querySelector('.pk-err-dialog')) return;
            e.preventDefault();
            e.stopPropagation();
        };
        v.addEventListener('dragstart', blockMediaDragWhenErr);

        const posterEl = d.querySelector('#pk_p_poster');
        const loaderEl = d.querySelector('.pk-p-loading');
        let shutterTargetTime = 0;
        let isPiPDesired = false;

        const stopSpinner = (force = false) => {
            const isErrorVisible = !!box.querySelector('.pk-err-dialog');
            if ((shutterTargetTime > 0 || isErrorVisible) && !force) return;

            box.classList.remove('buffering');

            if (v.paused && v.readyState >= 2) {
                box.classList.add('pk-v-started');
                updateState();
            }

            if (loaderEl) loaderEl.style.display = 'none';
            if (posterEl) {
                posterEl.style.pointerEvents = 'none';
                posterEl.style.opacity = '0';
                setTimeout(() => {
                    if(posterEl.style.opacity === '0') {
                        posterEl.style.display = 'none';
                        posterEl.style.pointerEvents = 'auto';
                    }
                }, 450);
            }
        };

        const showSpinner = () => {
            box.classList.add('buffering');
            if (loaderEl) loaderEl.style.display = 'block';
        };
        v.addEventListener('waiting', showSpinner);
        v.addEventListener('stalled', showSpinner);
        v.addEventListener('playing', stopSpinner);
        v.addEventListener('seeked', stopSpinner);
        v.addEventListener('canplaythrough', stopSpinner);

        let lastTextUpdate = 0;
        const updateTimeUI = () => {
            requestAnimationFrame(() => {
                if (isDragSeek) return;

                if (v.currentTime > 0.1) stopSpinner();

                const dur = v.duration;
                const cur = v.currentTime;
                const now = performance.now();

                if (dur > 0) {
                    const pct = (cur / dur) * 100;
                    progFilled.style.width = `${pct}%`;
                }

                if (now - lastTextUpdate > 250) {
                    tCur.textContent = fmtT(cur);
                    if (dur > 0 && isFinite(dur)) tDur.textContent = fmtT(dur);
                    lastTextUpdate = now;
                }
            });
        };

        v.addEventListener('timeupdate', updateTimeUI);
        v.addEventListener('durationchange', updateTimeUI);
        v.addEventListener('timeupdate', () => {
            if (shutterTargetTime > 0) {
                if (v.currentTime >= shutterTargetTime - 0.5 && v.readyState >= 3) {
                    v._isRestarting = false;
                    console.log(`[Shutter] Target reached: ${v.currentTime}, Unlocking...`);
                    shutterTargetTime = 0;
                    stopSpinner(true);
                }
            }
        });

        let hasCheckedProgress = false;
        let lastSaveTime = 0;

        const applyProgress = () => {
            if (hasCheckedProgress || v.duration <= 0) return;
            hasCheckedProgress = true;
            triggerResume(v, item);
        };

        v.addEventListener('canplay', applyProgress, { once: true });
        v.addEventListener('playing', () => {
            const now = Date.now();
            const pId = getPhysicalId(item);
            const existing = gmGet('pk_progress_' + pId);
            let data = { t: v.currentTime, d: v.duration || 0, ts: now };

            if (existing && typeof existing === 'object') {
                data.t = existing.t;
                if (!data.d) data.d = existing.d;
            }
            gmSet('pk_progress_' + pId, data);
        });

        v.addEventListener('timeupdate', () => {
            const now = Date.now();
            if (now - lastSaveTime > 3000) {
                const curT = v.currentTime;
                const totalT = v.duration || 0;
                const progressData = { t: curT, d: totalT, ts: now };
                const pId = getPhysicalId(item);

                if (totalT > 0 && (totalT - curT < 5)) {
                    progressData.t = 0;
                    gmSet('pk_progress_' + pId, progressData);
                } else if (curT > 1) {
                    gmSet('pk_progress_' + pId, progressData);
                }
                lastSaveTime = now;
            }
        });

        v.addEventListener('loadedmetadata', () => {
            triggerResume(v, item);
            updateTimeUI();
            const dur = v.duration;
            if (dur > 0 && isFinite(dur)) {
                const seconds = Math.round(dur);
                const pId = getPhysicalId(item);
                gmSet('pk_duration_' + pId, seconds);

                S.durationMap.set(pId, seconds);
                if (item.params) item.params.duration = seconds;
                if (typeof renderVisible === 'function') renderVisible();
            }
        });

        const enableMediaControls = () => {
            if (v.readyState >= 2) {
                if (btnSearch) btnSearch.style.setProperty('display', 'flex', 'important');
                const btnPip = d.querySelector('#pk_p_pip');
                if (btnPip && document.pictureInPictureEnabled && !v.disablePictureInPicture) {
                    btnPip.style.setProperty('display', 'flex', 'important');

                    if (isPiPDesired && !document.pictureInPictureElement) {
                        v.requestPictureInPicture().catch(() => { isPiPDesired = false; });
                    }
                }
            }
        };
        v.addEventListener('loadeddata', enableMediaControls);
        v.addEventListener('canplay', enableMediaControls);
        if (v.readyState >= 2) enableMediaControls();

        let isDragSeek = false;
        let hasUserSeeked = false;
        let initialTimeBeforeDrag = 0;
        let progRectCache = null;
        let lastSeekRequestTime = 0;
        let lastMouseX = 0;
        const seekIndicator = d.querySelector('#pk_p_seek_indicator');
        let seekIndicatorTimer = null;

        const showSeekIndicatorAt = (targetTime) => {
            if (!seekIndicator || !v.duration || isNaN(v.duration)) return;
            seekIndicator.textContent = `${fmtFullT(targetTime)}  /  ${fmtFullT(v.duration)}`;
            seekIndicator.style.display = 'flex';
            box.classList.add('pk-is-seeking');
            clearTimeout(seekIndicatorTimer);
            seekIndicatorTimer = setTimeout(() => {
                if (!isDragSeek) {
                    seekIndicator.style.display = 'none';
                    box.classList.remove('pk-is-seeking');
                }
            }, 900);
        };

        const updateVisualOnly = (clientX) => {
            if (!progRectCache || !v.duration) return 0;
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const logicalX = clientX / scale;
            const pos = Math.max(0, Math.min(1, (logicalX - progRectCache.left) / progRectCache.width));
            const targetTime = pos * v.duration;

            progFilled.style.setProperty('width', `${pos * 100}%`, 'important');
            seekIndicator.textContent = `${fmtFullT(targetTime)}  /  ${fmtFullT(v.duration)}`;

            if (tCur) tCur.textContent = fmtT(targetTime);

            return targetTime;
        };

        const updateVideoOnDemand = (targetTime) => {
            const now = performance.now();
            if (v.paused && (now - lastSeekRequestTime > 40)) {
                v.currentTime = targetTime;
                lastSeekRequestTime = now;
            }
        };

        const stopDragging = (isCancel = false) => {
            if (!isDragSeek) return;

            if (isCancel) {
                v.currentTime = initialTimeBeforeDrag;
                const revertPos = (initialTimeBeforeDrag / v.duration) * 100;
                progFilled.style.setProperty('width', `${revertPos}%`, 'important');
            } else {
                const finalT = updateVisualOnly(lastMouseX);
                v.currentTime = finalT;
                if (finalT > 5) {
                    gmSet('pk_progress_' + getPhysicalId(item), {
                        t: finalT,
                        d: v.duration,
                        ts: Date.now()
                    });
                }
            }

            isDragSeek = false;
            progRectCache = null;
            document.body.classList.remove('pk-dragging');
            box.classList.remove('pk-is-seeking');

            if (typeof ThumbnailEngine !== 'undefined') {
                if (progArea && !progArea.matches(':hover')) {
                    ThumbnailEngine.hide();
                }
            }

            const thumb = box.querySelector('.pk-player-progress-thumb');
            if (thumb) {
                thumb.classList.remove('pk-look-r', 'pk-look-l');
                thumb.classList.add('pk-blink-anim', 'pk-blink-hold');
                setTimeout(() => thumb.classList.remove('pk-blink-anim'), 200);
                setTimeout(() => thumb.classList.remove('pk-blink-hold'), 300);
            }

            seekIndicator.style.display = 'none';
            updateState();
        };

        const onMouseMove = (e) => {
            if (!isDragSeek) return;

            const topBar = d.querySelector('.pk-player-top');
            if (topBar) {
                const barRect = getLogicalRect(topBar);
                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                if ((e.clientY / scale) >= barRect.top && (e.clientY / scale) <= barRect.bottom) {
                    stopDragging(true);
                    return;
                }
            }

            const thumb = box.querySelector('.pk-player-progress-thumb');
            if (thumb) {
                if (e.clientX > lastMouseX + 1) {
                    thumb.classList.add('pk-look-r'); thumb.classList.remove('pk-look-l');
                } else if (e.clientX < lastMouseX - 1) {
                    thumb.classList.add('pk-look-l'); thumb.classList.remove('pk-look-r');
                }
            }

            lastMouseX = e.clientX;
            const targetT = updateVisualOnly(e.clientX);
            updateVideoOnDemand(targetT);

            if (typeof ThumbnailEngine !== 'undefined' && progRectCache) {
                ThumbnailEngine.show(e.clientX, progRectCache);
            }
        };

        progArea.addEventListener('mousedown', (e) => {
            if (!v.duration || isNaN(v.duration)) return;

            if (typeof ThumbnailEngine !== 'undefined') ThumbnailEngine.hide();

            isDragSeek = true;
            hasUserSeeked = true;
            lastMouseX = e.clientX;
            initialTimeBeforeDrag = v.currentTime;
            progRectCache = getLogicalRect(progArea);

            document.body.classList.add('pk-dragging');
            box.classList.add('pk-is-seeking');
            seekIndicator.style.display = 'flex';

            const targetT = updateVisualOnly(e.clientX);
            v.currentTime = targetT;

            e.preventDefault();
        });

        progArea.addEventListener('mousemove', (e) => {
            const rect = getLogicalRect(progArea);
            ThumbnailEngine.show(e.clientX, rect);
        });

        progArea.addEventListener('mouseleave', () => {
            ThumbnailEngine.hide();
        });

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', () => stopDragging(false));

        box.addEventListener('mouseleave', (e) => {
            if (isDragSeek) {
                const rect = getLogicalRect(box);
                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                const lx = e.clientX / scale, ly = e.clientY / scale;
                if (lx <= rect.left || lx >= rect.right || ly <= rect.top || ly >= rect.bottom) {
                    stopDragging(true);
                }
            }
        });

        btnPlay.onclick = togglePlay;
        btnClose.onclick = destroyPlayer;

        btnFull.onclick = () => {
            if (!document.fullscreenElement) { box.requestFullscreen(); btnFull.innerHTML = mkSvg(icons.exitFull); }
            else { document.exitFullscreen(); btnFull.innerHTML = mkSvg(icons.full); }
        };

        const subPanel = d.querySelector('#pk_sub_panel');
        if (subPanel) {
            btnFull.addEventListener('mouseenter', () => subPanel.style.setProperty('display', 'none', 'important'));
            btnFull.addEventListener('mouseleave', () => subPanel.style.removeProperty('display'));
        }

        const btnPip = d.querySelector('#pk_p_pip');
        if (btnPip) {
            if (!document.pictureInPictureEnabled || v.disablePictureInPicture) {
                btnPip.style.display = 'none';
            } else {
                btnPip.onclick = async (e) => {
                    e.stopPropagation();
                    try {
                        if (document.pictureInPictureElement) {
                            isPiPDesired = false;
                            await document.exitPictureInPicture();
                        } else {
                            isPiPDesired = true;
                            await v.requestPictureInPicture();
                            window.focus();
                        }
                    } catch (err) { console.error("PiP Error:", err); }
                };
            }
        }

        btnSearch.onclick = (e) => {
            e.stopPropagation();

            if (v) {
                v.pause();
                setTimeout(() => { if (v) v.pause(); }, 100);
            }

            const posterImg = d.querySelector('#pk_p_poster img');

            if (v.readyState >= 2 && v.videoWidth > 0) {
                startImageSearch(v, item.name, d, null);
            }
            else if (posterImg && posterImg.style.display !== 'none' && posterImg.src) {
                startImageSearch(posterImg, item.name, d, posterImg.src);
            }
            else {
                startImageSearch(v, item.name, d, null);
            }
        };

        const initPosterImg = d.querySelector('#pk_p_poster img');
        if (initPosterImg) {
            initPosterImg.addEventListener('dragstart', blockMediaDragWhenErr);
            if (initPosterImg.complete && initPosterImg.src && initPosterImg.src.startsWith('http')) {
                if (btnSearch) btnSearch.style.setProperty('display', 'flex', 'important');
            } else {
                initPosterImg.addEventListener('load', () => {
                    if (btnSearch) btnSearch.style.setProperty('display', 'flex', 'important');
                });
            }
        }

        const savedMute = gmGet('pk_vol_muted', false);
        const savedVol = parseFloat(gmGet('pk_vol_level', 1.0));

        v.muted = savedMute;
        v.volume = (Number.isFinite(savedVol) && savedVol >= 0 && savedVol <= 1) ? savedVol : 1.0;

        if (slideVol) slideVol.value = v.volume;

        updateVolUI();

        btnVol.onclick = () => {
            v.muted = !v.muted;
            updateVolUI();
            gmSet('pk_vol_muted', v.muted);
        };

        slideVol.oninput = (e) => {
            v.muted = false;
            const val = parseFloat(e.target.value);
            v.volume = val;
            updateVolUI();

            gmSet('pk_vol_muted', false);
            gmSet('pk_vol_level', val);
        };

        spdList.querySelectorAll('.pk-p-item').forEach(item => {
            item.onclick = () => {
                const s = parseFloat(item.dataset.spd);
                v.playbackRate = s;
                spdList.querySelector('.active').classList.remove('active');
                item.classList.add('active');
                d.querySelector('#pk_p_spd_txt').textContent = item.textContent;
            };
        });

        function bindResEvents() {
            resList.querySelectorAll('.pk-p-item').forEach(item => {
                item.onclick = () => {
                    const link = item.dataset.link;
                    if(link === currentLink) return;

                    const curT = v.currentTime;
                    const isPaused = v.paused;
                    const curRate = v.playbackRate;

                    box.classList.add('buffering');
                    if (loaderEl) loaderEl.style.display = 'block';

                    if (posterEl) {
                        if (v.readyState >= 2 && v.currentTime > 0) {
                            try {
                                const canvas = document.createElement('canvas');
                                canvas.width = v.videoWidth; canvas.height = v.videoHeight;
                                canvas.getContext('2d').drawImage(v, 0, 0);
                                const posterImg = posterEl.querySelector('img');
                                if (posterImg) {
                                    posterImg.src = canvas.toDataURL('image/jpeg', 0.7);
                                    posterImg.style.filter = 'brightness(0.8)';
                                }
                            } catch (e) { console.warn("[ClaritySwitch] Frame capture failed."); }
                        }
                        posterEl.style.transition = 'none';
                        posterEl.style.display = 'flex';
                        posterEl.style.opacity = '1';
                        posterEl.style.pointerEvents = 'auto';
                    }

                    currentLink = link;
                    currentResName = item.textContent.trim();

                    shutterTargetTime = curT > 0.1 ? curT : 0;

                    v.pause();
                    loadSource(link, curT);

                    v.playbackRate = curRate;
                    if(!isPaused) v.play().catch(()=>{});

                    resList.innerHTML = renderQualityMenu(qualityList, currentResName);
                    if(resTxt) resTxt.textContent = currentResName;
                    bindResEvents();
                };
            });
        }
        bindResEvents();

        const subState = {
            hasSub: false,
            size: 24,
            pos: 10,
            offset: 0,
            bgOpacity: 0.6,
            track: null,
            blobUrl: null
        };

        const processSubtitleFile = (file) => {
            if (!file) return;
            const ext = file.name.split('.').pop().toLowerCase();
            if (!['srt', 'vtt', 'ass', 'ssa'].includes(ext)) {
                showToast(L.err_sub_drop_type);
                return;
            }

            const reader = new FileReader();
            reader.onload = (evt) => {
                const buffer = evt.target.result;
                const encodings = ['utf-8', 'gbk', 'big5', 'utf-16le', 'shift_jis'];
                let text = "";
                for (let enc of encodings) {
                    try {
                        const decoder = new TextDecoder(enc, { fatal: true });
                        text = decoder.decode(buffer);
                        break;
                    } catch (e) { if(enc === 'shift_jis') text = new TextDecoder('utf-8').decode(buffer); }
                }

                if (subState.blobUrl) URL.revokeObjectURL(subState.blobUrl);

                let vttText = "";
                if (ext === 'ass' || ext === 'ssa') {
                    vttText = convertAssToVtt(text);
                } else {
                    vttText = convertSrtToVtt(text);
                }

                const blob = new Blob([vttText], { type: 'text/vtt' });
                subState.blobUrl = URL.createObjectURL(blob);

                let targetTrack = null;
                if (v.textTracks) {
                    for (let i = 0; i < v.textTracks.length; i++) {
                        const t = v.textTracks[i];
                        if (t.label === 'pk-subs') targetTrack = t;
                        t.mode = 'disabled';
                    }
                }
                if (!targetTrack) targetTrack = v.addTextTrack("subtitles", "pk-subs", "en");
                targetTrack.oncuechange = () => {
                    const cues = targetTrack.activeCues;
                    const txtEl = d.querySelector('#pk_sub_text');
                    if (cues && cues.length > 0 && txtEl) {
                        const text = cues[cues.length - 1].text;
                        txtEl.innerHTML = text.replace(/<[^>]+>/g, '').replace(/\n/g, '<br>');
                        txtEl.style.display = 'block';
                    } else if (txtEl) {
                        txtEl.style.display = 'none';
                    }
                };

                const vttLines = vttText.split('\n');
                let cueStart = null, cueEnd = null, cueText = [];
                const timeReg = /(\d{2}):(\d{2}):(\d{2})[.,](\d{3})\s*-->\s*(\d{2}):(\d{2}):(\d{2})[.,](\d{3})/;

                while (targetTrack.cues && targetTrack.cues.length > 0) targetTrack.removeCue(targetTrack.cues[0]);

                vttLines.forEach(line => {
                    const match = line.match(timeReg);
                    if (match) {
                        if (cueStart !== null && cueText.length > 0) {
                            try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                        }
                        cueStart = parseInt(match[1])*3600 + parseInt(match[2])*60 + parseInt(match[3]) + parseInt(match[4])/1000;
                        cueEnd = parseInt(match[5])*3600 + parseInt(match[6])*60 + parseInt(match[7]) + parseInt(match[8])/1000;
                        cueText = [];
                    } else if (line.trim() !== '' && !line.trim().startsWith('WEBVTT') && !/^\d+$/.test(line.trim())) {
                        if (cueStart !== null) cueText.push(line.trim());
                    }
                });
                if (cueStart !== null && cueText.length > 0) {
                    try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                }

                subState.hasSub = true;
                subState.track = targetTrack;
                subState.track.mode = 'hidden';
                updateSubStyle();
                d.querySelector('#pk_sub_toggle').checked = true;
                d.querySelector('#pk_sub_name').textContent = file.name;
                updateSubStyle();
                console.log(L.msg_sub_drop_load.replace('{n}', file.name));
            };
            reader.readAsArrayBuffer(file);
        };

        box.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.stopPropagation();
            box.style.boxShadow = "inset 0 0 50px var(--pk-pri)";
        });

        box.addEventListener('dragleave', (e) => {
            e.preventDefault();
            e.stopPropagation();
            box.style.boxShadow = "0 25px 50px rgba(0,0,0,0.5)";
        });

        box.addEventListener('drop', (e) => {
            e.preventDefault();
            e.stopPropagation();
            box.style.boxShadow = "0 25px 50px rgba(0,0,0,0.5)";
            const file = e.dataTransfer.files[0];
            processSubtitleFile(file);
        });

        const subStyleTag = document.createElement('style');
        document.head.appendChild(subStyleTag);

        const updateSubStyle = () => {
            const txt = d.querySelector('#pk_sub_text');
            const layer = d.querySelector('#pk_sub_render_layer');
            const toggle = d.querySelector('#pk_sub_toggle');

            if (txt && layer) {
                txt.style.fontSize = `${subState.size}px`;
                txt.style.backgroundColor = `rgba(0,0,0,${subState.bgOpacity})`;
                layer.style.paddingBottom = `${subState.pos / 2}%`;
                const isShow = toggle ? toggle.checked : true;
                layer.style.display = isShow ? 'flex' : 'none';
            }

            if (subState.track) {
                subState.track.mode = 'hidden';
            }
        };

        const fileInp = d.querySelector('#pk_sub_file');
        d.querySelector('#pk_sub_local_btn').onclick = (e) => { e.stopPropagation(); fileInp.click(); };

        fileInp.onchange = (e) => {
            processSubtitleFile(e.target.files[0]);
        };

        const cleanSubText = (text) => {
            return text.replace(/\{[^}]*?\}/g, '')
                       .replace(/<\/?i>/g, '')
                       .replace(/\\N/gi, '\n')
                       .replace(/\r\n/g, '\n')
                       .trim();
        };

        const convertAssToVtt = (assText) => {
            let vtt = "WEBVTT\n\n";
            const lines = assText.split('\n');
            let inEvents = false;
            let count = 1;

            const fmtTime = (t) => {
                if (!t) return "00:00:00.000";
                const parts = t.trim().split('.');
                const hms = parts[0].split(':');
                const ms = (parts[1] || '00').padEnd(3, '0');
                return `${hms[0].padStart(2,'0')}:${hms[1]}:${hms[2]}.${ms}`;
            };

            for (let line of lines) {
                line = line.trim();
                if (line.startsWith('[Events]')) { inEvents = true; continue; }
                if (!inEvents || !line.startsWith('Dialogue:')) continue;

                const parts = line.split(',');
                if (parts.length < 10) continue;

                const start = fmtTime(parts[1]);
                const end = fmtTime(parts[2]);
                const rawText = parts.slice(9).join(',');
                const text = cleanSubText(rawText);

                if (text) {
                    vtt += `${count++}\n${start} --> ${end}\n${text}\n\n`;
                }
            }
            return vtt;
        };

        const convertSrtToVtt = (srtText) => {
            if (/^WEBVTT/i.test(srtText)) return srtText;

            let vtt = "WEBVTT\n\n";
            const text = srtText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
            const regex = /(\d{2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}[,.]\d{3})/g;
            let match;
            const cues = [];
            while ((match = regex.exec(text)) !== null) {
                cues.push({
                    start: match[1],
                    end: match[2],
                    index: match.index,
                    endOfLine: regex.lastIndex
                });
            }

            let count = 1;
            for (let i = 0; i < cues.length; i++) {
                const cue = cues[i];
                const nextCue = cues[i + 1];
                const contentStart = cue.endOfLine;
                const contentEnd = nextCue ? nextCue.index : text.length;
                let rawContent = text.substring(contentStart, contentEnd);
                rawContent = rawContent.replace(/\n+\s*\d+\s*$/, '');
                const cleanContent = cleanSubText(rawContent);
                const vttStart = cue.start.replace(/,/g, '.');
                const vttEnd = cue.end.replace(/,/g, '.');

                if (cleanContent) {
                    vtt += `${count++}\n${vttStart} --> ${vttEnd}\n${cleanContent}\n\n`;
                }
            }

            return vtt;
        };

        const autoMatchSubtitle = async (videoItem) => {
            const parentId = videoItem.parent_id;
            if (!parentId) return;

            const videoNameBase = videoItem.name.substring(0, videoItem.name.lastIndexOf('.'));

            let files = [];
            if (typeof globalCache !== 'undefined' && globalCache.has(parentId)) {
                files = globalCache.get(parentId);
            } else {
                try {
                    files = await apiList(parentId, 100);
                } catch(e) { return; }
            }
            if (!files || files.length === 0) return;

            const subFiles = files.filter(f =>
                !f.trashed && f.kind !== 'drive#folder' &&
                /\.(srt|vtt|ass|ssa)$/i.test(f.name)
            );
            if (subFiles.length === 0) return;

            let targetSub = subFiles.find(f => {
                const subBase = f.name.substring(0, f.name.lastIndexOf('.'));
                return subBase === videoNameBase;
            });

            if (!targetSub) {
                targetSub = subFiles.find(f => f.name.includes(videoNameBase));
            }

            if (targetSub) {
                console.log(`[AutoSub] Matched: ${targetSub.name}`);
                const box = d.querySelector('#pk_p_box');
                const toast = document.createElement('div');
                toast.style.cssText = "position:absolute;top:80px;right:20px;background:rgba(0,0,0,0.6);color:#fff;padding:6px 12px;border-radius:4px;font-size:12px;pointer-events:none;animation:pkFadeIn 0.5s;z-index:90;";
                toast.textContent = L.msg_auto_sub_load.replace('{n}', targetSub.name);
                box.appendChild(toast);
                setTimeout(()=>toast.remove(), 4000);

                try {
                    let link = targetSub.web_content_link;
                    if (!link) {
                        const detail = await apiGet(targetSub.id);
                        link = detail.web_content_link;
                    }

                    const res = await fetch(link);
                    const buffer = await res.arrayBuffer();

                    const encodings = ['utf-8', 'gbk', 'big5', 'utf-16le', 'shift_jis'];
                    let text = "";
                    for (let enc of encodings) {
                        try {
                            const decoder = new TextDecoder(enc, { fatal: true });
                            text = decoder.decode(buffer);
                            break;
                        } catch (e) { if(enc === 'shift_jis') text = new TextDecoder('utf-8').decode(buffer); }
                    }

                    let vttText = "";
                    const subExt = targetSub.name.split('.').pop().toLowerCase();
                    if (subExt === 'ass' || subExt === 'ssa') {
                        vttText = convertAssToVtt(text);
                    } else {
                        vttText = convertSrtToVtt(text);
                    }

                    const blob = new Blob([vttText], { type: 'text/vtt' });
                    if (subState.blobUrl) URL.revokeObjectURL(subState.blobUrl);
                    subState.blobUrl = URL.createObjectURL(blob);

                    let targetTrack = null;
                    if (v.textTracks) {
                        for (let i = 0; i < v.textTracks.length; i++) {
                            const t = v.textTracks[i];
                            if (t.label === 'pk-subs') targetTrack = t;
                            t.mode = 'disabled';
                        }
                    }
                    if (!targetTrack) targetTrack = v.addTextTrack("subtitles", "pk-subs", "en");

                    targetTrack.oncuechange = () => {
                        const cues = targetTrack.activeCues;
                        const txtEl = d.querySelector('#pk_sub_text');
                        if (cues && cues.length > 0 && txtEl) {
                            const text = cues[cues.length - 1].text;
                            txtEl.innerHTML = text.replace(/<[^>]+>/g, '').replace(/\n/g, '<br>');
                            txtEl.style.display = 'block';
                        } else if (txtEl) {
                            txtEl.style.display = 'none';
                        }
                    };

                    const vttLines = vttText.split('\n');
                    let cueStart = null, cueEnd = null, cueText = [];
                    const timeReg = /(\d{2}):(\d{2}):(\d{2})[.,](\d{3})\s*-->\s*(\d{2}):(\d{2}):(\d{2})[.,](\d{3})/;

                    while (targetTrack.cues && targetTrack.cues.length > 0) targetTrack.removeCue(targetTrack.cues[0]);

                    vttLines.forEach(line => {
                        const match = line.match(timeReg);
                        if (match) {
                            if (cueStart !== null && cueText.length > 0) {
                                try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                            }
                            cueStart = parseInt(match[1])*3600 + parseInt(match[2])*60 + parseInt(match[3]) + parseInt(match[4])/1000;
                            cueEnd = parseInt(match[5])*3600 + parseInt(match[6])*60 + parseInt(match[7]) + parseInt(match[8])/1000;
                            cueText = [];
                        } else if (line.trim() !== '' && !line.trim().startsWith('WEBVTT') && !/^\d+$/.test(line.trim())) {
                            if (cueStart !== null) cueText.push(line.trim());
                        }
                    });
                    if (cueStart !== null && cueText.length > 0) {
                        try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                    }

                    subState.hasSub = true;
                    subState.track = targetTrack;
                    subState.track.mode = 'hidden';
                    updateSubStyle();

                    const toggle = d.querySelector('#pk_sub_toggle');
                    if(toggle) toggle.checked = true;
                    const nameLabel = d.querySelector('#pk_sub_name');
                    if(nameLabel) nameLabel.textContent = targetSub.name;

                    updateSubStyle();

                } catch (e) {
                    console.warn("[AutoSub] Load failed", e);
                }
            }
        };

        const btnCloudSub = d.querySelector('#pk_sub_cloud_btn');
        btnCloudSub.onclick = async (e) => {
            e.stopPropagation();

            const subPanel = d.querySelector('#pk_sub_panel');
            const subTrigger = d.querySelector('#pk_sub_trigger');
            if (subPanel && subTrigger) {
                subPanel.style.setProperty('display', 'none', 'important');
                const clearDisplay = () => {
                    subPanel.style.removeProperty('display');
                    subTrigger.removeEventListener('mouseleave', clearDisplay);
                };
                subTrigger.addEventListener('mouseleave', clearDisplay);
            }

            if (v && !v.paused) v.pause();

            const originalText = btnCloudSub.textContent;
            btnCloudSub.textContent = L.loading;
            btnCloudSub.style.opacity = "0.6";
            btnCloudSub.style.pointerEvents = "none";

            let startPath = [{ id: '', name: L.btn_nav_home }];
            let targetFolderId = '';

            try {
                let targetItem = item;

                if (S.offlineMode || S.uploadMode || S.recentMode || item.kind === 'drive#task') {
                    const realFileId = (item.kind === 'drive#task' || S.offlineMode || S.uploadMode)
                        ? (item.file_id || (item.params && item.params.file_id))
                        : item.id;

                    if (realFileId) {
                        try {
                            targetItem = await apiGet(realFileId);
                        } catch(err) {
                            console.warn("[CloudSub] Failed to resolve task file:", err);
                        }
                    }
                }

                if (targetItem.parent_id && targetItem.parent_id !== 'root') {
                    targetFolderId = targetItem.parent_id;
                }

                if (targetItem._lineage && Array.isArray(targetItem._lineage) && targetItem._lineage.length > 0) {
                     startPath = targetItem._lineage.map(x => ({ id: x.id || '', name: x.name }));
                     if (startPath.length > 0 && startPath[0].id !== '' && startPath[0].id !== 'root') {
                        startPath.unshift({ id: '', name: L.btn_nav_home });
                    }
                }
                else if (targetFolderId) {
                    const trace = [];
                    let curr = targetFolderId;
                    let safety = 6;

                    while (curr && curr !== 'root' && safety > 0) {
                        try {
                            const f = await apiGet(curr);
                            trace.unshift({ id: f.id, name: f.name });
                            curr = f.parent_id;
                        } catch(e) {
                            break;
                        }
                        safety--;
                    }

                    if (trace.length > 0) {
                        startPath = [{ id: '', name: L.btn_nav_home }, ...trace];
                    }
                }
                else if (S.path && S.path.length > 0) {
                    const cleanPath = S.path.filter(p => !p.id.startsWith('virtual_') && !p.id.includes('_root') && p.id !== 'analyze_root');
                    if (cleanPath.length > 0) {
                        startPath = cleanPath;
                        if (startPath[0].id !== '' && startPath[0].id !== 'root') {
                            startPath.unshift({ id: '', name: L.btn_nav_home });
                        }
                    }
                }

            } catch (error) {
                console.error("[CloudSub] Path resolve error:", error);
                targetFolderId = '';
                startPath = [{ id: '', name: L.btn_nav_home }];
            } finally {
                btnCloudSub.textContent = originalText;
                btnCloudSub.style.opacity = "1";
                btnCloudSub.style.pointerEvents = "auto";
            }

            showFolderSelector(
                targetFolderId,
                async (id, name, subItem) => {
                    const box = d.querySelector('#pk_p_box');
                    const toast = document.createElement('div');
                    toast.style.cssText = "position:absolute;top:80px;right:20px;background:rgba(0,0,0,0.8);color:#fff;padding:8px 16px;border-radius:4px;font-size:13px;pointer-events:none;z-index:90;display:flex;align-items:center;gap:8px;";
                    toast.innerHTML = `<div class="pk-spin-lg" style="width:14px;height:14px;border-width:2px;"></div> ${L.msg_dl_sub}`;
                    box.appendChild(toast);

                    try {
                        let link = subItem.web_content_link;
                        if (!link) {
                            const detail = await apiGet(subItem.id);
                            link = detail.web_content_link;
                        }

                        const res = await fetch(link);
                        const buffer = await res.arrayBuffer();

                        const encodings = ['utf-8', 'gbk', 'big5', 'utf-16le', 'shift_jis', 'windows-1252'];
                        let text = "";
                        for (let enc of encodings) {
                            try {
                                const decoder = new TextDecoder(enc, { fatal: true });
                                text = decoder.decode(buffer);
                                break;
                            } catch (e) { if(enc === 'windows-1252') text = new TextDecoder('utf-8').decode(buffer); }
                        }

                        let vttText = "";
                        const subExt = subItem.name.split('.').pop().toLowerCase();
                        if (subExt === 'ass' || subExt === 'ssa') {
                            vttText = convertAssToVtt(text);
                        } else {
                            vttText = convertSrtToVtt(text);
                        }

                        const blob = new Blob([vttText], { type: 'text/vtt' });
                        if (subState.blobUrl) URL.revokeObjectURL(subState.blobUrl);
                        subState.blobUrl = URL.createObjectURL(blob);

                        let targetTrack = null;
                        if (v.textTracks) {
                            for (let i = 0; i < v.textTracks.length; i++) {
                                const t = v.textTracks[i];
                                if (t.label === 'pk-subs') targetTrack = t;
                                t.mode = 'disabled';
                            }
                        }
                        if (!targetTrack) targetTrack = v.addTextTrack("subtitles", "pk-subs", "en");

                        targetTrack.oncuechange = () => {
                            const cues = targetTrack.activeCues;
                            const txtEl = d.querySelector('#pk_sub_text');
                            if (cues && cues.length > 0 && txtEl) {
                                const text = cues[cues.length - 1].text;
                                txtEl.innerHTML = text.replace(/<[^>]+>/g, '').replace(/\n/g, '<br>');
                                txtEl.style.display = 'block';
                            } else if (txtEl) {
                                txtEl.style.display = 'none';
                            }
                        };

                        const vttLines = vttText.split('\n');
                        let cueStart = null, cueEnd = null, cueText = [];
                        const timeReg = /(\d{2}):(\d{2}):(\d{2})[.,](\d{3})\s*-->\s*(\d{2}):(\d{2}):(\d{2})[.,](\d{3})/;

                        while (targetTrack.cues && targetTrack.cues.length > 0) targetTrack.removeCue(targetTrack.cues[0]);

                        vttLines.forEach(line => {
                            const match = line.match(timeReg);
                            if (match) {
                                if (cueStart !== null && cueText.length > 0) {
                                    try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                                }
                                cueStart = parseInt(match[1])*3600 + parseInt(match[2])*60 + parseInt(match[3]) + parseInt(match[4])/1000;
                                cueEnd = parseInt(match[5])*3600 + parseInt(match[6])*60 + parseInt(match[7]) + parseInt(match[8])/1000;
                                cueText = [];
                            } else if (line.trim() !== '' && !line.trim().startsWith('WEBVTT') && !/^\d+$/.test(line.trim())) {
                                if (cueStart !== null) cueText.push(line.trim());
                            }
                        });
                        if (cueStart !== null && cueText.length > 0) {
                            try { targetTrack.addCue(new VTTCue(cueStart, cueEnd, cueText.join('\n'))); } catch(e){}
                        }

                        subState.hasSub = true;
                        subState.track = targetTrack;
                        subState.track.mode = 'hidden';
                        updateSubStyle();

                        const toggle = d.querySelector('#pk_sub_toggle');
                        if(toggle) toggle.checked = true;
                        const nameLabel = d.querySelector('#pk_sub_name');
                        if(nameLabel) nameLabel.textContent = subItem.name;

                        updateSubStyle();
                        toast.innerHTML = `✅ ${L.msg_auto_sub_load.replace('{n}', subItem.name)}`;
                        setTimeout(() => toast.remove(), 3000);

                    } catch (err) {
                        console.error(err);
                        toast.innerHTML = `❌ ${L.err_sub_dl_fail}`;
                        setTimeout(() => toast.remove(), 4000);
                    }
                },
                startPath,
                (f) => /\.(srt|vtt|ass|ssa)$/i.test(f.name),
                L.title_sel_sub
            );
        };

        const loadJSZip = () => {
            if (window.JSZip) return Promise.resolve(window.JSZip);
            return new Promise((resolve, reject) => {
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js';
                script.onload = () => resolve(window.JSZip);
                script.onerror = () => reject(new Error(L.msg_jszip_fail));

                document.head.appendChild(script);
            });
        };

        const cleanFilename = (name) => {
            let n = name.toLowerCase();
            n = n.replace(/\.[^/.]+$/, "");
            n = n.replace(/^(\d{2,4}|[a-z]\d{2,4})[\.\s\-\_]+/, "");

            const garbage = [
                /\b(1080p|720p|2160p|4k|uhd|hd)\b.*/,
                /\b(bluray|web-dl|webrip|remux|hdtv)\b.*/,
                /\b(x264|x265|hevc|h264|aac|dts|ac3)\b.*/,
                /\[.*?\]/g,
                /\(.*?\)/g,
                /\{.*?\}/g
            ];
            garbage.forEach(g => n = n.replace(g, ''));

            n = n.replace(/[\._\+]/g, ' ').trim();

            const episodeMatch = n.match(/(.*?)s\d+e\d+/);
            if (episodeMatch) return episodeMatch[0];
            return n;
        };

        const gmxRequest = (url, type='text') => {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    responseType: type,
                    anonymous: false,
                    headers: {
                        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
                        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
                        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
                        "Cache-Control": "no-cache",
                        "Pragma": "no-cache"
                    },
                    onload: (res) => {
                        if (res.status === 200) {
                            resolve(res.response);
                        } else if (res.status === 404) {
                            resolve(null);
                        } else {
                            console.warn(`[Subtitle] HTTP ${res.status} from ${url}`);
                            resolve(null);
                        }
                    },
                    onerror: (e) => reject(new Error(L.err_req_blocked)),
                    ontimeout: () => reject(new Error(L.err_req_timeout))
                });
            });
        };

        const btnSearchSub = d.querySelector('#pk_sub_search_btn');

        btnSearchSub.onclick = async (e) => {
            e.stopPropagation();

            const subPanel = d.querySelector('#pk_sub_panel');
            const subTrigger = d.querySelector('#pk_sub_trigger');
            if (subPanel && subTrigger) {
                subPanel.style.setProperty('display', 'none', 'important');
                const clearDisplay = () => {
                    subPanel.style.removeProperty('display');
                    subTrigger.removeEventListener('mouseleave', clearDisplay);
                };
                subTrigger.addEventListener('mouseleave', clearDisplay);
            }

            const searchOv = document.createElement('div');
            searchOv.className = 'pk-sub-search-modal';
            searchOv.style.cssText = `
                position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
                background: rgba(20,20,20,0.95); border: 1px solid #444; border-radius: 8px;
                width: 380px; height: 450px; display: flex; flex-direction: column;
                box-shadow: 0 10px 40px rgba(0,0,0,0.8); z-index: 60; padding: 15px; backdrop-filter: blur(10px);
            `;
            box.appendChild(searchOv);

            searchOv.onclick = (evt) => evt.stopPropagation();

            const keyword = cleanFilename(item.name);

            searchOv.innerHTML = `
                <div style="margin-bottom:10px; padding-bottom:10px; border-bottom:1px solid #333;">
                    <input id="pk_sub_search_input" value="${esc(keyword)}" placeholder="${L.ph_sub_search}" style="width:100%; box-sizing:border-box; background:#181818; border:1px solid #444; color:#fff; padding:8px 12px; border-radius:4px; outline:none; font-size:13px;">
                </div>
                <div id="pk_sub_search_list" class="pk-scroll" style="flex:1; overflow-y:auto;">
                </div>
                <button id="pk_sub_search_close" style="margin-top:10px; background:#333; color:#fff; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:13px;">${L.btn_close}</button>
            `;

            const resultList = searchOv.querySelector('#pk_sub_search_list');
            const input = searchOv.querySelector('#pk_sub_search_input');


            const doSearch = (query) => {
                const cleanQ = query.trim();
                if (!cleanQ) return;

                const plusQ = encodeURIComponent(cleanQ).replace(/%20/g, '+');
                const spaceQ = encodeURIComponent(cleanQ);

                const curLang = L.lang_code;

                let engines = [];

                if (curLang === 'zh') {
                    engines = [
                        { name: 'Assrt', url: `https://assrt.net/sub/?searchword=${plusQ}` },
                        { name: 'SubHD', url: `https://subhd.tv/search/${spaceQ}` },
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/zh/search2/sublanguageid-kor/moviename-${plusQ}` },
                    ];
                } else if (curLang === 'tc') {
                    engines = [
                        { name: 'R3Sub', url: `https://r3sub.com/search.php?s=${plusQ}` },
                        { name: 'SubHD', url: `https://subhd.tv/search/${spaceQ}` },
                        { name: 'Assrt', url: `https://assrt.net/sub/?searchword=${plusQ}` }
                    ];
                } else if (curLang === 'ko') {
                    engines = [
                        { name: 'iSubtitles', url: `https://isubtitles.org/search?q=${plusQ}` },
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/ko/search2/sublanguageid-kor/moviename-${plusQ}` },
                        { name: 'SubtitleCat', url: `https://www.subtitlecat.com/index.php?search=${plusQ}` }
                    ];
                } else if (curLang === 'ja') {
                    engines = [
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/ja/search2/sublanguageid-jpn/moviename-${plusQ}` },
                        { name: 'MovieSubtitles', url: `https://www.moviesubtitles.org/search.php?q=${plusQ}` },
                        { name: 'Anime Tosho', url: `https://animetosho.org/search?q=${plusQ}` }
                    ];
                } else if (curLang === 'id') {
                    engines = [
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/id/search2/sublanguageid-ind/moviename-${plusQ}` },
                        { name: 'Samehadaku', url: `https://samehadaku.li/?s=${plusQ}` },
                        { name: 'Anoboy', url: `https://anoboy.be/?s=${plusQ}` }
                    ];
                } else if (curLang === 'ms') {
                    engines = [
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/ms/search2/sublanguageid-may/moviename-${plusQ}` },
                        { name: 'PencuriMovie', url: `https://pencurimovie.my/?s=${plusQ}` },
                        { name: 'Oh Flix', url: `https://vvww.ohflix.my.id/?s=${plusQ}` }
                    ];
                } else {
                    engines = [
                        { name: 'OpenSubtitles', url: `https://www.opensubtitles.org/en/search2/sublanguageid-eng/moviename-${plusQ}` },
                        { name: 'MovieSubtitles', url: `https://www.moviesubtitles.org/search.php?q=${plusQ}` },
                        { name: 'iSubtitles', url: `https://isubtitles.org/search?q=${plusQ}` }
                    ];
                }

                let html = `<div style="padding: 10px 15px; display: flex; flex-direction: column; gap: 8px;">`;

                engines.forEach(e => {
                    html += `
                        <a href="${e.url}" target="_blank" style="
                            display: block;
                            padding: 10px 16px;
                            background: #2a2a2a;
                            color: #eee;
                            text-decoration: none;
                            border-radius: 6px;
                            text-align: left;
                            font-size: 13px;
                            font-weight: 500;
                            border: 1px solid #3d3d3d;
                            transition: all 0.2s;
                        " onmouseover="this.style.background='#3d3d3d';this.style.borderColor='var(--pk-pri)'"
                           onmouseout="this.style.background='#2a2a2a';this.style.borderColor='#3d3d3d'">
                            ${L.btn_go_search.replace('{n}', e.name)}
                        </a>`;
                });

                html += `
                    <div style="font-size: 11px; color: #777; margin-top: 10px; line-height: 1.6; text-align: left;">
                        ${L.tip_manual_sub}
                    </div>
                </div>`;

                resultList.innerHTML = html;
            };

            try {
                await loadJSZip();
                doSearch(input.value);
            } catch (err) {
                resultList.innerHTML = `<div style="color:#d93025;">${L.msg_jszip_fail}</div>`;
            }

            input.oninput = () => doSearch(input.value);

            input.onkeydown = (ev) => {
                ev.stopPropagation();
            };

            searchOv.querySelector('#pk_sub_search_close').onclick = () => searchOv.remove();
        };

        d.querySelector('#pk_sub_toggle').onchange = (e) => {
            updateSubStyle();
            if (v.textTracks) {
                 Array.from(v.textTracks).forEach(t => {
                     if (t !== subState.track) t.mode = 'disabled';
                 });
            }
        };

        if (v.textTracks) {
            v.textTracks.addEventListener('addtrack', (e) => {
                if (subState.hasSub && e.track !== subState.track) {
                    e.track.mode = 'disabled';
                }
            });
        }

        const sizeVal = d.querySelector('#pk_sub_size_val');
        d.querySelector('#pk_sub_size_dec').onclick = (e) => {
            e.stopPropagation();
            subState.size = Math.max(12, subState.size - 2);
            sizeVal.textContent = subState.size;
            updateSubStyle();
        };
        d.querySelector('#pk_sub_size_inc').onclick = (e) => {
            e.stopPropagation();
            subState.size = Math.min(80, subState.size + 2);
            sizeVal.textContent = subState.size;
            updateSubStyle();
        };

        d.querySelector('#pk_sub_pos').oninput = (e) => {
            e.stopPropagation();
            subState.pos = parseInt(e.target.value);
            updateSubStyle();
        };

        d.querySelector('#pk_sub_bg_opacity').oninput = (e) => {
            e.stopPropagation();
            subState.bgOpacity = parseInt(e.target.value) / 100;
            updateSubStyle();
        };

        const timeVal = d.querySelector('#pk_sub_time_val');
        const adjustOffset = (delta) => {
            subState.offset += delta;
            timeVal.textContent = subState.offset.toFixed(1) + " " + L.unit_sec;

            if (subState.track && subState.track.cues) {
                const cues = Array.from(subState.track.cues);
                cues.forEach(cue => {
                    cue.startTime += delta;
                    cue.endTime += delta;
                });
            }
        };
        d.querySelector('#pk_sub_time_dec').onclick = (e) => { e.stopPropagation(); adjustOffset(-0.5); };
        d.querySelector('#pk_sub_time_inc').onclick = (e) => { e.stopPropagation(); adjustOffset(0.5); };

        d.querySelector('#pk_sub_panel').onclick = (e) => e.stopPropagation();

        d.querySelectorAll('.pk-sub-tab').forEach(t => {
            t.onclick = () => {
                d.querySelectorAll('.pk-sub-tab, .pk-sub-pane').forEach(el => el.classList.remove('active'));
                t.classList.add('active');
                d.querySelector('#' + t.dataset.target).classList.add('active');
            };
        });

        const curPMode = gmGet('pk_play_mode', 'stop');
        const pRadios = d.querySelectorAll('input[name="pk_pmode"]');
        pRadios.forEach(r => {
            if (r.value === curPMode) r.checked = true;
            r.onchange = () => gmSet('pk_play_mode', r.value);
        });

        let transformState = { rotate: 0, flipH: 1, flipV: 1, ratio: 'default' };

        const applyTransform = () => {
            if (document.pictureInPictureElement === v) {
                v.style.transform = 'none';
                return;
            }

            if (transformState.ratio === 'default') {
                v.style.objectFit = 'contain';
                v.style.width = '100%';
                v.style.height = box.classList.contains('plist-active') ? (document.fullscreenElement ? 'calc(100% - 84px)' : '100%') : '100%';
                v.style.aspectRatio = 'auto';
                v.style.margin = '0';
                v.style.inset = 'auto';
            } else {
                v.style.objectFit = 'fill';
                v.style.aspectRatio = transformState.ratio;
                v.style.width = 'auto';
                v.style.height = 'auto';
                v.style.maxWidth = '100%';
                v.style.maxHeight = '100%';
                v.style.margin = 'auto';
                v.style.inset = '0';
            }

            let autoScale = 1;
            if (Math.abs(transformState.rotate) % 180 !== 0) {
                const boxW = box.clientWidth;
                const boxH = box.clientHeight;
                const vW = v.offsetWidth || boxW;
                const vH = v.offsetHeight || boxH;

                if (vW > 0 && vH > 0) {
                    const scaleW = boxW / vH;
                    const scaleH = boxH / vW;
                    autoScale = Math.min(scaleW, scaleH);
                }
            }

            v.style.transform = `translateZ(0) rotate(${transformState.rotate}deg) scale(${autoScale}) scale(${transformState.flipH}, ${transformState.flipV})`;
        };

        const onResizeTransform = () => requestAnimationFrame(applyTransform);
        window.addEventListener('resize', onResizeTransform);


        d.querySelectorAll('#pk_ratio_opts .pk-size-btn').forEach(btn => {
            btn.onclick = (e) => {
                d.querySelectorAll('#pk_ratio_opts .pk-size-btn').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
                transformState.ratio = btn.dataset.ratio;
                applyTransform();
            };
        });

        d.querySelector('#pk_btn_rot_l').onclick = () => { transformState.rotate -= 90; applyTransform(); };
        d.querySelector('#pk_btn_rot_r').onclick = () => { transformState.rotate += 90; applyTransform(); };
        d.querySelector('#pk_btn_flip_h').onclick = () => { transformState.flipH *= -1; applyTransform(); };
        d.querySelector('#pk_btn_flip_v').onclick = () => { transformState.flipV *= -1; applyTransform(); };

        const opVal = d.querySelector('#pk_op_val');
        const edVal = d.querySelector('#pk_ed_val');
        let valOp = parseInt(gmGet('pk_skip_intro', 0)) || 0;
        let valEd = parseInt(gmGet('pk_skip_outro', 0)) || 0;
        opVal.textContent = valOp + " " + L.unit_sec;
        edVal.textContent = valEd + " " + L.unit_sec;

        const updateSkip = (type, delta) => {
            if (type === 'op') {
                valOp = Math.max(0, Math.min(3600, valOp + delta));
                opVal.textContent = valOp + " " + L.unit_sec;
                gmSet('pk_skip_intro', valOp);
            } else {
                valEd = Math.max(0, Math.min(3600, valEd + delta));
                edVal.textContent = valEd + " " + L.unit_sec;
                gmSet('pk_skip_outro', valEd);
            }
        };

        d.querySelector('#pk_op_dec').onclick = () => updateSkip('op', -5);
        d.querySelector('#pk_op_inc').onclick = () => updateSkip('op', 5);
        d.querySelector('#pk_ed_dec').onclick = () => updateSkip('ed', -5);
        d.querySelector('#pk_ed_inc').onclick = () => updateSkip('ed', 5);
        d.querySelector('#pk_op_mark').onclick = (e) => {
            e.stopPropagation();
            const markTime = Math.max(0, Math.floor(v.currentTime));
            valOp = markTime;
            opVal.textContent = valOp + " " + L.unit_sec;
            gmSet('pk_skip_intro', valOp);
        };

        d.querySelector('#pk_ed_mark').onclick = (e) => {
            e.stopPropagation();
            if (!v.duration) return;
            const markTime = Math.max(0, Math.floor(v.duration - v.currentTime));
            valEd = markTime;
            edVal.textContent = valEd + " " + L.unit_sec;
            gmSet('pk_skip_outro', valEd);
        };

        let hasTriggeredEnd = false;
        v.addEventListener('timeupdate', () => {
            const skipEd = parseInt(gmGet('pk_skip_outro', 0)) || 0;
            const skipOp = parseInt(gmGet('pk_skip_intro', 0)) || 0;
            if (skipEd > 0 && v.duration > 0 && !hasTriggeredEnd) {
                if (skipEd >= v.duration || (skipOp + skipEd) >= v.duration || v.currentTime < (v.duration / 2)) return;

                if (v.duration - v.currentTime <= skipEd) {
                    hasTriggeredEnd = true;
                    console.log(`[AutoSkip] Outro skipped at ${v.currentTime}`);
                    v.onended();
                }
            }
        });
        v.addEventListener('play', () => hasTriggeredEnd = false);
        v.addEventListener('seeking', () => hasTriggeredEnd = false);

        v.addEventListener('enterpictureinpicture', applyTransform);
        v.addEventListener('leavepictureinpicture', () => {
            if (typeof isSwitching !== 'undefined' && !isSwitching) {
                isPiPDesired = false;
            }
            applyTransform();
        });

        v.onended = () => {
            const mode = gmGet('pk_play_mode', 'stop');
            if (mode === 'single_loop') {
                v.currentTime = 0; v.play().catch(()=>{});
            } else if (mode === 'list_loop') {
                const nextIdx = (curListIdx + 1) % totalInList;
                if (totalInList > 1) softSwitch(nextIdx);
                else { v.currentTime = 0; v.play().catch(()=>{}); }
            }
        };

        const makeEditable = (el, type, callback) => {
            el.ondblclick = (e) => {
                e.stopPropagation();
                const oldText = el.textContent;
                const oldVal = type === 'offset' ? parseFloat(oldText) : parseInt(oldText);

                el.innerHTML = `<input type="text" value="${oldVal}" style="width:100%;height:100%;border:none;background:transparent;color:#fff;text-align:center;font-size:12px;outline:none;padding:0;margin:0;">`;
                const input = el.querySelector('input');
                input.focus();
                input.select();

                const finish = () => {
                    const val = parseFloat(input.value);
                    if (isNaN(val)) {
                        el.textContent = oldText;
                    } else {
                        let finalVal = type === 'offset' ? val : Math.round(val);
                        if (type !== 'offset') finalVal = Math.max(0, finalVal);

                        if (type === 'offset') el.textContent = finalVal.toFixed(1) + " " + L.unit_sec;
                        else el.textContent = finalVal;

                        callback(finalVal);
                    }
                    el.ondblclick = (evt) => { evt.stopPropagation(); makeEditable(el, type, callback).ondblclick(evt); };
                };

                input.onblur = finish;
                input.onkeydown = (ev) => {
                    ev.stopPropagation();
                    if (ev.key === 'Enter') {
                        input.blur();
                    }
                };
                input.onclick = (ev) => ev.stopPropagation();
            };
        };

        makeEditable(d.querySelector('#pk_sub_size_val'), 'int', (val) => {
            subState.size = val; updateSubStyle();
        });
        makeEditable(d.querySelector('#pk_sub_time_val'), 'offset', (val) => {
            const delta = val - subState.offset;
            adjustOffset(delta);
        });
        makeEditable(d.querySelector('#pk_op_val'), 'int', (val) => {
            valOp = Math.min(3600, val); gmSet('pk_skip_intro', valOp);
            d.querySelector('#pk_op_val').textContent = valOp + " " + L.unit_sec;
        });
        makeEditable(d.querySelector('#pk_ed_val'), 'int', (val) => {
            valEd = Math.min(3600, val); gmSet('pk_skip_outro', valEd);
            d.querySelector('#pk_ed_val').textContent = valEd + " " + L.unit_sec;
        });

        updateSubStyle();

        const playerKeyHandler = (e) => {
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
            if (!document.getElementById('pk-player-ov')) return;

            e.stopPropagation();
            e.preventDefault();
            resetHideTimer();

            switch(e.key) {
                case ' ':
                case 'k':
                    togglePlay();
                    break;
                case 'ArrowRight':
                    if (e.ctrlKey || e.metaKey) {
                        const nextIdx = (curListIdx + 1) % totalInList;
                        softSwitch(nextIdx);
                    } else {
                        const targetTime = Math.min(v.duration || 0, v.currentTime + 10);
                        v.currentTime = targetTime;
                        if (tCur) tCur.textContent = fmtT(targetTime);
                        if (progFilled && v.duration) progFilled.style.setProperty('width', `${(targetTime / v.duration) * 100}%`, 'important');
                        showSeekIndicatorAt(targetTime);
                    }
                    break;
                case 'ArrowLeft':
                    if (e.ctrlKey || e.metaKey) {
                        const prevIdx = (curListIdx - 1 + totalInList) % totalInList;
                        softSwitch(prevIdx);
                    } else {
                        const targetTime = Math.max(0, v.currentTime - 10);
                        v.currentTime = targetTime;
                        if (tCur) tCur.textContent = fmtT(targetTime);
                        if (progFilled && v.duration) progFilled.style.setProperty('width', `${(targetTime / v.duration) * 100}%`, 'important');
                        showSeekIndicatorAt(targetTime);
                    }
                    break;
                case 'p':
                case 'P':
                    const btnPip = d.querySelector('#pk_p_pip');
                    if (btnPip && btnPip.style.display !== 'none') btnPip.click();
                    break;
                case 'e':
                case 'E':
                    if (pTab) pTab.click();
                    break;
                case 'ArrowUp':
                case 'ArrowDown':
                    v.muted = false;
                    const delta = (e.key === 'ArrowUp' ? 0.05 : -0.05);
                    v.volume = Math.max(0, Math.min(1, v.volume + delta));
                    updateVolUI();

                    const volInd = d.querySelector('#pk_p_vol_indicator');
                    const volVal = d.querySelector('#pk_p_vol_val');
                    if (volInd && volVal) {
                        volVal.textContent = Math.round(v.volume * 100) + '%';
                        volInd.style.display = 'flex';
                        box.classList.add('pk-is-vol-active');
                        clearTimeout(v._volTimer);
                        v._volTimer = setTimeout(() => {
                            volInd.style.display = 'none';
                            box.classList.remove('pk-is-vol-active');
                        }, 1000);
                    }
                    break;
                case 'f':
                case 'F':
                    if (btnSearch && btnSearch.style.display !== 'none') btnSearch.click();
                    break;
                case 'Enter':
                    btnFull.click();
                    break;
                case 'Escape':
                    if (document.fullscreenElement) document.exitFullscreen();
                    else destroyPlayer();
                    break;
            }
        };
        document.addEventListener('keydown', playerKeyHandler);

        const initDur = (item.params && item.params.duration) ||
                        S.durationMap.get(item.id) ||
                        gmGet('pk_duration_' + item.id, 0);

        if (tDur && initDur > 0) tDur.textContent = fmtT(initDur);

        loadSource(currentLink, null);
        setTimeout(() => {
            if (isPlayerDestroyed) return;

            const p = v.play();
            if (p !== undefined) { p.catch(() => updateState()); }

            if (startFullscreen) {
                const box = d.querySelector('#pk_p_box');
                const btnFull = d.querySelector('#pk_p_full');
                if (box && box.requestFullscreen) {
                    box.requestFullscreen().then(() => {
                        if(btnFull) btnFull.innerHTML = mkSvg(icons.exitFull);
                    }).catch(err => console.warn("Fullscreen auto-resume failed", err));
                }
            }
        }, 100);

        autoMatchSubtitle(item);
    }

    let isImageOpening = false;
    async function showImage(startItem) {
        if (S.trashMode) return;

        if (document.querySelector('.pk-img-ov')) return;
        isImageOpening = true;
        let lastDirection = 1;
        const item = startItem;

        const imgList = S.display.filter(i => {
            if (i.isHeader) return false;
            if (S.offlineMode && i.phase !== 'PHASE_TYPE_COMPLETE') return false;
            if (S.uploadMode && i.status !== 'DONE') return false;
            return i.mime_type && i.mime_type.startsWith('image');
        });

        if (imgList.length === 0) return;

        let curIdx = imgList.findIndex(i => i.id === startItem.id);
        if (curIdx === -1) curIdx = 0;

        const d = document.createElement('div');
        d.className = 'pk-img-ov';
        d.tabIndex = 0;

        const icons = {
            close: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
            full: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>',
            exitFull: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/></svg>',
            prev: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>',
            next: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>',
            rotate: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg>',
            flipH: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 7l5 5-5 5M7 17l-5-5 5-5M2 12h20"/></svg>',
            flipV: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 7l5-5 5 5M17 17l-5 5-5-5M12 2v20"/></svg>',
            searchlens: `<svg viewBox="0 0 24 24" fill="currentColor"><g transform="scale(0.0234375)"><path d="M107.739429 580.388571a365.860571 365.860571 0 0 0 648.630857 85.504L635.611429 532.48a36.571429 36.571429 0 0 0-56.612572 2.852571l-39.131428 53.101715a109.714286 109.714286 0 0 1-167.716572 10.605714L235.008 455.387429a36.571429 36.571429 0 0 0-58.002286 6.729142l-69.266285 118.272z m-19.894858-110.738285l26.038858-44.470857a109.714286 109.714286 0 0 1 174.08-20.333715l137.216 143.798857a36.571429 36.571429 0 0 0 55.881142-3.584l39.131429-53.101714a109.714286 109.714286 0 0 1 169.691429-8.484571l102.985142 113.810285A365.714286 365.714286 0 1 0 87.844571 469.577143z m658.139429 318.317714a438.857143 438.857143 0 1 1 50.029714-52.736c1.316571 1.024 2.56 2.194286 3.803429 3.437714l206.921143 206.848a36.571429 36.571429 0 0 1-51.712 51.712l-206.921143-206.848a37.083429 37.083429 0 0 1-2.194286-2.413714zM526.628571 314.514286a73.142857 73.142857 0 1 1 0-146.285715 73.142857 73.142857 0 0 1 0 146.285715z" fill="currentColor"></path></g></svg>`,
            leftArr: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6 L9 12 L15 18"/></svg>`,
            rightArr: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6 L15 12 L9 18"/></svg>`,
            upArr: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>`
        };

        const renderImgListItems = () => {
            const RANGE = 150;
            const start = Math.max(0, curIdx - RANGE);
            const end = Math.min(imgList.length, curIdx + RANGE + 1);

            return imgList.slice(start, end).map((v, i) => {
                const absIdx = start + i;
                const coverSrc = (v.thumbnail_link && v.thumbnail_link !== v.icon_link) ? v.thumbnail_link : '';
                const phSrc = v.icon_link || v.thumbnail_link || '';
                const phSvg = getIcon(v);
                return `
            <div class="pk-p-plist-item ${absIdx === curIdx ? 'active' : ''}"
                data-idx="${absIdx}"
                data-name="${esc(v.name)}"
                data-size="${fmtSize(v.size)}">
                <div class="pk-p-plist-ph">
                    ${phSrc ? `<img src="${phSrc}" draggable="false">` : phSvg}
                </div>
                ${coverSrc ? `<img src="${coverSrc}" style="filter:none !important;opacity:0;" draggable="false" onload="this.style.opacity='1';if(this.previousElementSibling)this.previousElementSibling.style.display='none';" onerror="this.remove();if(this.previousElementSibling)this.previousElementSibling.style.display='flex';" loading="lazy">` : ``}
            </div>
        `;
            }).join('');
        };

        const listFixStyle = `<style>
        #pk_img_box { border-radius: 0 !important; }
        #pk_img_plist { pointer-events: none !important; left: 0 !important; right: 0 !important; width: 100% !important; margin: 0 !important; }
        #pk_img_plist .pk-p-plist-strip { display: none !important; opacity: 0 !important; position: relative !important; background: rgba(20, 20, 20, 0.98) !important; backdrop-filter: blur(15px) !important; -webkit-backdrop-filter: blur(15px) !important; border-top: 1px solid rgba(255,255,255,0.1); border-radius: 0 !important; width: 100% !important; }
        .pk-img-box.full #pk_img_plist .pk-p-plist-strip { border-radius: 0 !important; }
        .pk-img-box.full.plist-active { height: calc(100% - 84px) !important; }
        .pk-img-box.plist-active #pk_img_plist .pk-p-plist-strip { pointer-events: auto !important; }
        .pk-img-nav { position: absolute !important; top: 50% !important; transform: translateY(-50%) translateZ(0) !important; width: 60px !important; height: 60px !important; background: rgba(0, 0, 0, 0.3) !important; display: flex !important; align-items: center !important; justify-content: center !important; color: #fff !important; cursor: pointer !important; z-index: 40 !important; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important; opacity: 0 !important; border-radius: 50% !important; border: 1px solid rgba(255, 255, 255, 0.18) !important; pointer-events: auto !important; box-shadow: 0 4px 15px rgba(0,0,0,0.15) !important; }
        .pk-img-box:hover .pk-img-nav { opacity: 1 !important; }
        .pk-img-nav:hover { background: rgba(0, 0, 0, 0.45) !important; transform: translateY(-50%) scale(1.08) translateZ(0) !important; border-color: rgba(255, 255, 255, 0.3) !important; }
        .pk-img-prev { left: 30px !important; }
        .pk-img-next { right: 30px !important; }
        .pk-img-nav svg { width: 24px !important; height: 24px !important; fill: none; stroke: currentColor; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round; }
        .pk-img-prev svg { margin-left: -2px; }
        .pk-img-next svg { margin-left: 2px; }
        #pk_img_plist .pk-p-plist-nav { position: absolute !important; top: 0 !important; bottom: 0 !important; width: 50px !important; background: transparent !important; z-index: 35 !important; pointer-events: auto !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; border: none !important; transition: background 0.2s; }
        #pk_img_plist .pk-p-plist-nav.L { left: 0 !important; }
        #pk_img_plist .pk-p-plist-nav.R { right: 0 !important; }
        #pk_img_plist .pk-p-plist-nav:hover { background: rgba(255,255,255,0.15) !important; color: #fff !important; }
        #pk_img_plist .pk-p-plist-scroll { padding: 0 45px !important; pointer-events: auto !important; scrollbar-width: none; }
        #pk_img_plist_tab { position: absolute !important; bottom: 100% !important; left: 50% !important; transform: translateX(-50%) !important; z-index: 100 !important; }
        #pk_img_plist_tab svg { width: 14px !important; height: 14px !important; stroke-width: 3.5 !important; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
        #pk_img_plist.open #pk_img_plist_tab svg { transform: rotate(180deg); }
        #pk_img_plist .pk-p-plist-scroll { display: flex !important; align-items: center !important; overflow-x: auto !important; overflow-y: hidden !important; scroll-behavior: smooth; }
        .pk-img-view-port { flex: 1; width: 100%; height: 100%; overflow: hidden; position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; }
        .pk-img-view-port.pk-long-image-mode { overflow-y: auto !important; overflow-x: hidden !important; align-items: flex-start; }
        .pk-img-view-port.pk-long-image-mode::-webkit-scrollbar { width: 8px; }
        .pk-img-view-port.pk-long-image-mode::-webkit-scrollbar-track { background: rgba(0,0,0,0.3); }
        .pk-img-view-port.pk-long-image-mode::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.3); border-radius: 4px; }
        .pk-img-view-port.pk-long-image-mode::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.5); }
        .pk-long-image-mode img.pk-img-obj { width: 100% !important; max-width: 1200px !important; height: auto !important; object-fit: cover !important; margin: 0 auto !important; cursor: zoom-out !important; transform: none !important; } .pk-img-view-port.pk-fit-mode { overflow-y: hidden !important; align-items: center; } .pk-fit-mode img.pk-img-obj { height: 100% !important; object-fit: contain !important; cursor: zoom-in !important; }
        </style>`;

        d.innerHTML = listFixStyle + `
            <div class="pk-img-box" id="pk_img_box">
                <div class="pk-img-bar">
                    <div class="pk-img-title" id="pk_img_title"></div>
                    <div class="pk-img-actions">
                        <div class="pk-img-btn" id="pk_img_search" data-pk-tip="${L.btn_img_search}" style="display:none;">${icons.searchlens}</div>
                        <div class="pk-img-btn" id="pk_img_flip_v" data-pk-tip="${L.tip_flip_v}">${icons.flipV}</div>
                        <div class="pk-img-btn" id="pk_img_mirror" data-pk-tip="${L.tip_mirror}">${icons.flipH}</div>
                        <div class="pk-img-btn" id="pk_img_rot" data-pk-tip="${L.tip_rotate}">${icons.rotate}</div>
                        <div class="pk-img-btn" id="pk_img_full" data-pk-tip="${L.tip_maximize}">${icons.full}</div>
                        <div class="pk-img-btn" id="pk_img_close" data-pk-tip="${L.tip_close}">${icons.close}</div>
                    </div>
                </div>
                <div class="pk-img-nav pk-img-prev" id="pk_img_prev" data-pk-tip="${L.btn_prev_video}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M15.5 19l-7-7 7-7"/></svg></div>
                <div class="pk-img-nav pk-img-next" id="pk_img_next" data-pk-tip="${L.btn_next_video}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M8.5 19l7-7-7-7"/></svg></div>
                <div class="pk-p-loading" id="pk_img_load"><div class="pk-spin-lg" style="border-color:rgba(255,255,255,0.3); border-top-color:#fff;"></div></div>
                <div class="pk-img-view-port" id="pk_img_viewport"><img class="pk-img-obj" id="pk_img_el" draggable="false"></div>
                <div class="pk-p-plist-ov" id="pk_img_plist">
                    <div class="pk-p-plist-tab" id="pk_img_plist_tab" data-pk-tip="${L.tip_plist_open}">
                        <span id="pk_img_idx_txt">${curIdx + 1} / ${imgList.length}</span>
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><polyline points="18 15 12 9 6 15"></polyline></svg>
                    </div>
            <div class="pk-p-plist-strip">
                <div class="pk-p-plist-nav L" id="pk_img_plist_L">${icons.leftArr}</div>
                <div class="pk-p-plist-scroll" id="pk_img_plist_scroll">${renderImgListItems()}</div>
                <div class="pk-p-plist-nav R" id="pk_img_plist_R">${icons.rightArr}</div>
            </div>
        </div>
    </div>
`;
        document.body.appendChild(d);

        const box = d.querySelector('#pk_img_box');
        const img = d.querySelector('#pk_img_el');
        const viewport = d.querySelector('#pk_img_viewport');
        const title = d.querySelector('#pk_img_title');
        const loader = d.querySelector('#pk_img_load');
        const btnFull = d.querySelector('#pk_img_full');
        const btnRot = d.querySelector('#pk_img_rot');
        const btnMirror = d.querySelector('#pk_img_mirror');
        const btnFlipV = d.querySelector('#pk_img_flip_v');
        const btnSearch = d.querySelector('#pk_img_search');

        let scale = 1, transX = 0, transY = 0, rotation = 0, flipH = 1, flipV = 1, isDrag = false, startX, startY;
        let isLongImageMode = false;

        const updateTransform = () => {
            if (isLongImageMode) return;
            img.style.transform = `translate(${transX}px, ${transY}px) scale(${scale}) rotate(${rotation}deg) scaleX(${flipH}) scaleY(${flipV})`;
        };
        const resetView = (keepOrientation = false) => {
            scale = 1; transX = 0; transY = 0;
            if (!keepOrientation) {
                rotation = 0; flipH = 1; flipV = 1;
            }
            isLongImageMode = false;
            viewport.classList.remove('pk-long-image-mode');
            viewport.classList.remove('pk-fit-mode');
            if(viewport.scrollTop) viewport.scrollTop = 0;

            img.style.transition = 'none';
            img.style.width = '100%';
            img.style.height = '100%';
            img.style.objectFit = 'contain';
            img.style.maxWidth = 'none';
            img.style.cursor = 'grab';
            if (btnRot) btnRot.style.display = 'flex';
            if (btnMirror) btnMirror.style.display = 'flex';
            if (btnFlipV) btnFlipV.style.display = 'flex';
            updateTransform();

            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    img.style.transition = '';
                });
            });
        };

        let imgLoadId = 0;

        const loadCurrent = async (scrollMode = 'smooth') => {
            if (typeof updateImgPlistUI === 'function') {
                updateImgPlistUI(scrollMode);
            }

            imgLoadId++;
            const myId = imgLoadId;

            resetView();
            img.style.opacity = '0';

            const currentItem = imgList[curIdx];
            title.textContent = `[${curIdx + 1}/${imgList.length}] ${currentItem.name}`;

            btnSearch.style.display = 'none';
            btnSearch.onclick = (e) => {
                e.stopPropagation();
                const thumbUrl = currentItem.thumbnail_link ? currentItem.thumbnail_link.replace('SIZE_MEDIUM', 'SIZE_LARGE') : (currentItem.icon_link || '');
                const thumbImg = new Image();
                thumbImg.crossOrigin = 'anonymous';
                thumbImg.src = thumbUrl;
                startImageSearch(thumbImg, currentItem.name, d, thumbUrl);
            };

            loader.style.display = 'block';

            const checkLongImage = () => {
                if (myId !== imgLoadId) return;

                btnSearch.style.display = 'flex';

                const nw = img.naturalWidth;
                const nh = img.naturalHeight;

                if (nw > 0 && nh > 0) {
                    if (nh / nw > 2.5) {
                        isLongImageMode = true;
                        viewport.classList.add('pk-long-image-mode');
                        img.style.transform = 'none';
                        if (btnRot) btnRot.style.display = 'none';
                        if (btnMirror) btnMirror.style.display = 'none';
                        if (btnFlipV) btnFlipV.style.display = 'none';
                    }
                }
                img.style.opacity = '1';
            };

            img.removeAttribute('crossorigin');

            if (currentItem.thumbnail_link) {
                img.src = currentItem.thumbnail_link;
                const handleThumb = () => {
                    if (myId !== imgLoadId) return;
                    checkLongImage();
                    loader.style.display = 'none';
                };
                img.onerror = () => {
                    if (myId !== imgLoadId) return;
                    img.style.opacity = '0';
                };
                if (img.complete) handleThumb();
                else img.onload = handleThumb;
            } else {
                img.removeAttribute('src');
                img.style.opacity = '0';
            }

            let targetUrl = currentItem.web_content_link;
            if (!targetUrl && !currentItem._resolved) {
                try {
                    const targetApiId = ((S.offlineMode && currentItem.kind === 'drive#task') || (S.uploadMode && currentItem.file_id)) ? currentItem.file_id : currentItem.id;
                    const fullItem = await apiGet(targetApiId);
                    if (fullItem) {
                        if (fullItem.thumbnail_link) currentItem.thumbnail_link = fullItem.thumbnail_link;
                        if (fullItem.icon_link) currentItem.icon_link = fullItem.icon_link;
                        if (myId === imgLoadId) requestAnimationFrame(() => updateImgPlistUI(false));
                    }
                    if (myId !== imgLoadId) return;
                    targetUrl = fullItem.web_content_link;
                    currentItem.web_content_link = targetUrl;
                    currentItem._resolved = true;
                } catch (e) { console.warn("API Error", e); }
            }

            if (myId !== imgLoadId) return;

            const performFinalRender = () => {
                if (myId !== imgLoadId) return;

                if (targetUrl && targetUrl !== currentItem.thumbnail_link) {
                    img.crossOrigin = 'anonymous';
                    img.src = targetUrl;
                } else {
                    img.crossOrigin = 'anonymous';
                }

                if (img.complete && img.naturalWidth > 0) {
                    checkLongImage();
                } else {
                    img.addEventListener('load', checkLongImage, { once: true });
                }

                btnSearch.onclick = (e) => {
                    e.stopPropagation();
                    const thumbUrl = currentItem.thumbnail_link ? currentItem.thumbnail_link.replace('SIZE_MEDIUM', 'SIZE_LARGE') : (currentItem.icon_link || '');
                    const thumbImg = new Image();
                    thumbImg.crossOrigin = 'anonymous';
                    thumbImg.src = thumbUrl;
                    startImageSearch(thumbImg, currentItem.name, d, thumbUrl);
                };

                const mainDir = lastDirection, sideDir = -lastDirection, DEPTH = 5;
                const quickLoad = (url) => { if(!url) return; const p = new Image(); p.src = url; };
                const getIdx = (offset) => (curIdx + offset + imgList.length) % imgList.length;

                quickLoad(imgList[getIdx(sideDir)].thumbnail_link);
                for (let i = 1; i <= DEPTH; i++) {
                    if (myId !== imgLoadId) return;
                    const next = imgList[getIdx(i * mainDir)];
                    quickLoad(next.thumbnail_link);
                    if (next.web_content_link) {
                        const p = new Image(); p.crossOrigin = 'anonymous'; p.src = next.web_content_link;
                    }
                }
            };

            if (targetUrl && targetUrl !== currentItem.thumbnail_link) {
                const tempImg = new Image();
                tempImg.crossOrigin = 'anonymous';
                tempImg.onload = performFinalRender;
                tempImg.onerror = performFinalRender;
                tempImg.src = targetUrl;
            } else {
                performFinalRender();
            }
        };

        d.addEventListener('wheel', (e) => {
            if (isLongImageMode) return;

            e.preventDefault();
            const delta = e.deltaY > 0 ? -0.1 : 0.1;
            scale = Math.max(0.1, Math.min(10, scale + delta));
            updateTransform();
        });

        img.onclick = () => {
            if (isLongImageMode) {
                viewport.classList.toggle('pk-fit-mode');
            }
        };
        d.addEventListener('wheel', (e) => {
            if (isLongImageMode) return;

            e.preventDefault();
            const delta = e.deltaY > 0 ? -0.1 : 0.1;
            let newScale = Math.max(0.1, Math.min(10, scale + delta));

            if (img.naturalWidth && viewport) {
                const vw = viewport.clientWidth;
                const vh = viewport.clientHeight;
                const iw = img.naturalWidth;
                const ih = img.naturalHeight;

                const baseRatio = Math.min(vw / iw, vh / ih);
                let curW = iw * baseRatio * newScale;
                let curH = ih * baseRatio * newScale;

                if (Math.abs(rotation % 180) === 90) [curW, curH] = [curH, curW];

                const limitX = curW > vw ? (curW - vw) / 2 : 0;
                const limitY = curH > vh ? (curH - vh) / 2 : 0;

                transX = Math.max(-limitX, Math.min(limitX, transX));
                transY = Math.max(-limitY, Math.min(limitY, transY));
            }

            scale = newScale;
            updateTransform();
        });

        img.onclick = () => {
            if (isLongImageMode) {
                viewport.classList.toggle('pk-fit-mode');
            }
        };
        img.addEventListener('mousedown', (e) => {
            if (e.button !== 0 || (isLongImageMode && !viewport.classList.contains('pk-fit-mode'))) return;
            e.preventDefault();
            const z = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            isDrag = true;
            img.style.cursor = 'grabbing';
            startX = e.clientX - transX * z;
            startY = e.clientY - transY * z;
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDrag || isLongImageMode) return;
            const z = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            let tx = (e.clientX - startX) / z;
            let ty = (e.clientY - startY) / z;

            if (img.naturalWidth && viewport) {
                const vw = viewport.clientWidth;
                const vh = viewport.clientHeight;
                const iw = img.naturalWidth;
                const ih = img.naturalHeight;

                const baseRatio = Math.min(vw / iw, vh / ih);
                let curW = iw * baseRatio * scale;
                let curH = ih * baseRatio * scale;

                if (Math.abs(rotation % 180) === 90) [curW, curH] = [curH, curW];

                const limitX = curW > vw ? (curW - vw) / 2 : 0;
                const limitY = curH > vh ? (curH - vh) / 2 : 0;

                tx = Math.max(-limitX, Math.min(limitX, tx));
                ty = Math.max(-limitY, Math.min(limitY, ty));
            }

            transX = tx;
            transY = ty;
            updateTransform();
        });

        document.addEventListener('mouseup', () => {
            isDrag = false;
            if (!isLongImageMode && img) img.style.cursor = 'grab';
        });

        const resizeHandler = () => {
            if (isLongImageMode) return;
            resetView(true);
        };
        window.addEventListener('resize', resizeHandler);
        d._pkResizeHandler = resizeHandler;
        d._pkImgList = imgList;
        d._pkGetCurIdx = () => curIdx;
        Object.defineProperty(d, '_pkCurIdx', { get: () => curIdx, configurable: true });

        d.querySelector('#pk_img_close').onclick = (e) => {
            e.stopPropagation();
            S.closeImageOverlay();
        };

        btnFull.onclick = (e) => {
            e.stopPropagation();
            box.classList.toggle('full');
            const isNowFull = box.classList.contains('full');

            btnFull.innerHTML = isNowFull ? icons.exitFull : icons.full;
            btnFull.setAttribute('data-pk-tip', isNowFull ? L.tip_minimize : L.tip_maximize);

            if (isLongImageMode && viewport) {
                viewport.scrollTop = 0;
            } else {
                setTimeout(() => resetView(true), 210);
            }
        };

        btnRot.onclick = (e) => {
            e.stopPropagation();
            if (isLongImageMode) return;
            rotation += 90;
            updateTransform();
        };

        if (btnMirror) {
            btnMirror.onclick = (e) => {
                e.stopPropagation();
                if (isLongImageMode) return;
                flipH *= -1;
                img.style.transition = 'none';
                updateTransform();
                requestAnimationFrame(() => {
                    requestAnimationFrame(() => {
                        img.style.transition = '';
                    });
                });
            };
        }
        if (btnFlipV) {
            btnFlipV.onclick = (e) => {
                e.stopPropagation();
                if (isLongImageMode) return;
                flipV *= -1;
                img.style.transition = 'none';
                updateTransform();
                requestAnimationFrame(() => {
                    requestAnimationFrame(() => {
                        img.style.transition = '';
                    });
                });
            };
        }

        const plist = d.querySelector('#pk_img_plist');
        const pTab = d.querySelector('#pk_img_plist_tab');
        const pScroll = d.querySelector('#pk_img_plist_scroll');
        let pTip = document.getElementById('pk_p_plist_tip_global');
        if (!pTip) {
            pTip = document.createElement('div');
            pTip.id = 'pk_p_plist_tip_global';
            pTip.className = 'pk-p-plist-tip';
            document.body.appendChild(pTip);
        }
        const pTxt = d.querySelector('#pk_img_idx_txt');

        const updateImgPlistUI = (scrollType = 'smooth') => {
            if (pTxt) pTxt.textContent = `${curIdx + 1} / ${imgList.length}`;

            const RANGE = 150;
            const desiredStart = Math.max(0, curIdx - RANGE);
            const desiredEnd = Math.min(imgList.length, curIdx + RANGE + 1);

            const prevStart = parseInt(pScroll.dataset.pkStart || '-1', 10);
            const prevEnd = parseInt(pScroll.dataset.pkEnd || '-1', 10);

            const needRebuild =
                  !Number.isFinite(prevStart) ||
                  !Number.isFinite(prevEnd) ||
                  curIdx < prevStart ||
                  curIdx >= prevEnd ||
                  pScroll.childElementCount === 0 ||
                  pScroll.dataset.pkTotal !== String(imgList.length);

            if (needRebuild) {
                pScroll.innerHTML = renderImgListItems();
                pScroll.dataset.pkStart = String(desiredStart);
                pScroll.dataset.pkEnd = String(desiredEnd);
                pScroll.dataset.pkTotal = String(imgList.length);

                pScroll.querySelectorAll('.pk-p-plist-item').forEach(el => {
                    el.onclick = (e) => {
                        e.stopPropagation();
                        const idx = parseInt(e.currentTarget.dataset.idx, 10);
                        if (idx === curIdx) return;
                        curIdx = idx;
                        loadCurrent('instant');
                    };

                    el.onmouseenter = (e) => {
                        if (plist.classList.contains('open')) {
                            const name = e.currentTarget.dataset.name;
                            const size = e.currentTarget.dataset.size;
                            pTip.innerHTML = `<strong>${name}</strong><br>${size}`;
                            pTip.style.display = 'block';
                        }
                    };

                    el.onmousemove = (e) => {
                        if (pTip.style.display === 'block') {
                            const tW = pTip.offsetWidth || 150;
                            pTip.style.left = (e.clientX - (tW / 2)) + 'px';
                            pTip.style.top = (e.clientY - 60) + 'px';
                        }
                    };

                    el.onmouseleave = () => {
                        pTip.style.display = 'none';
                    };
                });
            }

            const itemsInDom = pScroll.querySelectorAll('.pk-p-plist-item');
            itemsInDom.forEach((el) => {
                const absIdx = parseInt(el.dataset.idx, 10);
                const isActive = absIdx === curIdx;
                el.classList.toggle('active', isActive);

                if (scrollType !== false && isActive && plist.classList.contains('open')) {
                    if (scrollType === 'instant') {
                        pScroll.style.scrollBehavior = 'auto';
                    } else {
                        pScroll.style.scrollBehavior = 'smooth';
                    }

                    el.scrollIntoView({
                        behavior: scrollType === 'instant' ? 'auto' : 'smooth',
                        block: 'nearest',
                        inline: 'center'
                    });

                    if (scrollType === 'instant') {
                        setTimeout(() => { pScroll.style.scrollBehavior = 'smooth'; }, 50);
                    }
                }
            });

            const sl = Math.ceil(pScroll.scrollLeft);
            const sw = pScroll.scrollWidth;
            const cw = pScroll.clientWidth;

            if (sw <= cw) {
                d.querySelector('#pk_img_plist_L').style.setProperty('display', 'none', 'important');
                d.querySelector('#pk_img_plist_R').style.setProperty('display', 'none', 'important');
            } else {
                if (sl <= 5) d.querySelector('#pk_img_plist_L').style.setProperty('display', 'none', 'important');
                else d.querySelector('#pk_img_plist_L').style.setProperty('display', 'flex', 'important');

                if (sl + cw >= sw - 5) d.querySelector('#pk_img_plist_R').style.setProperty('display', 'none', 'important');
                else d.querySelector('#pk_img_plist_R').style.setProperty('display', 'flex', 'important');
            }

            const btnPrev = d.querySelector('#pk_img_prev');
            const btnNext = d.querySelector('#pk_img_next');

            if (btnPrev) btnPrev.style.setProperty('display', curIdx === 0 ? 'none' : 'flex', 'important');
            if (btnNext) btnNext.style.setProperty('display', curIdx === imgList.length - 1 ? 'none' : 'flex', 'important');
        };

        pTab.onclick = (e) => {
            e.stopPropagation();

            const willOpen = !plist.classList.contains('open');

            plist.classList.toggle('open');
            if (typeof box !== 'undefined') box.classList.toggle('plist-active');

            pTab.setAttribute('data-pk-tip', plist.classList.contains('open') ? L.tip_plist_close : L.tip_plist_open);

            if (!isLongImageMode) resetView();

            if (willOpen) {
                updateImgPlistUI(false);
                pScroll.style.scrollBehavior = 'auto';

                const activeItem = pScroll.querySelector('.active');
                if (activeItem) {
                    const targetLeft = activeItem.offsetLeft - (pScroll.clientWidth / 2) + (activeItem.clientWidth / 2);
                    pScroll.scrollLeft = targetLeft;
                }

                setTimeout(() => { pScroll.style.scrollBehavior = 'smooth'; }, 50);
                setTimeout(() => updateImgPlistUI(false), 100);
            }
        };

        const handleListWheel = (e) => {
            e.stopPropagation();
            e.preventDefault();
            pScroll.scrollBy({ left: e.deltaY > 0 ? 300 : -300, behavior: 'smooth' });
        };

        pScroll.removeEventListener('wheel', handleListWheel);
        pScroll.addEventListener('wheel', handleListWheel, { passive: false });

        pScroll.addEventListener('scroll', () => {
             requestAnimationFrame(() => updateImgPlistUI(false));
        }, { passive: true });

        const btnListL = d.querySelector('#pk_img_plist_L');
        const btnListR = d.querySelector('#pk_img_plist_R');

        if (btnListL) {
            btnListL.onclick = (e) => {
                e.stopPropagation();
                pScroll.scrollBy({ left: -400, behavior: 'smooth' });
                setTimeout(() => updateImgPlistUI(false), 300);
            };
        }
        if (btnListR) {
            btnListR.onclick = (e) => {
                e.stopPropagation();
                pScroll.scrollBy({ left: 400, behavior: 'smooth' });
                setTimeout(() => updateImgPlistUI(false), 300);
            };
        }

        setTimeout(() => updateImgPlistUI(false), 100);

        const goPrev = () => {
            if (curIdx > 0) {
                lastDirection = -1;
                curIdx--;
                loadCurrent();
            }
        };
        const goNext = () => {
            if (curIdx < imgList.length - 1) {
                lastDirection = 1;
                curIdx++;
                loadCurrent();
            }
        };

        d.querySelector('#pk_img_prev').onclick = (e) => { e.stopPropagation(); goPrev(); };
        d.querySelector('#pk_img_next').onclick = (e) => { e.stopPropagation(); goNext(); };

        d.focus();
        d.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                S.closeImageOverlay();
            }
            else if (e.key === 'ArrowLeft') goPrev();
            else if (e.key === 'ArrowRight') goNext();
            else if (e.key === 'f' || e.key === 'F') {
                if (btnSearch && btnSearch.style.display !== 'none') btnSearch.click();
            }
            else if (e.key === 'e' || e.key === 'E') {
                if (pTab) pTab.click();
            }
            else if (e.key === 'm' || e.key === 'M') {
                e.preventDefault();
                if (btnFull) btnFull.click();
            }
            else if (e.key === 'r' || e.key === 'R') {
                e.preventDefault();
                if (!isLongImageMode && btnRot) btnRot.click();
            }
            else if (e.key === 'h' || e.key === 'H') {
                e.preventDefault();
                if (!isLongImageMode && btnMirror) btnMirror.click();
            }
            else if (e.key === 'v' || e.key === 'V') {
                e.preventDefault();
                if (!isLongImageMode && btnFlipV) btnFlipV.click();
            }
        });

        try { await loadCurrent(); } catch (e) { console.error(e); } finally { isImageOpening = false; }
    }

    const SEARCH_CSS = `
        .pk-search-ov {
            position: fixed; inset: 0; z-index: 2147483647;
            background: rgba(0,0,0,0.5); cursor: crosshair;
        }
        .pk-crop-box {
            position: absolute; border: 2px dashed #fff;
            background: rgba(255,255,255,0.1); pointer-events: none;
            box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);
        }
        .pk-search-msg {
            position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.8); color: #fff; padding: 10px 20px; border-radius: 5px;
            font-size: 14px; pointer-events: none;
        }
    `;

    let isImageSearchRunning = false;

    async function startImageSearch(mediaElement, fileName, containerElement, originalLink) {
        if (isImageSearchRunning) return;
        isImageSearchRunning = true;

        try { window.focus(); if (containerElement) containerElement.focus(); } catch (e) {}

        const L = getStrings();
        const isVideo = mediaElement.tagName === 'VIDEO';

        const MAX_SIDE = 1000;
        const BLOB_TYPE = 'image/jpeg';
        const BLOB_QUALITY = 0.7;

        let progressTask = null;
        if (typeof FloatBarManager !== 'undefined') {
            progressTask = FloatBarManager.create(L.str_processing);
        }

        if (isVideo && !mediaElement.paused) mediaElement.pause();

        try {
            let finalBlob = null;
            const cvs = document.createElement('canvas');
            const ctx = cvs.getContext('2d');

            const drawScaled = (source, srcW, srcH) => {
                let w = srcW, h = srcH;
                if (w === 0 || h === 0) throw new Error("Media dimensions not ready");
                if (w > MAX_SIDE || h > MAX_SIDE) {
                    const ratio = Math.min(MAX_SIDE / w, MAX_SIDE / h);
                    w = Math.floor(w * ratio); h = Math.floor(h * ratio);
                }
                cvs.width = w; cvs.height = h;
                ctx.drawImage(source, 0, 0, w, h);
            };

            let fetchUrl = originalLink;
            const isLocalData = fetchUrl && (fetchUrl.startsWith('blob:') || fetchUrl.startsWith('data:'));

            const BLOB_TYPE = 'image/jpeg';
            const BLOB_QUALITY = 0.85;

            if (isVideo) {
                const sourceW = mediaElement.videoWidth;
                const sourceH = mediaElement.videoHeight;
                drawScaled(mediaElement, sourceW, sourceH);
                try {
                    finalBlob = await new Promise((resolve, reject) => {
                        cvs.toBlob(b => b ? resolve(b) : reject(new Error("Empty")), BLOB_TYPE, BLOB_QUALITY);
                    });
                } catch (err) {
                    throw new Error("Tainted: Video CORS Blocked");
                }
            } else {
                let canvasSuccess = false;
                try {
                    const sourceW = mediaElement.naturalWidth || mediaElement.width;
                    const sourceH = mediaElement.naturalHeight || mediaElement.height;

                    if (sourceW > 0 && sourceH > 0) {
                        drawScaled(mediaElement, sourceW, sourceH);
                        finalBlob = await new Promise((resolve, reject) => {
                            try {
                                cvs.toBlob(b => b ? resolve(b) : reject(new Error("Tainted")), BLOB_TYPE, BLOB_QUALITY);
                            } catch (e) { reject(e); }
                        });
                        canvasSuccess = !!finalBlob;
                    }
                } catch (canvasErr) {
                    console.warn("[ImageSearch] DOM Canvas extraction failed (Tainted/Not Ready), fallback to network.");
                }

                if (!canvasSuccess && fetchUrl && !isLocalData) {
                    console.log("[ImageSearch] Fetching image via network as fallback...");
                    try {
                        const res = await fetch(fetchUrl, { mode: 'cors', credentials: 'omit' });
                        if (!res.ok && res.status !== 206) throw new Error(`Fetch HTTP ${res.status}`);
                        finalBlob = await res.blob();
                    } catch (fetchErr) {
                        console.warn(`[ImageSearch] Native fetch failed (${fetchErr.message}), fallback to GM_xhr...`);

                        let fetchRetry = 0;
                        let fetchSuccess = false;

                        while (fetchRetry < 3 && !fetchSuccess) {
                            try {
                                finalBlob = await new Promise((resolve, reject) => {
                                    GM_xmlhttpRequest({
                                        method: "GET",
                                        url: fetchUrl,
                                        responseType: "blob",
                                        timeout: 30000,
                                        headers: {
                                            "Referer": "https://mypikpak.com/",
                                            "User-Agent": navigator.userAgent
                                        },
                                        onload: (res) => {
                                            if (res.status === 200 || res.status === 206) resolve(res.response);
                                            else reject(new Error(`HTTP ${res.status}`));
                                        },
                                        onerror: () => reject(new Error("Network Error")),
                                        ontimeout: () => reject(new Error("Timeout"))
                                    });
                                });
                                fetchSuccess = true;
                            } catch (err) {
                                fetchRetry++;
                                if (fetchRetry < 3) {
                                    console.warn(`[ImageSearch] GM_xhr retry ${fetchRetry}/3...`);
                                    await new Promise(r => setTimeout(r, 1500));
                                }
                            }
                        }
                    }
                }

                if (!finalBlob) {
                    throw new Error(L.err_network_break);
                }

                if (finalBlob.size > 8 * 1024 * 1024) {
                    console.log(`[ImageSearch] Image too large (${(finalBlob.size/1024/1024).toFixed(1)}MB), applying compression...`);
                    try {
                        const bmp = await createImageBitmap(finalBlob);
                        drawScaled(bmp, bmp.width, bmp.height);
                        bmp.close();
                        finalBlob = await new Promise((resolve, reject) => {
                            cvs.toBlob(b => b ? resolve(b) : reject(new Error("Compression Failed")), BLOB_TYPE, BLOB_QUALITY);
                        });
                    } catch (e) {
                        console.warn("[ImageSearch] Compression failed, using original blob.", e);
                    }
                }
            }

            if (!finalBlob) throw new Error(L.err_capture || "Capture failed");

            const uploadAndGetUrl = async () => {
                if (typeof GM_xmlhttpRequest === 'undefined') throw new Error("Missing GM_xmlhttpRequest");

                const FAST_TIMEOUT = 4000;

                const uploadTask = (url, formData, parseType, stageText) => {
                    return new Promise((resolve, reject) => {
                        if (progressTask) progressTask.update(stageText);

                        GM_xmlhttpRequest({
                            method: "POST", url: url, data: formData,
                            timeout: FAST_TIMEOUT,
                            responseType: parseType === 'json' ? 'json' : 'text',
                            onload: (res) => {
                                if (res.status === 200) resolve(res);
                                else reject(new Error(`HTTP ${res.status}`));
                            },
                            onerror: () => reject(new Error("Network Error")),
                            ontimeout: () => reject(new Error("Timeout"))
                        });
                    });
                };

                const tryNode1 = async () => {
                    const fd = new FormData();
                    fd.append('files[]', finalBlob, `pk_1.jpg`);

                    const res = await uploadTask(
                        "https://uguu.se/upload.php",
                        fd, 'json',
                        L.str_upload_1
                    );

                    if (res.response && res.response.success && res.response.files?.[0]?.url) {
                        return res.response.files[0].url;
                    }
                    throw new Error("Uguu API Error");
                };

                const tryNode2 = async () => {
                    console.warn("Node 1 failed, switching to Litterbox...");
                    const fd = new FormData();
                    fd.append('reqtype', 'fileupload');
                    fd.append('time', '1h');
                    fd.append('fileToUpload', finalBlob, `pk_2.jpg`);

                    const res = await uploadTask(
                        "https://litterbox.catbox.moe/resources/internals/api.php",
                        fd, 'text',
                        L.str_upload_2
                    );
                    return res.responseText.trim();
                };

                const tryNode3 = async () => {
                    console.warn("Node 2 failed, switching to Catbox...");
                    const fd = new FormData();
                    fd.append('reqtype', 'fileupload');
                    fd.append('userhash', '');
                    fd.append('fileToUpload', finalBlob, `pk_3.jpg`);

                    const res = await uploadTask(
                        "https://catbox.moe/user/api.php",
                        fd, 'text',
                        L.str_upload_3
                    );
                    return res.responseText.trim();
                };

                try {
                    return await tryNode1();
                } catch (e1) {
                    try {
                        return await tryNode2();
                    } catch (e2) {
                        try {
                            return await tryNode3();
                        } catch (e3) {
                            console.error("All upload nodes failed:", e1, e2, e3);
                            throw new Error("All Upload Hosts Failed");
                        }
                    }
                }
            };

            try {
                const imgUrl = await uploadAndGetUrl();

                if (!imgUrl || !imgUrl.startsWith('http')) {
                    throw new Error("Invalid URL returned.");
                }

                const currentEngine = gmGet('pk_search_engine', 'google');
                const encUrl = encodeURIComponent(imgUrl);
                let jumpUrl = '';
                let engineName = '';

                switch (currentEngine) {
                    case 'yandex':
                        jumpUrl = `https://yandex.com/images/search?rpt=imageview&url=${encUrl}`;
                        engineName = 'Yandex';
                        break;
                    case 'saucenao':
                        jumpUrl = `https://saucenao.com/search.php?db=999&url=${encUrl}`;
                        engineName = 'SauceNAO';
                        break;
                    case 'tracemoe': {
                        const cleanUrl = imgUrl.replace(/^https?:\/\//, '');
                        const proxyUrl = `https://wsrv.nl/?url=${cleanUrl}&output=jpg`;
                        jumpUrl = `https://trace.moe/?url=${encodeURIComponent(proxyUrl)}`;
                        engineName = 'trace.moe';
                        break;
                    }
                    case 'google':
                    default:
                        jumpUrl = `https://lens.google.com/uploadbyurl?url=${encUrl}`;
                        engineName = 'Google Lens';
                        break;
                }

                if (progressTask) {
                    progressTask.update(L.str_redirecting.replace('Google Lens', engineName));
                    const el = document.querySelector('.pk-float-bar-item .pk-spin-lg');
                    if (el) el.style.borderColor = '#4CAF50';
                }

                await sleep(600);
                window.open(jumpUrl, '_blank');

            } catch (err) {
                console.warn("Auto upload failed, switching to manual fallback:", err);

                if (progressTask) progressTask.update(L.str_upload_fail_copy);

                let fallbackBlob = null;
                try {
                    const bmp = await createImageBitmap(finalBlob);
                    const tmpCvs = document.createElement('canvas');
                    tmpCvs.width = bmp.width;
                    tmpCvs.height = bmp.height;
                    tmpCvs.getContext('2d').drawImage(bmp, 0, 0);
                    bmp.close();
                    fallbackBlob = await new Promise(r => tmpCvs.toBlob(r, 'image/png'));
                } catch (e) {
                    console.warn("Bitmap conversion failed, falling back to origin canvas:", e);
                    fallbackBlob = await new Promise(r => cvs.toBlob(r, 'image/png'));
                }

                const MAX_CLIPBOARD_SIZE = 19.5 * 1024 * 1024;
                if (fallbackBlob.size > MAX_CLIPBOARD_SIZE) {
                    console.log(`PNG too large, resizing...`);
                    try {
                        const ratio = Math.sqrt(MAX_CLIPBOARD_SIZE / fallbackBlob.size);
                        const bmp = await createImageBitmap(fallbackBlob);
                        const newW = Math.floor(bmp.width * ratio);
                        const newH = Math.floor(bmp.height * ratio);
                        const tmpCvs = document.createElement('canvas');
                        tmpCvs.width = newW;
                        tmpCvs.height = newH;
                        tmpCvs.getContext('2d').drawImage(bmp, 0, 0, newW, newH);
                        bmp.close();
                        fallbackBlob = await new Promise(r => tmpCvs.toBlob(r, 'image/png'));
                    } catch (e) {
                        console.warn("Resize failed:", e);
                    }
                }

                try {
                    const item = new ClipboardItem({ 'image/png': fallbackBlob });
                    await navigator.clipboard.write([item]);
                } catch (clipErr) {
                    console.error("Clipboard write failed:", clipErr);
                    if (progressTask) progressTask.update(L.err_clipboard_failed);
                    await sleep(1000);
                }

                const ctrlVPhrase = (navigator.platform.toUpperCase().indexOf('MAC') >= 0) ? 'Cmd+V' : 'Ctrl+V';
                const hintText = L.msg_manual_paste.replace('{cmd}', `<b style="color:#fff">${ctrlVPhrase}</b>`);

                if (progressTask) progressTask.destroy();

                const fallbackOv = document.createElement('div');
                fallbackOv.className = 'pk-search-running-mask';
                fallbackOv.style.cssText = 'position:absolute; inset:0; z-index:2147483647; background:rgba(0,0,0,0.85); display:flex; align-items:center; justify-content:center; flex-direction:column; gap:20px; border-radius:inherit;';

                fallbackOv.innerHTML = `
                <div style="background:rgba(20,20,20,0.9); backdrop-filter:blur(10px); color:#FFC107; padding:20px 40px; border-radius:12px; text-align:center; box-shadow: 0 10px 30px rgba(0,0,0,0.5); border:1px solid rgba(255,255,255,0.1);">
                    <div style="font-size:24px; margin-bottom:8px;">⚠️</div>
                    <div style="font-size:15px; font-weight:bold;">${L.msg_copy_success}</div>
                    <div style="font-size:12px; color:#ddd; margin-top:8px;">${hintText}</div>
                </div>`;

                const fsEl = document.fullscreenElement || document.webkitFullscreenElement;
                if (fsEl) {
                    fsEl.appendChild(fallbackOv);
                } else if (containerElement) {
                    containerElement.appendChild(fallbackOv);
                } else {
                    document.body.appendChild(fallbackOv);
                }

                await sleep(2500);
                fallbackOv.remove();

                const currentEngine = gmGet('pk_search_engine', 'google');
                let manualUrl = '';
                switch (currentEngine) {
                    case 'yandex': manualUrl = 'https://yandex.com/images/'; break;
                    case 'saucenao': manualUrl = 'https://saucenao.com/'; break;
                    case 'tracemoe': manualUrl = 'https://trace.moe/'; break;
                    case 'google': default: manualUrl = 'https://lens.google.com/upload'; break;
                }

                window.open(manualUrl, '_blank');
            }
        } catch (e) {
            console.error("Search Error:", e);
            if (progressTask) progressTask.destroy();
            const errorMsg = e.message.includes('Tainted') ? L.err_cors_blocked : e.message;
            if (typeof showToast !== 'undefined') {
                showToast(`${errorMsg}`, 'error');
            }
        } finally {
            if (progressTask) progressTask.destroy();
            isImageSearchRunning = false;
        }
    }

    function dataURLtoBlob(dataurl) {
        let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], {type:mime});
    }

    const FILTER_EXTS = {
        video: ['mp4','mkv','avi','mov','wmv','flv','webm','ts','m4v','3gp','mpg','mpeg','rm','rmvb','asf','vob','dat','divx','f4v','m2ts','mts','tp','trp','ogv','mpe','m2v','m3u8'],
        audio: ['mp3','wav','flac','aac','ogg','wma','ape','m4a','amr','opus','m4b','alac','aiff','mid','midi','ra','dts','ac3','dsf','dff'],
        image: ['jpg','jpeg','png','gif','bmp','webp','svg','tif','tiff','ico','heic','heif','raw','cr2','nef','arw','dng','orf','avif','psd','ai','eps','jfif','jpe'],
        document: ['txt','html','pdf','pptx','chm','docx','xlsx','htm','doc','dwg','mdb','ppt','xls','rtf','odt','ods','odp','epub','mobi','azw3','djvu','cbz','cbr','md','log','csv','xml','json'],
        software: ['apk','exe','ipa','dmg','rpm','deb','msi','pkg','xapk','apks','aab','jar','bin','sh','bat','cmd'],
        archive: ['zip','rar','7z','tar','gz','iso','cab','bz2','xz','tgz','wim','esd','img','zst','lzh'],
        torrent: ['torrent']
    };

    const FILTER_NAMES = {
        video: L.cat_video,
        audio: L.cat_audio,
        image: L.cat_image,
        document: L.cat_document,
        software: L.cat_software,
        archive: L.cat_archive,
        torrent: L.cat_torrent,
        other: L.cat_other
    };

    const fIcons = {
        all: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>`,
        video: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"/><line x1="7" y1="2" x2="7" y2="22"/><line x1="17" y1="2" x2="17" y2="22"/><line x1="2" y1="12" x2="22" y2="12"/><line x1="2" y1="7" x2="7" y2="7"/><line x1="2" y1="17" x2="7" y2="17"/><line x1="17" y1="17" x2="22" y2="17"/><line x1="17" y1="7" x2="22" y2="7"/></svg>`,
        audio: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"/></svg>`,
        image: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`,
        document: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>`,
        software: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>`,
        archive: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8v13H3V8"/><path d="M1 3h22v5H1z"/><path d="M10 12h4v4h-4z"/></svg>`,
        torrent: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/><path d="M9 14v4"/><path d="M12 12v6"/><path d="M15 15v3"/></svg>`,
        other: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>`
    };

    const renderActiveFilterUI = () => {
        if (!UI.filterBar) return;
        const cat = S.filterState.cat;
        if (cat === 'all') return;

        UI.filterCatLabel.textContent = FILTER_NAMES[cat] || (L.cat_other);

        if (cat === 'other') {
             UI.filterExtsWrap.style.display = 'none';
        } else {
             UI.filterExtsWrap.style.display = 'flex';

             const exts = FILTER_EXTS[cat] ||[];
             const mainExts = exts.slice(0, 3);
             const moreExts = exts.slice(3);

             let html = `<span class="pk-f-ext ${S.filterState.ext === 'all' ? 'act' : ''}" data-ext="all">${L.cat_all}</span>`;

             let displayExts = [...mainExts];
             if (S.filterState.ext !== 'all' && moreExts.includes(S.filterState.ext)) {
                 displayExts[2] = S.filterState.ext;
             }

             displayExts.forEach(e => {
                 html += `<span class="pk-f-ext ${S.filterState.ext === e ? 'act' : ''}" data-ext="${e}">${e}</span>`;
             });

             UI.filterExtsMain.innerHTML = html;

             if (moreExts.length > 0) {
                 UI.filterExtsMoreBtn.style.display = 'flex';
             } else {
                 UI.filterExtsMoreBtn.style.display = 'none';
             }

             UI.filterExtsMain.querySelectorAll('.pk-f-ext').forEach(span => {
                 span.onclick = (e) => {
                      e.stopPropagation();
                      S.filterState.ext = span.dataset.ext;
                      renderActiveFilterUI();
                      S.sel.clear();
                      refresh();
                 };
             });
        }
    };

    const showFilterCatPopup = (triggerEl, e) => {
        e.stopPropagation();
        const existing = document.querySelector('#pk-filter-cat-pop');
        if (existing) { existing.remove(); return; }

        const pop = document.createElement('div');
        pop.id = 'pk-filter-cat-pop';
        pop.style.cssText = `
            position: absolute; background: var(--pk-bg); border: 1px solid var(--pk-bd);
            border-radius: 8px; padding: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.15);
            z-index: 2147483647; width: 420px; display: flex; flex-direction: column;
            zoom: var(--pk-zoom, 1);
        `;
        if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) pop.classList.add('pk-dark');

        pop.innerHTML = `
            <div style="font-size: 13px; color: #888; margin-bottom: 12px; padding-left: 5px;">${L.title_file_filter}</div>
            <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
                <div class="pk-fc-btn ${S.filterState.cat === 'all' ? 'act' : ''}" data-cat="all">${fIcons.all} <span>${L.cat_all}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'video' ? 'act' : ''}" data-cat="video">${fIcons.video} <span>${L.cat_video}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'audio' ? 'act' : ''}" data-cat="audio">${fIcons.audio} <span>${L.cat_audio}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'image' ? 'act' : ''}" data-cat="image">${fIcons.image} <span>${L.cat_image}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'document' ? 'act' : ''}" data-cat="document">${fIcons.document} <span>${L.cat_document}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'software' ? 'act' : ''}" data-cat="software">${fIcons.software} <span>${L.cat_software}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'archive' ? 'act' : ''}" data-cat="archive">${fIcons.archive} <span>${L.cat_archive}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'torrent' ? 'act' : ''}" data-cat="torrent">${fIcons.torrent} <span>${L.cat_torrent}</span></div>
                <div class="pk-fc-btn ${S.filterState.cat === 'other' ? 'act' : ''}" data-cat="other">${fIcons.other} <span>${L.cat_other}</span></div>
            </div>
        `;
        document.body.appendChild(pop);

        const updatePosition = () => {
            if (!pop.isConnected) return;
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const rect = getLogicalRect(triggerEl);
            let popLeft = rect.left;
            if (popLeft + 420 > window.innerWidth / scale) popLeft = (window.innerWidth / scale) - 430;
            pop.style.top = (rect.bottom + 5) + 'px';
            pop.style.left = popLeft + 'px';
        };
        updatePosition();
        window.addEventListener('resize', updatePosition);

        const cleanup = () => {
            window.removeEventListener('resize', updatePosition);
            document.removeEventListener('mousedown', closer);
            pop.remove();
        };

        pop.querySelectorAll('.pk-fc-btn').forEach(btn => {
            btn.onclick = (ev) => {
                ev.stopPropagation();
                const cat = btn.dataset.cat;
                S.filterState.cat = cat;
                S.filterState.ext = 'all';
                S.filterState.active = (cat !== 'all');
                cleanup();

                if (S.filterState.active) {
                    renderActiveFilterUI();
                }
                S.sel.clear();
                refresh();
            };
        });

        const closer = (ev) => {
            if (!pop.contains(ev.target) && !triggerEl.contains(ev.target)) {
                cleanup();
            }
        };
        setTimeout(() => document.addEventListener('mousedown', closer), 10);
    };

    if (UI.filterBtn) UI.filterBtn.onclick = (e) => showFilterCatPopup(UI.filterBtn, e);
    if (UI.filterCatLabel) UI.filterCatLabel.onclick = (e) => showFilterCatPopup(UI.filterCatLabel, e);

    if (UI.filterExtsMoreBtn) {
        UI.filterExtsMoreBtn.onclick = (e) => {
            e.stopPropagation();
            const existing = document.querySelector('#pk-filter-more-pop');
            if (existing) { existing.remove(); return; }

            const pop = document.createElement('div');
            pop.id = 'pk-filter-more-pop';
            pop.style.cssText = `
                position: absolute; background: var(--pk-bg); border: 1px solid var(--pk-bd);
                border-radius: 8px; padding: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.15);
                z-index: 2147483647; max-width: 340px; display: flex; flex-wrap: wrap; gap: 8px;
                zoom: var(--pk-zoom, 1);
            `;
            if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) pop.classList.add('pk-dark');
            const exts = FILTER_EXTS[S.filterState.cat] ||[];
            const mainExts = exts.slice(0, 3);
            const moreExts = exts.slice(3);

            let displayExts = [...mainExts];
            if (S.filterState.ext !== 'all' && moreExts.includes(S.filterState.ext)) {
                displayExts[2] = S.filterState.ext;
            }

            const dropdownExts = exts.filter(ex => !displayExts.includes(ex));

            pop.innerHTML = dropdownExts.map(ex => `<span class="pk-f-ext ${S.filterState.ext === ex ? 'act' : ''}" data-ext="${ex}" style="border:1px solid transparent; ${S.filterState.ext === ex ? 'background:rgba(0,103,192,0.1);' : 'background:var(--pk-hl);'}">${ex}</span>`).join('');

            document.body.appendChild(pop);

            const updatePosition = () => {
                if (!pop.isConnected) return;
                const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
                const rect = getLogicalRect(UI.filterExtsWrap);
                let popLeft = rect.left;
                if (popLeft + 340 > window.innerWidth / scale) popLeft = (window.innerWidth / scale) - 350;
                pop.style.top = (rect.bottom + 5) + 'px';
                pop.style.left = popLeft + 'px';
            };
            updatePosition();
            window.addEventListener('resize', updatePosition);

            const cleanup = () => {
                window.removeEventListener('resize', updatePosition);
                document.removeEventListener('mousedown', closer);
                pop.remove();
            };

            pop.querySelectorAll('.pk-f-ext').forEach(span => {
                 span.onclick = (ev) => {
                     ev.stopPropagation();
                     S.filterState.ext = span.dataset.ext;
                     cleanup();
                     renderActiveFilterUI();
                     S.sel.clear();
                     refresh();
                 };
            });

            const closer = (ev) => {
                if (!pop.contains(ev.target) && !UI.filterExtsMoreBtn.contains(ev.target)) {
                    cleanup();
                }
            };
            setTimeout(() => document.addEventListener('mousedown', closer), 10);
        };
    }

    if (UI.filterExitBtn) {
        UI.filterExitBtn.onclick = () => {
            S.filterState = { active: false, cat: 'all', ext: 'all' };
            UI.filterBtn.style.display = 'flex';
            UI.filterActiveUI.style.display = 'none';
            S.sel.clear();
            refresh();
        };
    }

    const getHistory = () => {
        try { return JSON.parse(gmGet('pk_search_history', '[]')); } catch { return []; }
    };
    const saveHistory = (txt) => {
        if (!txt) return;
        let list = getHistory();
        list = list.filter(x => x !== txt);
        list.unshift(txt);
        if (list.length > 3) list = list.slice(0, 3);
        gmSet('pk_search_history', JSON.stringify(list));
    };
    const renderHistory = () => {
        const list = getHistory();
        if (list.length === 0) {
            UI.searchHist.style.display = 'none';
            return;
        }

        let html = `<div class="pk-hist-hd"><span>${L.title_search_hist}</span><span class="pk-hist-clear-btn" id="pk-hist-del" style="cursor:pointer; opacity:0.8;">${L.btn_clear_hist}</span></div>`;

        list.forEach(txt => {
            html += `<div class="pk-select-item" style="display:flex; align-items:center; gap:10px; padding:8px 12px;">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="flex-shrink:0; opacity:0.5;">
                    <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
                </svg>
                <span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1;">${esc(txt)}</span>
            </div>`;
        });
        UI.searchHist.innerHTML = html;

        UI.searchHist.style.display = 'flex';

        UI.searchHist.querySelector('#pk-hist-del').onclick = (e) => {
            e.stopPropagation();
            gmSet('pk_search_history', '[]');
            UI.searchHist.style.display = 'none';
        };
        UI.searchHist.querySelectorAll('.pk-select-item').forEach(el => {
            el.onclick = (e) => {
                const val = el.querySelector('span').textContent;
                UI.searchInput.value = val;
                performSearch(val);
                UI.searchHist.style.display = 'none';
            };
        });
    };
    const performSearch = (val) => {
        const txt = val.trim();
        if (!txt) {
            if (S.search && UI.searchClear) UI.searchClear.click();
            return;
        }
        const isGlobal = UI.chkGlobal && UI.chkGlobal.checked && !S.uploadMode;

        if (txt) {
            saveHistory(txt);
            if (isGlobal && !S.preSearchPath) {
                S.preSearchPath = [...S.path];
            }

            if (isGlobal) {
                S.sort = 'modified_time'; S.dir = 1;

                S.path = [
                    { id: '', name: L.btn_nav_home },
                    { id: 'virtual_search_root', name: L.str_search_results }
                ];
                renderCrumb();
            }
        }

        S.search = txt;

        if (S.dupMode) {
            if (S.pinnedDupPath) {
                S.pinnedDupPath = null;
                S.clearSelection();
                UI.selDupFolder.value = "";

                const invertChk = document.getElementById('pk-dup-invert');
                if(invertChk) {
                    invertChk.checked = false;
                    invertChk.disabled = true;
                    invertChk.parentNode.style.opacity = '0.5';
                }
            }

            renderDupView();
            updateStat();
        }
        else if (isGlobal) {
            if (globalNeedsSync) {
                setLoad(true);
                updateLoadTxt(L.str_analyzing);

                setTimeout(async () => {
                    if (!S.scanning) {
                        S.scanning = true;
                        UI.stopBtn.onclick = () => {
                            S.scanning = false;
                            updateLoadTxt(L.str_stopping);
                            if (UI.chkGlobal) UI.chkGlobal.checked = false;
                        };
                        try {
                            await runFlattenScanOperation(true, [], true);
                            globalNeedsSync = false;
                        } catch (e) {
                            console.error("[GlobalSearch] Sync Error:", e);
                        } finally {
                            S.scanning = false;
                        }
                    }
                    load(false, true).finally(() => setLoad(false));
                }, 50);
            } else {
                load(false, false);
            }
        } else {
            refresh();
        }

        UI.searchClear.style.display = txt ? 'flex' : 'none';
        UI.searchHist.style.display = 'none';
        UI.searchInput.blur();
    };

    if (UI.searchInput) {

        UI.searchInput.oninput = (e) => {
            const val = e.target.value.trim();
            UI.searchClear.style.display = val ? 'flex' : 'none';
            if (!val && S.search && UI.searchClear) {
                UI.searchClear.click();
            }
        };

        const searchHist = UI.searchHist;
        const searchWrap = UI.searchInput.closest('.pk-search') || UI.searchInput.parentNode;
        let searchHistRaf = 0;
        let searchHistOpen = false;

        if (searchHist) {
            searchHist.style.display = 'none';
            searchHist.style.visibility = '';
            searchHist.style.pointerEvents = '';
        }

        const isSearchHistOpen = () => {
            return !!searchHist && searchHistOpen && getHistory()?.length > 0;
        };

        const requestPlaceSearchHist = () => {
            if (!isSearchHistOpen()) return;
            if (searchHistRaf) cancelAnimationFrame(searchHistRaf);
            searchHistRaf = requestAnimationFrame(() => {
                searchHistRaf = requestAnimationFrame(() => {
                    searchHistRaf = 0;
                    if (isSearchHistOpen()) placeSearchHist();
                });
            });
        };

        const placeSearchHist = () => {
            if (!searchHist || !searchWrap) return;
            if (!searchHist._pkOriginParent) searchHist._pkOriginParent = searchWrap;

            if (searchHist.parentNode !== document.body) {
                document.body.appendChild(searchHist);
            }

            searchHist.classList.toggle('pk-dark', el.classList.contains('pk-dark'));

            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const wrapRect = typeof getLogicalRect === 'function' ? getLogicalRect(searchWrap) : searchWrap.getBoundingClientRect();
            const pad = 8;

            searchHist.dataset.pkPortal = '1';
            searchHist.style.position = 'fixed';
            searchHist.style.left = '0';
            searchHist.style.top = '0';
            searchHist.style.right = 'auto';
            searchHist.style.bottom = 'auto';
            searchHist.style.marginTop = '0';
            searchHist.style.zIndex = '10080';
            searchHist.style.zoom = scale;
            searchHist.style.transformOrigin = 'top left';
            searchHist.style.width = Math.max(Math.round(wrapRect.width), 220) + 'px';
            searchHist.style.display = 'flex';
            searchHist.style.visibility = 'hidden';
            searchHist.style.pointerEvents = 'none';

            const histRect = searchHist.getBoundingClientRect();
            const popW = histRect.width / scale;
            const popH = histRect.height / scale;
            const winW = window.innerWidth / scale;
            const winH = window.innerHeight / scale;

            let left = wrapRect.left;
            let top = wrapRect.bottom + 6;

            if (left + popW > winW - pad) left = winW - pad - popW;
            if (left < pad) left = pad;

            if (top + popH > winH - pad) {
                top = wrapRect.top - popH - 6;
            }
            if (top < pad) top = pad;

            searchHist.style.left = Math.round(left) + 'px';
            searchHist.style.top = Math.round(top) + 'px';
            searchHist.style.visibility = '';
            searchHist.style.pointerEvents = '';
        };

        const closeSearchHist = () => {
            if (!searchHist) return;
            searchHistOpen = false;
            if (searchHistRaf) cancelAnimationFrame(searchHistRaf);
            searchHistRaf = 0;
            searchHist.style.display = 'none';
            searchHist.style.visibility = '';
            searchHist.style.pointerEvents = '';
        };

        UI.searchInput.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && isSearchHistOpen()) {
                e.preventDefault();
                e.stopPropagation();
                closeSearchHist();
            }
        });

        UI.searchInput.onfocus = () => {
            renderHistory();
            if (getHistory()?.length > 0) {
                searchHistOpen = true;
                searchHist.style.display = 'flex';
                requestPlaceSearchHist();
            } else {
                closeSearchHist();
            }
        };

        document.addEventListener('mousedown', (e) => {
            if (!UI || !UI.searchInput || !searchHist) return;
            if (!searchHist.contains(e.target) && !searchWrap.contains(e.target)) {
                closeSearchHist();
            }
        }, true);

        if (!window.__pkSearchHistPortalBound) {
            window.addEventListener('resize', () => {
                if (isSearchHistOpen()) requestPlaceSearchHist();
            }, { passive: true });

            document.addEventListener('scroll', () => {
                if (isSearchHistOpen()) requestPlaceSearchHist();
            }, true);

            if (window.visualViewport) {
                window.visualViewport.addEventListener('resize', () => {
                    if (isSearchHistOpen()) requestPlaceSearchHist();
                }, { passive: true });
                window.visualViewport.addEventListener('scroll', () => {
                    if (isSearchHistOpen()) requestPlaceSearchHist();
                }, { passive: true });
            }

            window.__pkSearchHistPortalBound = true;
        }

        if (window.ResizeObserver && !searchWrap.__pkSearchHistRO) {
            searchWrap.__pkSearchHistRO = new ResizeObserver(() => {
                if (!searchHistOpen) return;
                if (isSearchHistOpen()) requestPlaceSearchHist();
            });
            searchWrap.__pkSearchHistRO.observe(searchWrap);
        }

        UI.searchInput.onkeydown = (e) => {
            e.stopPropagation();
            if (e.key === 'Enter') {
                performSearch(e.target.value);
            }
        };

        if (UI.searchBtn) {
            UI.searchBtn.onclick = () => {
                performSearch(UI.searchInput.value);
            };
        }

        if (UI.searchClear) {
            UI.searchClear.onclick = async () => {
                const wasGlobalChecked = UI.chkGlobal ? UI.chkGlobal.checked : false;

                if (!S.search && UI.searchInput.value) {
                    UI.searchInput.value = '';
                    UI.searchClear.style.display = 'none';
                    UI.searchInput.focus();
                    return;
                }

                const searchWasActive = !!S.search;
                const isInVirtualStack = S.path.some(node => node.id === 'virtual_search_root');
                let requiresReload = false;

                UI.searchInput.value = '';
                S.search = '';
                S.lastGlobalResults = [];

                try {
                    const prefStore = JSON.parse(gmGet('pk_folder_sort_prefs', '{}'));
                    if (prefStore['virtual_search_root']) {
                        delete prefStore['virtual_search_root'];
                        gmSet('pk_folder_sort_prefs', JSON.stringify(prefStore));
                    }
                } catch(e) {}

                if ((wasGlobalChecked || isInVirtualStack) && searchWasActive) {
                    const currentFolder = S.path[S.path.length - 1];

                    if (currentFolder.id !== 'virtual_search_root' && currentFolder.id !== '') {
                        const traceStack = [];
                        let ptrId = currentFolder.id;
                        let ptrName = currentFolder.name;
                        let safety = 100;

                        traceStack.unshift({ id: ptrId, name: ptrName });

                        while (ptrId && ptrId !== 'root' && safety > 0) {
                            if (globalParentIndex.has(ptrId)) {
                                const parent = globalParentIndex.get(ptrId);
                                ptrId = parent.id;
                                ptrName = parent.name;

                                if (ptrId === 'root' || ptrId === '') break;

                                traceStack.unshift({ id: ptrId, name: ptrName });
                            } else {
                                const node = S.itemMap.get(ptrId);
                                if (node && node._lineage && node._lineage.length > 0) {
                                    const ancestors = node._lineage.filter(x => x.id !== '' && x.id !== 'root');
                                    for (let k = ancestors.length - 1; k >= 0; k--) {
                                        traceStack.unshift(ancestors[k]);
                                    }
                                }
                                break;
                            }
                            safety--;
                        }

                        const realPath = [{ id: '', name: L.btn_nav_home }, ...traceStack];
                        S.path = realPath;
                    }
                    else if (isInVirtualStack || S.preSearchPath) {
                        S.path = S.preSearchPath ? [...S.preSearchPath] : [{ id: '', name: L.btn_nav_home }];
                    }

                    requiresReload = true;
                }

                S.preSearchPath = null;
                UI.searchClear.style.display = 'none';
                UI.searchHist.style.display = 'none';

                if (S.dupMode && !isInVirtualStack) {
                    if (S.pinnedDupPath) {
                        S.pinnedDupPath = null;
                        S.clearSelection();
                        UI.selDupFolder.value = "";

                        const invertChk = document.getElementById('pk-dup-invert');
                        if(invertChk) {
                            invertChk.checked = false;
                            invertChk.disabled = true;
                            invertChk.parentNode.style.opacity = '0.5';
                        }
                    }

                    renderDupView();
                    updateStat();
                } else if (S.isFlattened && !isInVirtualStack) {
                    refresh();
                    updateStat();
                } else {
                    if (requiresReload) {
                        const targetNode = S.path[S.path.length - 1];
                        const targetKey = S.getRealCacheKey(targetNode.id);
                        const cachedData = (typeof globalCache !== 'undefined') ? (globalCache.get(targetKey) || globalCache.get('')) : null;

                        if (cachedData) {
                            if (cachedData.items) S.items = [...cachedData.items];
                            else if (Array.isArray(cachedData)) S.items = [...cachedData];
                            S.itemMap.clear();
                            for (const it of S.items) S.itemMap.set(it.id, it);
                        }

                        refresh();
                        setLoad(true);

                        const p = load(false, true);

                        if (UI.chkGlobal) UI.chkGlobal.checked = wasGlobalChecked;

                        await p;
                    } else {
                        refresh();
                        updateStat();
                    }
                }
            };
        }
    }

    UI.btnHelp.onclick = () => {
        const m = showModal(`
            <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg); flex-shrink:0;">${L.modal_help_title}</h3>
            <div style="position:relative; flex:1; min-height:0; display:flex; flex-direction:column;">
                ${L.help_desc}
                <div id="pk_help_fade" style="position:absolute; bottom:0; left:0; right:0; height:40px; background:linear-gradient(to bottom, transparent, var(--pk-bg)); pointer-events:none; transition:opacity 0.2s;"></div>
            </div>
            <div class="pk-modal-act" style="margin-top:20px; flex-shrink:0;">
                <button class="pk-btn pri" id="help_close" style="width:100%; height:44px; justify-content:center; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; border:none;">${L.btn_close}</button>
            </div>
        `);

        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) {
            Object.assign(modalBox.style, {
                display: 'flex', flexDirection: 'column',
                maxHeight: '85vh', overflow: 'hidden', padding: '30px'
            });
            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
        }

        const scrollEl = m.querySelector('.pk-help-scroll');
        const fadeEl = m.querySelector('#pk_help_fade');

        if (scrollEl) {
            scrollEl.style.maxHeight = 'none';
            scrollEl.style.flex = '1';
            scrollEl.style.minHeight = '0';
        }

        if (scrollEl && fadeEl) {
            scrollEl.style.paddingBottom = "24px";

            const updateFade = () => {
                if (scrollEl.scrollHeight <= scrollEl.clientHeight + 5 || Math.ceil(scrollEl.scrollTop + scrollEl.clientHeight) >= scrollEl.scrollHeight - 30) {
                    fadeEl.style.opacity = '0';
                } else {
                    fadeEl.style.opacity = '1';
                }
            };

            scrollEl.addEventListener('scroll', updateFade, { passive: true });

            const resizeObserver = new ResizeObserver(() => updateFade());
            resizeObserver.observe(scrollEl);

            const _orgRemove = m.remove.bind(m);
            m.remove = () => {
                resizeObserver.disconnect();
                _orgRemove();
            };

            setTimeout(updateFade, 50);
        }

        m.querySelector('#help_close').onclick = () => m.remove();

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                m.remove();
            }
        });
    };

    UI.chkGlobal.onchange = async (e) => {
        if (e.target.checked) {
            if (S.movingIds && S.movingIds.size > 0) {
                e.target.checked = false;
                showAlert(L.msg_global_index_blocked_moving);
                return;
            }

            const isSuppressed = gmGet('pk_suppress_global_warn', false);

            if (!isSuppressed && !hasShownGlobalWarnSession) {
                const userChoice = await new Promise((resolve) => {
                    const m = showModal(`
                        <h3 style="border:none; margin-bottom:16px; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_confirm}</h3>
                        <div style="margin-bottom:25px; line-height:1.6; font-size:14px; color:var(--pk-fg); opacity:0.9;">
                            ${esc(L.msg_global_warn).replace(/\n/g, '<br>')}
                        </div>
                        <div style="margin-bottom:25px; display:flex; justify-content:flex-end;">
                            <label style="display:flex; align-items:center; cursor:pointer; font-size:13px; color:#888; user-select:none;">
                                <input type="checkbox" id="pk_warn_ignore" style="margin-right:8px; width:16px; height:16px; accent-color:var(--pk-pri);">
                                <span>${L.lbl_dont_show}</span>
                            </label>
                        </div>
                        <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px;">
                            <button class="pk-btn" id="cfm_cancel" style="height:40px; min-width:86px; border-radius:8px; justify-content:center; background:transparent; font-weight:500;">${L.btn_cancel}</button>
                            <button class="pk-btn pri" id="cfm_ok" style="height:40px; min-width:86px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center; border:none;">${L.btn_ok}</button>
                        </div>
                    `);

                    const modalBox = m.querySelector('.pk-modal');
                    if (modalBox) {
                        Object.assign(modalBox.style, { width: '420px', padding: '30px', height: 'auto', minHeight: 'auto' });
                        const closeBtn = m.querySelector('.pk-modal-close');
                        if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
                    }

                    m.querySelector('#cfm_cancel').onclick = () => { m.remove(); resolve({ ok: false }); };
                    m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve({ ok: false }); };

                    m.tabIndex = 0;
                    setTimeout(() => m.focus(), 10);
                    m.addEventListener('keydown', (e) => {
                        if (e.key === 'Enter') {
                            e.preventDefault(); e.stopPropagation();
                            m.querySelector('#cfm_ok').click();
                        }
                    });

                    m.querySelector('#cfm_ok').onclick = () => {
                        const isChecked = m.querySelector('#pk_warn_ignore').checked;
                        m.remove();
                        resolve({ ok: true, suppress: isChecked });
                    };
                });

                if (!userChoice.ok) {
                    e.target.checked = false;
                    return;
                }

                hasShownGlobalWarnSession = true;
                if (userChoice.suppress) {
                    gmSet('pk_suppress_global_warn', true);
                }
            }

            if (!isGlobalIndexReady || globalNeedsSync) {
                S.scanning = true;

                UI.stopBtn.onclick = () => {
                    S.scanning = false;
                    updateLoadTxt(L.str_stopping);
                    UI.chkGlobal.checked = false;
                };

                await runFlattenScanOperation(true);
            }
            refresh();
            if (S._flattenEnterTopRaf) cancelAnimationFrame(S._flattenEnterTopRaf);
            if (S.isFlattened && isGridView()) {
                S._flattenEnterTopRaf = requestAnimationFrame(() => {
                    S._flattenEnterTopRaf = 0;
                    if (UI.vp && UI.vp.scrollTop !== 0) UI.vp.scrollTop = 0;
                });
            }

        } else {
            if (S.scanning) {
                S.scanning = false;
                updateLoadTxt(L.str_stopping);
            }
            refresh();
        }
    };

    const runFlattenScanOperation = async (isSyncOnly = false, specificTargets =[], isSilent = false) => {
        S.scanId = (S.scanId || 0) + 1;
        const myScanId = S.scanId;

        let fileMap = new Map();
        let processedFolders = 0;

        if (S.scanAbortController) S.scanAbortController.abort();
        S.scanAbortController = new AbortController();
        const signal = S.scanAbortController.signal;
        setLoad(true);

        const isPartialScan = specificTargets && specificTargets.length > 0;

        let rootNodes =[];
        if (isPartialScan) {
            updateLoadTxt(L.msg_init_scan_sel);
            specificTargets.forEach(item => {
                if (item.kind === 'drive#folder') {
                    rootNodes.push({
                        id: item.id,
                        name: item.name,
                        lineage:[{ id: item.id, name: item.name }],
                        retryCount: 0
                    });
                } else if (!isSyncOnly) {
                    item._lineage =[];
                    fileMap.set(item.id, item);
                }
            });
        } else {
            updateLoadTxt(`${L.str_scanning} 0`);
            const startNode = isSyncOnly ? { id: '', name: 'Root' } : S.path[S.path.length - 1];
            rootNodes =[{ id: startNode.id || '', name: startNode.name || 'Root', lineage: [], retryCount: 0 }];
        }

        UI.stopBtn.onclick = () => {
            S.scanning = false;
            if (S.scanAbortController) S.scanAbortController.abort();
            updateLoadTxt(L.str_stopping);

            if (S.isFlattened) {
                UI.scan.style.display = 'none';
                UI.btnExit.style.display = 'flex';
                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
                if (UI.btnExport) UI.btnExport.style.display = 'none';
            } else {
                UI.scan.style.display = 'flex';
                UI.btnExit.style.display = 'none';
                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                if (UI.btnExport) UI.btnExport.style.display = 'flex';

                if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';
                if(UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                if(UI.chkGlobal) UI.chkGlobal.checked = false;

                S.isFlattened = false;

                setTimeout(() => {
                    if (typeof resumeBackgroundDiscovery === 'function') {
                        console.log("♻️ Scan interrupted: Forcing background crawler to resume.");
                        resumeBackgroundDiscovery();
                    }
                }, 1000);
            }
        };

        S.scanning = true;

        try {
            await coreRecursiveEngine(rootNodes, {
                signal: signal,
                onFile: (f, parent) => {
                    if (!isSyncOnly) {
                        f._lineage = parent.lineage ||[];
                        fileMap.set(f.id, f);
                    }
                },
                onFolder: (folder, filesInFolder) => {
                    processedFolders++;
                    indexParents(folder.id, folder.name, filesInFolder);

                    if (typeof globalLineageMap !== 'undefined') {
                        globalLineageMap.set(folder.id, folder.lineage);
                    }
                },
                onProgress: (st) => {
                    const folderText = isPartialScan
                        ? L.status_scanning_selection.replace('{n}', st.folders + " " + L.unit_folders)
                        : `${L.str_scanning} ${st.folders} ${L.unit_folders}`;

                    const retryTag = st.isRetrying ? `\n[ ${L.str_retries} ]` : "";
                    const statusInfo = ` | ${L.str_files}: ${st.files} | ${L.str_speed}: ${st.currentConcurrency} | ${L.str_cached} ${st.cacheHits} ${L.unit_folders}`;

                    updateLoadTxt(folderText + statusInfo + retryTag);
                }
            });

            if (S.scanning && !signal.aborted && myScanId === S.scanId) {
                const didBuildFullGlobalIndex = isSyncOnly && !isPartialScan;
                if (didBuildFullGlobalIndex) {
                    globalNeedsSync = false;
                    isGlobalIndexReady = true;
                }
                if (!isSyncOnly) {
                    updateLoadTxt(L.str_merging);

                    let tempItems = Array.from(fileMap.values());

                    if (S.scanFilter && !isSyncOnly) {
                        const { minBytes, maxBytes, keyword } = S.scanFilter;
                        const kwList = keyword ? keyword.toLowerCase().split(/[,,]/).map(k => k.trim()).filter(k => k) : [];

                        tempItems = tempItems.filter(item => {
                            if (kwList.length > 0) {
                                const fullLowerName = (item.name || "").toLowerCase();
                                const lastDot = fullLowerName.lastIndexOf('.');
                                const nameWithoutExt = (item.kind !== 'drive#folder' && lastDot > 0)
                                    ? fullLowerName.substring(0, lastDot)
                                    : fullLowerName;

                                if (kwList.some(k => nameWithoutExt.includes(k))) return false;
                            }
                            const sz = parseInt(item.size || 0);
                            if (sz < minBytes) return false;
                            if (maxBytes > 0 && sz > maxBytes) return false;
                            return true;
                        });
                    }

                    const total = tempItems.length;

                    if (!isSyncOnly && !isSilent && total === 0) {
                        setLoad(false);
                        S.scanning = false;
                        showToast(L.msg_no_files);

                        UI.scan.style.display = 'flex';
                        UI.btnExit.style.display = 'none';
                        if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                        if (UI.btnExport) UI.btnExport.style.display = 'flex';
                        if (UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';
                        if (UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                        if (UI.chkGlobal) UI.chkGlobal.checked = false;
                        return;
                    }

                    S.items = new Array(total);
                    S.itemMap.clear();

                    let lastYield = performance.now();
                    for (let i = 0; i < total; i++) {
                        const item = tempItems[i];
                        S.items[i] = item;
                        S.itemMap.set(item.id, item);

                        if (item.starred || (item.tags && item.tags.some(t => t.name === 'STAR'))) {
                            S.starredSet.add(item.id);
                        }

                        if (i % 5000 === 0 && performance.now() - lastYield > 16) {
                            updateLoadTxt(`${L.str_merging} ${Math.round((i / total) * 100)}%`);
                            await sleep(0);
                            lastYield = performance.now();
                        }
                    }

                    rememberFolderFirstBeforeStrictMode();

                    S.isFlattened = true;
                    S.sort = 'modified_time'; S.dir = 1;
                    UI.chkAll.checked = false;
                    S.clearSelection();

                    UI.scan.style.display = 'none';
                    UI.btnExit.style.display = 'flex';
                    if (UI.btnNewFolder) UI.btnNewFolder.style.display = 'none';
                    if (UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';
                    if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
                    if (UI.crumb) UI.crumb.style.setProperty('display', 'none', 'important');

                    updateLoadTxt(L.str_rendering);
                    await refresh();
                    if (S._flattenEnterTopRaf) cancelAnimationFrame(S._flattenEnterTopRaf);
                    if (S.isFlattened && isGridView()) {
                        S._flattenEnterTopRaf = requestAnimationFrame(() => {
                            S._flattenEnterTopRaf = 0;
                            if (UI.vp && UI.vp.scrollTop !== 0) UI.vp.scrollTop = 0;
                        });
                    }

                    const msg = L.msg_scan_done.replace('{n}', total).replace('{f}', processedFolders);
                    if (!isSilent) showToast(msg.replace(/\n+/g, ' '), 'success', 3200);
                }
            }

        } catch (e) {
            if (e.name !== 'AbortError' && myScanId === S.scanId) {
                showAlert(`${L.str_error_crit}: ${e.message}`);
            }
            if (!isSyncOnly && myScanId === S.scanId) {
                UI.scan.style.display = 'flex';
                UI.btnExit.style.display = 'none';
                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                if (UI.btnExport) UI.btnExport.style.display = 'flex';
                UI.lblGlobal.style.display = 'flex';
            }
            if (myScanId === S.scanId) UI.chkGlobal.checked = false;
        } finally {
            if (myScanId === S.scanId) {
                setLoad(false);
                S.scanning = false;
                S.scanAbortController = null;
                if (typeof DurationProber !== 'undefined') DurationProber.checkAndRun();
            }
        }
    };

    const openScanDupModal = async (initialTab) => {
        if (S.loading || S.scanning) return;

        const curFolderId = S.path[S.path.length - 1].id || '';
        if (isPathBusy(curFolderId)) {
            showAlert(L.msg_flatten_blocked_moving);
            return;
        }

        S.wasGlobalChecked = UI.chkGlobal ? UI.chkGlobal.checked : false;

        const selectedTargets = S.getSelectedIds()
        .map(id => S.itemMap.get(id))
        .filter(Boolean);

        const lastMin = gmGet('pk_scan_last_min', 0);
        const lastMax = gmGet('pk_scan_last_max', '');
        const lastUnit = gmGet('pk_scan_last_unit', 'MB');
        const lastKeyword = gmGet('pk_scan_last_keyword', '');
        let currentStrict = gmGet('pk_dup_strictness', 'strict');

        const scanTxt = L.btn_scan;
        const dupTxt = L.tip_dup;
        const L_min = L.lbl_ana_min;
        const L_max = L.lbl_ana_max;

        let scanTargetDesc = selectedTargets.length > 0 ? L.lbl_scan_selected.replace('{n}', selectedTargets.length) : L.lbl_scan_current;
        let dupTargetDesc = selectedTargets.length > 0 ? L.lbl_dup_selected.replace('{n}', selectedTargets.length) : L.lbl_dup_current;

        const m = showModal(`
            <div class="pk-share-modal-root" style="width:480px; max-width:90vw; display:flex; flex-direction:column; overflow:visible;">
                <div style="padding: 30px 30px 15px 30px; flex-shrink:0;">
                    <h3 style="margin: 0; font-size: 18px; font-weight: 700; border: none; line-height: 1.2; color: var(--pk-fg);">${L.title_file_analysis}</h3>
                </div>

                <div class="pk-s-tabs" style="margin: 0 30px 20px 30px; display:flex;">
                    <div class="pk-s-tab ${initialTab === 'scan' ? 'act' : ''}" data-val="scan">${scanTxt}</div>
                    <div class="pk-s-tab ${initialTab === 'dup' ? 'act' : ''}" data-val="dup">${dupTxt}</div>
                </div>

                <div id="pane_scan" style="display:${initialTab === 'scan' ? 'block' : 'none'}; padding: 0 30px;">
                    <div style="font-size:13px; color:#888; margin-bottom:20px; line-height:1.5;">${scanTargetDesc}</div>

                    <div style="margin-bottom:20px; position:relative;">
                        <input type="text" id="sc_keyword" value="${esc(lastKeyword)}" placeholder="${L.ph_keyword_filter}"
                               oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                               style="width:100%; height:42px; padding:0 12px; border:2px solid ${lastKeyword ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; outline:none; transition:border-color 0.2s; box-sizing:border-box;">
                        <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L.lbl_keyword_filter}</div>
                    </div>

                    <div style="display:flex; align-items:center; gap:10px; margin-bottom:25px;">
                        <div style="flex:1; position:relative;">
                            <input type="number" id="sc_val_min" value="${lastMin === 0 ? '' : lastMin}" placeholder="0" min="0" step="1"
                                   style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace;">
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L_min}</div>
                            <div class="pk-num-ctrl">
                                <div class="pk-num-btn" id="sc_inc_min">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                <div class="pk-num-btn" id="sc_dec_min">${CONF.crumbIcons.down}</div>
                            </div>
                        </div>
                        <div style="color:#888; font-weight:bold; flex-shrink:0;">-</div>
                        <div style="flex:1; position:relative;">
                            <input type="number" id="sc_val_max" value="${lastMax}" min="0" step="1" placeholder="∞"
                                   style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace;">
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L_max}</div>
                            <div class="pk-num-ctrl">
                                <div class="pk-num-btn" id="sc_inc_max">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                <div class="pk-num-btn" id="sc_dec_max">${CONF.crumbIcons.down}</div>
                            </div>
                        </div>
                        <div class="pk-ana-select">
                            <div class="pk-ana-trigger" id="sc_unit_btn">
                                <span id="sc_unit_txt">${lastUnit}</span>
                                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
                            </div>
                            <div class="pk-ana-menu" id="sc_unit_menu">
                                <div class="pk-ana-item ${lastUnit === 'MB' ? 'act' : ''}" data-v="MB">MB</div>
                                <div class="pk-ana-item ${lastUnit === 'GB' ? 'act' : ''}" data-v="GB">GB</div>
                                <div class="pk-ana-item ${lastUnit === 'TB' ? 'act' : ''}" data-v="TB">TB</div>
                            </div>
                        </div>
                    </div>
                </div>

                <div id="pane_dup" class="pk-scroll" style="display:${initialTab === 'dup' ? 'flex' : 'none'}; flex-direction:column; gap:16px; padding: 0 30px 25px 30px;">
                    <div style="font-size:13px; color:#888; margin-bottom:4px; line-height:1.5;">${dupTargetDesc}</div>
                    <div style="position:relative;">
                        <label for="scan_video"
                               onmouseover="this.style.borderColor='var(--pk-pri)'"
                               onmouseout="this.style.borderColor='var(--pk-bd)'"
                               style="display:flex; align-items:center; height:54px; border:2px solid var(--pk-bd); border-radius:10px; padding:0 15px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                            <input type="checkbox" id="scan_video" checked style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer; margin-right:12px;">
                            <span style="font-size:14px; color:var(--pk-fg); font-weight:600; user-select:none;">${L.label_dup_video}</span>
                        </label>
                    </div>

                    <div style="position:relative;">
                        <label for="scan_image"
                               onmouseover="this.style.borderColor='var(--pk-pri)'"
                               onmouseout="this.style.borderColor='var(--pk-bd)'"
                               style="display:flex; align-items:center; height:54px; border:2px solid var(--pk-bd); border-radius:10px; padding:0 15px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                            <input type="checkbox" id="scan_image" checked style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer; margin-right:12px;">
                            <span style="font-size:14px; color:var(--pk-fg); font-weight:600; user-select:none;">${L.label_dup_image}</span>
                        </label>
                    </div>
                    <div style="position:relative;">
                        <label for="scan_other"
                               onmouseover="this.style.borderColor='var(--pk-pri)'"
                               onmouseout="this.style.borderColor='var(--pk-bd)'"
                               style="display:flex; align-items:center; height:54px; border:2px solid var(--pk-bd); border-radius:10px; padding:0 15px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                            <input type="checkbox" id="scan_other" checked style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer; margin-right:12px;">
                            <span style="font-size:14px; color:var(--pk-fg); font-weight:600; user-select:none;">${L.label_dup_other}</span>
                        </label>
                    </div>
                    <div class="pk-custom-select" id="cs_sc_strict" style="margin-top:5px;">
                        <div class="pk-select-label">${L.label_dup_strictness}</div>
                        <div class="pk-select-trigger"><span id="txt_sc_strict">${currentStrict === 'loose' ? L.opt_loose : L.opt_strict}</span>${CONF.crumbIcons.down}</div>
                        <div class="pk-select-menu pk-scroll">
                            <div class="pk-select-item ${currentStrict === 'strict' ? 'act' : ''}" data-val="strict">${L.opt_strict}</div>
                            <div class="pk-select-item ${currentStrict === 'loose' ? 'act' : ''}" data-val="loose">${L.opt_loose}</div>
                        </div>
                    </div>
                </div>

                <div style="padding: 20px 30px 30px 30px; flex-shrink:0;">
                    <div class="pk-modal-act" style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 0;">
                        <button class="pk-btn" id="sc_cancel" style="height:46px; border-radius:12px; justify-content:center; background:transparent; font-weight:600; font-size:15px;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="sc_start" style="height:46px; border-radius:12px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center; border:none; font-size:15px; transition: filter 0.2s;">${L.btn_ok}</button>
                    </div>
                </div>
            </div>
        `);

        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) {
            Object.assign(modalBox.style, { width: 'auto', padding: '0', overflow: 'visible', height: 'auto', minHeight: 'auto' });
            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
        }

        m.querySelectorAll('.pk-s-tab').forEach(tab => {
            tab.onclick = () => {
                m.querySelectorAll('.pk-s-tab').forEach(t => t.classList.remove('act'));
                tab.classList.add('act');
                const curMode = tab.dataset.val;
                m.querySelector('#pane_scan').style.display = curMode === 'scan' ? 'block' : 'none';
                m.querySelector('#pane_dup').style.display = curMode === 'dup' ? 'flex' : 'none';
            };
        });

        const inpMin = m.querySelector('#sc_val_min');
        const inpMax = m.querySelector('#sc_val_max');
        const unitBtn = m.querySelector('#sc_unit_btn');
        const unitMenu = m.querySelector('#sc_unit_menu');
        const unitTxt = m.querySelector('#sc_unit_txt');
        let currentUnit = lastUnit;

        m.querySelector('#sc_inc_min').onclick = (e) => { e.stopPropagation(); inpMin.value = (parseInt(inpMin.value) || 0) + 1; };
        m.querySelector('#sc_dec_min').onclick = (e) => { e.stopPropagation(); inpMin.value = Math.max(0, (parseInt(inpMin.value) || 1) - 1); };
        m.querySelector('#sc_inc_max').onclick = (e) => { e.stopPropagation(); inpMax.value = (parseInt(inpMax.value) || 0) + 1; };
        m.querySelector('#sc_dec_max').onclick = (e) => { e.stopPropagation(); inpMax.value = Math.max(0, (parseInt(inpMax.value) || 1) - 1); };

        unitBtn.onclick = (e) => { e.stopPropagation(); unitMenu.style.display = unitMenu.style.display === 'block' ? 'none' : 'block'; };
        m.querySelectorAll('.pk-ana-item').forEach(item => {
            item.onclick = () => {
                m.querySelectorAll('.pk-ana-item').forEach(i => i.classList.remove('act'));
                item.classList.add('act');
                currentUnit = item.dataset.v;
                unitTxt.textContent = currentUnit;
                unitMenu.style.display = 'none';
            };
        });
        const closeMenu = () => { if (unitMenu) unitMenu.style.display = 'none'; };
        setTimeout(() => document.addEventListener('click', closeMenu), 0);

        const _orgRemove = m.remove.bind(m);
        m.remove = () => {
            document.removeEventListener('click', closeMenu);
            _orgRemove();
        };

        if (S.dupConfig) {
            m.querySelector('#scan_video').checked = S.dupConfig.video;
            m.querySelector('#scan_image').checked = S.dupConfig.image;
            m.querySelector('#scan_other').checked = S.dupConfig.other;
        }

        const scStrictTrigger = m.querySelector('#cs_sc_strict .pk-select-trigger');
        const scStrictMenu = m.querySelector('#cs_sc_strict .pk-select-menu');
        const scStrictTxt = m.querySelector('#txt_sc_strict');

        scStrictTrigger.onclick = (e) => {
            e.stopPropagation();
            scStrictMenu.style.display = scStrictMenu.style.display === 'block' ? 'none' : 'block';
        };

        m.querySelectorAll('#cs_sc_strict .pk-select-item').forEach(item => {
            item.onclick = (e) => {
                e.stopPropagation();
                m.querySelectorAll('#cs_sc_strict .pk-select-item').forEach(i => i.classList.remove('act'));
                item.classList.add('act');
                currentStrict = item.dataset.val;
                scStrictTxt.textContent = item.textContent;
                scStrictMenu.style.display = 'none';
            };
        });

        const saveScanInputs = () => {
            gmSet('pk_scan_last_min', parseInt(inpMin.value) || 0);
            gmSet('pk_scan_last_max', inpMax.value.trim());
            gmSet('pk_scan_last_unit', currentUnit);
            gmSet('pk_scan_last_keyword', m.querySelector('#sc_keyword').value.trim());
            gmSet('pk_dup_strictness', currentStrict);
        };

        m.querySelector('#sc_cancel').onclick = () => { saveScanInputs(); m.remove(); };
        m.querySelector('.pk-modal-close').onclick = () => { saveScanInputs(); m.remove(); };

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                m.querySelector('#sc_start').click();
            }
        });

        m.querySelector('#sc_start').onclick = async () => {
            const mode = m.querySelector('.pk-s-tab.act').dataset.val;

            saveScanInputs();

            if (mode === 'scan') {
                const vMin = parseInt(inpMin.value) || 0;
                const vMax = parseInt(inpMax.value) || 0;
                const kw = m.querySelector('#sc_keyword').value.trim();

                if (vMin < 0 || (vMax > 0 && vMin > vMax)) {
                    inpMin.style.borderColor = '#d93025';
                    if (vMax > 0 && vMin > vMax) inpMax.style.borderColor = '#d93025';
                    return;
                }
                gmSet('pk_scan_last_min', vMin);
                gmSet('pk_scan_last_max', vMax > 0 ? vMax : '');
                gmSet('pk_scan_last_unit', currentUnit);
                gmSet('pk_scan_last_keyword', kw);

                let mult = 1;
                if (currentUnit === 'MB') mult = 1024 * 1024;
                else if (currentUnit === 'GB') mult = 1024 * 1024 * 1024;
                else if (currentUnit === 'TB') mult = 1024 * 1024 * 1024 * 1024;
                S.scanFilter = {
                    minBytes: Math.floor(vMin * mult),
                    maxBytes: vMax > 0 ? Math.floor(vMax * mult) : 0,
                    keyword: kw
                };

                m.remove();

                S.search = '';
                if (UI.searchInput) UI.searchInput.value = '';
                if (UI.searchClear) UI.searchClear.style.display = 'none';
                if (UI.chkSearchPath) UI.chkSearchPath.checked = false;

                S.scanning = true;

                UI.scan.style.display = 'none';
                UI.btnExit.style.display = 'flex';

                if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';
                if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
                if (UI.chkGlobal) UI.chkGlobal.checked = false;

                UI.stopBtn.onclick = () => {
                    S.scanning = false;
                    if (S.scanAbortController) S.scanAbortController.abort();
                    updateLoadTxt(L.str_stopping);

                    if (S.isFlattened) {
                        UI.scan.style.display = 'none';
                        UI.btnExit.style.display = 'flex';
                        if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
                        if (UI.btnExport) UI.btnExport.style.display = 'none';
                    } else {
                        UI.scan.style.display = 'flex';
                        UI.btnExit.style.display = 'none';
                        if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                        if (UI.btnExport) UI.btnExport.style.display = 'flex';

                        if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';
                        if (UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                        if (UI.chkGlobal) UI.chkGlobal.checked = false;

                        S.isFlattened = false;

                        setTimeout(() => {
                            if (typeof resumeBackgroundDiscovery === 'function') {
                                resumeBackgroundDiscovery();
                            }
                        }, 1000);
                    }
                };

                S.lastScanTargets = selectedTargets;
                await runFlattenScanOperation(false, selectedTargets, false);

            } else {
                S.dupConfig = {
                    video: m.querySelector('#scan_video').checked,
                    image: m.querySelector('#scan_image').checked,
                    other: m.querySelector('#scan_other').checked
                };

                if (!S.dupConfig.video && !S.dupConfig.image && !S.dupConfig.other) return;

                m.remove();

                S.scanning = true;
                S.scanId = (S.scanId || 0) + 1;
                const myScanId = S.scanId;

                let fileMap = new Map();
                let processedFolders = 0;

                if (S.scanAbortController) S.scanAbortController.abort();
                S.scanAbortController = new AbortController();
                const signal = S.scanAbortController.signal;
                setLoad(true);

                const isPartialScan = selectedTargets.length > 0;
                let rootNodes =[];
                if (isPartialScan) {
                    updateLoadTxt(L.msg_init_scan_sel);
                    selectedTargets.forEach(item => {
                        if (item.kind === 'drive#folder') {
                            rootNodes.push({
                                id: item.id,
                                name: item.name,
                                lineage:[{ id: item.id, name: item.name }],
                                retryCount: 0
                            });
                        } else {
                            item._lineage =[];
                            fileMap.set(item.id, item);
                        }
                    });
                } else {
                    updateLoadTxt(`${L.str_scanning} 0`);
                    const startNode = S.path[S.path.length - 1];
                    rootNodes =[{ id: startNode.id || '', name: startNode.name || 'Root', lineage: [], retryCount: 0 }];
                }

                UI.stopBtn.onclick = () => {
                    S.scanning = false;
                    if (S.scanAbortController) S.scanAbortController.abort();
                    updateLoadTxt(L.str_stopping);
                    setLoad(false);
                };

                try {
                    await coreRecursiveEngine(rootNodes, {
                        signal: signal,
                        onFile: (f, parent) => {
                            f._lineage = parent.lineage ||[];
                            fileMap.set(f.id, f);
                        },
                        onFolder: (folder, filesInFolder) => {
                            processedFolders++;
                            if (typeof globalCache !== 'undefined' && !globalCache.has(folder.id)) {
                                globalCache.set(folder.id, [...filesInFolder]);
                            }
                            indexParents(folder.id, folder.name, filesInFolder);
                            if (typeof globalLineageMap !== 'undefined') {
                                globalLineageMap.set(folder.id, folder.lineage);
                            }
                        },
                        onProgress: (st) => {
                            const folderText = isPartialScan
                                ? L.status_scanning_selection.replace('{n}', st.folders + " " + L.unit_folders)
                                : `${L.str_scanning} ${st.folders} ${L.unit_folders}`;
                            const retryTag = st.isRetrying ? `\n[ ${L.str_retries} ]` : "";
                            const statusInfo = ` | ${L.str_files}: ${st.files} | ${L.str_speed}: ${st.currentConcurrency} | ${L.str_cached} ${st.cacheHits} ${L.unit_folders}`;
                            updateLoadTxt(folderText + statusInfo + retryTag);
                        }
                    });

                    if (S.scanning && !signal.aborted && myScanId === S.scanId) {
                        updateLoadTxt(L.str_merging);

                        const tempItems = Array.from(fileMap.values());

                        const total = tempItems.length;

                        S.items = new Array(total);
                        S.itemMap.clear();

                        let lastYield = performance.now();
                        for (let i = 0; i < total; i++) {
                            const item = tempItems[i];
                            S.items[i] = item;
                            S.itemMap.set(item.id, item);

                            if (item.starred || (item.tags && item.tags.some(t => t.name === 'STAR'))) {
                                S.starredSet.add(item.id);
                            }

                            if (i % 5000 === 0 && performance.now() - lastYield > 16) {
                                updateLoadTxt(`${L.str_merging} ${Math.round((i / total) * 100)}%`);
                                await sleep(0);
                                lastYield = performance.now();
                            }
                        }

                        rememberFolderFirstBeforeStrictMode();

                        S.dupMode = true;
                        S.groupSortOp = 'path';
                        S.groupSortDir = 1;
                        S.isFlattened = false;
                        S.sort = 'modified_time'; S.dir = 1;
                        UI.chkAll.checked = false;
                        S.clearSelection();

                        S.search = '';
                        S.lastGlobalResults = [];
                        if (UI.searchInput) UI.searchInput.value = '';
                        if (UI.searchClear) UI.searchClear.style.display = 'none';
                        if (UI.searchHist) UI.searchHist.style.display = 'none';

                        UI.scan.style.display = 'none';
                        UI.btnExit.style.display = 'flex';
                        if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
                        UI.lblGlobal.style.display = 'none';
                        UI.chkGlobal.checked = false;
                        if (UI.btnNewFolder) UI.btnNewFolder.style.display = 'none';
                        if (UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';
                        if (UI.crumb) UI.crumb.style.setProperty('display', 'none', 'important');
                        if (UI.lblSearchPath) UI.lblSearchPath.style.display = 'flex';

                        updateLoadTxt(L.str_rendering);
                        S.display = [...S.items];
                        await refresh();
                        if (S._enterTopRaf) cancelAnimationFrame(S._enterTopRaf);
                        S._enterTopRaf = requestAnimationFrame(() => {
                            S._enterTopRaf = 0;
                            if (UI.vp && UI.vp.scrollTop !== 0) UI.vp.scrollTop = 0;
                        });
                    }

                } catch (e) {
                    if (e.name !== 'AbortError' && myScanId === S.scanId) {
                        showAlert(`${L.str_error_crit}: ${e.message}`);
                    }
                    if (myScanId === S.scanId) {
                        UI.scan.style.display = 'flex';
                        UI.btnExit.style.display = 'none';
                        if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                        if (UI.btnExport) UI.btnExport.style.display = 'flex';
                        UI.lblGlobal.style.display = 'flex';
                    }
                } finally {
                    if (myScanId === S.scanId) {
                        setLoad(false);
                        S.scanning = false;
                        S.scanAbortController = null;
                        if (typeof DurationProber !== 'undefined') DurationProber.checkAndRun();
                    }
                }
            }
        };
    };

    UI.scan.onclick = () => openScanDupModal('scan');

    const onOfflineFilterChange = () => {
        if (S.offlineMode) {
            S.offlineFilters = {
                running: UI.chkOffRun.checked,
                failed: UI.chkOffFail.checked,
                complete: UI.chkOffOk.checked
            };
            refresh();
            updateStat();
        }
    };
    if (UI.chkOffRun) UI.chkOffRun.onchange = onOfflineFilterChange;
    if (UI.chkOffFail) UI.chkOffFail.onchange = onOfflineFilterChange;
    if (UI.chkOffOk) UI.chkOffOk.onchange = onOfflineFilterChange;

    const onUploadFilterChange = () => {
        if (S.uploadMode) {
            S.uploadFilters = {
                running: UI.chkUpRun.checked,
                paused: UI.chkUpPause.checked,
                complete: UI.chkUpDone.checked
            };
            refresh();
            updateStat();
        }
    };
    if (UI.chkUpRun) UI.chkUpRun.onchange = onUploadFilterChange;
    if (UI.chkUpPause) UI.chkUpPause.onchange = onUploadFilterChange;
    if (UI.chkUpDone) UI.chkUpDone.onchange = onUploadFilterChange;

    const onDupFilterChange = () => {
        if(S.dupMode) {
            if (S.pinnedDupPath) {
                S.pinnedDupPath = null;
                S.clearSelection();
                UI.selDupFolder.value = "";

                const invertChk = document.getElementById('pk-dup-invert');
                if(invertChk) {
                    invertChk.checked = false;
                    invertChk.disabled = true;
                    invertChk.parentNode.style.opacity = '0.5';
                }
            }
            renderDupView();
        }
    };
    UI.chkName.onchange = onDupFilterChange;
    UI.chkSim.onchange = onDupFilterChange;
    UI.chkHash.onchange = onDupFilterChange;
    if (UI.chkSearchPath) {
        UI.chkSearchPath.onchange = () => {
            if (S.dupMode && S.search) {
                renderDupView();
            } else if ((S.isFlattened || S.analyzeMode) && S.search) {
                refresh();
            }
        };
    }

    UI.btnExit.onclick = async () => {
        await S.exitVirtualNavMode();

        if (typeof S.wasGlobalChecked !== 'undefined' && UI.chkGlobal) {
            UI.chkGlobal.checked = S.wasGlobalChecked;
        }

        setTimeout(() => {
            if (typeof resumeBackgroundDiscovery === 'function') {
                console.log("♻️ Sandbox exited: Forcing global discovery to resume.");
                resumeBackgroundDiscovery();
            }
        }, 1500);
    };

    UI.cols.forEach(c => c.onclick = () => {
        if (S.dupMode) return;

        const k = c.dataset.k;

        if (S.sort === k) {
            S.dir *= -1;
        }
        else {
            S.sort = k;
            S.dir = 1;
        }

        refresh();
    });

    if (UI.btnFolderFirst) {
        S.renderFolderFirst = () => {
            UI.btnFolderFirst.style.color = S.folderFirst ? 'var(--pk-pri)' : '#666';
        };

        UI.btnFolderFirst.onmouseenter = () => {
            if (!S.folderFirst) UI.btnFolderFirst.style.color = 'var(--pk-fg)';
        };
        UI.btnFolderFirst.onmouseleave = () => {
            if (!S.folderFirst) UI.btnFolderFirst.style.color = '#666';
        };

        const nameWrap = el.querySelector('#pk-name-text-wrap');
        if (nameWrap) {
            nameWrap.onmouseenter = () => {
                if (S.sort !== 'name') nameWrap.style.color = 'var(--pk-fg)';
            };
            nameWrap.onmouseleave = () => {
                if (S.sort !== 'name') nameWrap.style.color = '#666';
            };
        }

        S.renderFolderFirst();

        UI.btnFolderFirst.onclick = (e) => {
            e.stopPropagation();
            S.folderFirst = !S.folderFirst;

            gmSet('pk_folder_first', S.folderFirst);

            try {
                const globalPref = JSON.parse(gmGet('pk_global_sort_pref', '{"sort":"modified_time","dir":1}'));
                globalPref.sort = S.sort;
                globalPref.dir = S.dir;
                globalPref.folderFirst = S.folderFirst;
                gmSet('pk_global_sort_pref', JSON.stringify(globalPref));
            } catch(e) {
                gmSet('pk_global_sort_pref', JSON.stringify({ sort: S.sort, dir: S.dir, folderFirst: S.folderFirst }));
            }

            if (S.renderFolderFirst) S.renderFolderFirst();
            refresh();
        };
    }

    const btnInvert = document.getElementById('pk-btn-invert');
    if (btnInvert) {
        btnInvert.onclick = (e) => {
            e.stopPropagation();
            if (S.loading || S.display.length === 0) return;

            S.invertSelection();
            renderVisible();
            updateStat();
        };
        btnInvert.onmouseenter = () => btnInvert.style.color = 'var(--pk-pri)';
        btnInvert.onmouseleave = () => btnInvert.style.color = 'var(--pk-fg)';
    }

    S.handleSelectAll = (e) => {
        if (!e || !e.target) {
            if (UI.chkAll) UI.chkAll.checked = !UI.chkAll.checked;
        }

        S.activeId = null;
        S.lastSelIdx = -1;

        const totalVisible = S.getSelectableCount();
        const selectedCount = S.getSelectedCount();
        const isSelectAllAction = selectedCount < totalVisible;

        if (UI.chkAll) {
            UI.chkAll.checked = isSelectAllAction;
            UI.chkAll.indeterminate = false;
        }

        if (isSelectAllAction) {
            S.setAllSelection(true);
        } else {
            S.setAllSelection(false);
        }

        requestAnimationFrame(() => {
            renderVisible();
            updateStat();

            if (UI.chkAll) {
                UI.chkAll.checked = isSelectAllAction;
                UI.chkAll.indeterminate = false;
            }
        });
    };

    if (UI.btnAnaSelect || UI.btnAnaSort || UI.btnDupSmart || UI.btnDupSort) {
        const pop = document.createElement('div');
        pop.className = 'pk-ana-pop';
        pop.innerHTML = `
            <div class="pk-ana-pop-row"><div class="pk-ana-opt" data-op="new">${L.opt_keep_new}</div><div class="pk-ana-opt" data-op="old">${L.opt_keep_old}</div></div>
            <div class="pk-ana-pop-row"><div class="pk-ana-opt" data-op="large">${L.opt_keep_large}</div><div class="pk-ana-opt" data-op="small">${L.opt_keep_small}</div></div>
            <div class="pk-ana-pop-row"><div class="pk-ana-opt" data-op="short">${L.opt_keep_short}</div><div class="pk-ana-opt" data-op="long">${L.opt_keep_long}</div></div>`;

        const sortPop = document.createElement('div');
        sortPop.className = 'pk-ana-pop';
        sortPop.innerHTML = `
            <div class="pk-ana-pop-row"><div class="pk-ana-opt pk-sort-opt" data-op="time">${L.opt_sort_time}</div><div class="pk-ana-opt pk-sort-opt" data-op="size">${L.opt_sort_size}</div></div>
            <div class="pk-ana-pop-row"><div class="pk-ana-opt pk-sort-opt" data-op="path">${L.opt_sort_path}</div><div class="pk-ana-opt pk-sort-opt" data-op="name">${L.opt_sort_name}</div></div>`;

        UI.win.appendChild(pop);
        UI.win.appendChild(sortPop);
        let activeTargetBtn = null;
        let activePop = null;

        const updatePopPos = () => {
            if (!activePop || activePop.style.display !== 'flex' || !activeTargetBtn) return;
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const winRect = getLogicalRect(UI.win);
            const btnRect = getLogicalRect(activeTargetBtn);

            let left = btnRect.left - winRect.left;
            const top = btnRect.bottom - winRect.top;
            const winWidth = winRect.width;
            const popWidth = activePop === sortPop ? 200 : 340;

            if (left + popWidth > winWidth - 10) {
                left = btnRect.right - winRect.left - popWidth;
            }

            activePop.style.left = Math.max(10, left) + 'px';
            activePop.style.top = (top + 5) + 'px';
        };

        const togglePop = (e, btn, targetPop) => {
            e.stopPropagation();
            const isVisible = (targetPop.style.display === 'flex' && activeTargetBtn === btn);

            if (activePop && activePop !== targetPop) activePop.style.display = 'none';

            if (isVisible) {
                targetPop.style.display = 'none';
                activeTargetBtn = null;
                activePop = null;
            } else {
                if (targetPop === pop && S.analyzeMode && !S.hasShownAnaWarn) {
                    showToast(L.msg_ana_warn, 'warning', 6000);
                    S.hasShownAnaWarn = true;
                }
                if (targetPop === sortPop) {
                    if (!S.groupSortOp) { S.groupSortOp = 'path'; S.groupSortDir = 1; }
                    sortPop.querySelectorAll('.pk-sort-opt').forEach(el => {
                        let baseTxt = "";
                        if (el.dataset.op === 'time') baseTxt = L.opt_sort_time;
                        if (el.dataset.op === 'size') baseTxt = L.opt_sort_size;
                        if (el.dataset.op === 'path') baseTxt = L.opt_sort_path;
                        if (el.dataset.op === 'name') baseTxt = L.opt_sort_name;

                        if (el.dataset.op === S.groupSortOp) {
                            el.textContent = baseTxt + (S.groupSortDir === 1 ? ' ▼' : ' ▲');
                            el.style.color = 'var(--pk-pri)';
                        } else {
                            el.textContent = baseTxt;
                            el.style.color = '';
                        }
                    });
                }
                targetPop.style.display = 'flex';
                activeTargetBtn = btn;
                activePop = targetPop;
                updatePopPos();
            }
        };

        if (UI.btnAnaSelect) UI.btnAnaSelect.onclick = (e) => togglePop(e, UI.btnAnaSelect, pop);
        if (UI.btnAnaSort) UI.btnAnaSort.onclick = (e) => togglePop(e, UI.btnAnaSort, sortPop);
        if (UI.btnDupSmart) UI.btnDupSmart.onclick = (e) => togglePop(e, UI.btnDupSmart, pop);
        if (UI.btnDupSort) UI.btnDupSort.onclick = (e) => togglePop(e, UI.btnDupSort, sortPop);

        window.addEventListener('resize', updatePopPos);

        pop.querySelectorAll('.pk-ana-opt').forEach(opt => {
            opt.onclick = () => {
                const op = opt.dataset.op;
                const nextSel = [];

                const isBetter = (type, curW, curM) => {
                    if (type === 'new') return new Date(curM.modified_time) > new Date(curW.modified_time);
                    if (type === 'old') return new Date(curM.modified_time) < new Date(curW.modified_time);
                    if (type === 'large') return BigInt(curM.size || 0) > BigInt(curW.size || 0);
                    if (type === 'small') return BigInt(curM.size || 0) < BigInt(curW.size || 0);
                    if (type === 'short') return curM.name.length < curW.name.length;
                    if (type === 'long') return curM.name.length > curW.name.length;
                    return false;
                };

                if (S.analyzeMode && S.analyzeSimGroups) {
                    S.analyzeSimGroups.forEach(g => {
                        const members = g.ids.map(id => S.itemMap.get(id)).filter(Boolean);
                        if (members.length < 2) return;
                        let winner = members[0];
                        members.forEach(m => { if (isBetter(op, winner, m)) winner = m; });
                        members.forEach(m => { if (m.id !== winner.id) nextSel.push(m.id); });
                    });
                } else if (S.dupMode && S.dupGroups) {
                    const itemMap = new Map();
                    S.display.forEach(d => {
                        if (d.isHeader) return;
                        const gIdx = S.dupGroups.get(d.id);
                        if (gIdx !== undefined) {
                            if (!itemMap.has(gIdx)) itemMap.set(gIdx, []);
                            itemMap.get(gIdx).push(d);
                        }
                    });
                    itemMap.forEach(members => {
                        if (members.length < 2) return;
                        let winner = members[0];
                        members.forEach(m => { if (isBetter(op, winner, m)) winner = m; });
                        members.forEach(m => { if (m.id !== winner.id) nextSel.push(m.id); });
                    });
                }

                S.setExplicitSelection(nextSel);
                pop.style.display = 'none';
                activeTargetBtn = null;
                activePop = null;
                renderVisible();
                updateStat();
            };
        });

        sortPop.querySelectorAll('.pk-sort-opt').forEach(opt => {
            opt.onclick = (e) => {
                e.stopPropagation();
                const op = opt.dataset.op;

                if (S.groupSortOp === op) {
                    S.groupSortDir *= -1;
                } else {
                    S.groupSortOp = op;
                    S.groupSortDir = 1;
                }

                sortPop.querySelectorAll('.pk-sort-opt').forEach(el => {
                    let baseTxt = "";
                    if (el.dataset.op === 'time') baseTxt = L.opt_sort_time;
                    if (el.dataset.op === 'size') baseTxt = L.opt_sort_size;
                    if (el.dataset.op === 'path') baseTxt = L.opt_sort_path;
                    if (el.dataset.op === 'name') baseTxt = L.opt_sort_name;

                    if (el.dataset.op === S.groupSortOp) {
                        el.textContent = baseTxt + (S.groupSortDir === 1 ? ' ▼' : ' ▲');
                        el.style.color = 'var(--pk-pri)';
                    } else {
                        el.textContent = baseTxt;
                        el.style.color = '';
                    }
                });

                const cmp = (a, b) => {
                    let res = 0;
                    if (S.groupSortOp === 'time') {
                        res = new Date(b.modified_time) - new Date(a.modified_time);
                    } else if (S.groupSortOp === 'size') {
                        const sa = BigInt(a.size || 0), sb = BigInt(b.size || 0);
                        res = sa < sb ? 1 : (sa > sb ? -1 : 0);
                    } else if (S.groupSortOp === 'path') {
                        const pa = a._dupFullPath || a._pathStr || a.path || "";
                        const pb = b._dupFullPath || b._pathStr || b.path || "";
                        res = pa.localeCompare(pb);
                    } else if (S.groupSortOp === 'name') {
                        res = a.name.localeCompare(b.name);
                    }
                    return res * S.groupSortDir;
                };

                const newDisplay = [];
                let currentGroupHeader = null;
                let currentGroupMembers = [];

                const flushGroup = () => {
                    if (currentGroupHeader) newDisplay.push(currentGroupHeader);
                    if (currentGroupMembers.length > 0) {
                        const getDupPath = it => it._dupFullPath || it._pathStr || it.path || "";
                        let orderedMembers = currentGroupMembers;

                        if (S.pinnedDupPath) {
                            const pinnedMembers = [];
                            const otherMembers = [];

                            currentGroupMembers.forEach(it => {
                                if (getDupPath(it) === S.pinnedDupPath) pinnedMembers.push(it);
                                else otherMembers.push(it);
                            });

                            if (pinnedMembers.length > 0) {
                                pinnedMembers.sort(cmp);
                                otherMembers.sort(cmp);
                                orderedMembers = pinnedMembers.concat(otherMembers);
                            } else {
                                orderedMembers = currentGroupMembers.slice().sort(cmp);
                            }
                        } else {
                            orderedMembers = currentGroupMembers.slice().sort(cmp);
                        }

                        let lastPath = null;
                        orderedMembers.forEach((it, idx) => {
                            const curPath = getDupPath(it);
                            if (idx > 0 && curPath === lastPath && curPath !== "") {
                                it._isSameFolder = true;
                            } else {
                                it._isSameFolder = false;
                            }
                            lastPath = curPath;
                        });

                        newDisplay.push(...orderedMembers);
                    }
                    currentGroupHeader = null;
                    currentGroupMembers = [];
                };

                S.display.forEach(d => {
                    if (d.isHeader) {
                        flushGroup();
                        currentGroupHeader = d;
                    } else {
                        currentGroupMembers.push(d);
                    }
                });
                flushGroup();

                S.display = newDisplay;
                renderVisible();
            };
        });

        document.addEventListener('mousedown', (e) => {
            const isClickInsideBtn = (UI.btnAnaSelect && UI.btnAnaSelect.contains(e.target)) ||
                  (UI.btnAnaSort && UI.btnAnaSort.contains(e.target)) ||
                  (UI.btnDupSmart && UI.btnDupSmart.contains(e.target)) ||
                  (UI.btnDupSort && UI.btnDupSort.contains(e.target));

            if (!pop.contains(e.target) && !sortPop.contains(e.target) && !isClickInsideBtn) {
                pop.style.display = 'none';
                sortPop.style.display = 'none';
                activeTargetBtn = null;
                activePop = null;
            }
        });
    }

    UI.btnRefresh.onclick = async () => {
        updateQuotaUI();
        if (S.isFlattened) {
            if (!S.scanning) {
                S.scanning = true;
                UI.scan.style.display = 'none';
                UI.btnExit.style.display = 'flex';

                UI.stopBtn.onclick = () => {
                    S.scanning = false;
                    updateLoadTxt(L.str_stopping);
                    UI.scan.style.display = 'none';
                    UI.btnExit.style.display = 'flex';
                    if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
                    if (UI.btnExport) UI.btnExport.style.display = 'none';
                    if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';
                    if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
                };

                runFlattenScanOperation(false, S.lastScanTargets, true).catch(e => {
                    console.error(e);
                    S.scanning = false;
                });
            }
            return;
        }

        const cur = S.path[S.path.length - 1];
        const intent = UI.chkGlobal ? UI.chkGlobal.checked : false;

        if (cur.id === 'analyze_root') {
            setLoad(true);
            updateLoadTxt(L.str_refreshing);
            await sleep(200);
            await load();
            return;
        }

        if (cur.id) S.cache.delete(cur.id);

        const p = load(false, true);

        if (UI.chkGlobal) UI.chkGlobal.checked = intent;
        await p;
    };

    if (UI.btnAnalyze) {
        UI.btnAnalyze.onclick = async () => {
            if (S.trashMode) return;

            const curFolderId = S.path[S.path.length - 1].id || '';
            if (isPathBusy(curFolderId)) {
                showAlert(L.msg_op_blocked_analyzing);
                return;
            }

            S.search = '';
            if (UI.searchInput) UI.searchInput.value = '';
            if (UI.searchClear) UI.searchClear.style.display = 'none';

            S.wasGlobalChecked = UI.chkGlobal ? UI.chkGlobal.checked : false;

            const lastMin = gmGet('pk_analyze_last_min', 0);
            const lastMax = gmGet('pk_analyze_last_max', '');
            const lastUnit = gmGet('pk_analyze_last_unit', 'GB');
            const lastKeyword = gmGet('pk_analyze_last_keyword', '');
            const lastSim = gmGet('pk_analyze_last_sim', 1.0);
            const lastAlgo = gmGet('pk_analyze_last_algo', 'sim');

            const result = await new Promise((resolve) => {
                const L_min = L.lbl_ana_min;
                const L_max = L.lbl_ana_max;
                const selectedCount = S.getSelectedCount();
                const analyzeTargetDesc = selectedCount > 0 ? L.lbl_analyze_selected.replace('{n}', selectedCount) : L.lbl_analyze_current;
                const anaSimTargetDesc = selectedCount > 0 ? L.lbl_ana_sim_selected.replace('{n}', selectedCount) : L.lbl_ana_sim_current;
                const m = showModal(`
                    <h3 style="border:none; margin-bottom:15px; font-size:18px; font-weight:700;">${L.btn_analyze}</h3>

                    <div class="pk-s-tabs" id="ana_tabs" style="margin-bottom:20px; display:flex;">
                        <div class="pk-s-tab act" data-val="large">${L.opt_ana_large}</div>
                        <div class="pk-s-tab" data-val="similar">${L.opt_ana_sim}</div>
                    </div>

                    <div id="ana_pane_large">
                        <div style="font-size:13px; color:#888; margin-bottom:20px; line-height:1.5;">${analyzeTargetDesc}</div>

                        <div style="margin-bottom:20px; position:relative;">
                            <input type="text" id="an_keyword" value="${esc(lastKeyword)}" placeholder="${L.ph_keyword_filter}"
                                   oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                                   style="width:100%; height:42px; padding:0 12px; border:2px solid ${lastKeyword ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; outline:none; transition:border-color 0.2s; box-sizing:border-box;">
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L.lbl_keyword_filter}</div>
                        </div>

                        <div style="display:flex; align-items:center; gap:10px; margin-bottom:25px;">
                            <div style="flex:1; position:relative;">
                                <input type="number" id="an_val_min" value="${lastMin === 0 ? '' : lastMin}" placeholder="0" min="0" step="1"
                                       style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace;">
                                <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L_min}</div>
                                <div class="pk-num-ctrl">
                                    <div class="pk-num-btn" id="an_inc_min">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                    <div class="pk-num-btn" id="an_dec_min">${CONF.crumbIcons.down}</div>
                                </div>
                            </div>
                            <div style="color:#888; font-weight:bold; flex-shrink:0;">-</div>
                            <div style="flex:1; position:relative;">
                                <input type="number" id="an_val_max" value="${lastMax}" min="0" step="1" placeholder="∞"
                                       style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace;">
                                <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; line-height:1; white-space:nowrap;">${L_max}</div>
                                <div class="pk-num-ctrl">
                                    <div class="pk-num-btn" id="an_inc_max">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                    <div class="pk-num-btn" id="an_dec_max">${CONF.crumbIcons.down}</div>
                                </div>
                            </div>
                            <div class="pk-ana-select">
                                <div class="pk-ana-trigger" id="an_unit_btn">
                                    <span id="an_unit_txt">${lastUnit}</span>
                                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
                                </div>
                                <div class="pk-ana-menu" id="an_unit_menu">
                                    <div class="pk-ana-item ${lastUnit === 'MB' ? 'act' : ''}" data-v="MB">MB</div>
                                    <div class="pk-ana-item ${lastUnit === 'GB' ? 'act' : ''}" data-v="GB">GB</div>
                                    <div class="pk-ana-item ${lastUnit === 'TB' ? 'act' : ''}" data-v="TB">TB</div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div id="ana_pane_similar" style="display:none;">
                        <div style="font-size:13px; color:#888; margin-bottom:15px; line-height:1.5;">${anaSimTargetDesc}</div>
                        <div style="display:flex; flex-wrap:wrap; gap:12px; margin-bottom:20px; align-items:center; width:100%;">
                            <label style="display:flex; align-items:center; cursor:pointer; flex-shrink:0;">
                                <input type="radio" name="ana_sim_algo" value="name" ${lastAlgo === 'name' ? 'checked' : ''} style="accent-color:var(--pk-pri); margin-right:6px;">
                                <span style="font-size:13px; color:var(--pk-fg); font-weight:500;">${L.lbl_name_match}</span>
                            </label>
                            <label style="display:flex; align-items:center; cursor:pointer; flex-shrink:0;">
                                <input type="radio" name="ana_sim_algo" value="sim" ${lastAlgo === 'sim' || !lastAlgo ? 'checked' : ''} style="accent-color:var(--pk-pri); margin-right:6px;">
                                <span style="font-size:13px; color:var(--pk-fg); font-weight:500;">${L.lbl_sim_match}</span>
                            </label>
                            <label style="display:flex; align-items:center; cursor:pointer; flex-shrink:0;">
                                <input type="radio" name="ana_sim_algo" value="contain" ${lastAlgo === 'contain' ? 'checked' : ''} style="accent-color:var(--pk-pri); margin-right:6px;">
                                <span style="font-size:13px; color:var(--pk-fg); font-weight:500;">${L.lbl_contain_match}</span>
                            </label>
                            <div id="pk_algo_help" style="display:flex; align-items:center; cursor:pointer; color:#888; transition:color 0.2s; flex-shrink:0; width:20px; height:20px; justify-content:center;" data-pk-tip="${L.title_algo_help}" onmouseover="this.style.color=document.querySelector('.pk-ov').classList.contains('pk-dark')?'#ddd':'#666'" onmouseout="this.style.color='#888'">
                                <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; pointer-events:none;"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
                            </div>
                        </div>
                        <div class="pk-custom-select" id="cs_ana_sim" style="margin-bottom:25px;">
                            <div class="pk-select-label">${L.lbl_threshold}</div>
                            <div class="pk-select-trigger"><span id="txt_ana_sim">${(lastSim === 0.01) ? L.opt_loose : L.opt_strict}</span>${CONF.crumbIcons.down}</div>
                            <div class="pk-select-menu pk-scroll">
                                <div class="pk-select-item ${lastSim === 0.01 ? 'act' : ''}" data-val="0.01">${L.opt_loose}</div>
                                <div class="pk-select-item ${lastSim !== 0.01 ? 'act' : ''}" data-val="1.0">${L.opt_strict}</div>
                            </div>
                        </div>
                    </div>

                    <div class="pk-modal-act">
                        <button class="pk-btn" id="an_cancel" style="height:40px; padding:0 20px;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="an_confirm" style="height:40px; padding:0 30px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold;">${L.btn_ok}</button>
                    </div>
                `);

                let currentMode = 'large';
                let currentSim = lastSim;

                const updateAlgoLabel = () => {
                    const lblEl = m.querySelector('#cs_ana_sim .pk-select-label');
                    if (lblEl) lblEl.textContent = L.lbl_threshold;
                };
                m.querySelectorAll('input[name="ana_sim_algo"]').forEach(r => r.addEventListener('change', updateAlgoLabel));
                updateAlgoLabel();

                m.querySelector('#pk_algo_help').onclick = (e) => {
                    e.stopPropagation();
                    showAlert(L.algo_help_content, L.title_algo_help);
                };

                m.querySelectorAll('.pk-s-tab').forEach(tab => {
                    tab.onclick = () => {
                        m.querySelectorAll('.pk-s-tab').forEach(t => t.classList.remove('act'));
                        tab.classList.add('act');
                        currentMode = tab.dataset.val;
                        m.querySelector('#ana_pane_large').style.display = currentMode === 'large' ? 'block' : 'none';
                        m.querySelector('#ana_pane_similar').style.display = currentMode === 'similar' ? 'block' : 'none';
                    };
                });

                const simTrigger = m.querySelector('#cs_ana_sim .pk-select-trigger');
                const simMenu = m.querySelector('#cs_ana_sim .pk-select-menu');
                const simTxt = m.querySelector('#txt_ana_sim');
                simTrigger.onclick = (e) => { e.stopPropagation(); simMenu.style.display = simMenu.style.display === 'block' ? 'none' : 'block'; };
                m.querySelectorAll('#cs_ana_sim .pk-select-item').forEach(item => {
                    item.onclick = (e) => {
                        e.stopPropagation();
                        m.querySelectorAll('#cs_ana_sim .pk-select-item').forEach(i => i.classList.remove('act'));
                        item.classList.add('act');
                        currentSim = parseFloat(item.dataset.val);
                        gmSet('pk_analyze_last_sim', currentSim);
                        simTxt.textContent = (currentSim <= 0.5) ? L.opt_loose : L.opt_strict;
                        simMenu.style.display = 'none';
                    };
                });

                const inpMin = m.querySelector('#an_val_min');
                const inpMax = m.querySelector('#an_val_max');
                const btn = m.querySelector('#an_unit_btn');

                m.querySelector('#an_inc_min').onclick = (e) => { e.stopPropagation(); inpMin.value = (parseInt(inpMin.value) || 0) + 1; inpMin.dispatchEvent(new Event('input')); };
                m.querySelector('#an_dec_min').onclick = (e) => { e.stopPropagation(); inpMin.value = Math.max(0, (parseInt(inpMin.value) || 1) - 1); inpMin.dispatchEvent(new Event('input')); };
                m.querySelector('#an_inc_max').onclick = (e) => { e.stopPropagation(); inpMax.value = (parseInt(inpMax.value) || 0) + 1; inpMax.dispatchEvent(new Event('input')); };
                m.querySelector('#an_dec_max').onclick = (e) => { e.stopPropagation(); inpMax.value = Math.max(0, (parseInt(inpMax.value) || 1) - 1); inpMax.dispatchEvent(new Event('input')); };
                const menu = m.querySelector('#an_unit_menu');
                const txt = m.querySelector('#an_unit_txt');
                let currentUnit = lastUnit;

                btn.onclick = (e) => { e.stopPropagation(); menu.style.display = menu.style.display === 'block' ? 'none' : 'block'; };
                m.querySelectorAll('.pk-ana-item').forEach(item => {
                    item.onclick = () => {
                        m.querySelectorAll('.pk-ana-item').forEach(i => i.classList.remove('act'));
                        item.classList.add('act');
                        currentUnit = item.dataset.v;
                        txt.textContent = currentUnit;
                        menu.style.display = 'none';
                    };
                });

                const closeMenu = () => { if(menu) menu.style.display = 'none'; if(simMenu) simMenu.style.display = 'none'; };
                setTimeout(() => document.addEventListener('click', closeMenu), 0);

                const _orgRemove = m.remove.bind(m);
                m.remove = () => {
                    document.removeEventListener('click', closeMenu);
                    _orgRemove();
                };

                setTimeout(() => m.focus(), 50);

                const kHandler = (e) => {
                    if (e.key === 'Enter') m.querySelector('#an_confirm').click();
                    if (e.key === 'Escape') m.querySelector('#an_cancel').click();
                };
                inpMin.onkeydown = kHandler;
                inpMax.onkeydown = kHandler;

                const saveAnalyzeInputs = () => {
                    gmSet('pk_analyze_last_min', parseInt(inpMin.value) || 0);
                    gmSet('pk_analyze_last_max', inpMax.value.trim());
                    gmSet('pk_analyze_last_unit', currentUnit);
                    gmSet('pk_analyze_last_keyword', m.querySelector('#an_keyword').value.trim());
                    const algo = m.querySelector('input[name="ana_sim_algo"]:checked')?.value;
                    if (algo) gmSet('pk_analyze_last_algo', algo);
                };

                m.querySelector('#an_cancel').onclick = () => { saveAnalyzeInputs(); m.remove(); resolve(null); };
                m.querySelector('.pk-modal-close').onclick = () => { saveAnalyzeInputs(); m.remove(); resolve(null); };
                m.querySelector('#an_confirm').onclick = () => {
                    if (currentMode === 'large') {
                        const vMin = parseInt(inpMin.value) || 0;
                        const vMax = parseInt(inpMax.value) || 0;
                        const kw = m.querySelector('#an_keyword').value.trim();
                        if (vMin < 0 || (vMax > 0 && vMin > vMax)) {
                            inpMin.style.borderColor = '#d93025';
                            if (vMax > 0 && vMin > vMax) inpMax.style.borderColor = '#d93025';
                            return;
                        }
                        saveAnalyzeInputs();
                        let mult = 1;
                        if (currentUnit === 'MB') mult = 1024 * 1024;
                        else if (currentUnit === 'GB') mult = 1024 * 1024 * 1024;
                        else if (currentUnit === 'TB') mult = 1024 * 1024 * 1024 * 1024;
                        m.remove();
                        resolve({ mode: 'large', minBytes: Math.floor(vMin * mult), maxBytes: vMax > 0 ? Math.floor(vMax * mult) : 0, keyword: kw });
                    } else {
                        const algo = m.querySelector('input[name="ana_sim_algo"]:checked').value;
                        gmSet('pk_analyze_last_algo', algo);
                        m.remove();
                        resolve({ mode: 'similar', threshold: currentSim, algo: algo });
                    }
                };

                const modalBox = m.querySelector('.pk-modal');
                if (modalBox) {
                    Object.assign(modalBox.style, {
                        width: '540px',
                        height: 'auto',
                        minHeight: 'auto',
                        overflow: 'visible',
                        paddingBottom: '30px'
                    });

                    const closeBtn = m.querySelector('.pk-modal-close');
                    if (closeBtn) Object.assign(closeBtn.style, { top: '22px', right: '22px' });
                }
            });

            if (result === null) return;
            const isSimMode = result.mode === 'similar';
            const minBytes = result.minBytes || 0;
            const maxBytes = result.maxBytes || 0;
            const simThreshold = result.threshold || 0.9;
            const simAlgo = result.algo || 'sim';

            setLoad(true);
            isGUISensitive = true;

            let nodeMap = new Map();
            const largeFolders = [];

            const startNodes = [];

            const getRealLineage = (item) => {
                if (item._lineage && item._lineage.length > 0) {
                    return item._lineage;
                }
                const cleanPath = S.path.filter(p => p.id !== 'analyze_root' && p.id !== 'virtual_search_root');
                return [...cleanPath, { id: item.id, name: item.name }];
            };

            const analyzeSelectedIds = S.getSelectedIds();
            if (analyzeSelectedIds.length > 0) {
                analyzeSelectedIds.forEach(id => {
                    const item = S.itemMap.get(id);
                    if (item && item.kind === 'drive#folder') {
                        const fullLineage = getRealLineage(item);
                        startNodes.push({
                            id: item.id,
                            name: item.name,
                            icon_link: item.icon_link,
                            starred: item.starred,
                            tags: item.tags,
                            lineage: fullLineage,
                            retryCount: 0,
                            _pathStr: fullLineage.map(x => x.name).join('/')
                        });
                    }
                });
            } else {
                const subFolders = S.items.filter(it => it.kind === 'drive#folder');
                if (subFolders.length > 0) {
                    subFolders.forEach(item => {
                        const fullLineage = getRealLineage(item);
                        startNodes.push({
                            id: item.id,
                            name: item.name,
                            icon_link: item.icon_link,
                            starred: item.starred,
                            tags: item.tags,
                            lineage: fullLineage,
                            retryCount: 0,
                            _pathStr: fullLineage.map(x => x.name).join('/')
                        });
                    });
                } else {
                    const cur = S.path[S.path.length - 1];
                    const cleanPath = S.path.filter(p => p.id !== 'analyze_root' && p.id !== 'virtual_search_root');
                    if (cleanPath.length === 0) cleanPath.push({ id: '', name: L.btn_nav_home });
                    const rootName = cur.name || 'Root';
                    const actualCur = S.itemMap.get(cur.id);

                    startNodes.push({
                        id: cur.id || '',
                        name: rootName,
                        icon_link: cur.icon_link,
                        starred: actualCur ? actualCur.starred : false,
                        tags: actualCur ? actualCur.tags :[],
                        lineage: cleanPath,
                        retryCount: 0,
                        _pathStr: cleanPath.map(x => x.name).join('/')
                    });
                }
            }

            if (startNodes.length === 0) {
                setLoad(false); isGUISensitive = false;
                showToast(L.msg_analyze_only_normal_dir);
                return;
            }

            startNodes.forEach(n => {
                nodeMap.set(n.id, {
                    id: n.id,
                    name: n.name,
                    icon_link: n.icon_link,
                    starred: n.starred,
                    tags: n.tags,
                    size: 0,
                    parentId: null,
                    marked: false,
                    _pathStr: n._pathStr,
                    lineage: n.lineage,
                    isRoot: true,
                    files:[]
                });
            });

            const cacheKey = '__analyze_nodeMap_' + startNodes.map(n => n.id).join('_');
            let useCache = false;

            if (typeof globalCache !== 'undefined' && globalCache.has(cacheKey) && globalDirtyFolders.size === 0) {
                const cachedArr = globalCache.get(cacheKey);
                nodeMap = new Map(cachedArr);
                useCache = true;
                console.log("[Analyze] Using cached global nodeMap.");
            }

            const propagateSize = (parentId, addSize) => {
                let curId = parentId;
                while (curId !== null && nodeMap.has(curId)) {
                    const node = nodeMap.get(curId);
                    node.size += addSize;

                    if (!node.isRoot && node.size >= minBytes && !node.marked) {
                        node.marked = true;
                        largeFolders.push(node);
                    }
                    curId = node.parentId;
                }
            };

            S.scanId = (S.scanId || 0) + 1;
            const myScanId = S.scanId;

            if (S.scanAbortController) S.scanAbortController.abort();
            S.scanAbortController = new AbortController();
            const signal = S.scanAbortController.signal;

            let isRunning = true;
            UI.stopBtn.onclick = () => {
                isRunning = false;
                S.scanning = false;
                if (S.scanAbortController) S.scanAbortController.abort();
                updateLoadTxt(L.str_stopping);
            };

            S.scanning = true;
            let totalFilesScanned = 0;
            let totalDirsScanned = 0;

            try {
                if (!useCache) {
                    await coreRecursiveEngine(startNodes, {
                    signal: signal,

                    onFolder: (folder, filesInFolder, nextSubFolders) => {
                        totalDirsScanned++;

                        nextSubFolders.forEach(sub => {
                            if (!nodeMap.has(sub.id)) {
                                const fullPathStr = sub.lineage.map(x => x.name).join('/');

                                nodeMap.set(sub.id, {
                                    id: sub.id,
                                    name: sub.name,
                                    icon_link: sub.icon_link,
                                    starred: sub.starred,
                                    tags: sub.tags,
                                    size: 0,
                                    parentId: folder.id,
                                    marked: false,
                                    _pathStr: fullPathStr,
                                    lineage: sub.lineage,
                                    isRoot: false,
                                    files:[]
                                });
                            }
                        });
                    },

                    onFile: (file, parent) => {
                        totalFilesScanned++;
                        const sz = Number(file.size || 0);
                        if (sz > 0) {
                            propagateSize(parent.id, sz);
                        }
                        if (nodeMap.has(parent.id)) {
                            const fingerprint = file.hash ? `${file.hash}_${sz}` : `${file.name}_${sz}`;
                            let curId = parent.id;
                            while (curId !== null && nodeMap.has(curId)) {
                                nodeMap.get(curId).files.push(fingerprint);
                                curId = nodeMap.get(curId).parentId;
                            }
                        }
                    },

                    onProgress: (st) => {
                        updateLoadTxt(`${L.str_scanning} ${st.folders} ${L.unit_folders} | ${L.str_files}: ${st.files} | ${L.str_speed}: ${st.currentConcurrency} | ${L.str_cached} ${st.cacheHits} ${L.unit_folders}`);
                    }
                });

                if (!isRunning || signal.aborted || myScanId !== S.scanId) {
                    throw new Error('StoppedByUser');
                }

                if (typeof globalCache !== 'undefined') {
                    globalCache.set(cacheKey, Array.from(nodeMap.entries()));
                }
            } else {
                Array.from(nodeMap.values()).forEach(node => {
                    if (!node.isRoot && node.size >= minBytes) {
                        largeFolders.push(node);
                    }
                });
            }

            } catch (e) {
                if (isRunning && e.message !== 'StoppedByUser' && e.name !== 'AbortError') {
                    showAlert(`${L.str_error}: ${e.message}`);
                    setLoad(false);
                }
            } finally {
                isGUISensitive = false;
                S.scanning = false;
                S.scanAbortController = null;

                if (!isRunning) {
                    setLoad(false);
                    return;
                }

                let viewItems = [];

                if (isSimMode) {
                    updateLoadTxt(`${L.str_analyzing}...`);

                    Array.from(nodeMap.values()).forEach(node => {
                        node._ancestorSet = new Set(node.lineage ? node.lineage.map(p => p.id) : []);
                        if (node.parentId && nodeMap.has(node.parentId)) {
                            const parent = nodeMap.get(node.parentId);
                            if (parent.files.length === node.files.length) {
                                parent.isShell = true;
                            }
                        }
                    });

                    const isDescendant = (childId, parentId) => {
                        const childNode = nodeMap.get(childId);
                        return childNode ? childNode._ancestorSet.has(parentId) : false;
                    };

                    const folderArr = Array.from(nodeMap.values())
                    .filter(f => !f.isShell && (f.files.length >= 2 || f.size > 1024 * 1024))
                    .map(f => {
                        const counts = new Map();
                        f.files.forEach(h => counts.set(h, (counts.get(h) || 0) + 1));
                        return { ...f, fileCounts: counts, _keys: Array.from(counts.keys()), totalFiles: f.files.length };
                    })
                    .sort((a, b) => b.totalFiles - a.totalFiles);

                    const invertedIndex = new Map();
                    folderArr.forEach((f, i) => {
                        f.fileCounts.forEach((_, hash) => {
                            let arr = invertedIndex.get(hash);
                            if (!arr) { arr = []; invertedIndex.set(hash, arr); }
                            arr.push(i);
                        });
                    });

                    const totalDocs = folderArr.length;
                    const weightMap = new Map();
                    invertedIndex.forEach((arr, hash) => {
                        const df = arr.length;
                        let w = 1.0;
                        if (totalDocs >= 20 && (df / totalDocs) > 0.05) {
                            w = 0.05;
                        }
                        weightMap.set(hash, w);
                    });

                    folderArr.forEach(f => {
                        let wt = 0;
                        f.fileCounts.forEach((count, hash) => {
                            wt += count * weightMap.get(hash);
                        });
                        f.weightedTotal = wt;
                    });

                    let groups =[];
                    const assigned = new Set();
                    const total = folderArr.length;
                    const candidateSeen = new Uint32Array(total);
                    let lastYieldTime = performance.now();

                    updateLoadTxt(`${L.str_analyzing}... 0%`);

                    try {
                        if (simAlgo === 'name') {
                            const nameGroups = new Map();
                            const cleanFolderName = (oldName) => {
                                let cleanName = oldName.replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim();
                                cleanName = cleanName.replace(/^【[^】]+】 *[-_.]? */, '');
                                cleanName = cleanName.replace(/^[a-z0-9-]+[.](?:com|net|org|cc|xyz|vip|top|la) +/i, '');
                                const adKw = "(?:[.]com|[.]net|[.]org|[.]cc|[.]xyz|[.]vip|[.]top|[.]la|2048|www[.])";
                                const atRegex = new RegExp('^.*?' + adKw + '.*?(?:@|--+|_\\s)', 'i');
                                cleanName = cleanName.replace(atRegex, '');
                                const hyphenRegex = new RegExp('^[a-z0-9.-]+' + adKw + '-', 'i');
                                cleanName = cleanName.replace(hyphenRegex, '');
                                cleanName = cleanName.replace(/^(?:精品加群|福利合集)[0-9]+[-_]+ */, '');
                                cleanName = cleanName.replace(/^[-_. ,,::;;\p{Extended_Pictographic}]+/u, '');
                                const pairs = [['【','】'], ['[',']'], ['《','》'],['<','>'], ['(',')'],['(',')'], ['{','}']];
                                pairs.forEach(([L_char, R_char]) => {
                                    const idxR_Fix = cleanName.indexOf(R_char);
                                    const idxL_Check = cleanName.indexOf(L_char);
                                    if (idxR_Fix > 0 && idxR_Fix <= 10 && (idxL_Check === -1 || idxL_Check > idxR_Fix)) {
                                        cleanName = L_char + cleanName;
                                    }
                                    const chars = cleanName.split('');
                                    const stack = [];
                                    const toRemove = new Set();
                                    for (let i = 0; i < chars.length; i++) {
                                        const c = chars[i];
                                        if (c === L_char) stack.push(i);
                                        else if (c === R_char) {
                                            if (stack.length > 0) stack.pop();
                                            else toRemove.add(i);
                                        }
                                    }
                                    stack.forEach(i => toRemove.add(i));
                                    if (toRemove.size > 0) cleanName = chars.filter((_, i) => !toRemove.has(i)).join('');
                                });
                                const quoteCount = (cleanName.match(/'/g) || []).length;
                                if (quoteCount % 2 !== 0) cleanName = cleanName.replace(/'/, '');
                                let finalResult = cleanName.toLowerCase().trim();
                                if (simThreshold <= 0.5) {
                                    finalResult = finalResult.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
                                }
                                return finalResult || oldName.toLowerCase().trim();
                            };
                            folderArr.forEach(f => {
                                const k = cleanFolderName(f.name);
                                if (!nameGroups.has(k)) nameGroups.set(k,[]);
                                nameGroups.get(k).push(f);
                            });

                            const sizeRatioLimit = simThreshold >= 0.5 ? 0.05 : 0.10;

                            for (const[k, items] of nameGroups) {
                                if (items.length > 1) {
                                    const sorted = [...items].sort((a,b) => Number(a.size) - Number(b.size));
                                    let currentGroup = [sorted[0]];

                                    for (let i = 1; i < sorted.length; i++) {
                                        const target = sorted[i];
                                        const root = currentGroup[0];
                                        const rootSize = Number(root.size || 0);
                                        const targetSize = Number(target.size || 0);

                                        let isMatch = false;
                                        if (rootSize === 0 && targetSize === 0) isMatch = true;
                                        else {
                                            const sizeDiff = Math.abs(targetSize - rootSize);
                                            const maxBase = Math.max(targetSize, rootSize);
                                            if (maxBase > 0 && (sizeDiff / maxBase) <= sizeRatioLimit) isMatch = true;
                                        }

                                        if (isMatch) currentGroup.push(target);
                                        else {
                                            if (currentGroup.length > 1) {
                                                const gNodes = currentGroup;
                                                let minS = Number.MAX_SAFE_INTEGER, maxS = 0;
                                                gNodes.forEach(n => { const sz = Number(n.size||0); if(sz<minS) minS=sz; if(sz>maxS) maxS=sz; });
                                                if (minS === Number.MAX_SAFE_INTEGER) minS = 0;
                                                const range = (minS === maxS) ? fmtSize(minS) : `${fmtSize(minS)} ~ ${fmtSize(maxS)}`;
                                                groups.push({ ids: gNodes.map(f => f.id), type: `${gNodes.length} ${L.str_items} | ${range}`, _sim: 1 });
                                                gNodes.forEach(f => assigned.add(f.id));
                                            }
                                            currentGroup = [target];
                                        }
                                    }
                                    if (currentGroup.length > 1) {
                                        const gNodes = currentGroup;
                                        let minS = Number.MAX_SAFE_INTEGER, maxS = 0;
                                        gNodes.forEach(n => { const sz = Number(n.size||0); if(sz<minS) minS=sz; if(sz>maxS) maxS=sz; });
                                        if (minS === Number.MAX_SAFE_INTEGER) minS = 0;
                                        const range = (minS === maxS) ? fmtSize(minS) : `${fmtSize(minS)} ~ ${fmtSize(maxS)}`;
                                        groups.push({ ids: gNodes.map(f => f.id), type: `${gNodes.length} ${L.str_items} | ${range}`, _sim: 1 });
                                        gNodes.forEach(f => assigned.add(f.id));
                                    }
                                }
                            }
                        } else {
                        for (let i = 0; i < total; i++) {
                            if (i % 50 === 0 || performance.now() - lastYieldTime > 16) {
                                if (!isRunning) break;
                                updateLoadTxt(`${L.str_analyzing}\n${Math.round((i / total) * 100)}%`);
                                await sleep(0);
                                lastYieldTime = performance.now();
                            }

                            if (assigned.has(folderArr[i].id)) continue;

                            const f1 = folderArr[i];
                            const group = [f1];
                            let groupMinSim = 1.0;

                            const candidateIndices = [];
                            const marker = i + 1;
                            f1.fileCounts.forEach((_, hash) => {
                                const foldersWithHash = invertedIndex.get(hash);
                                if (foldersWithHash) {
                                    for (let k = 0, len = foldersWithHash.length; k < len; k++) {
                                        const idx = foldersWithHash[k];
                                        if (idx > i && candidateSeen[idx] !== marker && !assigned.has(folderArr[idx].id)) {
                                            candidateSeen[idx] = marker;
                                            candidateIndices.push(idx);
                                        }
                                    }
                                }
                            });

                            for (let m = 0, cLen = candidateIndices.length; m < cLen; m++) {
                                const j = candidateIndices[m];
                                const f2 = folderArr[j];

                                if (simAlgo === 'sim' && (isDescendant(f2.id, f1.id) || isDescendant(f1.id, f2.id))) continue;

                                let total1 = f1.weightedTotal;
                                let total2 = f2.weightedTotal;
                                let intersect = 0;

                                if (isDescendant(f2.id, f1.id)) {
                                    total1 -= total2;
                                    if (total1 <= 0 || total2 <= 0) continue;
                                    const maxS = total1 > total2 ? total1 : total2;
                                    const minS = total1 < total2 ? total1 : total2;
                                    if (simAlgo === 'sim' && minS / maxS < simThreshold) continue;

                                    for (let k = 0, len = f2._keys.length; k < len; k++) {
                                        const hash = f2._keys[k];
                                        const c2 = f2.fileCounts.get(hash);
                                        const c1 = f1.fileCounts.get(hash) || 0;
                                        const diff = c1 - c2;
                                        if (diff > 0) intersect += (diff < c2 ? diff : c2) * weightMap.get(hash);
                                    }
                                } else if (isDescendant(f1.id, f2.id)) {
                                    total2 -= total1;
                                    if (total1 <= 0 || total2 <= 0) continue;
                                    const maxS = total1 > total2 ? total1 : total2;
                                    const minS = total1 < total2 ? total1 : total2;
                                    if (simAlgo === 'sim' && minS / maxS < simThreshold) continue;

                                    for (let k = 0, len = f1._keys.length; k < len; k++) {
                                        const hash = f1._keys[k];
                                        const c1 = f1.fileCounts.get(hash);
                                        const c2 = f2.fileCounts.get(hash) || 0;
                                        const diff = c2 - c1;
                                        if (diff > 0) intersect += (diff < c1 ? diff : c1) * weightMap.get(hash);
                                    }
                                } else {
                                    if (total1 <= 0 || total2 <= 0) continue;
                                    const maxS = total1 > total2 ? total1 : total2;
                                    const minS = total1 < total2 ? total1 : total2;
                                    if (simAlgo === 'sim' && minS / maxS < simThreshold) continue;

                                    const fSmall = f1._keys.length < f2._keys.length ? f1 : f2;
                                    const fLarge = f1._keys.length < f2._keys.length ? f2 : f1;
                                    for (let k = 0, len = fSmall._keys.length; k < len; k++) {
                                        const hash = fSmall._keys[k];
                                        const cLarge = fLarge.fileCounts.get(hash);
                                        if (cLarge !== undefined) {
                                            const cSmall = fSmall.fileCounts.get(hash);
                                            intersect += (cSmall < cLarge ? cSmall : cLarge) * weightMap.get(hash);
                                        }
                                    }
                                }

                                const minTotal = total1 < total2 ? total1 : total2;
                                const union = total1 + total2 - intersect;
                                const sim = simAlgo === 'contain' ? (minTotal > 0 ? (intersect / minTotal) : 0) : (union > 0 ? (intersect / union) : 0);

                                if (sim >= simThreshold) {
                                    let isGroupQualified = true;
                                    let currentMinSim = sim;

                                    for (let gIdx = 1; gIdx < group.length; gIdx++) {
                                        const gMember = group[gIdx];

                                        if (simAlgo === 'sim' && (isDescendant(f2.id, gMember.id) || isDescendant(gMember.id, f2.id))) {
                                            isGroupQualified = false;
                                            break;
                                        }

                                        let tA = gMember.weightedTotal;
                                        let tB = f2.weightedTotal;
                                        let intS = 0;

                                        if (isDescendant(f2.id, gMember.id)) {
                                            tA -= tB;
                                        } else if (isDescendant(gMember.id, f2.id)) {
                                            tB -= tA;
                                        }

                                        if (tA <= 0 || tB <= 0) {
                                            isGroupQualified = false;
                                            break;
                                        }

                                        const maxS = tA > tB ? tA : tB;
                                        const minS = tA < tB ? tA : tB;
                                        if (simAlgo === 'sim' && minS / maxS < simThreshold) {
                                            isGroupQualified = false;
                                            break;
                                        }

                                        if (isDescendant(f2.id, gMember.id)) {
                                            for (let k = 0, len = f2._keys.length; k < len; k++) {
                                                const h = f2._keys[k];
                                                const cB = f2.fileCounts.get(h);
                                                const cA = gMember.fileCounts.get(h) || 0;
                                                const diff = cA - cB;
                                                if (diff > 0) intS += (diff < cB ? diff : cB) * weightMap.get(h);
                                            }
                                        } else if (isDescendant(gMember.id, f2.id)) {
                                            for (let k = 0, len = gMember._keys.length; k < len; k++) {
                                                const h = gMember._keys[k];
                                                const cA = gMember.fileCounts.get(h);
                                                const cB = f2.fileCounts.get(h) || 0;
                                                const diff = cB - cA;
                                                if (diff > 0) intS += (diff < cA ? diff : cA) * weightMap.get(h);
                                            }
                                        } else {
                                            const fSmall = gMember._keys.length < f2._keys.length ? gMember : f2;
                                            const fLarge = gMember._keys.length < f2._keys.length ? f2 : gMember;
                                            for (let k = 0, len = fSmall._keys.length; k < len; k++) {
                                                const h = fSmall._keys[k];
                                                const cLarge = fLarge.fileCounts.get(h);
                                                if (cLarge !== undefined) {
                                                    const cSmall = fSmall.fileCounts.get(h);
                                                    intS += (cSmall < cLarge ? cSmall : cLarge) * weightMap.get(h);
                                                }
                                            }
                                        }

                                        const minT = tA < tB ? tA : tB;
                                        const un = tA + tB - intS;
                                        const pairwiseSim = simAlgo === 'contain' ? (minT > 0 ? (intS / minT) : 0) : (un > 0 ? (intS / un) : 0);

                                        if (pairwiseSim < simThreshold) {
                                            isGroupQualified = false;
                                            break;
                                        }
                                        if (pairwiseSim < currentMinSim) {
                                            currentMinSim = pairwiseSim;
                                        }
                                    }

                                    if (isGroupQualified) {
                                        group.push(f2);
                                        if (currentMinSim < groupMinSim) groupMinSim = currentMinSim;
                                    }
                                }
                            }

                            if (group.length > 1) {
                                group.forEach(f => assigned.add(f.id));
                                groups.push({
                                    ids: group.map(f => f.id),
                                    type: `${group.length} ${L.str_items} | ${simAlgo === 'contain' ? L.lbl_containment : L.lbl_sim_score}: ${Math.round(groupMinSim * 100)}%`,
                                    _sim: groupMinSim
                                });
                            }
                        }
                        }

                        if (simAlgo !== 'name') {
                        const folderObjMap = new Map();
                        folderArr.forEach(f => folderObjMap.set(f.id, f));

                        const finalGroups =[];
                        groups.forEach(g => {
                            const nodes = g.ids.map(id => folderObjMap.get(id)).filter(Boolean);
                            const toRemove = new Set();

                            nodes.forEach(node => {
                                const descendants = nodes.filter(n => n.id !== node.id && isDescendant(n.id, node.id));
                                if (descendants.length === 0) return;

                                const hasExternal = nodes.some(n => n.id !== node.id && !isDescendant(n.id, node.id) && !isDescendant(node.id, n.id));
                                if (hasExternal) return;

                                const topDescendants = descendants.filter(d1 =>
                                    !descendants.some(d2 => d1.id !== d2.id && isDescendant(d1.id, d2.id))
                                );

                                let isCovered = true;
                                let sumTotal = 0;
                                const sumCounts = new Map();

                                topDescendants.forEach(td => {
                                    sumTotal += td.totalFiles;
                                    td.fileCounts.forEach((count, hash) => {
                                        sumCounts.set(hash, (sumCounts.get(hash) || 0) + count);
                                    });
                                });

                                if (sumTotal !== node.totalFiles) {
                                    isCovered = false;
                                } else {
                                    for (const [hash, count] of node.fileCounts) {
                                        if (sumCounts.get(hash) !== count) {
                                            isCovered = false;
                                            break;
                                        }
                                    }
                                }

                                if (isCovered) {
                                    toRemove.add(node.id);
                                }
                            });

                            const finalIds = g.ids.filter(id => !toRemove.has(id));
                            if (finalIds.length >= 2) {
                                finalGroups.push({
                                    ids: finalIds,
                                    type: g.type,
                                    _sim: g._sim
                                });
                            }
                        });

                        groups = finalGroups;

                        const partitionedGroups = [];
                        const varianceTolerance = 0.15;

                        groups.forEach(g => {
                            const nodes = g.ids.map(id => folderObjMap.get(id)).filter(Boolean);
                            if (nodes.length <= 2) {
                                const pct = Math.round(g._sim * 100);
                                g.type = `${nodes.length} ${L.str_items} | ${simAlgo === 'contain' ? L.lbl_containment : L.lbl_sim_score}: ${pct}%`;
                                partitionedGroups.push(g);
                                return;
                            }

                            const simMatrix = [];
                            let maxSim = 0, minSim = 1;

                            for (let i = 0; i < nodes.length; i++) {
                                simMatrix[i] = [];
                                for (let j = 0; j < nodes.length; j++) {
                                    if (i === j) { simMatrix[i][j] = 1; continue; }
                                    if (j < i) { simMatrix[i][j] = simMatrix[j][i]; continue; }

                                    const n1 = nodes[i], n2 = nodes[j];

                                    if (simAlgo === 'sim' && (isDescendant(n2.id, n1.id) || isDescendant(n1.id, n2.id))) {
                                        simMatrix[i][j] = 0;
                                        minSim = 0;
                                        continue;
                                    }

                                    let tA = n1.weightedTotal, tB = n2.weightedTotal, intS = 0;
                                    if (isDescendant(n2.id, n1.id)) tA -= tB; else if (isDescendant(n1.id, n2.id)) tB -= tA;

                                    if (tA > 0 && tB > 0) {
                                        if (isDescendant(n2.id, n1.id)) {
                                            for (let k = 0; k < n2._keys.length; k++) {
                                                const h = n2._keys[k], cB = n2.fileCounts.get(h), cA = n1.fileCounts.get(h) || 0, diff = cA - cB;
                                                if (diff > 0) intS += (diff < cB ? diff : cB) * weightMap.get(h);
                                            }
                                        } else if (isDescendant(n1.id, n2.id)) {
                                            for (let k = 0; k < n1._keys.length; k++) {
                                                const h = n1._keys[k], cA = n1.fileCounts.get(h), cB = n2.fileCounts.get(h) || 0, diff = cB - cA;
                                                if (diff > 0) intS += (diff < cA ? diff : cA) * weightMap.get(h);
                                            }
                                        } else {
                                            const fSmall = n1._keys.length < n2._keys.length ? n1 : n2, fLarge = n1._keys.length < n2._keys.length ? n2 : n1;
                                            for (let k = 0; k < fSmall._keys.length; k++) {
                                                const h = fSmall._keys[k], cLarge = fLarge.fileCounts.get(h);
                                                if (cLarge !== undefined) intS += (fSmall.fileCounts.get(h) < cLarge ? fSmall.fileCounts.get(h) : cLarge) * weightMap.get(h);
                                            }
                                        }
                                        const minT = tA < tB ? tA : tB;
                                        const un = tA + tB - intS;
                                        const s = simAlgo === 'contain' ? (minT > 0 ? (intS / minT) : 0) : (un > 0 ? (intS / un) : 0);
                                        simMatrix[i][j] = s;
                                        if (s > maxSim) maxSim = s;
                                        if (s < minSim) minSim = s;
                                    } else {
                                        simMatrix[i][j] = 0;
                                        minSim = 0;
                                    }
                                }
                            }

                            if (maxSim - minSim <= varianceTolerance) {
                                const minPct = Math.round(minSim * 100);
                                const maxPct = Math.round(maxSim * 100);
                                const range = (minPct === maxPct) ? `${minPct}%` : `${minPct}% ~ ${maxPct}%`;
                                g.type = `${nodes.length} ${L.str_items} | ${simAlgo === 'contain' ? L.lbl_containment : L.lbl_sim_score}: ${range}`;
                                partitionedGroups.push(g);
                            } else {
                                const unassigned = new Set(nodes.map((_, idx) => idx));
                                const pairs =[];
                                for (let i = 0; i < nodes.length; i++) {
                                    for (let j = i + 1; j < nodes.length; j++) pairs.push({i, j, s: simMatrix[i][j]});
                                }
                                pairs.sort((a, b) => b.s - a.s);

                                pairs.forEach(pair => {
                                    if (unassigned.has(pair.i) && unassigned.has(pair.j) && pair.s >= simThreshold) {
                                        const sg = [pair.i, pair.j];
                                        let sgMinSim = pair.s;
                                        unassigned.delete(pair.i); unassigned.delete(pair.j);

                                        for (const u of Array.from(unassigned)) {
                                            let canAdd = true, localMin = 1;
                                            for (const mem of sg) {
                                                const s = simMatrix[u < mem ? u : mem][u > mem ? u : mem];
                                                if (pair.s - s > varianceTolerance || s < simThreshold) { canAdd = false; break; }
                                                if (s < localMin) localMin = s;
                                            }
                                            if (canAdd) { sg.push(u); unassigned.delete(u); if (localMin < sgMinSim) sgMinSim = localMin; }
                                        }
                                        if (sg.length >= 2) {
                                            let subMin = 1, subMax = 0;
                                            for(let x=0; x<sg.length; x++){
                                                for(let y=x+1; y<sg.length; y++){
                                                    const idx1 = sg[x] < sg[y] ? sg[x] : sg[y];
                                                    const idx2 = sg[x] > sg[y] ? sg[x] : sg[y];
                                                    const sVal = simMatrix[idx1][idx2];
                                                    if (sVal < subMin) subMin = sVal;
                                                    if (sVal > subMax) subMax = sVal;
                                                }
                                            }
                                            const minPct = Math.round(subMin * 100);
                                            const maxPct = Math.round(subMax * 100);
                                            const range = (minPct === maxPct) ? `${minPct}%` : `${minPct}% ~ ${maxPct}%`;
                                            partitionedGroups.push({
                                                ids: sg.map(idx => nodes[idx].id),
                                                type: `${sg.length} ${L.str_items} | ${simAlgo === 'contain' ? L.lbl_containment : L.lbl_sim_score}: ${range}`,
                                                _sim: subMin
                                            });
                                        }
                                    }
                                });
                            }
                        });

                        groups = partitionedGroups;
                        }
                    } catch (e) {
                        console.error("[SimMode] Error:", e);
                    }

                    if (groups.length === 0) {
                        setLoad(false);
                        showToast(L.msg_dup_none);
                        return;
                    }
                    groups.sort((a, b) => (b._sim - a._sim) || (b.ids.length - a.ids.length));

                    S.analyzeSimGroups = groups;

                    viewItems = Array.from(assigned).map(id => {
                        const node = nodeMap.get(id);
                        return {
                            id: node.id,
                            kind: 'drive#folder',
                            name: node.name,
                            icon_link: node.icon_link,
                            starred: node.starred,
                            tags: node.tags,
                            size: node.size.toString(),
                            _pathStr: (node._pathStr && node._pathStr.includes('/')) ? node._pathStr.substring(0, node._pathStr.lastIndexOf('/')) : L.btn_nav_home,
                            _lineage: node.lineage,
                            modified_time: new Date(getServerNow()).toISOString(),
                            parent_id: node.parentId
                        };
                    });
                } else {
                    updateLoadTxt(L.str_rendering);
                    const uniqueResults = Array.from(new Set(largeFolders));
                    const kw = gmGet('pk_analyze_last_keyword', '');
                    const kwList = kw ? kw.toLowerCase().split(/[,,]/).map(k => k.trim()).filter(k => k) : [];

                    let filteredResults = uniqueResults.filter(n => n.size >= minBytes && (maxBytes === 0 || n.size <= maxBytes));

                    if (kwList.length > 0) {
                        filteredResults = filteredResults.filter(node => !kwList.some(k => (node.name || "").toLowerCase().includes(k)));
                    }

                    filteredResults.sort((a, b) => b.size - a.size);

                    if (filteredResults.length === 0) {
                        setLoad(false);
                        let rangeStr = maxBytes > 0 ? `${fmtSize(minBytes)} - ${fmtSize(maxBytes)}` : `≥ ${fmtSize(minBytes)}`;
                        showToast(L.msg_analyze_no_large_folders.replace('{s}', rangeStr));
                        return;
                    }

                    viewItems = filteredResults.map(node => ({
                        id: node.id,
                        kind: 'drive#folder',
                        name: node.name,
                        icon_link: node.icon_link,
                        starred: node.starred,
                        tags: node.tags,
                        size: node.size.toString(),
                        _pathStr: (node._pathStr && node._pathStr.includes('/')) ? node._pathStr.substring(0, node._pathStr.lastIndexOf('/')) : L.btn_nav_home,
                        _lineage: node.lineage,
                        modified_time: new Date(getServerNow()).toISOString(),
                        parent_id: node.parentId
                    }));
                }

                rememberFolderFirstBeforeStrictMode();

                S.analyzeMode = true;
                S.hasShownAnaWarn = false;
                S.sort = 'size'; S.dir = 1;
                S.isFlattened = false;
                S.dupMode = false;
                S.analyzeResultItems = [...viewItems];
                S.analyzeMap = nodeMap;
                S.path = [{ id: 'analyze_root', name: L.str_analyze_results }];
                renderCrumb();
                S.items = viewItems;
                S.itemMap.clear();
                S.items.forEach(i => S.itemMap.set(i.id, i));
                S.sel.clear();

                UI.scan.style.display = 'none';
                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
                UI.btnExit.style.display = 'flex';

                if (UI.dupTools) UI.dupTools.style.display = 'none';
                if (UI.dupFilters) UI.dupFilters.style.display = 'none';

                if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
                if (UI.chkGlobal) UI.chkGlobal.checked = false;

                refresh();
                if (S._enterTopRaf) cancelAnimationFrame(S._enterTopRaf);
                S._enterTopRaf = requestAnimationFrame(() => {
                    S._enterTopRaf = 0;
                    if (UI.vp && UI.vp.scrollTop !== 0) UI.vp.scrollTop = 0;
                });
                updateStat();

                setTimeout(() => {
                    setLoad(false);
                    isGUISensitive = false;
                }, 200);
            }
        };
    }

    function showAnalyzeResultModal(list, threshold) {
        const L = getStrings();
        const modalHtml = `
            <div style="padding-bottom:15px; border-bottom:1px solid var(--pk-bd); margin-bottom:10px;">
                <h3 style="margin:0; font-size:18px; font-weight:700;">${L.title_analyze_result}</h3>
                <div style="font-size:12px; color:#888; margin-top:5px;">
                    ${L.msg_analyze_summary_fmt.replace('{n}', list.length).replace('{s}', threshold)}
                </div>
            </div>

            <div class="pk-scroll" style="flex:1; overflow-y:auto; border:1px solid var(--pk-bd); border-radius:4px; background:var(--pk-bg);">
                <div style="display:grid; grid-template-columns: 1fr 100px 80px; font-weight:bold; padding:8px 12px; border-bottom:1px solid var(--pk-bd); background:var(--pk-hl); font-size:12px; position:sticky; top:0;">
                    <div>${L.col_path_name}</div>
                    <div style="text-align:right;">${L.col_size}</div>
                    <div style="text-align:center;">${L.col_action}</div>
                </div>
                <div id="pk_analyze_list"></div>
            </div>

            <div class="pk-modal-act" style="margin-top:15px;">
                <button class="pk-btn pri" id="pk_analyze_close" style="width:100px; justify-content:center;">${L.btn_close}</button>
            </div>
        `;

        const m = showModal(modalHtml);
        const modalBox = m.querySelector('.pk-modal');
        modalBox.style.width = "800px";
        modalBox.style.height = "80vh";

        const container = m.querySelector('#pk_analyze_list');
        const fragment = document.createDocumentFragment();

        list.forEach(item => {
            const row = document.createElement('div');
            row.style.cssText = "display:grid; grid-template-columns: 1fr 100px 80px; padding:10px 12px; border-bottom:1px solid var(--pk-bd); font-size:13px; align-items:center;";

            const fullPath = item.path;
            const name = item.name;
            const parentPath = fullPath.substring(0, fullPath.lastIndexOf(name));

            const sizeNum = Number(item.size);

            row.innerHTML = `
                <div style="overflow:hidden;">
                    <div style="font-weight:bold; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--pk-fg);" title="${esc(name)}">${esc(name)}</div>
                    <div style="color:#999; font-size:11px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${esc(parentPath)}">${esc(parentPath || '/')}</div>
                </div>
                <div style="text-align:right; font-family:monospace; color:var(--pk-pri); font-weight:600;">${fmtSize(sizeNum)}</div>
                <div style="text-align:center;">
                    <button class="pk-btn" style="padding:2px 8px; font-size:11px; height:24px;" title="${L.tip_jump_to_folder}">${L.btn_jump}</button>
                </div>
            `;

            row.querySelector('button').onclick = () => {
                m.remove();

                const wasAnalyzeMode = S.analyzeMode;

                const cleanLineage = item.lineage.filter(x => x.id);

                S.analyzeMode = false;
                S.isFlattened = false;
                S.dupMode = false;
                if (UI.chkSearchPath) UI.chkSearchPath.checked = false;

                S.path =[{ id: '', name: getStrings().btn_nav_home }, ...cleanLineage];

                if (UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
                if (UI.chkGlobal && wasAnalyzeMode && typeof S.wasGlobalChecked !== 'undefined') {
                    UI.chkGlobal.checked = S.wasGlobalChecked;
                }

                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                if (UI.scan) UI.scan.style.display = 'flex';

                load();
            };

            fragment.appendChild(row);
        });

        container.appendChild(fragment);

        m.querySelector('#pk_analyze_close').onclick = () => m.remove();
    }

    if (UI.btnExport) {
        UI.btnExport.onclick = async () => {
            if (S.trashMode) return;
            const curFolderId = S.path[S.path.length - 1].id || '';
            if (isPathBusy(curFolderId)) {
                showAlert(L.msg_op_blocked_exporting);
                return;
            }

            const format = await new Promise((resolve) => {
                const m = showModal(`
                    <h3 style="border:none; margin-bottom:0px; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_export_format}</h3>
                    <div style="font-size:13px; color:#888; margin-bottom:20px; line-height:1.5;">${L.lbl_export_current}</div>
                    <div style="display:grid; grid-template-columns: 1fr 1fr; gap:20px; margin-bottom:25px;">
                        <div class="pk-exp-card act" data-fmt="tree" style="border:2px solid var(--pk-pri); border-radius:8px; padding:15px; cursor:pointer; position:relative; background:var(--pk-bg); transition:all 0.2s;">
                            <div class="pk-exp-check" style="position:absolute; top:0; right:0; width:24px; height:24px; background:var(--pk-pri); border-bottom-left-radius:8px; display:flex; align-items:center; justify-content:center; color:#fff;">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
                            </div>
                            <div class="pk-exp-title" style="font-weight:bold; margin-bottom:10px; color:var(--pk-pri); font-size:15px;">${L.opt_tree_view}</div>
                            <div style="font-size:12px; color:var(--pk-fg); line-height:1.5; font-family:Consolas, 'Courier New', monospace; opacity:0.8; white-space:pre; letter-spacing:0px;">Root\n├─ Folder 1\n│  ├─ Folder 1-1\n│  └─ Folder 1-2\n└─ Folder 2\n   └─ Folder 2-1</div>
                        </div>
                        <div class="pk-exp-card" data-fmt="list" style="border:2px solid var(--pk-bd); border-radius:8px; padding:15px; cursor:pointer; position:relative; background:var(--pk-bg); transition:all 0.2s;">
                            <div class="pk-exp-check" style="position:absolute; top:0; right:0; width:24px; height:24px; background:var(--pk-pri); border-bottom-left-radius:8px; display:none; align-items:center; justify-content:center; color:#fff;">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
                            </div>
                            <div class="pk-exp-title" style="font-weight:bold; margin-bottom:10px; color:var(--pk-fg); font-size:15px;">${L.opt_list_view}</div>
                            <div style="font-size:12px; color:var(--pk-fg); line-height:1.5; font-family:Consolas, 'Courier New', monospace; opacity:0.8; white-space:pre;">Root/Folder 1\nRoot/Folder 1/Folder 1-1\nRoot/Folder 1/Folder 1-2\nRoot/Folder 2\nRoot/Folder 2/Folder 2-1</div>
                        </div>
                    </div>
                    <div class="pk-modal-act">
                        <button class="pk-btn" id="exp_cancel" style="height:40px; padding:0 20px;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="exp_confirm" style="height:40px; padding:0 30px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold;">${L.btn_ok}</button>
                    </div>
                `);

                const modalBox = m.querySelector('.pk-modal');
                if (modalBox) {
                    modalBox.style.width = '520px';
                    modalBox.style.height = 'auto';
                    modalBox.style.minHeight = 'auto';

                    const closeBtn = m.querySelector('.pk-modal-close');
                    if (closeBtn) Object.assign(closeBtn.style, { top: '22px', right: '22px' });
                }

                let selectedFormat = 'tree';

                m.querySelectorAll('.pk-exp-card').forEach(card => {
                    card.onclick = () => {
                        m.querySelectorAll('.pk-exp-card').forEach(c => {
                            c.style.borderColor = 'var(--pk-bd)';
                            c.querySelector('.pk-exp-title').style.color = 'var(--pk-fg)';
                            c.querySelector('.pk-exp-check').style.display = 'none';
                            c.classList.remove('act');
                        });
                        card.style.borderColor = 'var(--pk-pri)';
                        card.querySelector('.pk-exp-title').style.color = 'var(--pk-pri)';
                        card.querySelector('.pk-exp-check').style.display = 'flex';
                        card.classList.add('act');
                        selectedFormat = card.dataset.fmt;
                    };
                });

                m.querySelector('#exp_cancel').onclick = () => { m.remove(); resolve(null); };
                m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(null); };
                m.querySelector('#exp_confirm').onclick = () => { m.remove(); resolve(selectedFormat); };

                m.tabIndex = 0;
                setTimeout(() => m.focus(), 10);
                m.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter') {
                        e.preventDefault(); e.stopPropagation();
                        m.querySelector('#exp_confirm').click();
                    }
                });
            });

            if (!format) return;

            setLoad(true);
            S.scanning = true;
            S.scanId = (S.scanId || 0) + 1;
            const myScanId = S.scanId;

            if (S.scanAbortController) S.scanAbortController.abort();
            S.scanAbortController = new AbortController();
            const signal = S.scanAbortController.signal;

            let isRunning = true;
            UI.stopBtn.onclick = () => {
                isRunning = false;
                S.scanning = false;
                if (S.scanAbortController) S.scanAbortController.abort();
                updateLoadTxt(L.str_stopping);
            };

            const rootNode = S.path[S.path.length - 1];
            const startNodes =[{
                id: rootNode.id || '',
                name: rootNode.name || L.btn_nav_home,
                lineage: [],
                retryCount: 0
            }];

            const itemTree = new Map();

            try {
                await coreRecursiveEngine(startNodes, {
                    signal: signal,
                    onFolder: (folder, filesInFolder) => {
                        itemTree.set(folder.id, [...filesInFolder]);

                        if (typeof globalCache !== 'undefined' && !globalCache.has(folder.id)) {
                            globalCache.set(folder.id, [...filesInFolder]);
                        }

                        indexParents(folder.id, folder.name, filesInFolder);
                    },
                    onProgress: (st) => {
                        updateLoadTxt(`${L.msg_exporting}\n${L.str_folders}: ${st.folders} | ${L.str_files}: ${st.files}`);
                    }
                });

                if (!isRunning || signal.aborted || myScanId !== S.scanId) return;

                updateLoadTxt(L.str_processing);
                await sleep(50);

                const rootName = startNodes[0].name;
                let outputLines =[];
                const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });

                const sortItems = (items) => {
                    return items.sort((a, b) => {
                        if (a.kind !== b.kind) return a.kind === 'drive#folder' ? -1 : 1;
                        return collator.compare(a.name, b.name);
                    });
                };

                if (format === 'tree') {
                    outputLines.push(rootName);

                    const buildTree = (parentId, prefix) => {
                        const children = itemTree.get(parentId);
                        if (!children || children.length === 0) return;

                        const sorted = sortItems(children);
                        for (let i = 0; i < sorted.length; i++) {
                            const child = sorted[i];
                            const isLast = (i === sorted.length - 1);
                            const connector = isLast ? '└─ ' : '├─ ';
                            outputLines.push(prefix + connector + child.name);

                            if (child.kind === 'drive#folder') {
                                const childPrefix = prefix + (isLast ? '   ' : '│  ');
                                buildTree(child.id, childPrefix);
                            }
                        }
                    };

                    buildTree(startNodes[0].id, '');
                } else {
                    const buildList = (parentId, currentPath) => {
                        const children = itemTree.get(parentId);
                        if (!children || children.length === 0) return;

                        const sorted = sortItems(children);
                        for (let i = 0; i < sorted.length; i++) {
                            const child = sorted[i];
                            const fullPath = currentPath ? currentPath + '/' + child.name : child.name;
                            outputLines.push(fullPath);

                            if (child.kind === 'drive#folder') {
                                buildList(child.id, fullPath);
                            }
                        }
                    };

                    buildList(startNodes[0].id, rootName);
                }

                const outputText = outputLines.join('\r\n');
                const blob = new Blob([outputText], { type: 'text/plain;charset=utf-8' });
                const url = URL.createObjectURL(blob);

                const safeRootName = rootName.replace(/[\\/:*?"<>|]/g, '_');
                const a = document.createElement('a');
                a.href = url;
                a.download = `${safeRootName}_${format}.txt`;
                a.click();

                setTimeout(() => URL.revokeObjectURL(url), 1000);

            } catch (e) {
                if (e.name !== 'AbortError' && myScanId === S.scanId) {
                    showAlert(`${L.str_error}: ${e.message}`);
                }
            } finally {
                if (myScanId === S.scanId) {
                    setLoad(false);
                    S.scanning = false;
                    S.scanAbortController = null;
                    isGUISensitive = false;
                }
            }
        };
    }

    UI.btnNewFolder.onclick = async () => {
        const name = await showPrompt(L.msg_newfolder_prompt, '');
        if (!name) return;

        isGUISensitive = true;
        const cur = S.path[S.path.length - 1];
        const cacheKey = cur.id || 'root';

        try {
            await fetch('https://api-drive.mypikpak.com/drive/v1/files', { method: 'POST', headers: getHeaders(), body: JSON.stringify({ kind: 'drive#folder', parent_id: cur.id || '', name: name }) });

            if (cur.id) gmSet('pk_fmod_' + cur.id, new Date(getServerNow()).toISOString());

            await sleep(300);

            S.cache.delete(cacheKey);
            if (typeof globalCache !== 'undefined') globalCache.delete(cacheKey);

            if (typeof globalDirtyFolders !== 'undefined') {
                globalDirtyFolders.add(cacheKey === 'root' ? '' : cacheKey);
            }
            if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;

            if (cacheKey !== 'root') {
                scannedFolderIds.delete(cacheKey);
            }

            if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();

            load(false, true);
        } catch (e) {
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            isGUISensitive = false;
        }
    };

    UI.btnCopy.onclick = async () => {
        const ids = S.getSelectedIds();
        const totalSelected = ids.length;
        if (totalSelected === 0) return;

        if (totalSelected > 10000) {
            showToast(L.str_copying);
            await sleep(10);
        }

        const itemList = [];
        let count = 0;
        for (const id of ids) {
            const item = S.itemMap.get(id);
            if (item) itemList.push(item);
            count++;
            if (count % 10000 === 0) await sleep(0);
        }

        S.clipItems = itemList;
        S.clipType = 'copy';

        const curId = S.path[S.path.length - 1].id || '';

        S.clipSourceParentId = S.isFlattened ? '__VIRTUAL__' : curId;

        setLoad(false);
        UI.btnPaste.disabled = false;
        showToast(L.msg_copy_done);
    };

    UI.btnCut.onclick = async () => {
        const ids = S.getSelectedIds();
        if (ids.length === 0) return;

        const itemList = [];
        let count = 0;

        for (const id of ids) {
            const item = S.itemMap.get(id);

            if (!S.trashMode && isSystemItem(item)) {
                continue;
            }

            itemList.push(item);
            count++;

            if (count % 10000 === 0) await sleep(0);
        }

        if (itemList.length === 0) return;

        if (itemList.length > 10000) {
            showToast(L.str_moving);
            await sleep(10);
        }

        S.clipItems = itemList;
        S.clipType = 'move';

        const curId = S.path[S.path.length - 1].id || '';

        S.clipSourceParentId = S.isFlattened ? '__VIRTUAL__' : curId;

        setLoad(false);
        UI.btnPaste.disabled = false;
        showToast(L.msg_cut_done);
    };

    UI.btnPaste.onclick = async () => {
        if (!S.clipItems || S.clipItems.length === 0) {
            showToast(L.msg_paste_empty, 'error');
            return;
        }

        if (S.movingIds && S.movingIds.size > 0) {
            showAlert(L.msg_op_blocked_moving);
            return;
        }

        const items = S.clipItems;
        const type = S.clipType;
        const srcId = S.clipSourceParentId;
        const destId = S.path[S.path.length - 1].id || '';
        const normalize = (id) => (!id || id === 'root') ? 'root' : id;

        S.clipItems = [];
        S.clipType = '';
        updateStat();

        const targetFolderName = S.path[S.path.length - 1].name || L.str_target_folder;
        await executeFileTransfer(items, type, normalize(srcId), normalize(destId), targetFolderName);
    };

    UI.btnRename.onclick = async () => {
        if (S.getSelectedCount() !== 1) return;

        const id = S.getSelectedIds()[0];
        const item = S.itemMap.get(id);
        if (!item || (!S.trashMode && isSystemItem(item))) return;

        const newName = await showPrompt(L.msg_rename_prompt, item.name, L.modal_rename_title);

        if (!newName || newName === item.name) return;

        let progressTask = null;
        try {
            progressTask = FloatBarManager.create(L.str_renaming);

            await apiAction(`/${id}`, { name: newName });

            item.name = newName;
            const nowIso = new Date(getServerNow()).toISOString();
            item.modified_time = nowIso;

            if (item.kind === 'drive#folder') gmSet('pk_fmod_' + item.id, nowIso);
            const parentIdForFmod = item.parent_id || 'root';
            gmSet('pk_fmod_' + parentIdForFmod, nowIso);

            const row = UI.in.querySelector(`.pk-row[data-id="${id}"]`);
            if (row && row.lastElementChild) {
                row.lastElementChild.textContent = fmtDate(nowIso);
                row.lastElementChild.style.transition = 'color 0.3s';
                row.lastElementChild.style.color = 'var(--pk-pri)';
                setTimeout(() => { if(row.lastElementChild) row.lastElementChild.style.color = ''; }, 2000);
            }

            const pid = item.parent_id || 'root';
            if (typeof globalCache !== 'undefined') {
                if (globalCache.has(pid)) {
                    const list = globalCache.get(pid);
                    const target = list.find(f => f.id === id);
                    if (target) target.name = newName;
                }
                globalDirtyFolders.add(pid === 'root' ? '' : pid);
                if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
            }

            if (S.lastGlobalResults && S.lastGlobalResults.length > 0) {
                const globalTarget = S.lastGlobalResults.find(f => f.id === id);
                if (globalTarget) globalTarget.name = newName;
            }

            if (S.analyzeResultItems) {
                const anaItem = S.analyzeResultItems.find(x => x.id === id);
                if (anaItem) anaItem.name = newName;
            }
            if (S.analyzeMap && S.analyzeMap.has(id)) {
                S.analyzeMap.get(id).name = newName;
            }

            if (S.dupMode) renderDupView(); else refresh();

        } catch (e) {
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            if (progressTask) progressTask.destroy();
        }
    };

    UI.btnBulkRename.onclick = () => {
        const selectedIds = S.getSelectedIds();
        const selectedCount = selectedIds.length;
        if (selectedCount < 2) return;

        if (S.movingIds && S.movingIds.size > 0) {
            const hasConflict = selectedIds.some(id => S.movingIds.has(id));
            if (hasConflict) {
                showAlert(L.msg_op_blocked_analyzing);
                return;
            }
        }

        const getSmartDisplayHTML = (fullStr, hlStr) => {
            if (!hlStr || hlStr.indexOf('§§§MATCH_START§§§') === -1) {
                return esc(fullStr);
            }

            const parts = hlStr.split(/(§§§MATCH_START§§§.*?§§§MATCH_END§§§)/g);
            let result = "";

            const PRE_CTX = 12;
            const SUF_CTX = 80;

            parts.forEach((part, index) => {
                if (part.startsWith('§§§MATCH_START§§§')) {
                    const content = part.replace(/§§§MATCH_START§§§|§§§MATCH_END§§§/g, '');
                    result += `<span style="background:#fff2cc;color:#d93025;font-weight:bold;border-radius:2px;padding:0 2px;">${esc(content)}</span>`;
                } else {
                    let text = part;

                    if (index === 0) {
                        if (text.length > PRE_CTX + 3) {
                            text = "..." + text.slice(-PRE_CTX);
                        }
                    }
                    else if (index === parts.length - 1) {
                        if (text.length > SUF_CTX) {
                            text = text.slice(0, SUF_CTX) + "...";
                        }
                    }
                    else {
                        if (text.length > 20) {
                            text = text.slice(0, 8) + ".." + text.slice(-8);
                        }
                    }
                    result += esc(text);
                }
            });
            return result;
        };

        const extractKeyword = (fileName) => {
            const fc2Regex = /(?:FC2|FC)(?:[-_. ]*PPV)?[-_. @]*(\d{5,8})(?:[-_. ]*(?:part|pt|cd)?[-_. ]?(\d{1,2}|[a-e]))?(?![a-z\d])/i;
            const fc2Match = fileName.match(fc2Regex);

            if (fc2Match) {
                const id = fc2Match[1];
                const part = fc2Match[2];
                return (part && part.trim()) ? `FC2-PPV-${id}-${part.toUpperCase()}` : `FC2-PPV-${id}`;
            }
            return null;
        };

        const inputStyle = `
            width:100%; height:44px; padding:0 15px;
            border:2px solid var(--pk-bd); border-radius:8px;
            background:var(--pk-bg); color:var(--pk-fg);
            font-size:14px; font-weight:600; outline:none;
            transition:border-color 0.2s; box-sizing:border-box;
        `;

        const labelStyle = `
            position:absolute; top:0; transform:translateY(-50%); left:10px;
            background:var(--pk-bg); padding:0 5px; line-height:1;
            font-size:11px; color:var(--pk-pri);
            font-weight:bold; pointer-events:none; z-index:1;
        `;

        const m = showModal(`
            <div style="display:flex; flex-direction:column; gap:20px; padding:10px 0;">

                <div>
                    <h3 style="margin:0; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.modal_rename_multi_title}</h3>
                    <div style="font-size:12px; color:#888; margin-top:4px;">${L.rn_stat.replace('{n}', selectedCount).replace('{m}', '<span id="rn_stat_num">0</span>')}</div>
                </div>

                <div style="display:flex; gap:15px; align-items:center; flex-wrap:wrap;">
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" value="replace" checked style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.label_replace}</span>
                    </label>
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" value="pattern" style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.lbl_rn_mode_series}</span>
                    </label>
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" value="format" style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.lbl_rn_mode_format}</span>
                    </label>
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" value="jav" style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.label_jav}</span>
                    </label>
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" value="ad_remove" style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.lbl_rn_mode_ad}</span>
                    </label>
                    <label style="display:flex; align-items:center; cursor:pointer;">
                        <input type="radio" name="rn_mode" id="rb_ext_fix" value="ext_fix" style="accent-color:var(--pk-pri); transform:scale(1.2);">
                        <span style="margin-left:8px; font-weight:600; color:var(--pk-fg);">${L.lbl_rn_mode_ext}</span>
                    </label>
                </div>

                <div id="rn_inputs_area" style="min-height:85px; display:flex; flex-direction:column; justify-content:center;">
                    <div id="group_replace" style="position:relative; display:flex; flex-direction:column; gap:15px;">
                        <div style="position:absolute; top:-32px; right:0; display:flex; justify-content:flex-end; gap:15px; padding-right:5px;">
                            <label style="cursor:pointer; font-size:12px; color:var(--pk-fg); display:flex; align-items:center; gap:4px; user-select:none;">
                                <input type="checkbox" id="rn_include_ext" style="accent-color:var(--pk-pri);"> ${L.label_include_ext}
                            </label>
                            <label style="cursor:pointer; font-size:12px; color:var(--pk-fg); display:flex; align-items:center; gap:4px; user-select:none;">
                                <input type="checkbox" id="rn_case_sense" style="accent-color:var(--pk-pri);"> ${L.label_replace_note}
                            </label>
                            <label style="cursor:pointer; font-size:12px; color:var(--pk-fg); display:flex; align-items:center; gap:4px; user-select:none;">
                                <input type="checkbox" id="rn_regex" style="accent-color:var(--pk-pri);"> ${L.label_regex}
                            </label>
                        </div>
                        <div style="display:flex; gap:15px;">
                            <div style="position:relative; flex:1;">
                                <input type="text" id="rn_find" placeholder="${L.placeholder_find}" autocomplete="off" style="${inputStyle}">
                                <div style="${labelStyle}">${L.label_replace_find}</div>
                            </div>
                            <div style="position:relative; flex:1;">
                                <input type="text" id="rn_rep" placeholder="${L.placeholder_replace}" autocomplete="off" style="${inputStyle}">
                                <div style="${labelStyle}">${L.label_replace_to}</div>
                            </div>
                        </div>
                    </div>

                    <div id="group_format" style="display:none; gap:15px; align-items: flex-start;">
                        <div class="pk-custom-select" id="cs_rn_case" style="flex:1;">
                            <div class="pk-select-label">${L.lbl_rn_case_convert}</div>
                            <div class="pk-select-trigger"><span id="txt_rn_case">${L.btn_ok}</span>${CONF.crumbIcons.down}</div>
                            <div class="pk-select-menu pk-scroll">
                                <div class="pk-select-item act" data-val="">${L.btn_ok}</div>
                                <div class="pk-select-item" data-val="lower">${L.opt_rn_lower}</div>
                                <div class="pk-select-item" data-val="upper">${L.opt_rn_upper}</div>
                                <div class="pk-select-item" data-val="title">${L.opt_rn_title}</div>
                            </div>
                        </div>
                        <div class="pk-custom-select" id="cs_rn_width" style="flex:1;">
                            <div class="pk-select-label">${L.lbl_rn_width_convert}</div>
                            <div class="pk-select-trigger"><span id="txt_rn_width">${L.btn_ok}</span>${CONF.crumbIcons.down}</div>
                            <div class="pk-select-menu pk-scroll">
                                <div class="pk-select-item act" data-val="">${L.btn_ok}</div>
                                <div class="pk-select-item" data-val="half">${L.opt_rn_width_half}</div>
                                <div class="pk-select-item" data-val="full">${L.opt_rn_width_full}</div>
                            </div>
                        </div>
                    </div>

                    <div id="group_pattern" style="position:relative; display:none;">
                        <input type="text" id="rn_pattern" value="Video {n}" placeholder="Video {n}" style="${inputStyle}">
                        <div style="${labelStyle}">${L.lbl_rn_pattern}</div>
                    </div>

                    <div id="group_jav"
                         style="display:none; padding:15px; background:var(--pk-hl); border:2px dashed var(--pk-bd); border-radius:8px; color:var(--pk-fg); font-size:13px; text-align:center; user-select:none;">
                        ${L.tip_jav_mode_desc}
                    </div>

                    <div id="group_ad_remove"
                         style="display:none; padding:15px; background:var(--pk-hl); border:2px dashed var(--pk-bd); border-radius:8px; color:var(--pk-fg); font-size:13px; text-align:center; user-select:none;">
                        ${L.tip_ad_remove_desc}
                    </div>

                    <div id="group_ext_fix"
                         style="display:none; padding:15px; background:var(--pk-hl); border:2px dashed var(--pk-bd); border-radius:8px; color:var(--pk-fg); font-size:13px; text-align:center; user-select:none;">
                        ${L.tip_ext_fix_desc}
                    </div>

                </div>

                <div style="position:relative; flex:1; min-height:300px;">
                    <div style="position:absolute; inset:0; border:2px solid var(--pk-bd); border-radius:8px; display:flex; flex-direction:column; overflow:hidden;">
                        <div style="display:flex; padding:12px 20px 12px 0; background:var(--pk-bg); border-bottom:1px dashed var(--pk-bd); color:#888; font-size:12px; font-weight:bold;">
                            <div id="rn_cb_wrapper" style="width:50px; display:flex; justify-content:center; visibility: hidden;">
                                <input type="checkbox" id="rn_cb_all" checked style="accent-color:var(--pk-pri); cursor:pointer;">
                            </div>
                            <div style="width:36px; display:flex; align-items:center; justify-content:flex-start;">${L.col_type}</div>
                            <div style="flex:1;">${L.col_old}</div>
                            <div style="width:30px;"></div>
                            <div style="flex:1;">${L.col_new}</div>
                        </div>
                        <div id="pk-rn-vp" style="flex:1; overflow-y:auto; padding:0;">
                            <div id="pk-rn-in" style="position:relative;">
                                <div style="padding:40px; text-align:center; color:#888;">${L.rn_tip_wait}</div>
                            </div>
                        </div>
                    </div>
                    <div style="${labelStyle}">${L.lbl_rn_preview_title}</div>
                </div>

                <div class="pk-modal-act" style="display:grid; grid-template-columns: 1fr 1fr; gap:15px; margin-top:5px;">
                    <button class="pk-btn" id="rn_cancel" style="height:44px; justify-content:center; background:transparent; font-weight:600; font-size:15px; border-radius:8px;">${L.btn_cancel}</button>
                    <button class="pk-btn pri" id="rn_apply" disabled style="height:44px; justify-content:center; border-radius:8px; font-weight:bold; font-size:15px; border:none; background:var(--pk-pri); color:#fff;">${L.btn_ok}</button>
                </div>
            </div>
        `);

        const modalBox = m.querySelector('.pk-modal');
        Object.assign(modalBox.style, { width: '800px', maxWidth: '95vw', padding: '30px', borderRadius: '12px' });
        const closeBtn = m.querySelector('.pk-modal-close');
        if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });

        const radios = m.querySelectorAll('input[name="rn_mode"]');
        const groupsRaw = {
            jav: m.querySelector('#group_jav'),
            pattern: m.querySelector('#group_pattern'),
            replace: m.querySelector('#group_replace')
        };
        const inpPattern = m.querySelector('#rn_pattern');
        const inpFind = m.querySelector('#rn_find');
        const inpRep = m.querySelector('#rn_rep');
        const chkRegex = m.querySelector('#rn_regex');
        const chkCase = m.querySelector('#rn_case_sense');
        const chkIncludeExt = m.querySelector('#rn_include_ext');
        const btnApply = m.querySelector('#rn_apply');
        const rnVp = m.querySelector('#pk-rn-vp');
        const rnIn = m.querySelector('#pk-rn-in');
        const txtStatNum = m.querySelector('#rn_stat_num');

        const bindFocus = (el) => {
            el.onfocus = () => el.style.borderColor = 'var(--pk-pri)';
            el.onblur = () => el.style.borderColor = 'var(--pk-bd)';
        };
        [inpPattern, inpFind, inpRep].forEach(bindFocus);

        const updateInputs = () => {
            const mode = m.querySelector('input[name="rn_mode"]:checked').value;

            const groups = {
                'replace': m.querySelector('#group_replace'),
                'pattern': m.querySelector('#group_pattern'),
                'jav': m.querySelector('#group_jav'),
                'format': m.querySelector('#group_format'),
                'ad_remove': m.querySelector('#group_ad_remove'),
                'ext_fix': m.querySelector('#group_ext_fix')
            };

            Object.keys(groups).forEach(k => {
                if (groups[k]) {
                    let displayStyle = 'block';
                    if (k === 'replace' || k === 'format') displayStyle = 'flex';

                    groups[k].style.display = (k === mode) ? displayStyle : 'none';
                }
            });

            plannedChanges = [];
            rnDisplay = [];
            previewSelectedIds.clear();
            const initialTip = (mode === 'jav') ? L.rn_tip_jav : L.rn_tip_wait;
            rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#888;">${initialTip}</div>`;
            txtStatNum.innerText = "0";
            btnApply.disabled = true;

            const cbWrapper = m.querySelector('#rn_cb_wrapper');
            const cbAll = m.querySelector('#rn_cb_all');
            if (cbWrapper) cbWrapper.style.visibility = 'hidden';
            if (cbAll) { cbAll.checked = false; cbAll.indeterminate = false; }

            generatePreview();
        };
        radios.forEach(r => r.onchange = updateInputs);

        const setupInputHistory = (inputEl, storageKey) => {
            const pop = document.createElement('div');
            pop.className = 'pk-hist-pop';
            pop.style.cssText = "position:absolute; top:calc(100% + 5px); left:0; right:0; z-index:9999; display:none; flex-direction:column;";
            inputEl.parentNode.appendChild(pop);

            const loadHist = () => { try { return JSON.parse(gmGet(storageKey, '[]')); } catch { return []; } };

            const saveHist = (val) => {
                if (val === null || val === undefined) return;
                let list = loadHist();
                list = list.filter(x => x !== val);
                list.unshift(val);
                if (list.length > 3) list = list.slice(0, 3);
                gmSet(storageKey, JSON.stringify(list));
            };

            const render = () => {
                const list = loadHist();
                if (list.length === 0) { pop.style.display = 'none'; return; }

                document.querySelectorAll('.pk-hist-pop').forEach(p => p.style.display = 'none');

                let html = `<div class="pk-hist-hd"><span>${L.title_search_hist}</span><span class="pk-hist-clear-btn" id="pk-hist-del" style="cursor:pointer; opacity:0.8;">${L.btn_clear_hist}</span></div>`;

                list.forEach(txt => {
                    const displayTxt = txt === "" ? L.str_empty_str : (txt.replace(/\s/g, '') === "" ? `"${txt}"` : txt);

                    html += `<div class="pk-select-item" data-raw="${esc(txt)}" style="display:flex; align-items:center; gap:10px; padding:8px 12px;">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="flex-shrink:0; opacity:0.5;">
                            <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
                        </svg>
                        <span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1;">${esc(displayTxt)}</span>
                    </div>`;
                });

                pop.innerHTML = html;
                pop.style.display = 'flex';

                pop.querySelector('#pk-hist-del').onclick = (e) => {
                    e.stopPropagation();
                    gmSet(storageKey, '[]');
                    pop.style.display = 'none';
                };
                pop.querySelectorAll('.pk-select-item').forEach(el => {
                    el.onclick = (e) => {
                        e.stopPropagation();
                        const val = el.getAttribute('data-raw');
                        inputEl.value = val;
                        pop.style.display = 'none';
                        generatePreview();
                    };
                });
            };

            inputEl.addEventListener('focus', render);
            inputEl.addEventListener('click', (e) => { e.stopPropagation(); render(); });

            const closeHandler = (e) => {
                if (!inputEl || !pop || !pop.parentNode) return;
                if (!inputEl.contains(e.target) && !pop.contains(e.target)) pop.style.display = 'none';
            };

            setTimeout(() => document.addEventListener('click', closeHandler), 0);
            return { save: saveHist };
        };

        const findHist = setupInputHistory(inpFind, 'pk_bn_find_hist');
        const repHist = setupInputHistory(inpRep, 'pk_bn_rep_hist');

        const RN_ROW_HEIGHT = 40;
        const RN_BUFFER = 15;
        let rnDisplay = [];
        let plannedChanges = [];
        let previewSelectedIds = new Set();
        let isRnScrollScheduled = false;
        let currentPreviewId = 0;

        const VALID_EXTS_LIST = [
            'mp4', 'mkv', 'avi', 'mov', 'wmv', 'm4v', 'flv', '3gp', 'webm', 'ts', 'm2ts', 'mts', 'vob', 'mpg', 'mpeg', 'rm', 'rmvb', 'asf', 'divx', 'f4v', 'ogv', 'm2v', 'mpe',
            'mp3', 'aac', 'flac', 'wav', 'ogg', 'm4a', 'wma', 'opus', 'ape', 'alac', 'aiff', 'mid', 'midi', 'amr', 'mka', 'dts', 'ac3',
            'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tif', 'tiff', 'heic', 'raw', 'ico', 'psd', 'ai', 'eps', 'cdr',
            'srt', 'ass', 'ssa', 'vtt', 'smi', 'sub', 'idx', 'sup', 'lrc',
            'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'iso', 'img', 'dmg', 'pkg', 'apk', 'ipa', 'exe', 'msi', 'torrent', 'nzb',
            'pdf', 'txt', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'epub', 'mobi', 'azw3', 'cbz', 'cbr', 'md', 'rtf', 'csv', 'log', 'ini', 'cfg',
            'html', 'htm', 'css', 'js', 'json', 'xml', 'php', 'py', 'java', 'c', 'cpp'
        ];
        const VALID_EXTS = new Set(VALID_EXTS_LIST);

        const recalcPatternNames = () => {
            const mode = m.querySelector('input[name="rn_mode"]:checked').value;
            if (mode !== 'pattern') return;

            const pattern = inpPattern.value;
            let counter = 1;

            rnDisplay.forEach(item => {
                const originalItem = S.itemMap.get(item.id);
                const isFolder = originalItem?.kind === 'drive#folder';
                const isSelected = previewSelectedIds.has(item.id);

                if (isSelected && !isFolder) {
                    let ext = '';
                    const oldName = item.old;
                    const lastDotIndex = oldName.lastIndexOf('.');
                    if (lastDotIndex > 0) {
                        const potentialExt = oldName.substring(lastDotIndex + 1).toLowerCase();
                        if (VALID_EXTS.has(potentialExt)) ext = '.' + potentialExt;
                    }

                    const newName = pattern.replace(/{n}/g, String(counter).padStart(2, '0')) + ext;

                    item.new = newName;
                    item.newNameHTML = `<span style="color:var(--pk-pri);font-weight:bold;">${esc(newName)}</span>`;

                    counter++;
                } else {
                    item.new = item.old;
                    item.newNameHTML = `<span style="color:#aaa;text-decoration:line-through;">${esc(item.old)}</span>`;
                }
            });
        };

        const updateHeaderCheckbox = () => {
            const cbAll = m.querySelector('#rn_cb_all');
            if (!cbAll) return;
            const total = plannedChanges.length;
            const count = previewSelectedIds.size;
            cbAll.checked = total > 0 && count === total;
            cbAll.indeterminate = count > 0 && count < total;

            const validCount = rnDisplay.filter(c => c.new !== c.old && previewSelectedIds.has(c.id)).length;
            btnApply.disabled = validCount === 0;

            txtStatNum.style.display = 'inline';
            txtStatNum.innerHTML = `<span style="color:var(--pk-pri); font-weight:600;">${validCount}</span><span style="margin:0 2px;">/</span>${total}`;
        };

        const renderRNVisible = () => {
            const top = rnVp.scrollTop;
            const h = rnVp.clientHeight;
            const totalHeight = rnDisplay.length * RN_ROW_HEIGHT;
            rnIn.style.height = `${totalHeight}px`;

            const start = Math.max(0, Math.floor(top / RN_ROW_HEIGHT) - RN_BUFFER);
            const end = Math.min(rnDisplay.length, Math.ceil((top + h) / RN_ROW_HEIGHT) + RN_BUFFER);

            rnIn.innerHTML = '';
            const fragment = document.createDocumentFragment();

            const rowStyle = `
                display: flex; align-items: center;
                height: ${RN_ROW_HEIGHT}px; padding-right: 20px;
                border-bottom: 1px dashed #f0f0f0;
                box-sizing: border-box; font-size: 13px;
                color: var(--pk-fg);
            `;

            for (let i = start; i < end; i++) {
                const c = rnDisplay[i];
                if (!c) continue;
                const row = document.createElement('div');
                row.style.cssText = `position:absolute; top:${i * RN_ROW_HEIGHT}px; width:100%;` + rowStyle;

                let finalDisplayHTML = esc(c.old);
                if (c.hl_old) {
                    finalDisplayHTML = getSmartDisplayHTML(c.old, c.hl_old);
                }

                const isChecked = previewSelectedIds.has(c.id);

                const it = S.itemMap.get(c.id);
                let iconHtml = '';
                let thumbAttr = '';
                if (it) {
                    const isFolder = it.kind === 'drive#folder';
                    const mime = (it.mime_type || '').toLowerCase();
                    const isMedia = mime.startsWith('video/') || mime.startsWith('image/');
                    const hasCover = it.thumbnail_link && it.thumbnail_link !== it.icon_link;
                    const fallbackSvg = getIcon(it).replace(/width="\d+"/, 'width="24"').replace(/height="\d+"/, 'height="24"');

                    if (hasCover) {
                        thumbAttr = `data-pk-thumb="${it.thumbnail_link}"`;
                    }

                    if (!isFolder && isMedia && hasCover) {
                        iconHtml = `<img src="${it.thumbnail_link}" style="width:24px;height:24px;object-fit:cover;border-radius:4px;flex-shrink:0;" onerror="this.style.display='none';this.nextElementSibling.style.display='inline-flex';">`;
                        const secondFallback = it.icon_link
                            ? `<img src="${it.icon_link}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;">${fallbackSvg}</span>`
                            : fallbackSvg;
                        iconHtml += `<span style="display:none;align-items:center;justify-content:center;">${secondFallback}</span>`;
                    } else {
                        const iconSrc = it.icon_link;
                        iconHtml = iconSrc
                            ? `<img src="${iconSrc}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;flex-shrink:0;">${fallbackSvg}</span>`
                            : fallbackSvg;
                    }
                }

                row.innerHTML = `
                    <div style="width:50px; display:flex; justify-content:center; align-items:center; cursor:pointer;" class="rn-row-cb-area">
                        <input type="checkbox" class="rn-item-cb" ${isChecked ? 'checked' : ''} style="accent-color:var(--pk-pri); cursor:pointer;">
                    </div>
                    <div style="width:36px; display:flex; align-items:center; justify-content:flex-start;" ${thumbAttr}>${iconHtml}</div>
                    <div style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${esc(c.old)}" ${thumbAttr}>${finalDisplayHTML}</div>
                    <div style="width:30px; text-align:center; color:#ccc;">➜</div>

                    <div style="flex:1; display:flex; align-items:center; overflow:hidden;">
                        <div style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" data-pk-tip="${esc(c.new)}">
                            ${c.newNameHTML}
                        </div>
                        ${c.conflict ? `<div style="color:#d93025; font-size:10px; margin-left:6px; flex-shrink:0; background:rgba(217,48,37,0.1); padding:0 4px; border-radius:4px; white-space:nowrap;">${L.str_name_conflict}</div>` : ''}
                    </div>
                `;

                const toggleRow = () => {
                    const targetId = c.id;
                    if (previewSelectedIds.has(targetId)) previewSelectedIds.delete(targetId);
                    else previewSelectedIds.add(targetId);

                    recalcPatternNames();

                    renderRNVisible();
                    updateHeaderCheckbox();
                };

                row.onclick = () => {
                    toggleRow();
                };

                fragment.appendChild(row);
            }
            rnIn.appendChild(fragment);
        };

        rnVp.onscroll = () => {
            if (!isRnScrollScheduled) {
                requestAnimationFrame(() => { renderRNVisible(); isRnScrollScheduled = false; });
                isRnScrollScheduled = true;
            }
        };

        const handlePreviewResults = (changes, error) => {
            if (error) {
                rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#d93025;">❌ ${L.err_worker}: ${esc(error)}</div>`;
                return;
            }
            plannedChanges = changes;

            previewSelectedIds = new Set(changes.map(c => c.id));

            rnDisplay = changes.map(c => {
                let newH = esc(c.new);
                if (c.new !== c.old) {
                    newH = `<span style="color:var(--pk-pri);font-weight:bold;">${newH}</span>`;
                }
                return {
                    id: c.id,
                    old: c.old,
                    new: c.new,
                    hl_old: c.hl_old,
                    newNameHTML: newH,
                    conflict: c.conflict
                };
            });

            recalcPatternNames();

            const cbWrapper = m.querySelector('#rn_cb_wrapper');
            const cbAll = m.querySelector('#rn_cb_all');

            if (rnDisplay.length === 0) {
                const mode = m.querySelector('input[name="rn_mode"]:checked').value;
                const selectedIds = S.getSelectedIds();
                const selectedIdSet = new Set(selectedIds);
                const selectedItems = S.display.filter(i => !i.isHeader && selectedIdSet.has(i.id));
                const isSeriesFolderOnly = mode === 'pattern' && selectedItems.length > 0 && selectedItems.every(i => i.kind === 'drive#folder');
                rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#888;">${isSeriesFolderOnly ? L.rn_tip_series_folder_only : L.rn_tip_none}</div>`;
                txtStatNum.innerText = "0";
                btnApply.disabled = true;
                if (cbWrapper) cbWrapper.style.visibility = 'hidden';
                if (cbAll) { cbAll.checked = false; cbAll.disabled = true; }
            } else {
                rnVp.scrollTop = 0;
                renderRNVisible();
                updateHeaderCheckbox();
                if (cbWrapper) cbWrapper.style.visibility = 'visible';
                if (cbAll) cbAll.disabled = false;
                btnApply.disabled = false;
            }
        };

        const javState = {
            isRunning: false,
            runId: 0,
            signature: '',
            cache: new Map(),
            completed: 0,
            total: 0,
            ui: null,
            activePlannedMap: new Map(),
            activeNames: new Set()
        };

        const generatePreview = async () => {
            const myPreviewId = ++currentPreviewId;
            const mode = m.querySelector('input[name="rn_mode"]:checked').value;

            rnDisplay = [];
            plannedChanges = [];
            rnIn.innerHTML = '';

            const pattern = inpPattern.value;
            const findStr = inpFind.value;
            const repStr = inpRep.value || '';
            const useRegex = chkRegex.checked;
            const useIncludeExt = chkIncludeExt.checked;
            const caseMode = selectedCase;
            const widthMode = selectedWidth;
            const useCaseSense = m.querySelector('#rn_case_sense').checked;

            let isRuleActive = true;
            if (mode === 'replace') isRuleActive = !!findStr;
            else if (mode === 'format') isRuleActive = !!(caseMode || widthMode);
            else if (mode === 'pattern') isRuleActive = !!pattern;

            if (!isRuleActive && mode !== 'jav' && mode !== 'ad_remove' && mode !== 'ext_fix') {
                plannedChanges = [];
                rnDisplay = [];
                previewSelectedIds.clear();
                rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#888;">${L.rn_tip_wait}</div>`;
                txtStatNum.innerText = "0";
                btnApply.disabled = true;
                const cbWrapper = m.querySelector('#rn_cb_wrapper');
                if (cbWrapper) cbWrapper.style.visibility = 'hidden';
                return;
            }

            if (mode === 'replace') {
                if (findStr) findHist.save(findStr);
                if (repStr) repHist.save(repStr);
            }

            const cbWrapper = m.querySelector('#rn_cb_wrapper');
            if (cbWrapper) cbWrapper.style.visibility = 'hidden';

            rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:var(--pk-pri); display:flex; flex-direction:column; align-items:center; gap:10px;">
                <div class="pk-spinner"></div>
                <div>${L.str_calc_changes}</div>
            </div>`;
            btnApply.disabled = true;
            txtStatNum.innerText = "...";
            plannedChanges = []; rnDisplay = []; previewSelectedIds = new Set();
            const cbAll = m.querySelector('#rn_cb_all');
            if(cbAll) { cbAll.checked = false; cbAll.indeterminate = false; }

            const selectedIds = S.getSelectedIds();
            const selectedIdSet = new Set(selectedIds);
            const items = S.display.filter(i => !i.isHeader);

            if (false) {
                const targetIds =[];
                for (let i = 0; i < items.length; i++) {
                    const item = items[i];
                    const id = item.id;

                    if (!selectedIdSet.has(id)) continue;

                    if (isSystemItem(item)) continue;
                    const isFolder = item.kind === 'drive#folder';
                    const mime = (item.mime_type || '').toLowerCase();
                    const duration = (item.params && item.params.duration) || 0;
                    const isVideo = !isFolder && (mime.startsWith('video/') || duration > 0);

                    if ((isFolder || isVideo) && extractKeyword(item.name)) {
                        targetIds.push(id);
                    }
                }

                if (targetIds.length === 0) {
                    rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#888;">${L.rn_tip_none}</div>`;
                    txtStatNum.innerText = "0";
                    btnApply.disabled = true;
                    javState.isRunning = false;
                    return;
                }

                const sigIds = targetIds.length > 200
                    ? targetIds.slice(0, 100).concat(targetIds.slice(-100))
                    : targetIds;
                const currentSignature = [...sigIds].sort().join('|') + `_len_${targetIds.length}`;

                const isSameSelection = (currentSignature === javState.signature);

                if (!isSameSelection) {
                    javState.runId++;
                    javState.signature = currentSignature;
                    javState.cache.clear();
                    javState.completed = 0;
                    javState.total = targetIds.length;
                    javState.isRunning = false;
                }

                const changes = targetIds.map(id => {
                    const item = S.itemMap.get(id);
                    const cached = javState.cache.get(id);
                    if (cached) {
                        return {
                            id: item.id, old: item.name, new: cached.new,
                            hl_old: cached.hlOld, conflict: cached.conflict, parent_id: item.parent_id
                        };
                    } else {
                        return {
                            id: item.id, old: item.name, new: item.name,
                            hl_old: null, conflict: false, parent_id: item.parent_id
                        };
                    }
                });

                handlePreviewResults(changes, null);

                const plannedMap = new Map();
                plannedChanges.forEach(c => plannedMap.set(c.id, c));

                const displayMap = new Map();
                javState.activePlannedMap.clear();

                plannedChanges.forEach(c => javState.activePlannedMap.set(c.id, c));
                javState.activeNames = new Set(items.map(i => i.name));

                rnDisplay.forEach(d => {
                    displayMap.set(d.id, d);
                    const cached = javState.cache.get(d.id);
                    if (cached) {
                        if (cached.new === d.old) {
                             d.newNameHTML = `<span style="color:#aaa;">${L.str_jav_no_match}</span>`;
                        } else {
                             d.newNameHTML = `<span style="color:var(--pk-pri);font-weight:bold;">${esc(cached.new)}</span>`;
                        }
                        if (cached.hlOld) {
                             d.hl_old = cached.hlOld;
                        }
                    } else {
                        d.newNameHTML = `<span style="color:#888; display:flex; align-items:center; gap:6px; font-style:italic;"><svg width="14" height="14" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="animation:pk-spin 1s linear infinite"><path d="M12 2A10 10 0 1 0 22 12A10 10 0 0 0 12 2Zm0 18a8 8 0 1 1 8-8A8 8 0 0 1 12 20Z" opacity=".25" fill="currentColor"/><path d="M12 4a8 8 0 0 1 7.89 6.7 1 1 0 0 0 1.95-.48A10 10 0 0 0 12 2Z" fill="currentColor"/></svg> ${L.str_jav_querying}</span>`;
                    }
                });

                if (!document.getElementById('pk-spin-style')) {
                    const style = document.createElement('style');
                    style.id = 'pk-spin-style';
                    style.innerHTML = `@keyframes pk-spin { 100% { transform: rotate(360deg); } }`;
                    document.head.appendChild(style);
                }

                const updateProgress = () => {
                    if(!txtStatNum) return;
                    txtStatNum.style.display = 'inline-flex';
                    txtStatNum.style.alignItems = 'baseline';
                    txtStatNum.style.gap = '8px';

                    txtStatNum.innerHTML = `
                        <div style="width:80px; height:4px; background:#eee; border-radius:2px; overflow:hidden; flex-shrink:0; display:inline-block; align-self:center; transform:translateY(2px);">
                            <div style="width:${(javState.completed / javState.total) * 100}%; height:100%; background:var(--pk-pri); transition:width 0.2s;"></div>
                        </div>
                        <span style="display:inline-flex; align-items:baseline; gap:2px; color:inherit; vertical-align:baseline;">
                            <span style="color:var(--pk-pri); font-weight:600;">${javState.completed}</span>
                            <span style="opacity:0.6;">/</span>
                            <span>${javState.total}</span>
                        </span>`;
                };

                javState.ui = {
                    displayMap: displayMap,
                    render: renderRNVisible,
                    updateProgress: updateProgress
                };

                renderRNVisible();

                if (isSameSelection && (javState.isRunning || javState.completed === javState.total)) {
                    updateProgress();
                    if (javState.completed === javState.total) {
                        if (rnVp) rnVp.scrollTop = 0;
                        renderRNVisible();

                        btnApply.disabled = false;
                        updateHeaderCheckbox();
                        const validCount = plannedChanges.filter(c => c.new !== c.old && !c.conflict).length;
                        txtStatNum.innerHTML = `<b style="color:var(--pk-pri)">${validCount}</b> / ${javState.total}`;
                    }
                    return;
                }

                javState.isRunning = true;
                const currentRunId = javState.runId;
                btnApply.disabled = true;
                updateProgress();

                const allNames = new Set(items.map(i => i.name));
                const queue = targetIds.filter(id => !javState.cache.has(id));

                if (queue.length === 0) {
                     javState.isRunning = false;
                     btnApply.disabled = false;
                     updateHeaderCheckbox();
                     return;
                }

                let lastRenderTime = 0;

                const processItem = async (id) => {
                    if (currentRunId !== javState.runId || !document.body.contains(m)) return;

                    const item = S.itemMap.get(id);
                    if (!item) { javState.completed++; updateProgress(); return; }

                    const oldName = item.name;
                    let ext = '';

                    if (item.kind !== 'drive#folder') {
                        const extIndex = oldName.lastIndexOf('.');
                        if (extIndex > 0) {
                            const potentialExt = oldName.substring(extIndex + 1).toLowerCase();
                            if (VALID_EXTS.has(potentialExt)) {
                                ext = oldName.substring(extIndex);
                            }
                        }
                    }

                    const code = extractKeyword(oldName);

                    let newName = oldName;
                    let hlOld = null;
                    let displayHTML = `<span style="color:#aaa;">${L.str_jav_no_match}</span>`;

                    if (code) {
                        try {
                            const parts = code.split(/[^a-zA-Z0-9]+/).filter(p => p.length > 0);
                            if (parts.length > 0) {
                                const escapedParts = parts.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
                                const pattern = `(${escapedParts.join('|')})`;
                                const re = new RegExp(pattern, 'gi');
                                hlOld = oldName.replace(re, (m) => '§§§MATCH_START§§§' + m + '§§§MATCH_END§§§');
                            }
                        } catch(e) {}

                        newName = code + ext;
                        displayHTML = `<span style="color:var(--pk-pri);font-weight:bold;">${esc(newName)}</span>`;
                    }

                    let isConflict = false;
                    if (newName !== oldName) {
                         if (javState.activeNames.has(newName)) isConflict = true;
                         else javState.activeNames.add(newName);
                    }
                    javState.cache.set(id, { new: newName, hlOld, conflict: isConflict });
                    javState.completed++;

                    const currentMode = m.querySelector('input[name="rn_mode"]:checked').value;
                    if (currentMode === 'jav' && currentRunId === javState.runId && javState.ui) {
                        const displayItem = javState.ui.displayMap.get(id);
                        const planItem = javState.activePlannedMap.get(id);

                        if (displayItem) {
                            displayItem.new = newName;
                            displayItem.hl_old = hlOld;
                            displayItem.newNameHTML = displayHTML;
                            if (isConflict) displayItem.conflict = true;

                            if (planItem) {
                                planItem.new = newName;
                                planItem.hl_old = hlOld;
                                planItem.conflict = isConflict;
                            }

                            javState.ui.updateProgress();

                            const now = Date.now();
                            if (now - lastRenderTime > 500) {
                                javState.ui.render();
                                updateHeaderCheckbox();
                                lastRenderTime = now;
                            }
                        }
                    }
                };

                const CONCURRENCY = 6;
                await Promise.all(Array.from({ length: CONCURRENCY }).map(async () => {
                    while (queue.length > 0) {
                        if (currentRunId !== javState.runId) break;
                        const id = queue.shift();
                        if (id) await processItem(id);
                    }
                }));

                if (currentRunId === javState.runId) {
                    javState.isRunning = false;
                     const currentMode = m.querySelector('input[name="rn_mode"]:checked').value;
                     if (currentMode === 'jav') {
                        if (rnVp) rnVp.scrollTop = 0;
                        renderRNVisible();

                        btnApply.disabled = false;
                        updateHeaderCheckbox();
                        const validCount = plannedChanges.filter(c => c.new !== c.old && !c.conflict).length;
                        txtStatNum.style.display = 'inline';
                        txtStatNum.innerHTML = `<span style="color:var(--pk-pri); font-weight:600;">${validCount}</span><span style="margin:0 2px;">/</span>${javState.total}`;
                     }
                }
                return;
            }

            const workerFunction = function(e) {
                try {
                    const { selectedIds, items, mode, pattern, findStr, repStr, useRegex, validExtsList, caseMode, widthMode, useCaseSense, useIncludeExt } = e.data;
                    const changes =[];
                    const idSet = new Set(selectedIds);
                    const allNames = new Set();
                    items.forEach(i => allNames.add(i.name));

                    const VALID_EXTS = new Set(validExtsList);

                    const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

                    const extractKeyword = (fileName) => {
                        const fc2Regex = /(?:FC2|FC)(?:[-_. ]*PPV)?[-_. @]*(\d{5,8})(?:[-_. ]*(?:part|pt|cd)?[-_. ]?(\d{1,2}|[a-e]))?(?![a-z\d])/i;
                        const fc2Match = fileName.match(fc2Regex);
                        if (fc2Match) {
                            const id = fc2Match[1];
                            const part = fc2Match[2];
                            return (part && part.trim()) ? `FC2-PPV-${id}-${part.toUpperCase()}` : `FC2-PPV-${id}`;
                        }
                        return null;
                    };

                    const toHalf = (str) => str.replace(/[!-~]/g, c => String.fromCharCode(c.charCodeAt(0) - 0xFEE0)).replace(/ /g, ' ');
                    const toFull = (str) => str.replace(/[!-~]/g, c => String.fromCharCode(c.charCodeAt(0) + 0xFEE0)).replace(/ /g, ' ');
                    const toTitle = (str) => str.replace(/\b\w/g, c => c.toUpperCase());

                    let regex = null;
                    if (mode === 'replace' && findStr) {
                        try {
                            const flags = useCaseSense ? 'g' : 'gi';
                            const p = useRegex ? findStr : escapeRegExp(findStr);
                            regex = new RegExp(p, flags);
                        } catch (err) {}
                    }

                    let counter = 1;
                    for (let i = 0; i < items.length; i++) {
                        const item = items[i];
                        if (!idSet.has(item.id)) continue;
                        const oldName = item.name;
                        let newName = oldName, hlOld = null;

                        if (mode === 'pattern') {
                            if (item.kind === 'drive#folder') continue;
                            let ext = '';
                            const lastDotIndex = oldName.lastIndexOf('.');
                            if (lastDotIndex > 0) {
                                const potentialExt = oldName.substring(lastDotIndex + 1).toLowerCase();
                                if (VALID_EXTS.has(potentialExt)) ext = '.' + potentialExt;
                            }
                            newName = pattern.replace(/{n}/g, String(counter).padStart(2, '0')) + ext;
                            counter++;
                        }
                        else if (mode === 'replace') {
                            if (findStr && regex) {
                                let targetStr = oldName;
                                let extStr = "";

                                if (!useIncludeExt && item.kind !== 'drive#folder') {
                                    const lastDot = oldName.lastIndexOf('.');
                                    if (lastDot > 0) {
                                        targetStr = oldName.substring(0, lastDot);
                                        extStr = oldName.substring(lastDot);
                                    }
                                }

                                if (regex.test(targetStr)) {
                                    regex.lastIndex = 0;
                                    const newBase = targetStr.replace(regex, repStr);
                                    regex.lastIndex = 0;
                                    const hlBase = targetStr.replace(regex, (m) => '§§§MATCH_START§§§' + m + '§§§MATCH_END§§§');

                                    newName = newBase + extStr;
                                    hlOld = hlBase + extStr;
                                }
                            }
                        }
                        else if (mode === 'format') {
                            let base = newName;
                            let ext = '';
                            if (item.kind !== 'drive#folder') {
                                const lastDot = newName.lastIndexOf('.');
                                if (lastDot > 0) {
                                    base = newName.substring(0, lastDot);
                                    ext = newName.substring(lastDot);
                                }
                            }
                            if (widthMode === 'half') base = toHalf(base);
                            else if (widthMode === 'full') base = toFull(base);
                            if (caseMode === 'upper') base = base.toUpperCase();
                            else if (caseMode === 'lower') base = base.toLowerCase();
                            else if (caseMode === 'title') base = toTitle(base);
                            newName = base + ext;
                        }
                        else if (mode === 'jav') {
                            const code = extractKeyword(oldName);
                            if (code) {
                                let ext = '', ext_old = '', base_old = oldName;
                                if (item.kind !== 'drive#folder') {
                                    const lastDotIndex = oldName.lastIndexOf('.');
                                    if (lastDotIndex > 0) {
                                        const potentialExt = oldName.substring(lastDotIndex + 1).toLowerCase();
                                        if (VALID_EXTS.has(potentialExt)) {
                                            ext = oldName.substring(lastDotIndex);
                                            ext_old = ext;
                                            base_old = oldName.substring(0, lastDotIndex);
                                        }
                                    }
                                }
                                newName = code + ext;
                                try {
                                    const parts = code.split(/[^a-zA-Z0-9]+/).filter(p => p.length > 0);
                                    if (parts.length > 0) {
                                        const escapedParts = parts.map(p => escapeRegExp(p));
                                        const pat = `(${escapedParts.join('|')})`;
                                        const re = new RegExp(pat, 'gi');
                                        hlOld = base_old.replace(re, (m) => '§§§MATCH_START§§§' + m + '§§§MATCH_END§§§') + ext_old;
                                    }
                                } catch(e) {}
                            }
                        }
                        else if (mode === 'ad_remove') {
                            let cleanName = oldName;

                            cleanName = cleanName.replace(/^【[^】]+】 *[-_.]? */, '');

                            cleanName = cleanName.replace(/^[a-z0-9-]+[.](?:com|net|org|cc|xyz|vip|top|la) +/i, '');

                            const adKw = "(?:[.]com|[.]net|[.]org|[.]cc|[.]xyz|[.]vip|[.]top|[.]la|2048|www[.])";

                            const atRegex = new RegExp('^.*?' + adKw + '.*?(?:@|--+|_\\s)', 'i');
                            cleanName = cleanName.replace(atRegex, '');

                            const hyphenRegex = new RegExp('^[a-z0-9.-]+' + adKw + '-', 'i');
                            cleanName = cleanName.replace(hyphenRegex, '');

                            cleanName = cleanName.replace(/^(?:精品加群|福利合集)[0-9]+[-_]+ */, '');

                            cleanName = cleanName.replace(/^[-_. ,,::;;\p{Extended_Pictographic}]+/u, '');

                            const idxChnR_Fix = cleanName.indexOf('】');
                            const idxChnL_Check = cleanName.indexOf('【');
                            if (idxChnR_Fix > 0 && idxChnR_Fix <= 10 && (idxChnL_Check === -1 || idxChnL_Check > idxChnR_Fix)) {
                                cleanName = '【' + cleanName;
                            }
                            const idxEngR_Fix = cleanName.indexOf(']');
                            const idxEngL_Check = cleanName.indexOf('[');
                            if (idxEngR_Fix > 0 && idxEngR_Fix <= 10 && (idxEngL_Check === -1 || idxEngL_Check > idxEngR_Fix)) {
                                cleanName = '[' + cleanName;
                            }
                            const idxBkR_Fix = cleanName.indexOf('》');
                            const idxBkL_Check = cleanName.indexOf('《');
                            if (idxBkR_Fix > 0 && idxBkR_Fix <= 10 && (idxBkL_Check === -1 || idxBkL_Check > idxBkR_Fix)) {
                                cleanName = '《' + cleanName;
                            }
                            const idxAngR_Fix = cleanName.indexOf('>');
                            const idxAngL_Check = cleanName.indexOf('<');
                            if (idxAngR_Fix > 0 && idxAngR_Fix <= 10 && (idxAngL_Check === -1 || idxAngL_Check > idxAngR_Fix)) {
                                cleanName = '<' + cleanName;
                            }
                            const idxChnParR_Fix = cleanName.indexOf(')');
                            const idxChnParL_Check = cleanName.indexOf('(');
                            if (idxChnParR_Fix > 0 && idxChnParR_Fix <= 10 && (idxChnParL_Check === -1 || idxChnParL_Check > idxChnParR_Fix)) {
                                cleanName = '(' + cleanName;
                            }
                            const idxEngParR_Fix = cleanName.indexOf(')');
                            const idxEngParL_Check = cleanName.indexOf('(');
                            if (idxEngParR_Fix > 0 && idxEngParR_Fix <= 10 && (idxEngParL_Check === -1 || idxEngParL_Check > idxEngParR_Fix)) {
                                cleanName = '(' + cleanName;
                            }
                            const idxCurR_Fix = cleanName.indexOf('}');
                            const idxCurL_Check = cleanName.indexOf('{');
                            if (idxCurR_Fix > 0 && idxCurR_Fix <= 10 && (idxCurL_Check === -1 || idxCurL_Check > idxCurR_Fix)) {
                                cleanName = '{' + cleanName;
                            }

                            const cleanStack = (L, R) => {
                                const chars = cleanName.split('');
                                const stack = [];
                                const toRemove = new Set();
                                for (let i = 0; i < chars.length; i++) {
                                    const c = chars[i];
                                    if (c === L) {
                                        stack.push(i);
                                    } else if (c === R) {
                                        if (stack.length > 0) stack.pop();
                                        else toRemove.add(i);
                                    }
                                }
                                stack.forEach(i => toRemove.add(i));
                                if (toRemove.size > 0) {
                                    cleanName = chars.filter((_, i) => !toRemove.has(i)).join('');
                                }
                            };

                            cleanStack('【', '】');
                            cleanStack('[', ']');
                            cleanStack('{', '}');
                            cleanStack('(', ')');
                            cleanStack('(', ')');
                            cleanStack('《', '》');
                            cleanStack('<', '>');

                            const quote2 = (cleanName.match(/'/g) || []).length;
                            if (quote2 % 2 !== 0) cleanName = cleanName.replace(/"/, '');

                            const result = cleanName.trim();

                            const lastDot = oldName.lastIndexOf('.');
                            if (lastDot !== -1) {
                                const ext = oldName.substring(lastDot);
                                const extNoDot = ext.substring(1);

                                if (!result || result === ext || result === extNoDot) {
                                    newName = oldName;
                                } else {
                                    newName = result;
                                }
                            } else {
                                if (!result) newName = oldName;
                                else newName = result;
                            }
                        }

                        else if (mode === 'ext_fix') {

                            const mimeMap = {
                                'video/mp4': ['.mp4', '.m4v', '.f4v', '.mp4v', '.mov', '.avi', '.m4a', '.m4b'],
                                'video/x-matroska': ['.mkv', '.mk3d', '.mka', '.mks'],
                                'video/x-msvideo': ['.avi'],
                                'video/quicktime': ['.mov', '.qt', '.mp4', '.m4v'],
                                'video/x-flv': ['.flv'],
                                'video/webm': ['.webm'],
                                'video/mpeg': ['.mpg', '.mpeg', '.mpe', '.vob'],
                                'video/3gpp': ['.3gp', '.3g2', '.mp4'],
                                'video/mp2t': ['.ts', '.m2ts', '.mts'],
                                'video/x-m4v': ['.m4v', '.mp4'],
                                'video/x-ms-wmv': ['.wmv'],
                                'video/x-ms-asf': ['.asf', '.wmv', '.wma'],

                                'audio/mpeg': ['.mp3', '.mp2'],
                                'audio/mp4': ['.m4a', '.m4b', '.mp4'],
                                'audio/x-wav': ['.wav'],
                                'audio/flac': ['.flac'],
                                'audio/aac': ['.aac'],
                                'audio/ogg': ['.ogg', '.opus'],
                                'audio/x-ms-wma': ['.wma'],
                                'audio/webm': ['.weba'],

                                'image/jpeg': ['.jpg', '.jpeg', '.jpe', '.jif', '.jfif'],
                                'image/png': ['.png'],
                                'image/gif': ['.gif'],
                                'image/webp': ['.webp'],
                                'image/bmp': ['.bmp'],
                                'image/svg+xml': ['.svg'],
                                'image/heif': ['.heic', '.heif'],
                                'image/vnd.adobe.photoshop': ['.psd', '.abr'],
                                'image/x-icon': ['.ico'],
                                'image/vnd.microsoft.icon': ['.ico'],
                                'image/tiff': ['.tif', '.tiff', '.cr2', '.cr3', '.nef', '.dng', '.arw', '.orf', '.rw2', '.pef', '.sr2', '.raf'],
                                'application/postscript': ['.ps', '.eps', '.ai'],
                                'application/dicom': ['.dcm'],

                                'application/zip': [
                                    '.zip',
                                    '.exe',
                                    '.pt', '.pth',
                                    '.apk', '.xapk', '.apks', '.obb', '.aar', '.aab', '.ipa', '.ipsw', '.wgt',
                                    '.docx', '.docm', '.dotx', '.dotm',
                                    '.xlsx', '.xlsm', '.xltx', '.xltm',
                                    '.pptx', '.pptm', '.potx', '.potm',
                                    '.vsdx', '.xmind', '.xlam', '.thmx',
                                    '.odt', '.ods', '.odp', '.oxps', '.xps',
                                    '.pages', '.numbers', '.key',
                                    '.xd', '.idml', '.zxp', '.fig', '.sketch', '.brush', '.brushset', '.3mf', '.usdz', '.dwfx', '.ora', '.ufo',
                                    '.h5p', '.apkg', '.colpkg', '.mcpack', '.mcworld', '.unitypackage', '.sb3', '.love', '.egg',
                                    '.nsp', '.xci', '.cia',
                                    '.alfredworkflow', '.sublime-package', '.otf', '.ttf', '.woff', '.woff2',
                                    '.epub', '.kmz', '.cbz',
                                    '.jar', '.war', '.ear', '.sar', '.whl', '.nupkg', '.wsz', '.crx', '.xpi', '.vsix', '.msix', '.appx', '.msixbundle', '.appxbundle', '.kra', '.appv',
                                    '.jks', '.keystore', '.truststore'
                                ],

                                'application/x-rar-compressed': ['.rar', '.cbr', '.exe'],
                                'application/x-rar': ['.rar', '.cbr', '.exe'],
                                'application/vnd.rar': ['.rar', '.cbr', '.exe'],

                                'application/x-7z-compressed': ['.7z', '.exe', '.cb7', '.wim', '.esd'],
                                'application/x-tar': ['.tar', '.cbt', '.ova', '.unitypackage', '.gem'],
                                'application/gzip': ['.gz', '.tgz', '.svgz', '.als', '.schematic', '.litematic', '.tgs', '.unitypackage', '.box'],

                                'application/x-lzh-compressed': ['.lzh', '.lha'],
                                'application/x-lha': ['.lzh', '.lha'],

                                'application/x-iso9660-image': ['.iso', '.img'],
                                'application/vnd.android.package-archive': ['.apk'],
                                'application/x-apple-diskimage': ['.dmg'],
                                'application/x-debian-package': ['.deb'],
                                'application/x-redhat-package-manager': ['.rpm'],

                                'application/pdf': ['.pdf', '.ai'],

                                'text/plain': [
                                    '.txt', '.log', '.md', '.markdown', '.nfo', '.rtf', '.rst', '.adoc', '.org','.mhtml', '.mht',
                                    '.dts', '.dtsi',
                                    '.ofx', '.qif', '.gnucash',
                                    '.tscn', '.tres', '.gd', '.godot',
                                    '.out', '.err', '.pid',
                                    '.asc', '.md5', '.sha1', '.sha256', '.sha512',
                                    '.dockerfile', '.makefile', '.jenkinsfile', '.tf',
                                    '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.astro', '.mdx',
                                    '.css', '.scss', '.less', '.html', '.htm', '.pug', '.jade', '.coffee', '.wat', '.pac',
                                    '.graphql', '.gql', '.prisma',
                                    '.py', '.java', '.c', '.cpp', '.h', '.hh', '.hpp', '.cs', '.php', '.go', '.rs', '.rb', '.lua',
                                    '.kt', '.swift', '.dart', '.pl', '.pm', '.scala', '.groovy', '.hs', '.asp', '.aspx', '.jsp',
                                    '.m', '.mm', '.r', '.rmd', '.jl', '.nb',
                                    '.ex', '.exs', '.erl', '.hrl', '.clj', '.lisp', '.ml',
                                    '.v', '.sv', '.vhd', '.vhdl', '.sas', '.do',
                                    '.sh', '.bat', '.cmd', '.ps1', '.psd1', '.psm1', '.vbs', '.reg', '.vmx',
                                    '.lock', '.toml', '.hex', '.gradle', '.cmake', '.editorconfig',
                                    '.ini', '.cfg', '.conf', '.rc', '.list', '.yaml', '.yml', '.json', '.xml', '.properties', '.env', '.gitignore', '.sql', '.drawio', '.dio',
                                    '.htaccess', '.npmrc', '.eps', '.ps',
                                    '.meta', '.asset',
                                    '.fbx', '.step', '.stp', '.iges', '.igs', '.gcode', '.stl', '.ply',
                                    '.fasta', '.fa',
                                    '.eml', '.mbox', '.ics', '.ifb',
                                    '.vcf', '.ovpn', '.glsl', '.hlsl', '.shader', '.cginc', '.unity',
                                    '.pem', '.key', '.crt', '.csr', '.p7b', '.p7c',
                                    '.tex', '.sty', '.cls', '.bib',
                                    '.srt', '.ass', '.ssa', '.sub', '.vtt', '.smi', '.lrc', '.sup', '.idx', '.sbv',
                                    '.m3u', '.m3u8', '.cue', '.torrent'
                                ],

                                'text/html': ['.html', '.htm', '.mhtml', '.mht', '.vue', '.svelte', '.astro', '.txt'],

                                'text/xml': [
                                    '.xml', '.ui', '.opml', '.kml', '.gpx', '.rss', '.nfo', '.txt', '.svg', '.plist',
                                    '.mobileconfig', '.webloc', '.ttml', '.musicxml',
                                    '.drawio', '.dio', '.csproj', '.vbproj', '.xaml', '.kdenlive', '.fb2', '.xmp', '.dae',
                                    '.fods', '.fodt', '.fodp', '.mobileprovision',
                                    '.nuspec', '.resx', '.vbox', '.osm',
                                    '.application', '.manifest'
                                ],

                                'application/json': [
                                    '.json', '.txt', '.ipynb', '.gltf', '.geojson', '.map', '.har',
                                    '.topojson', '.webmanifest', '.postman_collection', '.tfstate', '.webapp',
                                    '.uproject', '.uplugin', '.glyphs'
                                ],

                                'application/x-hdf': ['.h5', '.hdf5', '.keras'],
                                'application/x-hdf5': ['.h5', '.hdf5', '.keras'],

                                'text/calendar': ['.ics', '.ifb'],
                                'application/x-bittorrent': ['.torrent'],
                                'message/rfc822': ['.mhtml', '.mht', '.eml'],
                                'multipart/related': ['.mhtml', '.mht'],
                                'application/x-mobipocket-ebook': ['.mobi', '.azw3'],
                                'application/vnd.amazon.ebook': ['.azw3', '.mobi'],
                                'text/vcard': ['.vcf'],

                                'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx', '.docm', '.dotx', '.dotm'],
                                'application/msword': ['.doc'],
                                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx', '.xlsm', '.xltx', '.xltm', '.csv'],
                                'application/vnd.ms-excel': ['.xls', '.csv'],
                                'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['.pptx', '.pptm', '.potx', '.potm'],
                                'application/vnd.ms-powerpoint': ['.ppt'],
                                'application/epub+zip': ['.epub', '.zip']
                            };

                            const exactNamesToKeep = new Set([
                                'thumbs.db', 'desktop.ini', '.ds_store',
                                'dockerfile', 'makefile', 'jenkinsfile', 'rakefile', 'gemfile', 'vagrantfile', 'procfile',
                                'license', 'readme', 'changelog', 'copying', 'authors', 'cmakelists.txt',
                                'contributors', 'patents', 'security', 'notice', 'version',
                                'cname', 'owners', 'robots.txt',
                                'go.mod', 'go.sum', 'podfile', 'podfile.lock', 'yarn.lock', 'package-lock.json'
                            ]);

                            const binaryMimes = ['application/octet-stream', 'binary/octet-stream'];

                            const safeImgExts = ['.jpg', '.jpeg', '.png', '.bmp', '.heic'];
                            const safeVidExts = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.3gp', '.ts', '.mpg', '.mpeg', '.vob', '.rmvb', '.asf'];
                            const highRiskExts = [
                                '.rar', '.zip', '.7z', '.iso', '.img', '.dmg', '.apk', '.ipa',
                                '.mhtml', '.mht', '.html', '.htm', '.xml', '.json',
                                '.db', '.dat', '.tmp', '.dts', '.dtsi',
                                '.ts', '.3gp', '.mkv', '.avi', '.mp4', '.flv', '.mov', '.wmv'
                            ];

                            if (item.mimeType === 'application/vnd.google-apps.folder') continue;

                            const pureMimeType = (item.mimeType || '').split(';')[0].trim().toLowerCase();
                            const lowerName = oldName.toLowerCase();
                            const lastDotIndex = oldName.lastIndexOf('.');
                            const currentExt = lastDotIndex !== -1 ? oldName.substring(lastDotIndex).toLowerCase() : '';

                            const isPartFile = /^\.\d+$/.test(currentExt) || /\.part\d+$/i.test(currentExt);

                            let newName = oldName;
                            let shouldFix = false;
                            if (binaryMimes.includes(pureMimeType) || exactNamesToKeep.has(lowerName) || oldName.startsWith('.') || isPartFile) {
                                shouldFix = false;
                            }
                            else {
                                const validExtensions = mimeMap[pureMimeType];
                                if (validExtensions && validExtensions.length > 0) {
                                    const primaryExt = validExtensions[0];

                                    if (validExtensions.includes(currentExt)) {
                                        newName = oldName.substring(0, lastDotIndex) + currentExt;
                                        shouldFix = true;
                                    }
                                    else if (
                                        (safeImgExts.includes(currentExt) && safeImgExts.includes(primaryExt)) ||
                                        (safeVidExts.includes(currentExt) && safeVidExts.includes(primaryExt))
                                    ) {
                                        newName = oldName.substring(0, lastDotIndex) + currentExt;
                                        shouldFix = true;
                                    }
                                    else if (
                                        (pureMimeType === 'application/pdf' && ['.rar', '.zip', '.7z'].includes(currentExt)) ||
                                        highRiskExts.includes(currentExt)
                                    ) {
                                        shouldFix = false;
                                    }
                                    else {
                                        shouldFix = true;

                                        if (lastDotIndex === -1) {
                                            const ambiguousTextExts = ['.svg', '.html', '.htm', '.xml', '.json'];
                                            if (ambiguousTextExts.includes(primaryExt) && !oldName.includes(' ')) {
                                                shouldFix = false;
                                            } else {
                                                if (ambiguousTextExts.includes(primaryExt)) newName = oldName + '.txt';
                                                else newName = oldName + primaryExt;
                                            }
                                        }
                                        else {
                                            const isSourceText = ['.txt', '.log', '.md', '.ini', '.nfo'].includes(currentExt);
                                            const ambiguousTextExts = ['.svg', '.html', '.htm', '.xml', '.json'];

                                            if (isSourceText && ambiguousTextExts.includes(primaryExt)) {
                                                newName = oldName.substring(0, lastDotIndex) + currentExt;
                                            } else {
                                                newName = oldName.substring(0, lastDotIndex) + primaryExt;
                                            }
                                        }
                                    }
                                }
                            }

                            if (shouldFix && newName !== oldName) {
                                changes.push({
                                    id: item.id, old: oldName, new: newName,
                                    hl_old: null, conflict: false, parent_id: item.parent_id
                                });
                            }
                        }

                        if (item._isSystem) continue;

                        if (newName !== oldName || (mode === 'replace' && hlOld) || mode === 'pattern') {
                            let isConflict = false;
                            const cleanNewName = newName.trim();
                            if (!cleanNewName) { isConflict = true; newName = e.data.STR_EMPTY_FILENAME; }
                            else if (allNames.has(cleanNewName)) { isConflict = true; }
                            else { allNames.add(cleanNewName); }

                            changes.push({
                                id: item.id, old: oldName, new: newName,
                                hl_old: hlOld, conflict: isConflict, parent_id: item.parent_id
                            });
                        }
                    }
                    self.postMessage({ changes: changes, error: null });
                } catch (e) { self.postMessage({ changes: [], error: e.toString() }); }
            };

            const workerCode = 'self.onmessage = ' + workerFunction.toString();

            const itemsCopy = items.map(i => ({ id: i.id, name: i.name, kind: i.kind, mimeType: i.mime_type, parent_id: i.parent_id, _isSystem: isSystemItem(i) }));
            const blob = new Blob([workerCode], { type: 'application/javascript' });
            const workerUrl = URL.createObjectURL(blob);
            const previewWorker = new Worker(workerUrl);
            previewWorker.onmessage = (e) => {
                const { changes, error } = e.data;
                previewWorker.terminate();
                URL.revokeObjectURL(workerUrl);

                if (myPreviewId !== currentPreviewId) return;

                handlePreviewResults(changes, error);
            };
            previewWorker.onerror = (e) => {
                console.error("Worker Error:", e);
                rnIn.innerHTML = `<div style="padding:40px; text-align:center; color:#d93025;">❌ ${L.err_worker_failed}</div>`;
                previewWorker.terminate();
                URL.revokeObjectURL(workerUrl);
            };
            previewWorker.postMessage({
                selectedIds, items: itemsCopy, mode, pattern, findStr, repStr, useRegex,
                STR_INVALID_REGEX: L.err_invalid_regex,
                STR_EMPTY_FILENAME: L.str_empty_filename,
                validExtsList: VALID_EXTS_LIST,
                caseMode, widthMode, useCaseSense, useIncludeExt
            });
        };

        m.querySelector('#rn_cancel').onclick = () => m.remove();
        [inpPattern, inpFind, inpRep, chkRegex, chkCase, chkIncludeExt].forEach(el => el.onchange = generatePreview);
        [inpPattern, inpFind, inpRep].forEach(el => el.onkeydown = (e) => { if(e.key==='Enter') generatePreview(); });

        let selectedCase = "";
        let selectedWidth = "";

        const bindRNSelect = (id, onSelect) => {
            const container = m.querySelector(`#${id}`);
            if (!container) return;
            const trigger = container.querySelector('.pk-select-trigger');
            const menu = container.querySelector('.pk-select-menu');
            const txt = container.querySelector('span');
            const items = container.querySelectorAll('.pk-select-item');

            trigger.onclick = (e) => {
                e.stopPropagation();
                const allMenus = m.querySelectorAll('.pk-select-menu');
                const isOpen = menu.style.display === 'block';
                allMenus.forEach(om => om.style.display = 'none');
                menu.style.display = isOpen ? 'none' : 'block';
            };

            items.forEach(item => {
                item.onclick = (e) => {
                    e.stopPropagation();
                    items.forEach(i => i.classList.remove('act'));
                    item.classList.add('act');
                    txt.textContent = item.textContent;
                    menu.style.display = 'none';
                    onSelect(item.dataset.val);
                    generatePreview();
                };
            });
        };

        bindRNSelect('cs_rn_case', (val) => { selectedCase = val; });
        bindRNSelect('cs_rn_width', (val) => { selectedWidth = val; });

        const closeDropdowns = () => m.querySelectorAll('.pk-select-menu').forEach(menu => menu.style.display = 'none');
        setTimeout(() => document.addEventListener('click', closeDropdowns), 0);

        const _orgRemove = m.remove.bind(m);
        m.remove = () => {
            document.removeEventListener('click', closeDropdowns);
            _orgRemove();
        };

        const bindHeaderCheckbox = () => {
            const cbAll = m.querySelector('#rn_cb_all');
            if(cbAll) {
                cbAll.onclick = (e) => {
                    const isChecked = e.target.checked;
                    if(isChecked) {
                        plannedChanges.forEach(c => previewSelectedIds.add(c.id));
                    } else {
                        previewSelectedIds.clear();
                    }
                    recalcPatternNames();
                    renderRNVisible();
                    updateHeaderCheckbox();
                };
            }
        };
        setTimeout(bindHeaderCheckbox, 0);

        btnApply.onclick = async () => {

            const validChanges = rnDisplay.filter(c => previewSelectedIds.has(c.id) && c.new !== c.old);
            const skippedCount = plannedChanges.length - validChanges.length;

            if (validChanges.length === 0) {
                showAlert(L.msg_rn_all_skipped);
                return;
            }

            let confirmMsg = L.rn_warn_confirm.replace('{n}', validChanges.length);
            if (!await showConfirm(confirmMsg)) return;

            const progressTask = FloatBarManager.create(L.str_renaming);
            m.remove();
            let isRunning = true;
            UI.stopBtn.onclick = () => { isRunning = false; updateLoadTxt(L.str_stopping); };

            const USER_LIMIT = parseInt(localStorage.getItem('pk_user_limit') || "200");
            let currentLimit = 2; const MIN_LIMIT = 2;
            const queue = [...validChanges];
            const activeTasks = new Set();
            const stats = { success: 0, fail: 0, lastUiTime: 0 };
            const total = validChanges.length;

            const runRenameTask = async (task) => {
                try {
                    await apiAction(`/${task.id}`, { name: task.new });
                    stats.success++;
                    const item = S.itemMap.get(task.id);
                    if (item) {
                        item.name = task.new;
                        const nowIso = new Date(getServerNow()).toISOString();
                        item.modified_time = nowIso;
                        if (item.kind === 'drive#folder') gmSet('pk_fmod_' + item.id, nowIso);
                        const parentIdForFmod = item.parent_id || 'root';
                        gmSet('pk_fmod_' + parentIdForFmod, nowIso);

                        const row = UI.in.querySelector(`.pk-row[data-id="${task.id}"]`);
                        if (row && row.lastElementChild) row.lastElementChild.textContent = fmtDate(nowIso);

                        if (S.analyzeResultItems) {
                            const anaItem = S.analyzeResultItems.find(x => x.id === task.id);
                            if (anaItem) anaItem.name = task.new;
                        }
                        if (S.analyzeMap && S.analyzeMap.has(task.id)) {
                            S.analyzeMap.get(task.id).name = task.new;
                        }
                        if (S.lastGlobalResults && S.lastGlobalResults.length > 0) {
                            const gItem = S.lastGlobalResults.find(x => x.id === task.id);
                            if (gItem) gItem.name = task.new;
                        }

                        if (typeof globalDirtyFolders !== 'undefined') {
                            const pid = item.parent_id || '';
                            globalDirtyFolders.add(pid);
                        }
                    }
                    if (currentLimit < USER_LIMIT) currentLimit++;
                } catch (e) {
                    if (!isRunning) return;
                    if ((e.message && e.message.includes('429')) || (e.message && e.message.includes('Network'))) {
                        currentLimit = Math.max(MIN_LIMIT, Math.floor(currentLimit / 2));
                        task.retryCount = (task.retryCount || 0) + 1;
                        await sleep(Math.min(task.retryCount * 1000, 10000));
                        queue.push(task);
                    } else {
                        stats.fail++;
                    }
                }
            };

            try {
                updateLoadTxt(L.str_init_rename);
                while ((queue.length > 0 || activeTasks.size > 0) && isRunning) {
                    while (queue.length > 0 && activeTasks.size < currentLimit && isRunning) {
                        const task = queue.shift();
                        const p = runRenameTask(task).finally(() => activeTasks.delete(p));
                        activeTasks.add(p);
                    }
                    if (activeTasks.size > 0) await Promise.race(activeTasks);
                    const now = Date.now();
                    if (now - stats.lastUiTime > 150) {
                        progressTask.update(`${L.str_renaming} ${stats.success + stats.fail}/${total} | ${L.str_speed}: ${activeTasks.size} | ${L.str_success}: ${stats.success}`);
                        stats.lastUiTime = now;
                    }
                }
                if (!isRunning) throw new Error('StoppedByUser');
                updateLoadTxt(L.str_refreshing_cache);

                if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();

                if (S.dupMode) renderDupView(); else refresh();
                let msgParts = [];
                if (stats.success > 0) msgParts.push(L.msg_bulkrename_done.replace('{n}', stats.success));
                if (stats.fail > 0) msgParts.push(L.msg_rn_fail_count.replace('{n}', stats.fail));
                const finalMsg = msgParts.join('\n');
                await sleep(300); showAlert(finalMsg);
            } catch (e) {
                if (e.message !== 'StoppedByUser') showAlert(`${L.str_error_crit}: ${e.message}`);
            } finally {
                if (progressTask) progressTask.destroy();
                setLoad(false);
            }
        };
    };

    UI.btnPrune.onclick = async () => {
        isGUISensitive = true;
        ensureItemMap();

        const selectedFolders = S.getSelectedIds()
            .map(id => S.itemMap.get(id))
            .filter(i => i && i.kind === 'drive#folder');

        if (selectedFolders.length === 0) return;
        const hasConflict = selectedFolders.some(f => isPathBusy(f.id));
        if (hasConflict) {
            showAlert(L.msg_prune_blocked_moving);
            return;
        }
        if (!await showConfirm(L.msg_prune_confirm)) return;

        setLoad(true);
        S.scanning = true;
        S.scanId = (S.scanId || 0) + 1;
        const myScanId = S.scanId;

        if (S.scanAbortController) S.scanAbortController.abort();
        S.scanAbortController = new AbortController();
        const signal = S.scanAbortController.signal;

        UI.stopBtn.onclick = () => {
            S.scanning = false;
            if (S.scanAbortController) S.scanAbortController.abort();
            updateLoadTxt(L.str_stopping);
            setLoad(false);
            isGUISensitive = false;
        };

        const folderMap = new Map();
        updateLoadTxt(L.str_scanning_dir);

        try {
            await coreRecursiveEngine(selectedFolders, {
                signal: signal,
                onFolder: (folder, filesInFolder, subFolders) => {
                    const hasFiles = filesInFolder.some(f => f.kind !== 'drive#folder');

                    folderMap.set(folder.id, {
                        id: folder.id,
                        name: folder.name,
                        parent_id: folder.parent_id,
                        depth: folder.depth || 0,
                        hasFiles: hasFiles,
                        subFolderIds: subFolders.map(s => s.id)
                    });
                },
                onProgress: (st) => {
                    const folderText = `${L.str_scanning_dir} ${st.folders} ${L.unit_folders}`;

                    const statusInfo = ` | ${L.str_files}: ${st.files} | ${L.str_speed}: ${st.currentConcurrency} | ${L.str_cached} ${st.cacheHits} ${L.unit_folders}`;

                    updateLoadTxt(folderText + statusInfo);
                }
            });

            if (!S.scanning || signal.aborted || myScanId !== S.scanId) return;

            updateLoadTxt(L.str_analyzing);
            await sleep(50);

            const allScanned = Array.from(folderMap.values()).sort((a, b) => b.depth - a.depth);
            const toDeleteList = [];
            const toDeleteIds = new Set();

            for (let i = 0; i < allScanned.length; i++) {
                if (!S.scanning) return;
                const folder = allScanned[i];

                const isSystemProtected = isSystemItem({ ...folder, kind: 'drive#folder' });
                if (isSystemProtected) continue;

                if (!folder.hasFiles) {
                    const allSubsWillBeDeleted = folder.subFolderIds.every(subId => toDeleteIds.has(subId));
                    if (allSubsWillBeDeleted) {
                        toDeleteIds.add(folder.id);
                        toDeleteList.push(folder);
                    }
                }
            }

            if (toDeleteList.length === 0) {
                setLoad(false);
                showAlert(L.msg_prune_none);
            } else {
                setLoad(false);
                const cacheHitCount = Array.from(folderMap.keys()).filter(id => globalCache.has(id)).length;
                let confirmMsg = L.msg_prune_found.replace('{n}', toDeleteList.length);

                const delRes = await showDeleteConfirm(confirmMsg);
                if (delRes.confirm) {
                    const allIds = toDeleteList.map(f => f.id);

                    await executeBatchDelete(allIds, {
                        silent: true,
                        forceRefresh: false,
                        hardDelete: delRes.hardDelete
                    });

                    if (myScanId === S.scanId) {
                        const deletedSet = new Set(allIds);

                        if (S.lastGlobalResults && S.lastGlobalResults.length > 0) {
                            S.lastGlobalResults = S.lastGlobalResults.filter(x => !deletedSet.has(x.id));
                        }

                        if (S.analyzeMode && S.analyzeResultItems) {
                            S.analyzeResultItems = S.analyzeResultItems.filter(x => !deletedSet.has(x.id));
                        }

                        updateLoadTxt(L.str_refreshing);
                        const affectedParentIds = new Set();
                        affectedParentIds.add(S.path[S.path.length - 1].id || 'root');

                        toDeleteList.forEach(folder => {
                            if (folder.parent_id) affectedParentIds.add(folder.parent_id);
                            else affectedParentIds.add('root');
                        });

                        affectedParentIds.forEach(pid => {
                            if (typeof globalCache !== 'undefined') globalCache.delete(pid);
                            S.cache.delete(pid);
                        });

                        await load(false, true);
                        showToast(L.str_cleanup_done);
                    }
                }
            }
        } catch (e) {
            if (e.name !== 'AbortError' && myScanId === S.scanId) {
                setLoad(false);
                showAlert(`${L.str_error}: ${e.message}`);
            }
        } finally {
            if (myScanId === S.scanId) {
                setLoad(false);
                S.scanning = false;
                S.scanAbortController = null;
                isGUISensitive = false;
                if (typeof DurationProber !== 'undefined') DurationProber.checkAndRun();
            }
        }
    };

    if (UI.btnUpPause) UI.btnUpPause.onclick = () => {
        const ids = S.getSelectedIds();
        ids.forEach(id => {
            const task = S.uploadTasks.find(t => t.id === id);
            if (task && S.upMng) S.upMng.pause(task, true);
        });
        if (S.uploadMode) { refresh(); }
    };

    if (UI.btnUpStart) UI.btnUpStart.onclick = () => {
        const ids = S.getSelectedIds();
        ids.forEach(id => {
            const task = S.uploadTasks.find(t => t.id === id);
            if (task && S.upMng) S.upMng.resume(task, true);
        });
        if (S.uploadMode) { refresh(); }
    };

    if (UI.btnUpDel) UI.btnUpDel.onclick = () => {
        const count = S.getSelectedCount();
        if (count === 0) return;

        const html = `
            <div style="padding: 5px 0 0 0;">
                <h3 style="margin:0 0 25px 0; font-size:18px; font-weight:700; color:var(--pk-fg); border:none; line-height:1.4;">
                    ${L.title_del_task_confirm_fmt.replace('{n}', count)}
                </h3>
                <label style="display:flex; align-items:center; cursor:pointer; user-select:none; margin-bottom:35px; font-size:14px; color:var(--pk-fg);">
                    <input type="checkbox" id="del_up_files" style="width:18px; height:18px; margin-right:10px; accent-color:var(--pk-pri); cursor:pointer;">
                    <span style="opacity:0.9;">${L.lbl_del_cloud_files_too}</span>
                </label>
                <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px;">
                    <button class="pk-btn" id="del_up_cancel" style="height:36px; padding:0 24px; border-radius:6px; background:transparent; border:1px solid var(--pk-bd); font-weight:500; color:var(--pk-fg);">${L.btn_cancel}</button>
                    <button class="pk-btn pri" id="del_up_confirm" style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; border:none; box-shadow: 0 2px 6px rgba(0,0,0,0.2);">${L.btn_del}</button>
                </div>
            </div>
        `;

        const m = showModal(html);
        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) { modalBox.style.width = "420px"; modalBox.style.padding = "24px"; modalBox.style.borderRadius = "12px"; }

        const close = () => m.remove();
        m.querySelector('#del_up_cancel').onclick = close;
        const closeBtn = m.querySelector('.pk-modal-close');
        if(closeBtn) closeBtn.onclick = close;

        m.querySelector('#del_up_confirm').onclick = async () => {
            const isDeleteFile = m.querySelector('#del_up_files').checked;
            m.remove();

            const ids = S.getSelectedIds();
            const filesToTrash = [];
            const ghostsToHardDelete = [];

            ids.forEach(id => {
                const task = S.uploadTasks.find(t => t.id === id);
                if (task) {
                    task._deleted = true;
                    const forceDelete = task.status !== 'DONE';
                    task._deleteFileIntent = forceDelete || isDeleteFile;

                    if (task.file_id) {
                        if (forceDelete) ghostsToHardDelete.push(task.file_id);
                        else if (isDeleteFile) filesToTrash.push(task.file_id);
                    }
                    if (S.upMng) S.upMng.pause(task, true);
                }
            });

            const idSet = S.sel;
            S.uploadTasks = S.uploadTasks.filter(t => {
                if(idSet.has(t.id)) {
                    if (S.upMng) S.upMng.removeTask(t.id);
                    return false;
                }
                return true;
            });

            S.clearSelection();
            load(false, true);

            if (filesToTrash.length > 0) {
                try {
                    await executeBatchDelete(filesToTrash, { silent: false, hardDelete: false, forceRefresh: false });
                } catch(e) { console.error("Failed to trash uploaded files:", e); }
            }

            if (ghostsToHardDelete.length > 0) {
                try {
                    await fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                        method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: ghostsToHardDelete })
                    });
                    ghostsToHardDelete.forEach(id => {
                        if (typeof window.pkRemoveGhostFile === 'function') window.pkRemoveGhostFile(id);
                    });
                } catch (e) { console.error("Failed to hard delete ghost files:", e); }
            }

            showToast(L.msg_task_del_success_fmt.replace('{n}', ids.length));
        };

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                m.querySelector('#del_up_confirm').click();
            }
        });
    };

    if (UI.btnUpClearAll) UI.btnUpClearAll.onclick = () => {
        if (S.uploadTasks.length === 0) return;
        const count = S.uploadTasks.length;

        const html = `
            <div style="padding: 5px 0 0 0;">
                <h3 style="margin:0 0 25px 0; font-size:18px; font-weight:700; color:var(--pk-fg); border:none; line-height:1.4;">
                    ${L.title_clear_task_confirm}
                </h3>
                <label style="display:flex; align-items:center; cursor:pointer; user-select:none; margin-bottom:35px; font-size:14px; color:var(--pk-fg);">
                    <input type="checkbox" id="clear_all_up_files" style="width:18px; height:18px; margin-right:10px; accent-color:var(--pk-pri); cursor:pointer;">
                    <span style="opacity:0.9;">${L.lbl_del_cloud_files_too}</span>
                </label>
                <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px;">
                    <button class="pk-btn" id="clear_up_cancel" style="height:36px; padding:0 24px; border-radius:6px; background:transparent; border:1px solid var(--pk-bd); font-weight:500; color:var(--pk-fg);">${L.btn_cancel}</button>
                    <button class="pk-btn pri" id="clear_up_confirm" style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; border:none; box-shadow: 0 2px 6px rgba(0,0,0,0.2);">${L.btn_del}</button>
                </div>
            </div>
        `;

        const m = showModal(html);
        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) { modalBox.style.width = "420px"; modalBox.style.padding = "24px"; modalBox.style.borderRadius = "12px"; }

        const close = () => m.remove();
        m.querySelector('#clear_up_cancel').onclick = close;
        const closeBtn = m.querySelector('.pk-modal-close');
        if(closeBtn) closeBtn.onclick = close;

        m.querySelector('#clear_up_confirm').onclick = async () => {
            const isDeleteFile = m.querySelector('#clear_all_up_files').checked;
            m.remove();

            const filesToTrash = [];
            const ghostsToHardDelete = [];
            const count = S.uploadTasks.length;

            S.uploadTasks.forEach(task => {
                task._deleted = true;
                const forceDelete = task.status !== 'DONE';
                task._deleteFileIntent = forceDelete || isDeleteFile;

                if (S.upMng) {
                    S.upMng.pause(task, true);
                    S.upMng.removeTask(task.id);
                }

                if (task.file_id) {
                    if (forceDelete) ghostsToHardDelete.push(task.file_id);
                    else if (isDeleteFile) filesToTrash.push(task.file_id);
                }
            });

            S.uploadTasks = [];
            S.clearSelection();
            load(false, true);

            if (filesToTrash.length > 0) {
                try {
                    await executeBatchDelete(filesToTrash, { silent: false, hardDelete: false, forceRefresh: false });
                } catch(e) {}
            }

            if (ghostsToHardDelete.length > 0) {
                try {
                    await fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                        method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: ghostsToHardDelete })
                    });
                    ghostsToHardDelete.forEach(id => {
                        if (typeof window.pkRemoveGhostFile === 'function') window.pkRemoveGhostFile(id);
                    });
                } catch (e) {}
            }
            showToast(L.msg_task_clear_success_fmt.replace('{n}', count));
        };

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                m.querySelector('#clear_up_confirm').click();
            }
        });
    };

    UI.btnExt.onclick = async () => {
        const id = S.getSelectedIds()[0];
        const item = S.itemMap.get(id);
        if (!item) return;

        setLoad(true);
        updateLoadTxt(L.loading_detail);

        const targetApiId = ((S.offlineMode && item.kind === 'drive#task') || (S.uploadMode && item.file_id)) ? item.file_id : item.id;

        let detail = item;
        try {
            if (!targetApiId) throw new Error("File ID not ready");
            detail = await apiGet(targetApiId);
        } catch (e) {
            console.warn("Fetch detail failed, using cached info");
            if (!detail.web_content_link && !detail.medias) {
                setLoad(false);
                showToast(L.msg_video_fail, 'error');
                return;
            }
        }
        setLoad(false);

        const qualities = generateQualityList(detail);
        const bestSource = getBestSource(detail);
        let selectedUrl = bestSource.src;
        let selectedResName = bestSource.name;
        const savedPlayer = gmGet('pk_ext_player', 'potplayer');
        let selectedPlayer = (savedPlayer === 'potplayer') ? 'potplayer' : 'other';
        const initialBtnTxt = (selectedPlayer === 'potplayer') ? L.btn_start_play : L.btn_copy_link;

        const m = showModal(`
            <h3 style="border:none; margin-bottom:24px; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.btn_ext}</h3>

            <div style="display:flex; flex-direction:column; gap:25px; margin-bottom:10px;">
                <div class="pk-custom-select" id="cs_res">
                    <div class="pk-select-label">${L.lbl_resolution}</div>
                    <div class="pk-select-trigger">
                        <span id="txt_res">${selectedResName}</span>
                        <div style="display:flex; color:#999;">${CONF.crumbIcons.down}</div>
                    </div>
                    <div class="pk-select-menu pk-scroll">
                        ${qualities.map(q => `<div class="pk-select-item ${q.url === selectedUrl ? 'act' : ''}" data-val="${q.url}">${q.name}</div>`).join('')}
                    </div>
                </div>

                <div class="pk-custom-select" id="cs_player">
                    <div class="pk-select-label">${L.lbl_player}</div>
                    <div class="pk-select-trigger">
                        <span id="txt_player">${selectedPlayer === 'potplayer' ? 'PotPlayer' : L.opt_player_other}</span>
                        <div style="display:flex; color:#999;">${CONF.crumbIcons.down}</div>
                    </div>
                    <div class="pk-select-menu">
                        <div class="pk-select-item ${selectedPlayer === 'potplayer' ? 'act' : ''}" data-val="potplayer">PotPlayer</div>
                        <div class="pk-select-item ${selectedPlayer === 'other' ? 'act' : ''}" data-val="other">${L.opt_player_other}</div>
                    </div>
                </div>
            </div>

            <div class="pk-modal-act" style="margin-top:20px; display:flex; justify-content:flex-end; gap:12px;">
                <button class="pk-btn" id="ext_cancel" style="height:40px; min-width:86px; border-radius:8px; justify-content:center; background:transparent; font-weight:500;">${L.btn_cancel}</button>
                <button class="pk-btn pri" id="ext_run" style="height:40px; min-width:120px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center; border:none; font-size:15px;">${initialBtnTxt}</button>
            </div>
        `);

        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) {
            Object.assign(modalBox.style, { width: '480px', padding: '30px', height: 'auto', minHeight: 'auto', overflow: 'visible' });
            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
        }

        const bindSelect = (id, onSelect) => {
            const container = m.querySelector(`#${id}`);
            const trigger = container.querySelector('.pk-select-trigger');
            const menu = container.querySelector('.pk-select-menu');
            const txt = container.querySelector('span');

            trigger.onclick = (e) => {
                e.stopPropagation();
                const allMenus = m.querySelectorAll('.pk-select-menu');
                const isCurrentlyOpen = menu.style.display === 'block';
                allMenus.forEach(om => om.style.display = 'none');
                menu.style.display = isCurrentlyOpen ? 'none' : 'block';
            };

            container.querySelectorAll('.pk-select-item').forEach(item => {
                item.onclick = (e) => {
                    e.stopPropagation();
                    container.querySelectorAll('.pk-select-item').forEach(i => i.classList.remove('act'));
                    item.classList.add('act');
                    txt.textContent = item.textContent;
                    menu.style.display = 'none';
                    onSelect(item.dataset.val);
                };
            });
        };

        const runBtn = m.querySelector('#ext_run');

        bindSelect('cs_res', (val) => { selectedUrl = val; });
        bindSelect('cs_player', (val) => {
            selectedPlayer = val;
            runBtn.textContent = (val === 'potplayer') ? L.btn_start_play : L.btn_copy_link;
        });

        const closeAllMenus = () => m.querySelectorAll('.pk-select-menu').forEach(om => om.style.display = 'none');
        setTimeout(() => document.addEventListener('click', closeAllMenus), 0);

        const _orgRemove = m.remove.bind(m);
        m.remove = () => {
            document.removeEventListener('click', closeAllMenus);
            _orgRemove();
        };

        m.querySelector('#ext_cancel').onclick = () => m.remove();

        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                runBtn.click();
            }
        });

        runBtn.onclick = () => {
            gmSet('pk_ext_player', selectedPlayer);

            let cleanUrl = selectedUrl.replace('&ext=.m3u8', '');
            if (cleanUrl.includes('ts_downloader') && cleanUrl.includes('url=')) {
                const urlParam = new URL(cleanUrl).searchParams.get('url');
                if (urlParam) cleanUrl = decodeURIComponent(urlParam);
            }

            if (selectedPlayer === 'potplayer') {
                m.remove();
                const ua = navigator.userAgent.replace(/"/g, '');
                const cmd = `${cleanUrl} /user_agent="${ua}" /referer="https://mypikpak.com/"`;
                window.location.href = `potplayer://${cmd}`;
            } else {
                GM_setClipboard(cleanUrl);
                runBtn.textContent = L.msg_copy_success;
                runBtn.style.background = "#52c41a";
                runBtn.disabled = true;
                setTimeout(() => m.remove(), 1000);
            }
        };
    };

    if (UI.btnImgSearch) {
        UI.btnImgSearch.onclick = () => {
            if (UI.btnImgSearch.disabled) return;
            const id = S.getSelectedIds()[0];
            const item = S.itemMap.get(id);
            if (!item || !item.thumbnail_link) return;

            const thumbUrl = item.thumbnail_link.replace('SIZE_MEDIUM', 'SIZE_LARGE');
            const thumbImg = new Image();
            thumbImg.crossOrigin = 'anonymous';
            thumbImg.src = thumbUrl;
            startImageSearch(thumbImg, item.name, document.body, thumbUrl);
        };
    }

    UI.win.querySelector('#pk-down').onclick = async () => {
        const selectedIds = S.getSelectedIds();
        const hasConflict = selectedIds.some(id => {
            const item = S.itemMap.get(id);
            if (item && item.kind === 'drive#folder') return isPathBusy(item.id);
            return S.movingIds.has(id);
        });
        if (hasConflict) {
            showAlert(L.msg_resource_locked_download);
            return;
        }

        setLoad(true);
        isGUISensitive = true;
        const abortCtrl = new AbortController();
        const { signal } = abortCtrl;
        let isRunning = true;

        UI.stopBtn.onclick = () => { isRunning = false; abortCtrl.abort(); updateLoadTxt(L.str_stopping); };

        const allFiles = [];
        const rootNodes = [];
        const HYDRATE_LIMIT = 20;

        selectedIds.forEach(id => {
            const item = S.itemMap.get(id);
            if (item) {
                if (item.kind === 'drive#folder') rootNodes.push({...item, lineage:[], retryCount: 0});
                else allFiles.push(item);
            }
        });

        let progressTask = null;
        const fExtStr = gmGet('pk_dl_filter_ext', '').toLowerCase();
        const fNameStr = gmGet('pk_dl_filter_name', '').toLowerCase();
        const fExts = fExtStr.split(/[,,]/).map(s => s.trim().replace(/^\./, '')).filter(Boolean);
        const fNames = fNameStr.split(/[,,]/).map(s => s.trim()).filter(Boolean);
        const filterStats = { scanned: 0, blocked: 0 };

        const fSizeMinStr = gmGet('pk_dl_filter_size_min', '');
        const fSizeMaxStr = gmGet('pk_dl_filter_size_max', '');
        const fSizeUnitStr = gmGet('pk_dl_filter_size_unit', 'MB');
        let fSizeMin = fSizeMinStr ? parseFloat(fSizeMinStr) : -1;
        let fSizeMax = fSizeMaxStr ? parseFloat(fSizeMaxStr) : -1;
        if (fSizeUnitStr === 'GB') {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024 * 1024;
        } else if (fSizeUnitStr === 'TB') {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024 * 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024 * 1024 * 1024;
        } else {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024;
        }

        const hasDownloadFilterRules = fExts.length > 0 || fNames.length > 0 || (Number.isFinite(fSizeMin) && fSizeMin >= 0) || (Number.isFinite(fSizeMax) && fSizeMax >= 0);

        try {
            await coreRecursiveEngine(rootNodes, {
                signal,
                onFile: (f) => {
                    filterStats.scanned++;
                    const lowName = f.name.toLowerCase();
                    const ext = lowName.split('.').pop();
                    let isBlocked = fExts.some(e => ext === e) || fNames.some(n => lowName.includes(n));
                    if (!isBlocked && (fSizeMin >= 0 || fSizeMax >= 0)) {
                        const sz = parseInt(f.size || 0);
                        if (fSizeMin >= 0 && sz < fSizeMin) isBlocked = true;
                        if (fSizeMax >= 0 && sz > fSizeMax) isBlocked = true;
                    }
                    if (isBlocked) {
                        filterStats.blocked++;
                        return;
                    }
                    allFiles.push(f);
                },
                onProgress: (st) => {
                    updateLoadTxt(`${L.msg_batch_scanning}\n${L.str_files}: ${allFiles.length} | ${L.str_speed}: ${st.currentConcurrency}`);
                }
            });

            if (!isRunning) throw new Error('StoppedByUser');
            if (allFiles.length === 0) {
                setLoad(false);
                if (hasDownloadFilterRules && filterStats.scanned > 0 && filterStats.blocked === filterStats.scanned) {
                    showToast(L.msg_batch_all_filtered.replace('{n}', filterStats.blocked));
                } else {
                    showToast(L.msg_batch_no_files);
                }
                return;
            }
            if (hasDownloadFilterRules && filterStats.blocked > 0) {
                showToast(L.msg_batch_filtered.replace('{n}', filterStats.blocked));
            }

            let totalBytes = 0;
            for (let i = 0; i < allFiles.length; i++) {
                totalBytes += parseInt((allFiles[i] && allFiles[i].size) || 0, 10) || 0;
            }
            const needsSoftConfirm = allFiles.length > CONF.browserDownloadConfirmFileCount || totalBytes > CONF.browserDownloadConfirmTotalBytes;

            setLoad(false);
            if (needsSoftConfirm) {
                const confirmText = L.msg_down_confirm_total
                .replace('{n}', allFiles.length)
                .replace('{s}', fmtSize(totalBytes))
                .replace('{fc}', CONF.browserDownloadConfirmFileCount)
                .replace('{fs}', fmtSize(CONF.browserDownloadConfirmTotalBytes));
                if (!await showConfirm(confirmText)) return;
            }

            progressTask = FloatBarManager.create(L.msg_batch_hydrating);

            const readyFiles = [];
            const hydrateQueue = [...allFiles];
            const activeTasks = new Set();
            const hydrateWithRetry = async (file, maxRetries = 3) => {
                if (file.web_content_link) return file;
                if (file.phase === "PHASE_TYPE_PENDING" || file.phase === "PHASE_TYPE_RUNNING" || file.trashed) return null;
                let lastErr = null;
                for (let i = 0; i < maxRetries; i++) {
                    if (!isRunning) return null;
                    try {
                        const detail = await apiGet(file.id);
                        if (detail && detail.web_content_link) return detail;
                        if (detail && (detail.phase === "PHASE_TYPE_PENDING" || detail.phase === "PHASE_TYPE_RUNNING")) return null;
                        throw new Error("Link Empty");
                    } catch (e) {
                        lastErr = e;
                        if (i < maxRetries - 1) await sleep(1000 * (i + 1));
                    }
                }
                throw lastErr;
            };

            while ((hydrateQueue.length > 0 || activeTasks.size > 0) && isRunning) {
                while (hydrateQueue.length > 0 && activeTasks.size < HYDRATE_LIMIT && isRunning) {
                    const file = hydrateQueue.pop();
                    const p = (async () => {
                        try {
                            const detail = await hydrateWithRetry(file);
                            if (detail) {
                                readyFiles.push(detail);
                            } else if (detail === null) {
                                console.warn(`[Hydrate Skipped] ${file.name}: File is pending/running or trashed`);
                            }
                        } catch (e) {
                            console.error(`[Hydrate Failed] ${file.name}:`, e);
                        }
                    })().finally(() => activeTasks.delete(p));
                    activeTasks.add(p);
                }
                if (activeTasks.size > 0) await Promise.race(activeTasks);
                if (progressTask) progressTask.update(`${L.msg_batch_hydrating} ${readyFiles.length} / ${allFiles.length}`);
            }

            for (let i = 0; i < readyFiles.length; i++) {
                if (!isRunning) break;
                if (progressTask) progressTask.update(`${L.msg_down_progress} ${i + 1} / ${readyFiles.length}`);

                const link = document.createElement('a');
                link.href = readyFiles[i].web_content_link;
                link.setAttribute('download', readyFiles[i].name);
                link.style.display = 'none';
                document.body.appendChild(link);
                link.click();

                setTimeout(() => { if (link.parentNode) link.remove(); }, 10000);

                if (i < readyFiles.length - 1) {
                    await sleep(2000);
                } else {
                    await sleep(1500);
                }
            }

            if (isRunning && readyFiles.length > 0) {
                showToast(L.msg_down_success.replace('{n}', readyFiles.length));
            }
        } catch (e) {
            if (e.message !== 'StoppedByUser' && e.name !== 'AbortError') showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            setLoad(false);
            isGUISensitive = false;
            if (progressTask) progressTask.destroy();
        }
    };

    UI.win.querySelector('#pk-aria2').onclick = async () => {
        const selectedIds = S.getSelectedIds();
        const hasConflict = selectedIds.some(id => {
            const item = S.itemMap.get(id);
            if (item && item.kind === 'drive#folder') return isPathBusy(item.id);
            return S.movingIds.has(id);
        });
        if (hasConflict) {
            showAlert(L.msg_resource_locked_aria2);
            return;
        }
        let ariaUrl = gmGet('pk_aria2_url', '');
        let ariaToken = gmGet('pk_aria2_token', '');

        if (!ariaUrl) {
            const result = await new Promise((resolve) => {
                const m = showModal(`
                    <h3 style="border:none; margin:0 0 12px 0; font-size:18px; font-weight:700; color:var(--pk-fg); display:flex; align-items:center; gap:10px; line-height:24px;">
                        <span style="width:22px; height:22px; display:flex; align-items:center; justify-content:center; flex-shrink:0;">
                            ${CONF.icons.aria2.replace('width="16"', 'width="22"').replace('height="16"', 'width="22"')}
                        </span>
                        <span>${L.btn_aria2} - ${L.btn_settings}</span>
                    </h3>
                    <div style="font-size:13px; color:#888; margin-bottom:28px; line-height:1.5;">${L.msg_aria2_not_set}</div>

                    <div style="display:flex; flex-direction:column; gap:25px;">
                        <div style="position:relative; transform: translateZ(0);">
                            <div style="position:relative;">
                                <input type="text" id="pop_aria_url" value="${ariaUrl || 'http://localhost:6800/jsonrpc'}"
                                       placeholder="http://localhost:6800/jsonrpc" autocomplete="off"
                                       oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                                       style="width:100%; height:44px; padding:0 70px 0 12px; border:2px solid ${ (ariaUrl || 'http://localhost:6800/jsonrpc') ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box; transform: translateZ(0);">
                                <div class="pk-select-label">${L.label_aria2_url}</div>
                                <div id="btn_pop_aria_default" style="position:absolute; right:10px; top:50%; transform:translateY(-50%); font-size:11px; color:var(--pk-pri); cursor:pointer; font-weight:bold; padding:4px 8px; border-radius:4px; background:rgba(0,103,192,0.1); border:1px solid rgba(0,103,192,0.2);" onmouseover="this.style.background='rgba(0,103,192,0.2)'" onmouseout="this.style.background='rgba(0,103,192,0.1)'">${L.btn_default}</div>
                            </div>
                            <div class="pk-aria-status-box" id="pop_aria_test_res" style="cursor:help; margin-top: 8px;">
                                <div class="pk-aria-dot" id="pop_aria_test_dot"></div>
                                <span id="pop_aria_test_txt" style="color:#888; font-size: 11px;">${L.lbl_aria2_status}</span>
                            </div>
                        </div>

                        <div style="position:relative; transform: translateZ(0);">
                            <input type="text" id="pop_aria_token" value="${ariaToken || ''}" placeholder="${L.ph_aria2_secret}"
                                   autocomplete="one-time-code" spellcheck="false" data-lpignore="true" readonly onfocus="this.removeAttribute('readonly');"
                                   oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                                   style="width:100%; height:44px; padding:0 48px 0 12px; border:2px solid ${ariaToken ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box; -webkit-text-security: disc; transform: translateZ(0);">
                            <button type="button" id="btn_pop_aria_token_eye" class="pk-token-eye">${CONF.icons.eye}</button>
                            <div class="pk-select-label">${L.label_aria2_token}</div>
                        </div>
                    </div>

                    <div class="pk-modal-act" style="margin-top:30px; display:flex; justify-content:flex-end; gap:12px;">
                        <button class="pk-btn" id="pop_aria_cancel" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; justify-content:center; background:transparent; font-weight:500;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="pop_aria_save" style="height:40px; min-width:86px; padding:0 24px; border-radius:8px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center; border:none; font-size:14px;">${L.btn_save} & ${L.btn_aria2}</button>
                    </div>
                `);

                const modalBox = m.querySelector('.pk-modal');
                if (modalBox) Object.assign(modalBox.style, { width: '480px', padding: '30px', height: 'auto', minHeight: 'auto', overflow: 'visible' });

                const closeBtn = m.querySelector('.pk-modal-close');
                if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });

                const inpU = m.querySelector('#pop_aria_url');
                const inpT = m.querySelector('#pop_aria_token');
                const inpTEye = m.querySelector('#btn_pop_aria_token_eye');
                const dot = m.querySelector('#pop_aria_test_dot');
                const txt = m.querySelector('#pop_aria_test_txt');
                const boxRes = m.querySelector('#pop_aria_test_res');
                let testTimer = null;
                let popAriaTokenVisible = false;

                const runLiveTest = async () => {
                    const url = inpU.value.trim();
                    const token = inpT.value.trim();
                    const showTip = () => showAlert(L.tip_mixed_content, L.lbl_aria2_status);

                    if (!url) {
                        dot.className = 'pk-aria-dot';
                        txt.textContent = L.lbl_aria2_status;
                        boxRes.onclick = showTip;
                        boxRes.style.cursor = 'pointer';
                        return;
                    }
                    dot.className = 'pk-aria-dot wait';
                    txt.textContent = L.str_connecting;
                    boxRes.onclick = showTip;
                    boxRes.style.cursor = 'pointer';
                    const testUrl = normalizeAriaRpcUrl(url);
                    const payload = { jsonrpc: '2.0', method: 'aria2.getVersion', id: 'pk_quick_test', params: buildAriaRpcParams(token) };
                    try {
                        await aria2RpcRequest(testUrl, payload, 3000);
                        dot.className = 'pk-aria-dot ok';
                        txt.textContent = L.str_connected;
                        boxRes.onclick = null;
                        boxRes.style.cursor = 'default';
                    } catch (e) {
                        dot.className = 'pk-aria-dot err';
                        txt.textContent = L.str_conn_fail;
                        boxRes.onclick = showTip;
                        boxRes.style.cursor = 'pointer';
                    }
                };

                const triggerTest = () => { clearTimeout(testTimer); testTimer = setTimeout(runLiveTest, 600); };
                inpU.oninput = (e) => {
                    e.target.style.borderColor = e.target.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)';
                    triggerTest();
                };
                inpT.oninput = (e) => {
                    e.target.style.borderColor = e.target.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)';
                    triggerTest();
                };
                if (inpTEye) {
                    inpTEye.onclick = (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        popAriaTokenVisible = !popAriaTokenVisible;
                        inpT.style.webkitTextSecurity = popAriaTokenVisible ? 'none' : 'disc';
                        inpTEye.innerHTML = popAriaTokenVisible ? CONF.icons.eyeOff : CONF.icons.eye;
                    };
                }

                m.querySelector('#btn_pop_aria_default').onclick = () => {
                    inpU.value = 'http://localhost:6800/jsonrpc';
                    inpU.style.borderColor = 'var(--pk-pri)';
                    triggerTest();
                };

                setTimeout(runLiveTest, 200);

                m.querySelector('#pop_aria_cancel').onclick = () => { m.remove(); resolve(null); };
                m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve(null); };

                m.querySelector('#pop_aria_save').onclick = async () => {
                    let u = inpU.value.trim();
                    let t = inpT.value.trim();
                    if (!u) { inpU.style.borderColor = '#d93025'; return; }
                    if (!/^https?:\/\/|^wss?:\/\//i.test(u)) u = 'http://' + u;

                    const saveBtn = m.querySelector('#pop_aria_save');
                    const originalTxt = saveBtn.textContent;

                    saveBtn.disabled = true;
                    saveBtn.textContent = L.str_saving_dots;

                    try {
                        u = normalizeAriaRpcUrl(u);
                        const payload = { jsonrpc: '2.0', method: 'aria2.getVersion', id: 'pk_quick_test', params: buildAriaRpcParams(t) };
                        await aria2RpcRequest(u, payload, 5000);

                        gmSet('pk_aria2_url', u); gmSet('pk_aria2_token', t);
                        m.remove();
                        resolve({ url: u, token: t });

                    } catch (err) {
                        const confirmed = await showConfirm(
                            L.msg_aria2_test_fail,
                            L.title_aria2_fail
                        );

                        if (confirmed) {
                            gmSet('pk_aria2_url', u); gmSet('pk_aria2_token', t);
                            m.remove();
                            resolve({ url: u, token: t });
                        } else {
                            saveBtn.disabled = false;
                            saveBtn.textContent = originalTxt;
                        }
                    }
                };

                m.tabIndex = 0;
                setTimeout(() => m.focus(), 10);
                m.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter') {
                        e.preventDefault(); e.stopPropagation();
                        m.querySelector('#pop_aria_save').click();
                    }
                });
            });

            if (!result) return;
            ariaUrl = result.url;
            ariaToken = result.token;
        }

        setLoad(true);
        isGUISensitive = true;
        const abortCtrl = new AbortController();
        const { signal } = abortCtrl;
        let isRunning = true;

        UI.stopBtn.onclick = () => { isRunning = false; abortCtrl.abort(); updateLoadTxt(L.str_stopping); };

        ariaUrl = normalizeAriaRpcUrl(ariaUrl);

        const allFiles = [];
        const rootNodes = [];
        const HYDRATE_LIMIT = 40;
        const fExtStr = gmGet('pk_dl_filter_ext', '').toLowerCase();
        const fNameStr = gmGet('pk_dl_filter_name', '').toLowerCase();
        const fExts = fExtStr.split(/[,,]/).map(s => s.trim().replace(/^\./, '')).filter(Boolean);
        const fNames = fNameStr.split(/[,,]/).map(s => s.trim()).filter(Boolean);
        const stats = { hydratedCount: 0, lastUiTime: 0 };
        const filterStats = { scanned: 0, blocked: 0 };

        const fSizeMinStr = gmGet('pk_dl_filter_size_min', '');
        const fSizeMaxStr = gmGet('pk_dl_filter_size_max', '');
        const fSizeUnitStr = gmGet('pk_dl_filter_size_unit', 'MB');
        let fSizeMin = fSizeMinStr ? parseFloat(fSizeMinStr) : -1;
        let fSizeMax = fSizeMaxStr ? parseFloat(fSizeMaxStr) : -1;
        if (fSizeUnitStr === 'GB') {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024 * 1024;
        } else if (fSizeUnitStr === 'TB') {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024 * 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024 * 1024 * 1024;
        } else {
            if (fSizeMin >= 0) fSizeMin *= 1024 * 1024;
            if (fSizeMax >= 0) fSizeMax *= 1024 * 1024;
        }

        const hasDownloadFilterRules = fExts.length > 0 || fNames.length > 0 || (Number.isFinite(fSizeMin) && fSizeMin >= 0) || (Number.isFinite(fSizeMax) && fSizeMax >= 0);

        const selectedIdsForAria2 = S.getSelectedIds();
        selectedIdsForAria2.forEach(id => {
            const item = S.itemMap.get(id);
            if (item) {
                if (item.kind === 'drive#folder') {
                    rootNodes.push({...item, lineage: [{ id: item.id, name: item.name }], retryCount: 0});
                } else {
                    allFiles.push({ ...item, _lineage: [] });
                }
            }
        });

        let progressTask = null;
        try {
            await coreRecursiveEngine(rootNodes, {
                signal,
                onFile: (f, parent) => {
                    filterStats.scanned++;
                    const lowName = f.name.toLowerCase();
                    const ext = lowName.split('.').pop();
                    let isBlocked = fExts.some(e => ext === e) || fNames.some(n => lowName.includes(n));
                    if (!isBlocked && (fSizeMin >= 0 || fSizeMax >= 0)) {
                        const sz = parseInt(f.size || 0);
                        if (fSizeMin >= 0 && sz < fSizeMin) isBlocked = true;
                        if (fSizeMax >= 0 && sz > fSizeMax) isBlocked = true;
                    }
                    if (isBlocked) {
                        filterStats.blocked++;
                        return;
                    }
                    f._lineage = parent.lineage || [];
                    allFiles.push(f);
                },
                onProgress: (st) => {
                    const now = Date.now();
                    if (now - stats.lastUiTime > 150) {
                        updateLoadTxt(`${L.msg_batch_scanning}\n${L.str_files}: ${allFiles.length} | ${L.str_speed}: ${st.currentConcurrency}`);
                        stats.lastUiTime = now;
                    }
                }
            });

            if (!isRunning) throw new Error('StoppedByUser');
            if (allFiles.length === 0) {
                setLoad(false);
                if (hasDownloadFilterRules && filterStats.scanned > 0 && filterStats.blocked === filterStats.scanned) {
                    showToast(L.msg_batch_all_filtered.replace('{n}', filterStats.blocked));
                } else {
                    showToast(L.msg_batch_no_files);
                }
                return;
            }
            if (hasDownloadFilterRules && filterStats.blocked > 0) {
                showToast(L.msg_batch_filtered.replace('{n}', filterStats.blocked));
            }

            setLoad(false);
            progressTask = FloatBarManager.create(L.msg_batch_hydrating);

            const readyFiles =[];
            const failedFiles = [];
            const hydrateQueue = [...allFiles];
            const activeTasks = new Set();

            const hydrateWithRetry = async (file, maxRetries = 3) => {
                if (file.web_content_link) return file;
                if (file.phase === "PHASE_TYPE_PENDING" || file.phase === "PHASE_TYPE_RUNNING" || file.trashed) return null;
                let lastErr = null;
                for (let i = 0; i < maxRetries; i++) {
                    if (!isRunning) return null;
                    try {
                        const detail = await apiGet(file.id);
                        if (detail && detail.web_content_link) return detail;
                        if (detail && (detail.phase === "PHASE_TYPE_PENDING" || detail.phase === "PHASE_TYPE_RUNNING")) return null;
                        throw new Error("Link Empty");
                    } catch (e) {
                        lastErr = e;
                        if (i < maxRetries - 1) await sleep(1000 * (i + 1));
                    }
                }
                throw lastErr;
            };

            while ((hydrateQueue.length > 0 || activeTasks.size > 0) && isRunning) {
                while (hydrateQueue.length > 0 && activeTasks.size < HYDRATE_LIMIT && isRunning) {
                    const file = hydrateQueue.pop();
                    const p = (async () => {
                        try {
                            const detail = await hydrateWithRetry(file);
                            if (detail) {
                                detail._lineage = file._lineage;
                                readyFiles.push(detail);
                            } else if (detail === null) {
                                console.warn(`[Hydrate Skipped] ${file.name}: File is pending/running or trashed`);
                            }
                        } catch (e) {
                            console.error(`[Hydrate Failed] ${file.name}:`, e);
                            failedFiles.push(file.name + " " + L.str_aria2_fetch_err);
                        }
                    })().finally(() => {
                        activeTasks.delete(p);
                        stats.hydratedCount++;
                        if (progressTask) progressTask.update(`${L.msg_batch_hydrating} ${stats.hydratedCount} / ${allFiles.length}`);
                    });
                    activeTasks.add(p);
                }
                if (activeTasks.size > 0) await Promise.race(activeTasks);
            }

            if (!isRunning) throw new Error('StoppedByUser');

            if (readyFiles.length > 0 && isRunning) {
                const BATCH_SIZE = 50;
                let successCount = 0;
                let rpcFatalError = false;

                for (let i = 0; i < readyFiles.length; i += BATCH_SIZE) {
                    if (!isRunning || rpcFatalError) break;
                    const chunk = readyFiles.slice(i, i + BATCH_SIZE);

                    const sanitize = (s) => s.replace(/[\\/:*?"<>|]/g, '_').trim();
                    const payload = chunk.map(f => {
                        let relativePrefix = '';
                        if (f._lineage && f._lineage.length > 0) {
                            relativePrefix = f._lineage.map(n => sanitize(n.name)).join('/') + '/';
                        }
                        const outPath = relativePrefix + sanitize(f.name);

                        return {
                            jsonrpc: '2.0', method: 'aria2.addUri',
                            id: `pk_${Date.now()}_${Math.random().toString(16).slice(2)}`,
                            params: buildAriaRpcParams(ariaToken, [[f.web_content_link], {
                                out: outPath,
                                header: [`User-Agent: ${navigator.userAgent}`, `Referer: https://mypikpak.com/`]
                            }])
                        };
                    });

                    try {
                        await aria2RpcRequest(ariaUrl, payload, Math.max(10000, chunk.length * 800));
                        successCount += chunk.length;
                    } catch (rpcErr) {
                        console.error("[RPC Batch Error]", rpcErr);
                        chunk.forEach(f => failedFiles.push(f.name + " " + L.str_aria2_rpc_err));

                        if (rpcErr.message === "Network Error" || rpcErr.message === "Timeout") {
                            console.warn("[RPC Circuit Breaker] Aria2 disconnected. Aborting remaining batches.");
                            rpcFatalError = true;
                            const remainingFiles = readyFiles.slice(i + BATCH_SIZE);
                            remainingFiles.forEach(f => failedFiles.push(f.name + " " + L.str_aria2_aborted));
                        }
                    }

                    if (progressTask) progressTask.update(`${L.msg_aria2_sending_batch} ${successCount} / ${readyFiles.length}`);
                }

                if (failedFiles.length > 0) {
                    let failListText = failedFiles.slice(0, 10).join('\n');
                    if (failedFiles.length > 10) {
                        failListText += '\n...';
                        failListText += L.msg_aria2_batch_fail_log;
                    }

                    await showAlert(`${L.str_failed} ${failedFiles.length} ${L.str_items}\n\n${failListText}`, L.title_alert);

                    if (failedFiles.length > 10) {
                        try {
                            const blob = new Blob([failedFiles.join('\r\n')], { type: 'text/plain;charset=utf-8' });
                            const url = URL.createObjectURL(blob);
                            const a = document.createElement('a');
                            const now = new Date();
                            const dateStr = now.toISOString().replace(/[:.]/g, '-').slice(0, 19);

                            a.href = url;
                            a.download = `${L.str_aria2_fail_file_name}_${dateStr}.txt`;
                            document.body.appendChild(a);
                            a.click();

                            setTimeout(() => {
                                if (a.parentNode) document.body.removeChild(a);
                                URL.revokeObjectURL(url);
                            }, 1000);
                        } catch (exportErr) {
                            console.error("[Export Error] Failed to generate failure log:", exportErr);
                        }
                    }
                } else {
                    showToast(L.msg_aria2_sent.replace('{n}', successCount));
                }
            }
        } catch (e) {
            if (e.message !== 'StoppedByUser' && e.name !== 'AbortError') {
                showAlert(`${L.msg_aria2_check_fail}\n(${e.message})`);
            }
        } finally {
            setLoad(false);
            isGUISensitive = false;
            if (progressTask) progressTask.destroy();
        }
    };

    const ensureItemMap = () => {
        S.itemMap.clear();
        const len = S.items.length;
        for (let i = 0; i < len; i++) {
            const item = S.items[i];
            if (item && item.id) {
                S.itemMap.set(item.id, item);
            }
        }
    };

    const executeBatchDelete = async (ids, options = {}) => {
        const {
            silent = false,
            deleteFiles = false,
            isTask = false,
            forceRefresh = false,
            hardDelete = false,
            explicitItems = []
        } = options;

        if (!ids || ids.length === 0) return;

        const tempItemLookup = new Map();
        if (S.itemMap) S.itemMap.forEach((v, k) => tempItemLookup.set(k, v));
        if (explicitItems && explicitItems.length > 0) {
            explicitItems.forEach(item => {
                if (item && item.id) tempItemLookup.set(item.id, item);
            });
        }

        const BATCH_SIZE = 200;
        const SKIP_VERIFY = false;

        const progressTask = FloatBarManager.create(L.str_deleting);
        const updateFloat = progressTask.update;

        S.movingSourceId = S.path[S.path.length - 1].id || 'root';
        S.movingDestId = 'trash';

        const allLockedIdsArray =[];

        ids.forEach(id => {
            S.movingIds.add(id);
            allLockedIdsArray.push(id);

            if (isTask && deleteFiles) {
                const taskItem = tempItemLookup.get(id);
                if (taskItem && taskItem.file_id) {
                    S.movingIds.add(taskItem.file_id);
                    allLockedIdsArray.push(taskItem.file_id);
                }
            }
        });

        if (S.broadcast) S.broadcast.postMessage({
            type: 'LOCK_ADD',
            ids: allLockedIdsArray,
            src: S.movingSourceId,
            dst: S.movingDestId
        });

        if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();

        isGUISensitive = true;
        S.sel.clear();
        S.lastSelIdx = -1;
        S.activeId = null;

        try {
            const totalToDelete = ids.length;
            let deletedCount = 0;
            const affectedParentIds = new Set();
            const deletedSet = new Set();

            affectedParentIds.add(S.path[S.path.length - 1].id || 'root');

            for (let i = 0; i < totalToDelete; i += BATCH_SIZE) {
                const chunk = ids.slice(i, i + BATCH_SIZE);

                let retry = 0;
                const maxRetries = 3;
                let success = false;

                while (retry < maxRetries && !success) {
                    try {
                        if (isTask) {
                            await apiCancelTask(chunk, deleteFiles);
                        } else {
                            const action = hardDelete ? 'files:batchDelete' : 'files:batchTrash';
                            const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/${action}`, {
                                method: 'POST',
                                headers: getHeaders(),
                                body: JSON.stringify({ ids: chunk })
                            });
                            if (!res.ok) throw new Error(`API ${res.status}`);
                        }
                        success = true;
                    } catch (err) {
                        retry++;
                        console.warn(`[Delete] Retry ${retry}/${maxRetries}`);
                        updateFloat(`${L.str_deleting} (Retry ${retry})...`);
                        await sleep(1000 * retry);
                        if (retry >= maxRetries) throw err;
                    }
                }

                if (!isTask && chunk.length > 0 && !SKIP_VERIFY) {
                    const lastId = chunk[chunk.length - 1];
                    let verifyRetries = 0;
                    while (verifyRetries < 20) {
                        try {
                            const meta = await apiGet(lastId);
                            if (!hardDelete && meta.trashed) break;
                        } catch (e) {
                            break;
                        }
                        updateFloat(`${L.str_deleting} ${deletedCount}/${totalToDelete}`);
                        verifyRetries++;
                        await sleep(500);
                    }
                }

                const chunkSet = new Set(chunk);
                const chunkLockedIds =[];

                chunk.forEach(id => {
                    S.movingIds.delete(id);
                    chunkLockedIds.push(id);

                    const it = tempItemLookup.get(id);

                    let physicalFolderId = null;
                    if (it) {
                        if (it.kind === 'drive#folder') {
                            physicalFolderId = it.id;
                        } else if (isTask && deleteFiles && it.file_id) {
                            const isFolderTask = (it.mime_type && (it.mime_type.includes('folder') || it.mime_type.includes('directory'))) ||
                                                 (it.icon_link && it.icon_link.includes('folder')) ||
                                                 (typeof globalCache !== 'undefined' && globalCache.has(it.file_id));
                            if (isFolderTask) {
                                physicalFolderId = it.file_id;
                            }
                        }
                    }

                    if (physicalFolderId && typeof globalCache !== 'undefined') {
                        const purgeDescendants = (fid) => {
                            const data = globalCache.get(fid) || (S.cache ? S.cache.get(fid) : null);
                            if (data) {
                                globalTombstoneCache.set(fid, Array.isArray(data) ?[...data] : {...data});

                                const list = Array.isArray(data) ? data : (data.items ||[]);
                                list.forEach(child => {
                                    deletedSet.add(child.id);
                                    S.itemMap.delete(child.id);
                                    if (child.kind === 'drive#folder') {
                                        purgeDescendants(child.id);
                                    }
                                });
                                globalCache.delete(fid);
                                if (S.cache) S.cache.delete(fid);
                            }
                        };
                        purgeDescendants(physicalFolderId);
                    }

                    if (isTask && deleteFiles && it && it.file_id) {
                        S.movingIds.delete(it.file_id);
                        chunkLockedIds.push(it.file_id);
                        deletedSet.add(it.file_id);

                        if (typeof globalParentIndex !== 'undefined' && globalParentIndex.has(it.file_id)) {
                            const parentInfo = globalParentIndex.get(it.file_id);
                            if (parentInfo && parentInfo.id) {
                                affectedParentIds.add(parentInfo.id === 'root' ? '' : parentInfo.id);
                            }
                        }
                        affectedParentIds.add('root');
                        affectedParentIds.add('');
                    }

                    if (it && S.analyzeMap) {
                        const analyzeTargetId = physicalFolderId || id;

                        const analyzeIdsToRemove = new Set([analyzeTargetId, id]);
                        const queue = [analyzeTargetId, id];
                        while (queue.length > 0) {
                            const currId = queue.shift();
                            S.analyzeMap.forEach((node, nId) => {
                                if (node.parentId === currId && !analyzeIdsToRemove.has(nId)) {
                                    analyzeIdsToRemove.add(nId);
                                    queue.push(nId);
                                }
                            });
                        }

                        analyzeIdsToRemove.forEach(remId => {
                            if (S.analyzeMap.has(remId)) S.analyzeMap.delete(remId);
                        });

                        const lostSize = parseInt(it.size || 0);
                        if (lostSize > 0) {
                            let currPid = it.parent_id;
                            if (!currPid && isTask && typeof globalParentIndex !== 'undefined') {
                                const pInfo = globalParentIndex.get(it.file_id);
                                if (pInfo) currPid = pInfo.id;
                            }
                            let safety = 50;
                            while (currPid && S.analyzeMap.has(currPid) && safety > 0) {
                                const pNode = S.analyzeMap.get(currPid);
                                pNode.size = Math.max(0, pNode.size - lostSize);
                                currPid = pNode.parentId;
                                safety--;
                            }
                        }

                        if (S.analyzeSimGroups) {
                            S.analyzeSimGroups.forEach(group => {
                                group.ids = group.ids.filter(gid => !analyzeIdsToRemove.has(gid));
                            });
                            S.analyzeSimGroups = S.analyzeSimGroups.filter(group => group.ids.length >= 2);

                            if (S.analyzeSimGroups.length === 0) {
                                setTimeout(() => { if (UI.btnExit) UI.btnExit.click(); }, 500);
                            }
                        }

                        if (S.analyzeResultItems) {
                            S.analyzeResultItems = S.analyzeResultItems.filter(x => !analyzeIdsToRemove.has(x.id));
                            S.analyzeResultItems.forEach(resItem => {
                                if (S.analyzeMap.has(resItem.id)) {
                                    resItem.size = S.analyzeMap.get(resItem.id).size.toString();
                                }
                            });
                        }
                    }

                    if (it && it.parent_id) affectedParentIds.add(it.parent_id);
                    S.itemMap.delete(id);
                    deletedSet.add(id);
                });

                if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_REM', ids: chunkLockedIds });

                if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();

                S.items = S.items.filter(x => !deletedSet.has(x.id));

                if (typeof pkState !== 'undefined' && pkState && pkState.lastGlobalResults) {
                    pkState.lastGlobalResults = pkState.lastGlobalResults.filter(x => !deletedSet.has(x.id));
                }

                if (typeof globalCache !== 'undefined') {
                    const cleanListChunk = (raw) => {
                        if (Array.isArray(raw)) return raw.filter(f => !deletedSet.has(f.id));
                        if (raw && Array.isArray(raw.items)) {
                            raw.items = raw.items.filter(f => !deletedSet.has(f.id));
                            return raw;
                        }
                        return raw;
                    };
                    for (const key of globalCache.keys()) {
                        globalCache.set(key, cleanListChunk(globalCache.get(key)));
                    }
                    for (const key of S.cache.keys()) {
                        S.cache.set(key, cleanListChunk(S.cache.get(key)));
                    }
                }

                if (!forceRefresh) {
                    if (S.dupMode) renderDupView();
                    else refresh();
                }

                await new Promise(r => requestAnimationFrame(r));

                deletedCount += chunk.length;
                updateFloat(`${L.str_deleting} ${Math.min(deletedCount, totalToDelete)} / ${totalToDelete}`);

                if (deletedCount < totalToDelete) await sleep(50);
            }

            const cur = S.path[S.path.length - 1];
            if (cur && cur.id) gmSet('pk_fmod_' + cur.id, new Date(getServerNow()).toISOString());

            if (typeof globalCache !== 'undefined') {
                const cleanList = (raw) => {
                    if (Array.isArray(raw)) return raw.filter(f => !deletedSet.has(f.id));
                    if (raw && Array.isArray(raw.items)) {
                        raw.items = raw.items.filter(f => !deletedSet.has(f.id));
                        return raw;
                    }
                    return raw;
                };

                for (const key of globalCache.keys()) {
                    globalCache.set(key, cleanList(globalCache.get(key)));
                }

                for (const key of S.cache.keys()) {
                    S.cache.set(key, cleanList(S.cache.get(key)));
                }

                affectedParentIds.forEach(pid => {
                    const keysToCheck = (pid === 'root' || pid === '') ? ['root', ''] : [pid];
                    keysToCheck.forEach(key => {
                        if (typeof globalDirtyFolders !== 'undefined') globalDirtyFolders.add(key);
                    });
                });
                globalCache.delete('root_trashed');
                S.cache.delete('root_trashed');

                if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
            }

            if (window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger();

            if (forceRefresh) {
                await load(false, true);
            } else {
                updateStat();
                setTimeout(() => updateQuotaUI(), 2000);
            }

            if (!silent && !S.dupMode) {
                showToast(isTask ? L.msg_task_deleted : L.msg_del_items_done.replace('{n}', deletedCount));
            }

        } catch (e) {
            console.error(e);
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {

            if (typeof allLockedIdsArray !== 'undefined' && allLockedIdsArray.length > 0) {
                allLockedIdsArray.forEach(id => S.movingIds.delete(id));

                if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_REM', ids: allLockedIdsArray });
            } else if (ids && ids.length > 0) {
                ids.forEach(id => S.movingIds.delete(id));
                if (S.broadcast) S.broadcast.postMessage({ type: 'LOCK_REM', ids: ids });
            }

            if (typeof updateGlobalLockCSS === 'function') updateGlobalLockCSS();

            if (S.movingIds.size === 0) {
                isGUISensitive = false;
            }

            if (progressTask) progressTask.destroy();
        }
    };

    UI.btnDel.onclick = async () => {
        const selectedIds = S.getSelectedIds();
        const count = selectedIds.length;
        if (!count) return;

        if (S.historyMode) {
            if (!await showConfirm(L.warn_clear_history.replace('{n}', count))) return;

            const selIds = new Set(selectedIds);
            selIds.forEach(id => {
                gmSet('pk_progress_' + id, null);
                S.itemMap.delete(id);
            });
            S.items = S.items.filter(it => !selIds.has(it.id));
            S.clearSelection();
            refresh();
            showToast(L.msg_clear_history_done);
            return;
        }

        if (S.offlineMode) {
            const html = `
                <div style="padding: 5px 0 0 0;">
                    <h3 style="margin:0 0 25px 0; font-size:18px; font-weight:700; color:var(--pk-fg); border:none; line-height:1.4;">
                        ${L.title_del_task_confirm_fmt.replace('{n}', count)}
                    </h3>
                    <label style="display:flex; align-items:center; cursor:pointer; user-select:none; margin-bottom:35px; font-size:14px; color:var(--pk-fg);">
                        <input type="checkbox" id="del_task_files" style="width:18px; height:18px; margin-right:10px; accent-color:var(--pk-pri); cursor:pointer;">
                        <span style="opacity:0.9;">${L.lbl_del_cloud_files_too}</span>
                    </label>
                    <div class="pk-modal-act" style="display:flex; justify-content:flex-end; gap:12px;">
                        <button class="pk-btn" id="del_task_cancel" style="height:36px; padding:0 24px; border-radius:6px; background:transparent; border:1px solid var(--pk-bd); font-weight:500; color:var(--pk-fg);">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="del_task_confirm" style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; border:none; box-shadow: 0 2px 6px rgba(0,0,0,0.2);">${L.btn_del}</button>
                    </div>
                </div>
            `;

            if (typeof m !== 'undefined' && m.remove) m.remove();
            const taskModal = showModal(html);
            const modalBox = taskModal.querySelector('.pk-modal');
            if (modalBox) { modalBox.style.width = "420px"; modalBox.style.padding = "24px"; modalBox.style.borderRadius = "12px"; }

            const close = () => taskModal.remove();
            taskModal.querySelector('#del_task_cancel').onclick = close;
            const closeBtn = taskModal.querySelector('.pk-modal-close');
            if(closeBtn) closeBtn.onclick = close;

            taskModal.querySelector('#del_task_confirm').onclick = async () => {
                const isDeleteFile = taskModal.querySelector('#del_task_files').checked;
                taskModal.remove();
                await executeBatchDelete(selectedIds, { isTask: true, deleteFiles: isDeleteFile, forceRefresh: true });
            };

            taskModal.tabIndex = 0;
            setTimeout(() => taskModal.focus(), 10);
            taskModal.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    taskModal.querySelector('#del_task_confirm').click();
                }
            });
            return;
        }

        ensureItemMap();

        setLoad(true);
        const totalCount = selectedIds.length;
        updateLoadTxt(`${L.str_checking_bl}\n0 / ${totalCount}`);
        await sleep(16);
        const blSet = S.blSet;
        const blFolderSet = S.blFolderSet;
        const toDeleteIds = [];
        let blacklistedCount = 0;
        let processed = 0;
        let lastYieldTime = performance.now();
        let protectedCount = 0;

        for (const id of selectedIds) {
            const item = S.itemMap.get(id);
            if (!item) { processed++; continue; }

            if (!S.trashMode && isSystemItem(item)) {
                protectedCount++;
                processed++;
                continue;
            }

            const lowerName = item.name.toLowerCase().trim();
            const isFolder = item.kind === 'drive#folder';

            const isProtectedMode = gmGet('pk_skip_bl_on_del', true);
            if (isProtectedMode && (isFolder ? blFolderSet.has(lowerName) : blSet.has(lowerName))) {
                blacklistedCount++;
            } else {
                toDeleteIds.push(id);
            }
            processed++;
            if ((processed % 500) === 0) {
                const now = performance.now();
                if (now - lastYieldTime > 12) {
                    updateLoadTxt(`${L.str_checking_bl}\n${processed} / ${totalCount}`);
                    await sleep(0);
                    lastYieldTime = performance.now();
                }
            }
        }

        setLoad(false);

        if (blacklistedCount > 0) {
            showToast(L.msg_del_protected.replace('{n}', blacklistedCount));
        }

        if (blacklistedCount > 0 && toDeleteIds.length === 0) {
            S.clearSelection();
            refresh();
            updateStat();
            return;
        }

        if (toDeleteIds.length === 0) {
            showAlert(L.msg_del_none);
            return;
        }

        const delRes = await showDeleteConfirm(L.warn_del.replace('{n}', toDeleteIds.length));
        if (!delRes.confirm) return;

        await executeBatchDelete(toDeleteIds, { hardDelete: delRes.hardDelete });
    };

    UI.btnDeselect.onclick = () => {
        S.clearSelection();
        refresh();
    };

    const processBlacklistAction = async (action) => {
        const selectedIds = S.getSelectedIds();
        const selectedIdSet = new Set(selectedIds);
        const totalSelected = selectedIds.length;
        if (totalSelected === 0) return;

        const isRemove = action === 'remove';
        const progressTask = FloatBarManager.create(L.str_init_op);
        const updateFloat = progressTask.update;

        let isRunning = true;
        UI.stopBtn.onclick = () => { isRunning = false; updateFloat(L.str_stopping); };

        await sleep(16);

        const getCleanKey = (str) => str ? str.toLowerCase().trim() : "";
        const parseList = (str) => str ? str.split(/[\r\n]+/).map(s => s.trim()).filter(s => s) : [];

        const fileListStr = gmGet('pk_blacklist', '');
        const folderListStr = gmGet('pk_blacklist_folders', '');

        let currentFiles = parseList(fileListStr);
        let currentFolders = parseList(folderListStr);

        const targetFileKeys = new Set();
        const targetFolderKeys = new Set();
        const toAddFiles = [];
        const toAddFolders = [];

        let processedCount = 0;
        let lastYieldTime = performance.now();

        const len = S.items.length;
        for (let i = 0; i < len; i++) {
            if (!isRunning) break;
            const item = S.items[i];
            if (selectedIdSet.has(item.id)) {
                const name = item.name.replace(/[\r\n\v\f\u2028\u2029]+/g, ' ').trim();
                const key = getCleanKey(name);
                if (item.kind === 'drive#folder') {
                    targetFolderKeys.add(key);
                    if (!isRemove) toAddFolders.push(name);
                } else {
                    targetFileKeys.add(key);
                    if (!isRemove) toAddFiles.push(name);
                }
                processedCount++;
                if ((processedCount & 63) === 0) {
                    const now = performance.now();
                    if (now - lastYieldTime > 12) {
                        updateFloat(`${L.str_analyzing} ${processedCount} / ${totalSelected}`);
                        await sleep(0); lastYieldTime = performance.now();
                    }
                }
            }
        }

        if (!isRunning) { progressTask.destroy(); showAlert(L.msg_bl_stop); return; }

        updateFloat(L.str_processing);
        await sleep(10);

        let finalCount = 0;
        let dataChanged = false;

        if (isRemove) {
            const oldFileCount = currentFiles.length;
            const oldFolderCount = currentFolders.length;
            currentFiles = currentFiles.filter(name => !targetFileKeys.has(getCleanKey(name)));
            currentFolders = currentFolders.filter(name => !targetFolderKeys.has(getCleanKey(name)));
            if (oldFileCount !== currentFiles.length || oldFolderCount !== currentFolders.length) {
                dataChanged = true;
                finalCount = (oldFileCount - currentFiles.length) + (oldFolderCount - currentFolders.length);
            }
        } else {
            const existingFileKeys = new Set(currentFiles.map(s => getCleanKey(s)));
            const existingFolderKeys = new Set(currentFolders.map(s => getCleanKey(s)));
            let addedCount = 0;
            for (const name of toAddFiles) {
                const key = getCleanKey(name);
                if (!existingFileKeys.has(key)) { currentFiles.push(name); existingFileKeys.add(key); addedCount++; dataChanged = true; }
            }
            for (const name of toAddFolders) {
                const key = getCleanKey(name);
                if (!existingFolderKeys.has(key)) { currentFolders.push(name); existingFolderKeys.add(key); addedCount++; dataChanged = true; }
            }
            finalCount = addedCount;
        }

        if (dataChanged) {
            updateFloat(L.str_saving);
            await sleep(10);
            gmSet('pk_blacklist', currentFiles.join('\n'));
            gmSet('pk_blacklist_folders', currentFolders.join('\n'));
            S.updateBlCache();
            renderVisible();
        }

        progressTask.destroy();
        const msgTemplate = isRemove ? L.msg_bl_remove_done : L.msg_bl_add_done;
        showToast(msgTemplate.replace('{n}', finalCount));
    };

    UI.btnBlacklistManager.onclick = showBlacklistModal;
    if (UI.btnTrashBlacklistManager) UI.btnTrashBlacklistManager.onclick = showBlacklistModal;

    if (UI.uploadWrap && UI.btnUpload) {
        const uploadMenu = UI.uploadWrap.querySelector('.pk-dropdown-menu');

        const restoreUploadMenu = () => {
            if (!uploadMenu) return;
            if (uploadMenu._pkOriginParent && uploadMenu.parentNode !== uploadMenu._pkOriginParent) {
                uploadMenu._pkOriginParent.appendChild(uploadMenu);
            }
            uploadMenu.style.position = '';
            uploadMenu.style.top = '';
            uploadMenu.style.left = '';
            uploadMenu.style.right = '';
            uploadMenu.style.bottom = '';
            uploadMenu.style.marginTop = '';
            uploadMenu.style.zIndex = '';
            uploadMenu.style.minWidth = '';
            uploadMenu.style.visibility = '';
            uploadMenu.style.pointerEvents = '';
            uploadMenu.style.zoom = '';
            uploadMenu.style.transformOrigin = '';
            delete uploadMenu.dataset.pkPortal;
        };

        const closeUploadMenu = () => {
            if (!uploadMenu) return;
            if (uploadMenu._pkPlaceRaf1) cancelAnimationFrame(uploadMenu._pkPlaceRaf1);
            if (uploadMenu._pkPlaceRaf2) cancelAnimationFrame(uploadMenu._pkPlaceRaf2);
            uploadMenu._pkPlaceRaf1 = 0;
            uploadMenu._pkPlaceRaf2 = 0;
            uploadMenu.style.display = 'none';
            restoreUploadMenu();
            UI.uploadWrap.classList.remove('active');
        };

        const placeUploadMenu = () => {
            if (!uploadMenu) return;
            if (!uploadMenu._pkOriginParent) uploadMenu._pkOriginParent = UI.uploadWrap;

            const isGridView = !!(UI.win && UI.win.classList.contains('pk-grid-view'));

            if (!isGridView) {
                if (uploadMenu.parentNode !== uploadMenu._pkOriginParent) {
                    uploadMenu._pkOriginParent.appendChild(uploadMenu);
                }
                uploadMenu.classList.toggle('pk-dark', !!(el && el.classList.contains('pk-dark')));
                uploadMenu.style.position = 'absolute';
                uploadMenu.style.top = '100%';
                uploadMenu.style.right = '0';
                uploadMenu.style.left = 'auto';
                uploadMenu.style.bottom = 'auto';
                uploadMenu.style.marginTop = '6px';
                uploadMenu.style.zIndex = '';
                uploadMenu.style.minWidth = '';
                uploadMenu.style.visibility = '';
                uploadMenu.style.pointerEvents = '';
                delete uploadMenu.dataset.pkPortal;
                return;
            }

            if (uploadMenu.parentNode !== document.body) {
                document.body.appendChild(uploadMenu);
            }

            uploadMenu.classList.toggle('pk-dark', !!(el && el.classList.contains('pk-dark')));

            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const btnRect = typeof getLogicalRect === 'function' ? getLogicalRect(UI.btnUpload) : UI.btnUpload.getBoundingClientRect();
            const pad = 8;

            uploadMenu.dataset.pkPortal = '1';
            uploadMenu.style.position = 'fixed';
            uploadMenu.style.left = '0';
            uploadMenu.style.top = '0';
            uploadMenu.style.right = 'auto';
            uploadMenu.style.bottom = 'auto';
            uploadMenu.style.marginTop = '0';
            uploadMenu.style.zIndex = '10060';
            uploadMenu.style.zoom = scale;
            uploadMenu.style.transformOrigin = 'top right';
            uploadMenu.style.minWidth = Math.max(Math.round(btnRect.width), 140) + 'px';
            uploadMenu.style.display = 'flex';
            uploadMenu.style.visibility = 'hidden';
            uploadMenu.style.pointerEvents = 'none';

            const menuRect = uploadMenu.getBoundingClientRect();
            const menuW = menuRect.width / scale;
            const menuH = menuRect.height / scale;
            const winW = window.innerWidth / scale;
            const winH = window.innerHeight / scale;

            let left = btnRect.right - menuW;
            let top = btnRect.bottom + 6;

            if (left < pad) left = pad;
            if (left + menuW > winW - pad) {
                left = winW - pad - menuW;
            }

            if (top + menuH > winH - pad) {
                top = btnRect.top - menuH - 6;
            }
            if (top < pad) top = pad;

            uploadMenu.style.left = Math.round(left) + 'px';
            uploadMenu.style.top = Math.round(top) + 'px';
            uploadMenu.style.visibility = '';
            uploadMenu.style.pointerEvents = '';
        };

        const requestPlaceUploadMenu = () => {
            if (!uploadMenu) return;
            if (uploadMenu.style.display !== 'flex') return;
            if (!UI.uploadWrap.classList.contains('active')) return;
            if (uploadMenu._pkPlaceRaf1) cancelAnimationFrame(uploadMenu._pkPlaceRaf1);
            if (uploadMenu._pkPlaceRaf2) cancelAnimationFrame(uploadMenu._pkPlaceRaf2);
            uploadMenu._pkPlaceRaf1 = requestAnimationFrame(() => {
                uploadMenu._pkPlaceRaf2 = requestAnimationFrame(() => {
                    uploadMenu._pkPlaceRaf1 = 0;
                    uploadMenu._pkPlaceRaf2 = 0;
                    if (!uploadMenu || uploadMenu.style.display !== 'flex') return;
                    if (!UI.uploadWrap.classList.contains('active')) return;
                    placeUploadMenu();
                });
            });
        };

        window.__pkRequestPlaceUploadMenu = requestPlaceUploadMenu;

        if (uploadMenu && !uploadMenu.dataset.pkBindStop) {
            uploadMenu.addEventListener('click', (e) => e.stopPropagation());
            uploadMenu.dataset.pkBindStop = '1';
        }

        if (!window.__pkUploadMenuPortalBound) {
            window.addEventListener('resize', () => {
                requestPlaceUploadMenu();
            }, { passive: true });

            document.addEventListener('scroll', () => {
                closeUploadMenu();
            }, true);

            if (window.visualViewport) {
                window.visualViewport.addEventListener('resize', () => {
                    requestPlaceUploadMenu();
                }, { passive: true });
                window.visualViewport.addEventListener('scroll', () => {
                    requestPlaceUploadMenu();
                }, { passive: true });
            }

            document.addEventListener('mousedown', (e) => {
                if (!uploadMenu || uploadMenu.style.display !== 'flex') return;
                const target = e.target;
                if (uploadMenu.contains(target)) return;
                if (UI.btnUpload.contains(target)) return;
                if (UI.uploadWrap.contains(target) && !uploadMenu.dataset.pkPortal) return;
                closeUploadMenu();
            }, true);

            window.__pkUploadMenuPortalBound = true;
        }

        if (window.ResizeObserver && !UI.uploadWrap.__pkUploadMenuRO) {
            UI.uploadWrap.__pkUploadMenuRO = new ResizeObserver(() => {
                requestPlaceUploadMenu();
            });
            UI.uploadWrap.__pkUploadMenuRO.observe(UI.uploadWrap);
        }

        UI.btnUpload.onclick = (e) => {
            e.stopPropagation();
            const isActive = uploadMenu && uploadMenu.style.display === 'flex' && UI.uploadWrap.classList.contains('active');

            document.querySelectorAll('.pk-dropdown-menu, .pk-select-menu').forEach(m => m.style.display = 'none');
            document.querySelectorAll('.pk-dropdown-wrap').forEach(w => w.classList.remove('active'));

            if (isActive) {
                closeUploadMenu();
                return;
            }

            if (!uploadMenu) return;
            uploadMenu.style.display = 'flex';
            placeUploadMenu();
            UI.uploadWrap.classList.add('active');
        };

        UI.actUpFile.onclick = (e) => {
            e.stopPropagation();
            closeUploadMenu();
            UI.inpFile.click();
        };

        UI.actUpFolder.onclick = (e) => {
            e.stopPropagation();
            closeUploadMenu();
            UI.inpFolder.click();
        };

        const updateRowUI = (task) => {
            if (document.hidden) return;
            const row = document.querySelector(`.pk-row[data-id="${task.id}"]`);
            if (row) {
                const progBar = row.querySelector('.pk-up-prog-bar');
                const progTxt = row.querySelector('.pk-up-prog-txt');
                const spdTxt = row.querySelector('.pk-up-spd');
                const statusCol = row.children[4];
                const msgSpan = statusCol ? statusCol.querySelector('span:first-child') : null;

                if (progBar) progBar.style.width = task.progress + '%';
                if (progTxt) progTxt.textContent = Math.floor(task.progress) + '%';

                if (msgSpan) {
                    msgSpan.textContent = task.message;
                    const activeStatus = ['UPLOADING', 'HASHING', 'WAITING', 'RUNNING'];
                    if (activeStatus.includes(task.status)) {
                        msgSpan.style.color = 'var(--pk-pri)';
                        if (progBar) progBar.style.backgroundColor = 'var(--pk-pri)';
                    }
                }

                if (spdTxt) spdTxt.innerHTML = task.status === 'DONE' ? '<span style="color:#52c41a">${L.lbl_done_check}</span>' : S.upMng.fmtSpeed(task.speed);
            }
        };
        const resolveTask = (task) => { if (S.uploadMode) updateRowUI(task); };

        S.upMng = {
            limit: 3,
            running: 0,
            store: null,
            initStore: () => {},
            saveTask: (task) => {},
            removeTask: (id) => {},

            fmtSpeed: (bytesPerSec) => {
                if (bytesPerSec === 0) return '0 B/s';
                const units = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
                let i = 0;
                while (bytesPerSec >= 1024 && i < units.length - 1) { bytesPerSec /= 1024; i++; }
                return bytesPerSec.toFixed(2) + ' ' + units[i];
            },

            createTask: (file, parentId) => {
                const task = {
                    id: 'up_' + Date.now() + '_' + Math.random().toString(36).substr(2),
                    kind: 'pk#upload',
                    file: file,
                    name: file.name,
                    size: file.size,
                    parentId: parentId,
                    status: 'WAITING',
                    progress: 0,
                    speed: 0,
                    message: L.msg_task_waiting,
                    _xhr: null,
                    _lastCalcTime: 0,
                    _lastCalcLoaded: 0,
                    _lastUiTime: 0
                };
                S.upMng.saveTask(task);
                return task;
            },

            scheduler: () => {
                if (S.upMng.running >= S.upMng.limit) return;
                const waiting = S.uploadTasks.find(t => t.status === 'WAITING');
                if (waiting) S.upMng.start(waiting);
            },

            start: async (task) => {
                if (S.quota && S.quota.limitRaw > 0 && !task.file_id) {
                    const remaining = S.quota.limitRaw - S.quota.usedRaw;
                    if (task.size > remaining) {
                        task.status = 'ERROR';
                        task.message = L.err_quota_exceeded ;
                        if (S.uploadMode) updateRowUI(task);
                        S.upMng.scheduler();
                        return;
                    }
                }

                S.upMng.running++;

                if (!task.hash) {
                    task.status = 'HASHING';
                    task.message = L.msg_task_hashing;
                    if (S.uploadMode) updateRowUI(task);
                    task.hash = await calcSha1(task.file);
                }
                const hash = task.hash;
                S.upMng.saveTask(task);

                if (!task._totalUploadedBytes) task._totalUploadedBytes = 0;

                let _lastPollTime = Date.now();
                let _lastPollBytes = task._totalUploadedBytes;

                const speedTimer = setInterval(() => {
                    if (task.status === 'UPLOADING') {
                        const now = Date.now();
                        const timeDiff = now - _lastPollTime;
                        const bytesDiff = task._totalUploadedBytes - _lastPollBytes;

                        if (timeDiff > 0) {
                            task.speed = Math.max(0, (bytesDiff / timeDiff) * 1000);
                        }

                        _lastPollTime = now;
                        _lastPollBytes = task._totalUploadedBytes;

                        if (S.uploadMode) updateRowUI(task);
                    }
                }, 1500);

                const cryptoSign = async (secret, stringToSign) => {
                    const enc = new TextEncoder();
                    const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-1" }, false, ["sign"]);
                    const signature = await crypto.subtle.sign("HMAC", key, enc.encode(stringToSign));
                    return btoa(String.fromCharCode(...new Uint8Array(signature)));
                };

                try {
                    let finalParentId = task.parentId;

                    if (finalParentId === 'root' || finalParentId === 'upload_root' || finalParentId === '') {
                        const UPLOAD_FOLDER_NAME = 'My Upload';
                        const cacheKey = `lock_root_${UPLOAD_FOLDER_NAME}`;

                        if (!S.upMng._syncLocks) S.upMng._syncLocks = new Map();

                        if (!S.upMng._syncLocks.has(cacheKey)) {
                            const createAction = (async () => {
                                const checkExisting = async (targetName) => {
                                    try {
                                        const list = await apiList('', 1000);
                                        const found = list.find(f => f.kind === 'drive#folder' && f.name === targetName);
                                        return found ? found.id : null;
                                    } catch (e) { return null; }
                                };

                                let existingId = await checkExisting(UPLOAD_FOLDER_NAME);
                                if (existingId) return existingId;

                                let retry = 0;
                                while (retry < 3) {
                                    try {
                                        const res = await fetch('https://api-drive.mypikpak.com/drive/v1/files', {
                                            method: 'POST', headers: getHeaders(),
                                            body: JSON.stringify({ kind: "drive#folder", parent_id: "", name: UPLOAD_FOLDER_NAME })
                                        });
                                        if (res.ok) {
                                            const data = await res.json();
                                            if (typeof globalDirtyFolders !== 'undefined') globalDirtyFolders.add('');
                                            if (typeof globalCache !== 'undefined') globalCache.delete('');
                                            if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
                                            return data.file.id;
                                        }
                                        if (res.status === 400 || res.status === 429) {
                                            await new Promise(r => setTimeout(r, 1000));
                                            existingId = await checkExisting(UPLOAD_FOLDER_NAME);
                                            if (existingId) return existingId;
                                        }
                                    } catch (e) {}
                                    retry++;
                                }
                                existingId = await checkExisting(UPLOAD_FOLDER_NAME);
                                if (existingId) return existingId;
                                throw new Error("My Upload folder creation failed");
                            })();
                            S.upMng._syncLocks.set(cacheKey, createAction);
                        }

                        try {
                            finalParentId = await S.upMng._syncLocks.get(cacheKey);
                            task.parentId = finalParentId;
                        } catch (e) {
                            S.upMng._syncLocks.delete(cacheKey);
                            throw e;
                        }
                    }

                    if (task.relativeFolder) {
                        const folderNames = task.relativeFolder.split('/');
                        let currentPid = (task.parentId === 'root' || task.parentId === 'upload_root') ? '' : (task.parentId || '');

                        if (!S.upMng._syncLocks) S.upMng._syncLocks = new Map();

                        for (const name of folderNames) {
                            const cacheKey = `lock_${currentPid}_${name}`;

                            if (!S.upMng._syncLocks.has(cacheKey)) {
                                const createAction = (async () => {
                                    const checkExisting = async (pid, targetName) => {
                                        try {
                                            const list = await apiList(pid, 1000);
                                            const found = list.find(f => f.kind === 'drive#folder' && f.name === targetName);
                                            return found ? found.id : null;
                                        } catch (e) { return null; }
                                    };

                                    let existingId = await checkExisting(currentPid, name);
                                    if (existingId) return existingId;

                                    let retry = 0;
                                    while (retry < 3) {
                                        try {
                                            const res = await fetch('https://api-drive.mypikpak.com/drive/v1/files', {
                                                method: 'POST', headers: getHeaders(),
                                                body: JSON.stringify({ kind: "drive#folder", parent_id: currentPid, name: name })
                                            });
                                            if (res.ok) {
                                                const data = await res.json();
                                                return data.file.id;
                                            }
                                            if (res.status === 400 || res.status === 429) {
                                                await new Promise(r => setTimeout(r, 1000));
                                                existingId = await checkExisting(currentPid, name);
                                                if (existingId) return existingId;
                                            }
                                        } catch (e) {}
                                        retry++;
                                    }

                                    existingId = await checkExisting(currentPid, name);
                                    if (existingId) return existingId;

                                    throw new Error("Folder creation failed");
                                })();
                                S.upMng._syncLocks.set(cacheKey, createAction);
                            }

                            try {
                                currentPid = await S.upMng._syncLocks.get(cacheKey);
                            } catch (e) {
                                S.upMng._syncLocks.delete(cacheKey);
                                throw e;
                            }
                        }
                        finalParentId = currentPid;
                    }

                    if (task._deleted) throw new Error("Aborted");

                    task.status = 'UPLOADING';
                    task.message = L.msg_task_init_upload;

                    _lastPollTime = Date.now();
                    _lastPollBytes = 0;

                    const safePid = (finalParentId === 'root' || finalParentId === 'upload_root') ? '' : (finalParentId || '');

                    let data = null;

                    if (task._initData && task.file_id) {
                        data = task._initData;
                    } else {
                        let res = null;
                        let createRetry = 0;
                        const maxCreateRetries = 5;

                        while (createRetry < maxCreateRetries) {
                            try {
                                res = await fetch('https://api-drive.mypikpak.com/drive/v1/files', {
                                    method: 'POST', headers: getHeaders(), body: JSON.stringify({
                                        kind: "drive#file", parent_id: safePid, name: task.name, size: task.size, hash: hash, upload_type: "UPLOAD_TYPE_RESUMABLE"
                                    })
                                });

                                if (res.status === 429) {
                                    const waitMs = 2000 + Math.random() * 2000 * (createRetry + 1);
                                    console.warn(`[Upload] Rate limited (429). Retrying in ${Math.round(waitMs)}ms...`);
                                    await new Promise(r => setTimeout(r, waitMs));
                                    createRetry++;
                                    continue;
                                }

                                if (!res.ok) {
                                    const errData = await res.json().catch(() => ({}));
                                    const errMsg = errData.error_description || `HTTP ${res.status}`;

                                    const isQuotaExceeded = res.status === 400 && (errData.error_code === 12 || errMsg.toLowerCase().includes('quota'));

                                    if (isQuotaExceeded) {
                                        const quotaErr = new Error(L.err_quota_exceeded);
                                        quotaErr.isFatal = true;
                                        throw quotaErr;
                                    }

                                    if (res.status === 404 || errMsg.toLowerCase().includes('not found') || errMsg.toLowerCase().includes('invalid parent')) {
                                        if (S.upMng && S.upMng._syncLocks) S.upMng._syncLocks.clear();
                                        throw new Error(L.err_parent_not_found);
                                    }

                                    throw new Error(errMsg);
                                }

                                break;

                            } catch (e) {
                                console.warn(`[Upload] Init request failed (${createRetry + 1}/${maxCreateRetries}):`, e.message);

                                if (e.isFatal) throw e;

                                createRetry++;
                                if (createRetry >= maxCreateRetries) throw e;
                                await new Promise(r => setTimeout(r, 1500));
                            }
                        }

                        data = await res.json();
                        task._initData = data;
                    }

                    let newlyCreatedFileId = null;
                    if (data.file) {
                        if (data.file.id) {
                            task.file_id = data.file.id;
                            newlyCreatedFileId = data.file.id;
                        }
                        if (data.file.name) task.name = data.file.name;
                        if (data.file.thumbnail_link) task.thumbnail_link = data.file.thumbnail_link;
                        if (data.file.icon_link) task.icon_link = data.file.icon_link;
                    } else if (data.task && data.task.file_id) {
                        task.file_id = data.task.file_id;
                        newlyCreatedFileId = data.task.file_id;
                        if (data.task.name) task.name = data.task.name;
                    } else if (data.id) {
                        task.file_id = data.id;
                        newlyCreatedFileId = data.id;
                        if (data.name) task.name = data.name;
                    }

                    if (newlyCreatedFileId && !task._ghostAdded) {
                        if (typeof window.pkAddGhostFile === 'function') window.pkAddGhostFile(newlyCreatedFileId);
                        task._ghostAdded = true;
                    }
                    S.upMng.saveTask(task);

                    if (task._deleted) {
                        console.warn(`[Upload] Task ${task.id} was deleted by user during initialization. Triggering self-destruct.`);
                        if (newlyCreatedFileId && task._deleteFileIntent) {
                            try {
                                await fetch('https://api-drive.mypikpak.com/drive/v1/files:batchTrash', {
                                    method: 'POST',
                                    headers: getHeaders(),
                                    body: JSON.stringify({ ids: [newlyCreatedFileId] })
                                });
                                console.log(`[Upload] Ghost file ${newlyCreatedFileId} cleaned up.`);
                            } catch (err) {
                                console.error(`[Upload] Failed to cleanup ghost file ${newlyCreatedFileId}`, err);
                            }
                        }
                        throw new Error("Aborted");
                    }

                    if (data.upload_type === "UPLOAD_TYPE_URL" || data.phase === "PHASE_TYPE_COMPLETE" || (data.file && data.file.phase === "PHASE_TYPE_COMPLETE")) {
                        task.status = 'DONE'; task.progress = 100; task.speed = 0; task.message = L.msg_task_fast_success;
                        if (typeof window.pkRemoveGhostFile === 'function' && task.file_id) window.pkRemoveGhostFile(task.file_id);
                        S.upMng.removeTask(task.id);
                        if (S.uploadMode) {
                            updateRowUI(task);
                            if (!S._upRenderScheduled) {
                                S._upRenderScheduled = true;
                                requestAnimationFrame(() => { S._upRenderScheduled = false; if (typeof renderVisible === 'function' && !document.hidden) renderVisible(); });
                            }
                        }
                        setTimeout(() => updateQuotaUI(), 1000);

                        if (typeof globalCache !== 'undefined') {
                            const targetPid = (task.parentId === 'root' || task.parentId === 'upload_root') ? '' : (task.parentId || '');
                            if (globalCache.has(targetPid)) {
                                const cacheEntry = globalCache.get(targetPid);
                                const list = Array.isArray(cacheEntry) ? cacheEntry : (cacheEntry.items || []);
                                const newFileStub = {
                                    id: task.file_id, kind: 'drive#file', name: task.name, size: task.size,
                                    parent_id: targetPid, mime_type: task.mime_type || '',
                                    thumbnail_link: task.thumbnail_link || task.icon_link, icon_link: task.icon_link,
                                    modified_time: new Date().toISOString(), hash: hash
                                };
                                if (!list.some(f => f.id === newFileStub.id)) list.push(newFileStub);
                            }
                        }

                        if (typeof globalDirtyFolders !== 'undefined') {
                            const targetPid = task.parentId === 'root' ? '' : (task.parentId || '');
                            globalDirtyFolders.add(targetPid);
                            if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
                        }
                        if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                    }
                    else if (data.resumable && data.resumable.params) {
                        const p = data.resumable.params;
                        const ossUA = 'aliyun-sdk-js/6.23.0 Microsoft Edge 144.0.0.0 on Windows 10 64-bit';
                        const objectName = p.key;
                        const host = `https://${p.bucket}.${p.endpoint}`;

                        const totalSize = task.file.size;
                        let PART_SIZE = 5 * 1024 * 1024;
                        if (totalSize > 4 * 1024 * 1024 * 1024) {
                            PART_SIZE = 20 * 1024 * 1024;
                        } else if (totalSize > 1 * 1024 * 1024 * 1024) {
                            PART_SIZE = 10 * 1024 * 1024;
                        }

                        const partCount = Math.ceil(totalSize / PART_SIZE);

                        const getAuth = async (method, subResource = '', contentType = '') => {
                            const dateStr = new Date().toUTCString();
                            const headersToSign = {
                                'x-oss-date': dateStr,
                                'x-oss-security-token': p.security_token,
                                'x-oss-user-agent': ossUA
                            };
                            let canonicalResource = `/${p.bucket}/${objectName}`;
                            if (subResource) canonicalResource += `?${subResource}`;
                            const canonicalHeaders = Object.keys(headersToSign).sort().map(k => `${k}:${headersToSign[k]}`).join('\n') + '\n';
                            const stringToSign = [method, "", contentType, dateStr, canonicalHeaders + canonicalResource].join("\n");
                            const signature = await cryptoSign(p.access_key_secret, stringToSign);
                            return { date: dateStr, auth: `OSS ${p.access_key_id}:${signature}` };
                        };

                        const ossRequest = (method, query, body, contentType, onProgress) => {
                            return new Promise(async (resolve, reject) => {
                                try {
                                    const creds = await getAuth(method, query, contentType);
                                    const url = `${host}/${objectName.split('/').map(encodeURIComponent).join('/')}${query ? '?' + query : ''}`;

                                    if (!task._xhrs) task._xhrs = new Set();

                                    const req = GM_xmlhttpRequest({
                                        method: method, url: url, data: body,
                                        headers: {
                                            'Authorization': creds.auth,
                                            'x-oss-date': creds.date,
                                            'x-oss-security-token': p.security_token,
                                            'x-oss-user-agent': ossUA,
                                            'Content-Type': contentType || ''
                                        },
                                        upload: { onprogress: onProgress },
                                        onload: (res) => {
                                            task._xhrs.delete(req);
                                            if (res.status >= 200 && res.status < 300) resolve(res);
                                            else {
                                                const err = new Error(`OSS ${method} Error: ${res.status} ${res.statusText}`);
                                                err.status = res.status;
                                                reject(err);
                                            }
                                        },
                                        onerror: (err) => {
                                            task._xhrs.delete(req);
                                            reject(new Error("Network Error"));
                                        },
                                        onabort: () => {
                                            task._xhrs.delete(req);
                                            reject(new Error("Aborted"));
                                        }
                                    });
                                    task._xhrs.add(req);
                                    if (onProgress) task._xhr = { abort: () => req.abort() };
                                } catch (e) { reject(e); }
                            });
                        };

                        task.message = L.msg_task_uploading;

                        if (partCount <= 1) {
                            await ossRequest('PUT', '', task.file, 'application/octet-stream', (pe) => {
                                task._totalUploadedBytes = pe.loaded;
                                task.progress = (pe.loaded / totalSize) * 100;
                            });
                            task.progress = 100;
                        } else {
                            task.message = L.msg_task_init_part;
                            if (S.uploadMode) updateRowUI(task);

                            let uploadId = task._uploadId;
                            let isResuming = !!uploadId;

                            if (!uploadId) {
                                const initRes = await ossRequest('POST', 'uploads', null, '');
                                const initXml = new DOMParser().parseFromString(initRes.responseText, "text/xml");
                                uploadId = initXml.querySelector('UploadId')?.textContent || initXml.getElementsByTagName('UploadId')[0]?.textContent;
                                if (!uploadId) throw new Error("Failed to get UploadId");
                                task._uploadId = uploadId;
                            }

                            const parts = new Array(partCount);
                            const CONCURRENCY = 3;

                            let completedBytes = 0;
                            const activeParts = new Map();
                            const uploadedPartNumbers = new Set();

                            if (isResuming) {
                                try {
                                    let nextMarker = '';
                                    let isTruncated = true;
                                    while (isTruncated) {
                                        const query = `uploadId=${uploadId}` + (nextMarker ? `&part-number-marker=${nextMarker}` : '');
                                        const listRes = await ossRequest('GET', query, null, '');
                                        const listXml = new DOMParser().parseFromString(listRes.responseText, "text/xml");

                                        const partNodes = listXml.querySelectorAll('Part') || listXml.getElementsByTagName('Part');
                                        Array.from(partNodes).forEach(node => {
                                            const pNum = parseInt(node.querySelector('PartNumber')?.textContent || node.getElementsByTagName('PartNumber')[0]?.textContent);
                                            const etag = node.querySelector('ETag')?.textContent || node.getElementsByTagName('ETag')[0]?.textContent;
                                            if (pNum && etag && !uploadedPartNumbers.has(pNum)) {
                                                parts[pNum - 1] = { partNumber: pNum, etag: etag };
                                                uploadedPartNumbers.add(pNum);
                                                const pSize = (pNum === partCount) ? (totalSize - (partCount - 1) * PART_SIZE) : PART_SIZE;
                                                completedBytes += pSize;
                                            }
                                        });

                                        const truncNode = listXml.querySelector('IsTruncated') || listXml.getElementsByTagName('IsTruncated')[0];
                                        isTruncated = truncNode ? (truncNode.textContent === 'true') : false;
                                        if (isTruncated) {
                                            const markerNode = listXml.querySelector('NextPartNumberMarker') || listXml.getElementsByTagName('NextPartNumberMarker')[0];
                                            nextMarker = markerNode ? markerNode.textContent : '';
                                        }
                                    }

                                    task._totalUploadedBytes = completedBytes;
                                    task.progress = (completedBytes / totalSize) * 100;
                                    console.log(`[Upload] Resume Check: Found ${uploadedPartNumbers.size}/${partCount} parts. Total: ${fmtSize(completedBytes)}`);
                                } catch (e) {
                                    console.warn(`[Upload] Failed to fetch existing parts. Overwriting.`, e);
                                }
                            }

                            const updateProgress = () => {
                                let activeTotal = 0;
                                for (const bytes of activeParts.values()) activeTotal += bytes;
                                const currentTotal = Math.min(totalSize, completedBytes + activeTotal);

                                task._totalUploadedBytes = currentTotal;
                                task.progress = (currentTotal / totalSize) * 100;
                            };

                            const pool = Array.from({length: partCount}, (_, k) => k + 1).filter(num => !uploadedPartNumbers.has(num));

                            const worker = async () => {
                                while (pool.length > 0) {
                                    if (task.status === 'PAUSED' || !document.body.contains(el)) {
                                        activeParts.clear();
                                        throw new Error("Aborted");
                                    }

                                    const i = pool.shift();

                                    const startByte = (i - 1) * PART_SIZE;
                                    const endByte = Math.min(i * PART_SIZE, totalSize);
                                    const chunk = task.file.slice(startByte, endByte);

                                    activeParts.set(i, 0);

                                    const query = `partNumber=${i}&uploadId=${uploadId}`;

                                    try {
                                        const partRes = await ossRequest('PUT', query, chunk, 'application/octet-stream', (pe) => {
                                            activeParts.set(i, pe.loaded);
                                            updateProgress();
                                        });

                                        const finalizedSize = chunk.size;
                                        completedBytes += finalizedSize;
                                        activeParts.delete(i);

                                        updateProgress();

                                        const etagHeader = partRes.responseHeaders.match(/etag:\s*"?([^"\r\n]+)"?/i);
                                        const etag = etagHeader ? etagHeader[1] : null;
                                        if (!etag) throw new Error(`Part ${i} missing ETag`);

                                        parts[i - 1] = { partNumber: i, etag: etag };
                                        if (!task._parts) task._parts = [];
                                        task._parts[i - 1] = parts[i - 1];
                                        S.upMng.saveTask(task);
                                    } catch (err) {
                                        activeParts.delete(i);
                                        pool.unshift(i);
                                        throw err;
                                    }
                                }
                            };

                            task.message = L.msg_task_uploading_2;
                            if (S.uploadMode) updateRowUI(task);

                            if (pool.length > 0) {
                                const workers = Array(Math.min(pool.length, CONCURRENCY)).fill(0).map(worker);
                                await Promise.all(workers);
                            }

                            if (task._deleted) throw new Error("Aborted");

                            const xmlBody = `<CompleteMultipartUpload>${parts.map(p => `<Part><PartNumber>${p.partNumber}</PartNumber><ETag>${p.etag}</ETag></Part>`).join('')}</CompleteMultipartUpload>`;
                            await ossRequest('POST', `uploadId=${uploadId}`, xmlBody, 'application/xml');

                            if (task._deleted && task.file_id && task._deleteFileIntent) {
                                fetch('https://api-drive.mypikpak.com/drive/v1/files:batchTrash', {
                                    method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: [task.file_id] })
                                }).catch(()=>{});
                                throw new Error("Aborted");
                            }

                            task.progress = 100;
                            task._totalUploadedBytes = totalSize;

                            setTimeout(() => updateQuotaUI(), 1500);

                            const tryFetchMeta = async (isRetry = false) => {
                                if (task._deleted) return true;
                                try {
                                    const meta = await apiGet(task.file_id);
                                    if (meta) {
                                        if (meta.icon_link) task.icon_link = meta.icon_link;
                                        if (meta.mime_type) task.mime_type = meta.mime_type;
                                        if (meta.medias) task.medias = meta.medias;

                                        const isValidThumb = meta.thumbnail_link && meta.thumbnail_link !== meta.icon_link;

                                        if (isValidThumb) {
                                            const testUrl = meta.thumbnail_link + (meta.thumbnail_link.includes('?') ? '&' : '?') + '_t=' + Date.now();

                                            const isImageReady = await new Promise(resolve => {
                                                const img = new Image();
                                                img.onload = () => resolve(true);
                                                img.onerror = () => resolve(false);
                                                img.src = testUrl;
                                            });

                                            if (isImageReady) {
                                                task.thumbnail_link = testUrl;

                                                if (typeof globalCache !== 'undefined') {
                                                    const pid = task.parentId === 'root' ? '' : (task.parentId || '');
                                                    if (globalCache.has(pid)) {
                                                        const list = globalCache.get(pid);
                                                        const target = Array.isArray(list) ? list.find(f => f.id === task.file_id) :
                                                        (list.items ? list.items.find(f => f.id === task.file_id) : null);
                                                        if (target) target.thumbnail_link = testUrl;
                                                    }
                                                }

                                                if (isRetry && S.uploadMode) {
                                                    if (!S._upRenderScheduled) {
                                                        S._upRenderScheduled = true;
                                                        requestAnimationFrame(() => { S._upRenderScheduled = false; if (typeof renderVisible === 'function' && !document.hidden) renderVisible(); });
                                                    }
                                                    console.log(`[Upload] Cover synced globally: ${task.name}`);
                                                }
                                                return true;
                                            } else {
                                                console.log(`[Upload] Cover CDN not ready (404). Retrying later...`);
                                                return false;
                                            }
                                        }
                                    }
                                } catch(e) { console.warn("[Upload] Meta fetch warning", e); }
                                return false;
                            };

                            (async () => {
                                if (await tryFetchMeta(false)) return;

                                for (let i = 0; i < 8; i++) {
                                    await sleep(3500);
                                    if (await tryFetchMeta(true)) return;
                                }

                                for (let i = 0; i < 20; i++) {
                                    await sleep(15000);
                                    if (await tryFetchMeta(true)) return;
                                }

                                console.log(`[Upload] Entering infinite polling mode for: ${task.name}`);

                                while (true) {
                                    await sleep(60000);
                                    if (await tryFetchMeta(true)) return;
                                }
                            })();
                        }

                        task.status = 'DONE'; task.progress = 100; task.speed = 0; task.message = L.msg_task_upload_done;
                        if (typeof window.pkRemoveGhostFile === 'function' && task.file_id) window.pkRemoveGhostFile(task.file_id);
                        S.upMng.removeTask(task.id);
                        if (S.uploadMode) {
                            updateRowUI(task);
                            if (!S._upRenderScheduled) {
                                S._upRenderScheduled = true;
                                requestAnimationFrame(() => { S._upRenderScheduled = false; if (typeof renderVisible === 'function' && !document.hidden) renderVisible(); });
                            }
                        }

                        if (typeof globalCache !== 'undefined') {
                            const targetPid = (task.parentId === 'root' || task.parentId === 'upload_root') ? '' : (task.parentId || '');
                            if (globalCache.has(targetPid)) {
                                const cacheEntry = globalCache.get(targetPid);
                                const list = Array.isArray(cacheEntry) ? cacheEntry : (cacheEntry.items || []);
                                const newFileStub = {
                                    id: task.file_id, kind: 'drive#file', name: task.name, size: task.size,
                                    parent_id: targetPid, mime_type: task.mime_type || '',
                                    thumbnail_link: task.thumbnail_link || task.icon_link, icon_link: task.icon_link,
                                    modified_time: new Date().toISOString(), hash: hash
                                };
                                if (!list.some(f => f.id === newFileStub.id)) list.push(newFileStub);
                            }
                        }

                        if (typeof globalDirtyFolders !== 'undefined') {
                            const targetPid = task.parentId === 'root' ? '' : (task.parentId || '');
                            globalDirtyFolders.add(targetPid);
                            if (typeof runBackgroundCrawler === 'function') runBackgroundCrawler();
                        }
                        if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                    }
                } catch (e) {
                    const isManualAbort = e.message === 'Aborted';
                    task.status = isManualAbort ? 'PAUSED' : 'ERROR';
                    task.message = isManualAbort ? L.msg_task_paused : (e.message || L.err_unknown);

                    if (e.status === 403 && task.file_id) {
                        task.message = L.msg_token_expired_retry;
                        task._initData = null;
                        task._uploadId = null;
                        task.progress = 0;
                        task._totalUploadedBytes = 0;
                        fetch('https://api-drive.mypikpak.com/drive/v1/files:batchDelete', {
                            method: 'POST', headers: getHeaders(), body: JSON.stringify({ ids: [task.file_id] })
                        }).catch(()=>{});
                        if (typeof window.pkRemoveGhostFile === 'function') window.pkRemoveGhostFile(task.file_id);
                        task.file_id = null;
                    }

                    if (S.uploadMode) updateRowUI(task);
                    S.upMng.saveTask(task);
                } finally {
                    clearInterval(speedTimer);
                    task._xhr = null; S.upMng.running--;

                    if (S.uploadMode) { refresh(); }

                    S.upMng.scheduler();
                }
            },

            pause: (task, skipRender = false) => {
                if (task.status === 'UPLOADING') {
                    task.status = 'PAUSED';
                    task.message = L.msg_task_paused;

                    if (task._xhrs && task._xhrs.size > 0) {
                        task._xhrs.forEach(req => req.abort());
                        task._xhrs.clear();
                    }
                    if (task._xhr) {
                        task._xhr.abort();
                        task._xhr = null;
                    }
                    S.upMng.saveTask(task);
                    if (S.uploadMode && !skipRender) { refresh(); }
                } else if (task.status === 'WAITING') {
                    task.status = 'PAUSED';
                    task.message = L.msg_task_paused;
                    S.upMng.saveTask(task);
                    if (S.uploadMode && !skipRender) { refresh(); }
                }
            },

            resume: (task, skipRender = false) => {
                if (task.status === 'PAUSED' || task.status === 'ERROR') {
                    task.status = 'WAITING';
                    task.message = L.msg_task_waiting;
                    S.upMng.saveTask(task);
                    S.upMng.scheduler();
                    if (S.uploadMode && !skipRender) { refresh(); }
                }
            }
        };

        S.upMng.initStore();

        const handleUploadInput = async (files) => {
            if (!files || files.length === 0) return;

            if (S.upMng && S.upMng._syncLocks) S.upMng._syncLocks.clear();

            const curPath = S.path[S.path.length - 1];
            const isVirtual = curPath.id.startsWith('virtual_') || curPath.id.includes('_root') || curPath.id === 'upload_root';
            const safeParentId = (curPath.id && !isVirtual) ? curPath.id : '';

            let fileList = Array.from(files);

            let existingFiles = [];
            if (typeof globalCache !== 'undefined' && globalCache.has(safeParentId)) {
                const raw = globalCache.get(safeParentId);
                existingFiles = Array.isArray(raw) ? raw : (raw.items || []);
            } else if (!isVirtual && S.items && S.items.length > 0) {
                existingFiles = S.items;
            }

            if (existingFiles.length > 0) {
                const existingMap = new Set();
                existingFiles.forEach(f => {
                    if (f.kind !== 'drive#folder') {
                        existingMap.add(`${f.name}_${f.size}`);
                    }
                });

                let dupCount = 0;
                const duplicateIndices = new Set();
                fileList.forEach((file, index) => {
                    if (file.name.startsWith('.')) return;
                    let relativeFolder = "";
                    if (file.webkitRelativePath) {
                        const parts = file.webkitRelativePath.split('/');
                        if (parts.length > 1) {
                            parts.pop();
                            relativeFolder = parts.join('/');
                        }
                    }
                    if (!relativeFolder) {
                        const key = `${file.name}_${file.size}`;
                        if (existingMap.has(key)) {
                            dupCount++;
                            duplicateIndices.add(index);
                        }
                    }
                });

                if (dupCount > 0) {
                    const skipDups = await showConfirm(L.msg_upload_dup_confirm.replace('{n}', dupCount));
                    if (skipDups) {
                        fileList = fileList.filter((_, idx) => !duplicateIndices.has(idx));
                    }
                }
            }

            if (fileList.length === 0) {
                UI.inpFile.value = '';
                UI.inpFolder.value = '';
                return;
            }

            let addedCount = 0;
            const BATCH_SIZE = 50;

            if (fileList.length > BATCH_SIZE) {
                showToast(L.msg_parsing_files, 'info', 2000);
            }

            for (let i = 0; i < fileList.length; i += BATCH_SIZE) {
                const batch = fileList.slice(i, i + BATCH_SIZE);

                await new Promise(resolve => setTimeout(resolve, 0));

                for (const file of batch) {
                    if (file.name.startsWith('.')) continue;

                    let relativeFolder = "";
                    if (file.webkitRelativePath) {
                        const parts = file.webkitRelativePath.split('/');
                        if (parts.length > 1) {
                            parts.pop();
                            relativeFolder = parts.join('/');
                        }
                    }

                    if (S.upMng) {
                        const task = S.upMng.createTask(file, safeParentId);
                        task.relativeFolder = relativeFolder;
                        S.uploadTasks.unshift(task);
                        addedCount++;
                    }
                }
            }

            showToast(L.msg_task_added.replace('{n}', addedCount));

            if (!S.uploadMode) {
                switchTab('upload');
            } else {
                renderVisible();
                updateStat();
            }

            if (S.upMng) S.upMng.scheduler();

            UI.inpFile.value = '';
            UI.inpFolder.value = '';
        };

        UI.inpFile.onchange = (e) => handleUploadInput(e.target.files);
        UI.inpFolder.onchange = (e) => handleUploadInput(e.target.files);

        const dragMask = document.createElement('div');
        dragMask.className = 'pk-drag-mask';
        UI.win.appendChild(dragMask);

        const parseEntries = async (entries, relPath = "") => {
            const BATCH_SIZE = 10;
            for (let i = 0; i < entries.length; i += BATCH_SIZE) {
                const batch = entries.slice(i, i + BATCH_SIZE);
                await Promise.all(batch.map(async (entry) => {
                    if (entry.isFile) {
                        return new Promise((res) => {
                            entry.file(file => {
                                if (!file.name.startsWith('.')) {
                                    const curPath = S.path[S.path.length - 1];
                                    const safeParentId = (curPath.id && !curPath.id.includes('_root')) ? curPath.id : '';
                                    const task = S.upMng.createTask(file, safeParentId);
                                    task.relativeFolder = relPath;
                                    S.uploadTasks.unshift(task);
                                }
                                res();
                            }, () => res());
                        });
                    } else if (entry.isDirectory) {
                        return new Promise((res) => {
                            const reader = entry.createReader();
                            const readAllEntries = async () => {
                                let allSubEntries = [];
                                let readBatch = async () => {
                                    return new Promise((r) => {
                                        reader.readEntries((sub) => {
                                            if (sub.length > 0) {
                                                allSubEntries = allSubEntries.concat(sub);
                                                readBatch().then(r);
                                            } else {
                                                r();
                                            }
                                        }, () => r());
                                    });
                                };
                                await readBatch();
                                await parseEntries(allSubEntries, (relPath ? relPath + "/" : "") + entry.name);
                                res();
                            };
                            readAllEntries();
                        });
                    }
                }));
            }
        };

        const canDragUpload = () => {
            return !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode &&
                   !S.recentMode && !S.historyMode && !S.isFlattened &&
                   !S.dupMode && !S.analyzeMode && !S.uploadMode;
        };

        let dragCounter = 0;

        const handleDragGuard = (e) => {
            e.preventDefault();
            e.stopPropagation();
            if (canDragUpload()) {
                e.dataTransfer.dropEffect = 'copy';
            } else {
                e.dataTransfer.dropEffect = 'none';
            }
        };

        el.addEventListener('dragenter', (e) => {
            handleDragGuard(e);

            if (!canDragUpload()) return;

            dragCounter++;
            const curPath = S.path[S.path.length - 1];
            const isRoot = S.path.length === 1 && (curPath.id === '' || curPath.id === 'root');

            let destHtml = "";
            if (isRoot) {
                const homeIcon = CONF.icons.home.replace('width="24"', 'width="16"').replace('height="24"', 'height="16"').replace('<svg', '<svg style="vertical-align:-3px;margin-right:4px;"');
                destHtml = `${homeIcon}${L.btn_nav_home}`;
            } else {
                destHtml = esc(curPath.name);
            }

            dragMask.innerHTML = `
                <div class="pk-drag-icon"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></div>
                <div class="pk-drag-hint">${L.msg_drag_drop_hint}</div>
                <div class="pk-drag-path">${L.lbl_upload_to}<span style="color:var(--pk-pri);font-weight:600;display:inline-flex;align-items:center;">${destHtml}</span></div>
            `;
            dragMask.style.display = 'flex';
        });

        el.addEventListener('dragover', handleDragGuard);

        el.addEventListener('dragleave', (e) => {
            e.preventDefault();
            e.stopPropagation();
            if (!canDragUpload()) return;

            dragCounter--;
            if (dragCounter <= 0) { dragMask.style.display = 'none'; dragCounter = 0; }
        });

        el.addEventListener('drop', async (e) => {
            handleDragGuard(e);

            if (!canDragUpload()) return;

            dragMask.style.display = 'none';
            dragCounter = 0;

            const items = e.dataTransfer.items;
            if (!items) return;

            if (S.upMng && S.upMng._syncLocks) S.upMng._syncLocks.clear();

            const entries =[];
            for (let i = 0; i < items.length; i++) {
                const entry = items[i].webkitGetAsEntry();
                if (entry) entries.push(entry);
            }

            if (entries.length > 0) {
                const curPath = S.path[S.path.length - 1];
                const isVirtual = curPath.id.startsWith('virtual_') || curPath.id.includes('_root') || curPath.id === 'upload_root';
                const safeParentId = (curPath.id && !isVirtual) ? curPath.id : '';

                let existingFiles = [];
                if (typeof globalCache !== 'undefined' && globalCache.has(safeParentId)) {
                    const raw = globalCache.get(safeParentId);
                    existingFiles = Array.isArray(raw) ? raw : (raw.items || []);
                } else if (!isVirtual && S.items && S.items.length > 0) {
                    existingFiles = S.items;
                }

                if (existingFiles.length > 0) {
                    const existingMap = new Set();
                    existingFiles.forEach(f => {
                        if (f.kind !== 'drive#folder') {
                            existingMap.add(`${f.name}_${f.size}`);
                        }
                    });

                    let dupCount = 0;
                    const duplicateIndices = new Set();

                    const topLevelFiles = [];
                    for (let i = 0; i < entries.length; i++) {
                        const entry = entries[i];
                        if (entry.isFile) {
                            const file = await new Promise((res) => entry.file(res, () => res(null)));
                            if (file && !file.name.startsWith('.')) {
                                topLevelFiles.push({ file, index: i });
                            }
                        }
                    }

                    topLevelFiles.forEach(tf => {
                        const key = `${tf.file.name}_${tf.file.size}`;
                        if (existingMap.has(key)) {
                            dupCount++;
                            duplicateIndices.add(tf.index);
                        }
                    });

                    if (dupCount > 0) {
                        const skipDups = await showConfirm(L.msg_upload_dup_confirm.replace('{n}', dupCount));
                        if (skipDups) {
                            for (let i = entries.length - 1; i >= 0; i--) {
                                if (duplicateIndices.has(i)) {
                                    entries.splice(i, 1);
                                }
                            }
                        }
                    }
                }

                if (entries.length === 0) return;

                await parseEntries(entries);
                if (!S.uploadMode) switchTab('upload');
                else { refresh(); updateStat(); }
                if (S.upMng) S.upMng.scheduler();
            }
        });

        document.addEventListener('click', (e) => {
            if (UI.uploadWrap && !UI.uploadWrap.contains(e.target)) {
                UI.uploadWrap.querySelector('.pk-dropdown-menu').style.display = 'none';
                UI.uploadWrap.classList.remove('active');
            }
        });
    }

    const switchTab = (mode) => {
        if (S.historyMode && mode !== 'history' && typeof globalCache !== 'undefined') {
            const historySession = globalCache.get('history_session');
            if (historySession) {
                const hasPending = !historySession.completed && (historySession.cursor || 0) < (((historySession.targetIds || []).length) || 0);
                const historySnapshot = { items: Array.isArray(S.items) ? [...S.items] : [], nextToken: hasPending ? `history:${historySession.cursor || 0}` : null };
                S.cache.set('history_root', historySnapshot);
                globalCache.set('history_root', historySnapshot);
            }
        }

        if (S.abortController) S.abortController.abort();

        activeLoadId++;
        S.sortId++;

        if (S.loading) {
            S.loading = false;
        }

        const isForcedListTab = mode === 'offline' || mode === 'upload' || mode === 'history' || mode === 'trash' || mode === 'share' || mode === 'starred' || mode === 'recent';
        const needListTabSync = isForcedListTab && (isGridView() || (UI.win && UI.win.classList.contains('pk-grid-view')) || CONF.rowHeight !== getListRowHeight() || (UI.vp && UI.vp.scrollTop > 0));
        if (needListTabSync) {
            beginFolderViewSync();
            if (UI.win) {
                UI.win.classList.add('pk-view-switching');
                UI.win.classList.remove('pk-grid-view', 'pk-grid-resizing', 'pk-grid-scrolling');
            }
            S.viewMode = 'list';
            CONF.rowHeight = getListRowHeight();
            S._gridLayoutKey = '';
            S.dupGridMeta = null;
            S.dupGridMetaKey = '';
            if (UI.in) {
                UI.in.style.height = '0px';
                UI.in.style.transform = 'none';
            }
            if (UI.vp) {
                UI.vp.scrollTop = 0;
                UI.vp.scrollLeft = 0;
            }
        }

        S.items = [];
        S.display = [];
        S.recentResultItems = null;
        S.itemMap.clear();
        S.sel.clear();
        if (UI.in) {
            UI.in.innerHTML = '';
            UI.in.style.height = '0px';
            UI.in.style.transform = 'none';
        }
        if (UI.vp) {
            UI.vp.scrollTop = 0;
            UI.vp.scrollLeft = 0;
        }

        if (mode === 'history') {
            S.sort = 'play_time';
            S.dir = 1;
        } else if (mode === 'offline') {
            S.sort = 'created_time';
            S.dir = 1;
        } else if (mode === 'recent') {
            S.sort = 'modified_time';
            S.dir = 1;
        } else if (mode === 'home') {
            S._sortAppliedForId = null;
        } else {
            S.sort = 'modified_time';
        }

        S.folderFirst = false;
        if (S.renderFolderFirst) S.renderFolderFirst();

        if (UI.chkGlobal && !S.trashMode && !S.shareMode && !S.starredMode && !S.offlineMode && !S.historyMode && !S.recentMode && !S.uploadMode) {
            S.wasGlobalChecked = UI.chkGlobal.checked;
        }

        S.trashMode = (mode === 'trash');
        S.shareMode = (mode === 'share');
        S.starredMode = (mode === 'starred');
        S.recentMode = (mode === 'recent');
        S.historyMode = (mode === 'history');
        S.offlineMode = (mode === 'offline');
        S.uploadMode = (mode === 'upload');
        S.scanFilter = null;
        if (UI.chkSearchPath) UI.chkSearchPath.checked = false;

        let rootName = L.btn_nav_home;
        if (S.trashMode) rootName = L.btn_nav_trash;
        if (S.shareMode) rootName = L.btn_nav_share;
        if (S.starredMode) rootName = L.btn_nav_starred;
        if (S.recentMode) rootName = L.btn_nav_recent;
        if (S.historyMode) rootName = L.btn_nav_history;
        if (S.offlineMode) rootName = L.title_offline;
        if (S.uploadMode) rootName = L.btn_nav_upload;

        if (S.offlineMode) {
             S.path = [{ id: 'offline_root', name: rootName }];
        } else if (S.uploadMode) {
             S.path = [{ id: 'upload_root', name: rootName }];
        } else if (S.recentMode) {
             S.path = [{ id: 'recent_root', name: rootName }];
        } else if (S.historyMode) {
             S.path = [{ id: 'history_root', name: rootName }];
        } else {
             S.path = [{ id: '', name: rootName }];
        }

        UI.btnNavHome.classList.toggle('act', mode === 'home');
        UI.btnNavTrash.classList.toggle('act', mode === 'trash');
        if (UI.btnNavShare) UI.btnNavShare.classList.toggle('act', mode === 'share');
        if (UI.btnNavStarred) UI.btnNavStarred.classList.toggle('act', mode === 'starred');
        if (UI.btnNavRecent) UI.btnNavRecent.classList.toggle('act', mode === 'recent');
        if (UI.btnNavHistory) UI.btnNavHistory.classList.toggle('act', mode === 'history');
        if (UI.btnNavOffline) UI.btnNavOffline.classList.toggle('act', mode === 'offline');
        if (UI.btnNavUpload) UI.btnNavUpload.classList.toggle('act', mode === 'upload');

        if(UI.topBar) UI.topBar.style.display = 'flex';
        if(UI.actionBar) UI.actionBar.style.display = 'flex';
        if(UI.trashBar) UI.trashBar.style.display = 'none';
        if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
        if(UI.crumb) { UI.crumb.style.opacity = '1'; UI.crumb.style.display = 'flex'; }
        if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';

        const stdBtns = [UI.btnNewFolder, UI.btnDel, UI.btnCopy, UI.btnCut, UI.btnPaste, UI.btnRename, UI.btnBulkRename, UI.btnPrune, UI.btnUnzip, UI.btnMigrate, UI.btnBlacklistManager];
        const shareBtns = [document.getElementById('pk-cancel-share')];
        const upBtns = [UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll];
        const upSep = document.getElementById('pk-up-sep');

        upBtns.forEach(b => { if(b) b.style.display = 'none'; });
        if(upSep) upSep.style.display = 'none';

        if (UI.btnRefresh) UI.btnRefresh.style.display = 'inline-flex';

        if (S.historyMode) {
            UI.win.classList.remove('pk-mode-trash');

            stdBtns.forEach(b => { if(b && b !== UI.btnBlacklistManager && b !== UI.btnMigrate) b.style.display = 'none'; });
            if (UI.btnBlacklistManager) UI.btnBlacklistManager.style.display = 'inline-flex';
            if (UI.btnMigrate) UI.btnMigrate.style.display = 'inline-flex';
            if (UI.btnRefresh) UI.btnRefresh.style.display = 'inline-flex';

            [UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll, UI.btnUpClearDone, UI.btnUpClearIng].forEach(b => { if(b) b.style.display = 'none'; });
            const upSep = document.getElementById('pk-up-sep');
            if(upSep) upSep.style.display = 'none';
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'none';

            [UI.btnAria2, UI.btnDown, UI.btnExt, UI.btnImgSearch].forEach(b => { if(b) b.style.display = 'inline-flex'; });
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });

            if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
            if (UI.scan) UI.scan.style.display = 'none';
            if (UI.chkGlobal) UI.chkGlobal.checked = false;

            if (UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';

            if (UI.searchInput && UI.searchInput.parentNode) {
                UI.searchInput.parentNode.style.display = 'flex';
            }
            if (UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
        }
        else if (S.starredMode || S.recentMode) {
            UI.win.classList.remove('pk-mode-trash');
            [UI.btnAria2, UI.btnDown, UI.btnExt, UI.btnImgSearch].forEach(b => { if(b) b.style.display = 'inline-flex'; });
            stdBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            [UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll, UI.btnUpClearDone, UI.btnUpClearIng].forEach(b => { if(b) b.style.display = 'none'; });
            const upSep = document.getElementById('pk-up-sep');
            if(upSep) upSep.style.display = 'none';
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'inline-flex';

            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });

            if(UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if(UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
            if(UI.scan) UI.scan.style.display = 'none';
            if(UI.chkGlobal) UI.chkGlobal.checked = false;

            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
        }
        else if (S.shareMode) {
            UI.win.classList.remove('pk-mode-trash');
            stdBtns.forEach(b => { if(b && b !== UI.btnBlacklistManager) b.style.display = 'none'; });
            if (UI.btnBlacklistManager) UI.btnBlacklistManager.style.display = 'inline-flex';
            if (UI.btnRefresh) UI.btnRefresh.style.display = 'inline-flex';
            shareBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            if(UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if(UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
            if(UI.scan) UI.scan.style.display = 'none';
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'none';
        }
        else if (S.offlineMode) {
            UI.win.classList.remove('pk-mode-trash');

            [UI.btnNewFolder, UI.btnCopy, UI.btnCut, UI.btnPaste, UI.btnRename, UI.btnBulkRename, UI.btnPrune, UI.btnUnzip, UI.btnMigrate].forEach(b => { if(b) b.style.display = 'none'; });

            [UI.btnDel, UI.btnRefresh, UI.btnBlacklistManager].forEach(b => { if(b) b.style.display = 'inline-flex'; });

            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });

            if(UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if(UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
            if(UI.scan) UI.scan.style.display = 'none';
            if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';

            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
            [UI.btnAria2, UI.btnDown].forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.btnExt) UI.btnExt.style.display = 'inline-flex';
            if(UI.btnImgSearch) UI.btnImgSearch.style.display = 'inline-flex';
        }
        else if (S.uploadMode) {
            UI.win.classList.remove('pk-mode-trash');

            stdBtns.forEach(b => { if(b) b.style.display = 'none'; });
            if (UI.btnRefresh) UI.btnRefresh.style.display = 'none';
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });

            upBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            if(upSep) upSep.style.display = 'block';

            if(UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if(UI.btnAnalyze) UI.btnAnalyze.style.display = 'none';
            if(UI.scan) UI.scan.style.display = 'none';
            if(UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'none';
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'none';

            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
            [UI.btnAria2, UI.btnDown].forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.btnExt) UI.btnExt.style.display = 'inline-flex';
            if(UI.btnImgSearch) UI.btnImgSearch.style.display = 'inline-flex';
        }
        else if (S.trashMode) {
            UI.win.classList.add('pk-mode-trash');
            if(UI.topBar) UI.topBar.style.display = 'flex';
            if(UI.actionBar) UI.actionBar.style.display = 'none';
            if(UI.trashBar) UI.trashBar.style.display = 'flex';
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'none';
        }
        else {
            UI.win.classList.remove('pk-mode-trash');

            [UI.btnAria2, UI.btnDown, UI.btnExt, UI.btnImgSearch].forEach(b => { if(b) b.style.display = 'inline-flex'; });
            stdBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            [UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll, UI.btnUpClearDone, UI.btnUpClearIng].forEach(b => { if(b) b.style.display = 'none'; });
            const upSep = document.getElementById('pk-up-sep');
            if(upSep) upSep.style.display = 'none';
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'inline-flex';

            if(UI.lblGlobal) UI.lblGlobal.style.display = 'flex';
            if(UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
            if(UI.scan) UI.scan.style.display = 'flex';
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
            if (UI.chkGlobal && typeof S.wasGlobalChecked !== 'undefined') {
                UI.chkGlobal.checked = S.wasGlobalChecked;
            }
        }

        if (S.starredMode || S.offlineMode || S.uploadMode) {
            if (UI.lblGlobal) UI.lblGlobal.style.display = 'none';
            if (UI.chkGlobal) UI.chkGlobal.checked = false;
        }

        S.clearSelection();

        const isOffline = mode === 'offline';
        const isRecent = mode === 'recent';

        const realKey = S.getRealCacheKey(isOffline ? 'offline_root' : (isRecent ? 'recent_root' : ''));
        const hasCache = typeof globalCache !== 'undefined' && globalCache.has(realKey);

        const session = typeof globalCache !== 'undefined' ? globalCache.get('offline_session') : null;
        const isResumingOffline = isOffline && session && !session.completed;

        const recentCache = isRecent && hasCache ? globalCache.get(realKey) : null;
        const isResumingRecent = isRecent && recentCache && !Array.isArray(recentCache) && recentCache.nextToken;

        if (!((isOffline && (hasCache || isResumingOffline)) || (isRecent && (hasCache || isResumingRecent)))) {
            setLoad(true, true);
        }

        load(false, !(isOffline || isRecent));

        if (window.pkSmartRefreshTrigger) {
            setTimeout(() => window.pkSmartRefreshTrigger(isOffline || isRecent), 100);
        }
    };

    const btnCloud = el.querySelector('#pk-btn-cloud');

    const showFolderSelector = (initialId, onConfirm, initialPath = null, fileFilter = null, customTitle = null) => {
        let currentPath = initialPath ? JSON.parse(JSON.stringify(initialPath)) : [{ id: '', name: L.picker_all }];
        if (currentPath.length > 0) currentPath[0].name = L.picker_all;
        let currentList = [];
        let selectedFile = null;
        let sortMode = 'new';

        const titleStr = customTitle || (fileFilter ? L.title_select_file : L.picker_title);

        const picker = showModal(`
            <div style="display:flex; flex-direction:column; height:480px; width:500px; max-width:90vw;">
                <div style="padding:0 0 20px 0; display:flex; justify-content:space-between; align-items:center; flex-shrink:0;">
                    <h3 style="margin:0; font-size:18px; font-weight:700; color:var(--pk-fg); border:none;">${titleStr}</h3>
                    <div id="pk_picker_close_btn" style="cursor:pointer; color:var(--pk-icon-c); display:flex; align-items:center; justify-content:center; padding:4px; border-radius:4px; transition:background 0.2s;">
                        ${CONF.icons.close}
                    </div>
                </div>

                <div style="display:flex; align-items:center; gap:10px; margin-bottom:10px; height:32px;">
                    <div id="pk_picker_crumb" class="pk-no-scrollbar" style="flex:1; overflow-x:auto; overflow-y:hidden; white-space:nowrap; display:flex; align-items:center; font-size:14px; color:#666; height:100%; scroll-behavior: smooth;"></div>
                    <div style="position:relative; flex-shrink:0;">
                        <div id="pk_sort_trigger" style="cursor:pointer; font-size:13px; color:var(--pk-fg); font-weight:500; display:flex; align-items:center; gap:4px; user-select:none; padding:4px 8px; border-radius:6px; background:transparent; transition:background 0.2s;">
                            <span id="pk_sort_txt">${L.picker_sort_new}</span>
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="opacity:0.6;"><polyline points="6 9 12 15 18 9"/></svg>
                        </div>
                        <div id="pk_sort_menu" style="display:none; position:absolute; top:100%; right:0; background:var(--pk-bg); border:1px solid var(--pk-bd); border-radius:6px; box-shadow:0 4px 15px rgba(0,0,0,0.2); z-index:20; min-width:125px; overflow:hidden; margin-top:4px;">
                            <div class="pk-sort-opt" data-val="az" style="padding:8px 12px; font-size:14px; cursor:pointer; color:var(--pk-fg); display:flex; align-items:center; gap:8px;">${CONF.crumbIcons.sortAZ}<span>A-Z</span></div>
                            <div class="pk-sort-opt" data-val="za" style="padding:8px 12px; font-size:14px; cursor:pointer; color:var(--pk-fg); display:flex; align-items:center; gap:8px;">${CONF.crumbIcons.sortZA}<span>Z-A</span></div>
                            <div class="pk-sort-opt" data-val="new" style="padding:8px 12px; font-size:14px; cursor:pointer; color:var(--pk-fg); display:flex; align-items:center; gap:8px;">${CONF.crumbIcons.sortNew}<span>${L.picker_sort_new}</span></div>
                            <div class="pk-sort-opt" data-val="old" style="padding:8px 12px; font-size:14px; cursor:pointer; color:var(--pk-fg); display:flex; align-items:center; gap:8px;">${CONF.crumbIcons.sortOld}<span>${L.picker_sort_old}</span></div>
                        </div>
                    </div>
                </div>

                <div id="pk_picker_list" class="pk-scroll" style="flex:1; overflow-y:auto; padding:0; border-top:1px solid var(--pk-bd);">
                    <div style="text-align:center; padding:20px; color:#999;">${L.loading}</div>
                </div>

                <div style="padding-top:15px; border-top:1px solid var(--pk-bd); display:flex; justify-content:space-between; align-items:center; flex-shrink:0;">
                    <div id="pk_picker_new" style="cursor:pointer; color:var(--pk-pri); display:${fileFilter ? 'none' : 'flex'}; align-items:center; gap:6px; font-size:14px; font-weight:500;">
                        ${CONF.icons.newfolder} <span>${L.picker_new}</span>
                    </div>
                    <div style="display:flex; gap:10px; margin-left:auto;">
                        <button class="pk-btn" id="pk_picker_cancel" style="border:none; background:transparent;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="pk_picker_ok" style="padding:0 24px; border-radius:6px;">${L.btn_ok}</button>
                    </div>
                </div>
            </div>
        `);

        const mContent = picker.querySelector('.pk-modal');
        mContent.style.padding = '20px';
        mContent.style.width = 'fit-content';
        picker.querySelector('.pk-modal-close').style.display = 'none';

        const crumbEl = picker.querySelector('#pk_picker_crumb');
        const listEl = picker.querySelector('#pk_picker_list');

        listEl.onscroll = () => {
            const oldPop = document.querySelector('.pk-crumb-pop');
            if (oldPop && typeof oldPop._cleanup === 'function') oldPop._cleanup();
        };

        const sortTrigger = picker.querySelector('#pk_sort_trigger');
        const sortMenu = picker.querySelector('#pk_sort_menu');
        const sortTxt = picker.querySelector('#pk_sort_txt');
        const closeBtn = picker.querySelector('#pk_picker_close_btn');

        closeBtn.onmouseover = () => closeBtn.style.background = 'var(--pk-hl)';
        closeBtn.onmouseout = () => closeBtn.style.background = 'transparent';
        closeBtn.onclick = () => picker.remove();

        const applySort = () => {
            const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
            const getCharWeight = (str) => {
                if (!str) return 0;
                const c = str.charAt(0);
                if (/[0-9]/.test(c)) return 20;
                if (/[\u4e00-\u9fa5]/.test(c)) return 30;
                if (/[a-zA-Z]/.test(c)) return 40;
                return 10;
            };
            const compareNames = (nameA, nameB) => {
                const rankA = getCharWeight(nameA);
                const rankB = getCharWeight(nameB);
                if (rankA !== rankB) return rankA - rankB;
                return collator.compare(nameA, nameB);
            };

            currentList.sort((a, b) => {
                if (a.kind !== b.kind) return a.kind === 'drive#folder' ? -1 : 1;

                const isSysA = a.name === CONF.SYSTEM_FOLDER_NAME && (!a.parent_id || a.parent_id === '' || a.parent_id === 'root');
                const isSysB = b.name === CONF.SYSTEM_FOLDER_NAME && (!b.parent_id || b.parent_id === '' || b.parent_id === 'root');
                if (isSysA !== isSysB) return isSysA ? -1 : 1;

                if (sortMode === 'az') return compareNames(a.name, b.name);
                if (sortMode === 'za') return compareNames(b.name, a.name);
                if (sortMode === 'new') return new Date(b.modified_time) - new Date(a.modified_time);
                if (sortMode === 'old') return new Date(a.modified_time) - new Date(b.modified_time);
                return 0;
            });
        };

        const renderList = () => {
            listEl.innerHTML = '';
            if (currentList.length === 0) {
                listEl.innerHTML = `<div class="pk-empty" style="padding-bottom:0;position:relative;height:100%;justify-content:center;">${CONF.emptySVG}<div class="pk-empty-txt" style="margin-top:10px;">${L.str_no_files}</div></div>`;
                return;
            }

            currentList.forEach(item => {
                const div = document.createElement('div');
                div.style.cssText = "display:flex; align-items:center; padding:10px 8px; cursor:pointer; border-radius:6px; transition:background 0.1s; border-bottom:1px dashed var(--pk-bd);";

                const isDir = item.kind === 'drive#folder';
                const isSelected = selectedFile && selectedFile.id === item.id;
                const isProtected = isDir && ((typeof isSystemItem === 'function') ? isSystemItem(item) : false);
                const tagHtml = isProtected ? `<span class="pk-tag-default" style="flex:0 0 auto; margin-left:6px; min-width:auto; height:16px; padding:0 6px; line-height:1; display:inline-flex; align-items:center; justify-content:center; white-space:nowrap; overflow:visible; text-overflow:clip;">${L.tag_default}</span>` : '';

                let checkHtml = "";
                if (!isDir && fileFilter) {
                    checkHtml = `<input type="checkbox" ${isSelected ? 'checked' : ''} style="margin-right:12px; width:16px; height:16px; accent-color:var(--pk-pri); cursor:pointer;">`;
                }

                const iconSrc = item.icon_link || '';
                let iconHtml = '';
                if (isDir) {
                    const fallbackSvg = CONF.typeIcons.folder.replace(/width="\d+"/, 'width="24"').replace(/height="\d+"/, 'height="24"');
                    iconHtml = iconSrc ? `<img src="${iconSrc}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;margin-right:10px;">` : `<div style="margin-right:10px; display:flex; color:#FFC107;">${fallbackSvg}</div>`;
                } else {
                    const fallbackSvg = getIcon(item).replace(/width="\d+"/, 'width="24"').replace(/height="\d+"/, 'height="24"');
                    iconHtml = iconSrc ? `<img src="${iconSrc}" style="width:24px;height:24px;object-fit:contain;flex-shrink:0;margin-right:10px;">` : `<div style="margin-right:10px; display:flex; color:#888;">${fallbackSvg}</div>`;
                }

                div.innerHTML = `
                    ${checkHtml}
                    ${iconHtml}
                    <div style="flex:1; min-width:0; display:flex; align-items:center; font-size:14px; color:var(--pk-fg); font-weight:${isSelected?'bold':'normal'};">
                        <span style="min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:0 1 auto;">${esc(item.name)}</span>
                        ${tagHtml}
                    </div>
                    ${!isDir ? `<div style="font-size:12px;color:#999;margin-left:8px;">${fmtSize(item.size)}</div>` : ''}
                `;

                div.onmouseover = () => div.style.background = 'var(--pk-hl)';
                div.onmouseout = () => div.style.background = 'transparent';
                if (isSelected) div.style.background = 'var(--pk-sel-bg)';

                div.onclick = (e) => {
                    if (isDir) {
                        selectedFile = null;
                        loadFolder(item.id, item.name);
                    } else if (fileFilter) {
                        selectedFile = (selectedFile && selectedFile.id === item.id) ? null : item;
                        renderList();
                    }
                };
                listEl.appendChild(div);
            });
        };

        let _pickerCrumbIdx = 0;
        let _lastPickerScroll = 0;

        const showPickerDropdown = async (e, parentId, triggerEl) => {
            const old = document.querySelector('.pk-crumb-pop');
            if (old) {
                const wasSame = old._sourceEl === triggerEl;
                if (typeof old._cleanup === 'function') old._cleanup();
                if (wasSame) return;
            }

            triggerEl.style.background = 'transparent';
            triggerEl.innerHTML = CONF.crumbIcons.down;
            const svgD = triggerEl.querySelector('svg');
            if (svgD) { svgD.style.width = '14px'; svgD.style.height = '14px'; svgD.style.display = 'block'; }

            const pop = document.createElement('div');
            pop.className = 'pk-crumb-pop pk-scroll pk-show';
            if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) pop.classList.add('pk-dark');

            pop.style.zIndex = '2147483647';
            pop._sourceEl = triggerEl;
            document.body.appendChild(pop);

            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            pop.style.zoom = scale;

            const rect = getLogicalRect(triggerEl);
            pop.style.top = (rect.bottom + 5) + 'px';
            pop.style.left = rect.left + 'px';

            const cleanup = () => {
                if (pop.parentNode) pop.remove();
                triggerEl.innerHTML = CONF.crumbIcons.right;
                const svgR = triggerEl.querySelector('svg');
                if (svgR) { svgR.style.width = '14px'; svgR.style.height = '14px'; svgR.style.display = 'block'; svgR.style.opacity = '0.6'; }
                document.removeEventListener('mousedown', closer);
                window.removeEventListener('resize', cleanup);
            };
            pop._cleanup = cleanup;
            const closer = (ev) => { if (!pop.contains(ev.target) && ev.target !== triggerEl) cleanup(); };
            document.addEventListener('mousedown', closer);
            window.addEventListener('resize', cleanup);

            const cacheKey = parentId || 'root';
            let folders = null;

            if (typeof globalCache !== 'undefined' && globalCache.has(cacheKey)) {
                const raw = globalCache.get(cacheKey);
                if (Array.isArray(raw) || (raw && raw.items && !raw.nextToken)) {
                     const items = Array.isArray(raw) ? raw : raw.items;
                     folders = items.filter(f => f.kind === 'drive#folder');
                }
            }

            const renderMenu = (list) => {
                const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
                const getCharWeight = (str) => {
                    if (!str) return 0;
                    const c = str.charAt(0);
                    if (/[0-9]/.test(c)) return 20;
                    if (/[\u4e00-\u9fa5]/.test(c)) return 30;
                    if (/[a-zA-Z]/.test(c)) return 40;
                    return 10;
                };
                const compareNames = (nameA, nameB) => {
                    const rankA = getCharWeight(nameA);
                    const rankB = getCharWeight(nameB);
                    if (rankA !== rankB) return rankA - rankB;
                    return collator.compare(nameA, nameB);
                };

                list.sort((a, b) => {
                    const isSysA = a.name === CONF.SYSTEM_FOLDER_NAME && (!a.parent_id || a.parent_id === '' || a.parent_id === 'root');
                    const isSysB = b.name === CONF.SYSTEM_FOLDER_NAME && (!b.parent_id || b.parent_id === '' || b.parent_id === 'root');
                    if (isSysA !== isSysB) return isSysA ? -1 : 1;

                    if (sortMode === 'az') return compareNames(a.name, b.name);
                    if (sortMode === 'za') return compareNames(b.name, a.name);
                    if (sortMode === 'new') return new Date(b.modified_time || 0) - new Date(a.modified_time || 0);
                    if (sortMode === 'old') return new Date(a.modified_time || 0) - new Date(b.modified_time || 0);
                    return 0;
                });

                if (list.length === 0) { cleanup(); return; }
                pop.innerHTML = '';
                list.forEach(f => {
                    const itemDiv = document.createElement('div');
                    itemDiv.className = 'pk-crumb-item';
                    const iconSrc = f.icon_link || '';
                    const fallbackSvg = CONF.typeIcons.folder.replace(/width="\d+"/, 'width="18"').replace(/height="\d+"/, 'height="18"');
                    const iconHtml = iconSrc ? `<img src="${iconSrc}" style="width:18px;height:18px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;flex-shrink:0;">${fallbackSvg}</span>` : fallbackSvg;
                    const isInPath = S.path.some(pathNode => pathNode.id === f.id);
                    const textStyle = isInPath ? 'font-weight:bold; color:var(--pk-pri);' : '';
                    const isProtected = (typeof isSystemItem === 'function') ? isSystemItem(f) : false;
                    const tagHtml = isProtected ? `<span class="pk-tag-default" style="flex:0 0 auto; margin-left:6px; min-width:auto; height:16px; padding:0 6px; line-height:1; display:inline-flex; align-items:center; justify-content:center; white-space:nowrap; overflow:visible; text-overflow:clip;">${L.tag_default}</span>` : '';

                    itemDiv.innerHTML = `${iconHtml}<span style="display:inline-flex; align-items:center; justify-content:flex-start; min-width:0; max-width:calc(100% - 26px); overflow:hidden; flex:1 1 auto;"><span style="min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:0 1 auto; ${textStyle}">${esc(f.name)}</span>${tagHtml}</span>`;

                    itemDiv.onclick = (ev) => {
                        ev.stopPropagation();
                        cleanup();
                        const idx = currentPath.findIndex(p => p.id === parentId);
                        if (idx !== -1) {
                            currentPath = currentPath.slice(0, idx + 1);
                            loadFolder(f.id, f.name);
                        }
                    };
                    pop.appendChild(itemDiv);
                });
            };

            if (folders !== null) {
                renderMenu(folders);
            } else {
                pop.innerHTML = `<div style="padding:10px; display:flex; justify-content:center;"><div class="pk-spin-lg" style="width:16px; height:16px; border-width:2px;"></div></div>`;
                try {
                    const listItems = await apiList(parentId || '', 1000);
                    if (typeof globalCache !== 'undefined') globalCache.set(cacheKey, listItems);
                    renderMenu(listItems.filter(f => f.kind === 'drive#folder'));
                } catch (err) { cleanup(); }
            }
        };

        const renderCrumb = () => {
            crumbEl.innerHTML = '';
            currentPath.forEach((p, i) => {
                const sp = document.createElement('span');
                const isLast = i === currentPath.length - 1;

                if (i === 0) {
                    const homeIcon = CONF.icons.home.replace('<svg', '<svg style="width:15px;height:15px;margin-right:4px;"');
                    sp.innerHTML = `${homeIcon}${L.btn_nav_home}`;
                } else {
                    sp.textContent = p.name;
                }

                sp.style.cssText = "display:flex; align-items:center; height:100%; padding:0 6px; border-radius:4px; flex-shrink:0; transition:background 0.2s; white-space:nowrap;";
                sp.style.cursor = 'pointer';
                sp.style.color = isLast ? 'var(--pk-fg)' : '#888';
                sp.style.fontWeight = isLast ? 'bold' : 'normal';

                sp.onclick = () => {
                    if (!isLast) {
                        currentPath = currentPath.slice(0, i + 1);
                    }
                    loadFolder(p.id, null, true);
                };
                sp.onmouseover = () => sp.style.background = 'var(--pk-hl)';
                sp.onmouseout = () => sp.style.background = 'transparent';

                crumbEl.appendChild(sp);

                let showArrow = !isLast;
                if (isLast) {
                    if (currentList && currentList.some(item => item.kind === 'drive#folder')) {
                        showArrow = true;
                    }
                }

                if (showArrow) {
                    const sep = document.createElement('span');
                    sep.className = 'pk-picker-arrow';
                    sep.innerHTML = CONF.crumbIcons.right;
                    const svg = sep.querySelector('svg');
                    if(svg) { svg.style.width = '14px'; svg.style.height = '14px'; svg.style.display = 'block'; svg.style.opacity = '0.6'; }
                    sep.style.cssText = "margin:0 2px; display:flex; align-items:center; cursor:pointer; padding:4px; border-radius:4px; transition:all 0.2s; flex-shrink:0; color:var(--pk-icon-c);";
                    sep.onmouseover = () => { sep.style.background = 'var(--pk-hl)'; sep.style.color = 'var(--pk-pri)'; if(svg) svg.style.opacity='1'; };
                    sep.onmouseout = () => { sep.style.background = 'transparent'; sep.style.color = 'var(--pk-icon-c)'; if(svg) svg.style.opacity='0.6'; };
                    sep.onclick = (e) => {
                        e.stopPropagation();
                        if (typeof showPickerDropdown === 'function') showPickerDropdown(e, p.id, sep);
                    };
                    crumbEl.appendChild(sep);
                }
            });
            _pickerCrumbIdx = currentPath.length - 1;
            requestAnimationFrame(() => { crumbEl.scrollLeft = crumbEl.scrollWidth; });
        };

        const handlePickerWheel = (e) => {
            e.preventDefault();
            document.querySelectorAll('.pk-crumb-pop').forEach(p => {
                if (typeof p._cleanup === 'function') p._cleanup();
            });
            const now = Date.now();
            if (now - _lastPickerScroll < 120) return;
            _lastPickerScroll = now;
            const nodes = Array.from(crumbEl.children).filter(c => !c.classList.contains('pk-picker-arrow'));
            if (!nodes.length) return;
            if (e.deltaY < 0) _pickerCrumbIdx = Math.max(0, _pickerCrumbIdx - 1);
            else _pickerCrumbIdx = Math.min(nodes.length - 1, _pickerCrumbIdx + 1);
            const target = nodes[_pickerCrumbIdx];
            if (target) {
                const centerOffset = target.offsetLeft + (target.offsetWidth / 2) - (crumbEl.clientWidth / 2);
                crumbEl.scrollTo({ left: centerOffset, behavior: 'smooth' });
            }
        };
        crumbEl.addEventListener('wheel', handlePickerWheel, { passive: false });

        const loadFolder = async (id, name, isBack = false) => {
            listEl.innerHTML = `<div style="display:flex;justify-content:center;padding:20px;"><div class="pk-spin-lg" style="width:24px;height:24px;border-width:2px;"></div></div>`;
            if (name && !isBack) currentPath.push({ id, name });
            renderCrumb();

            const cacheKey = id || 'root';
            let items = null;

            if (typeof globalCache !== 'undefined' && globalCache.has(cacheKey)) {
                const raw = globalCache.get(cacheKey);
                const isComplete = Array.isArray(raw) || (raw && raw.items && !raw.nextToken);

                if (isComplete) {
                    items = Array.isArray(raw) ? raw : raw.items;
                }
            }

            try {
                if (items === null) {
                    items = await apiList(id || '', 1000);

                    if (typeof globalCache !== 'undefined') globalCache.set(cacheKey, items);
                    indexParents(id, name || L.picker_all, items);
                }

                currentList = items.filter(i => {
                    if (i.kind === 'drive#folder') return true;
                    if (fileFilter && fileFilter(i)) return true;
                    return false;
                });

                applySort();
                renderList();
                renderCrumb();
            } catch (e) {
                listEl.innerHTML = `<div style="color:#d93025; text-align:center; padding:20px;">${L.str_error}: ${esc(e.message)}</div>`;
            }
        };

        picker.querySelector('#pk_picker_cancel').onclick = () => picker.remove();
        picker.querySelector('#pk_picker_ok').onclick = () => {
            const cur = currentPath[currentPath.length - 1];
            const returnPathChain = JSON.parse(JSON.stringify(currentPath));

            if (fileFilter && selectedFile) {
                onConfirm(selectedFile.id, selectedFile.name, selectedFile, returnPathChain);
            } else {
                onConfirm(cur.id, cur.name, null, returnPathChain);
            }
            picker.remove();
        };

        if (!fileFilter) {
            picker.querySelector('#pk_picker_new').onclick = async () => {
                const cur = currentPath[currentPath.length - 1];
                const name = await showPrompt(L.msg_newfolder_prompt, '', L.picker_new);
                if (name) {
                    try {
                        const res = await fetch('https://api-drive.mypikpak.com/drive/v1/files', {
                            method: 'POST', headers: getHeaders(),
                            body: JSON.stringify({ kind: 'drive#folder', parent_id: cur.id || '', name: name })
                        });
                        if (!res.ok) throw new Error("Create Failed");
                        const data = await res.json();
                        const newFolder = data.file || data;
                        const parentId = cur.id || 'root';
                        if (typeof globalCache !== 'undefined') globalCache.delete(parentId);
                        if (S.cache) S.cache.delete(parentId);
                        globalDirtyFolders.add(parentId);
                        gmSet('pk_fmod_' + parentId, new Date(getServerNow()).toISOString());
                        if (newFolder && newFolder.id) await loadFolder(newFolder.id, newFolder.name);
                        else await loadFolder(cur.id, null, true);
                    } catch(e) { showAlert(e.message); }
                }
            };
        }

        sortTrigger.onclick = (e) => {
            e.stopPropagation();
            const isOpening = sortMenu.style.display !== 'block';
            sortMenu.style.display = isOpening ? 'block' : 'none';
            sortTrigger.style.background = isOpening ? 'var(--pk-hl)' : 'transparent';
        };
        picker.querySelectorAll('.pk-sort-opt').forEach(el => {
            el.onclick = () => {
                sortMode = el.dataset.val;
                sortTxt.textContent = el.textContent;
                sortMenu.style.display = 'none';
                sortTrigger.style.background = 'transparent';
                applySort();
                renderList();
            };
            el.onmouseover = () => { el.style.background = 'var(--pk-hl)'; el.style.color = 'var(--pk-pri)'; };
            el.onmouseout = () => { el.style.background = 'transparent'; el.style.color = 'var(--pk-fg)'; };
        });

        const closeSortMenu = () => {
            if (sortMenu) sortMenu.style.display = 'none';
            if (sortTrigger) sortTrigger.style.background = 'transparent';
        };
        setTimeout(() => document.addEventListener('click', closeSortMenu), 0);

        const _orgRemove = picker.remove.bind(picker);
        picker.remove = () => {
            document.removeEventListener('click', closeSortMenu);
            _orgRemove();
        };

        loadFolder(initialId || '', null, true);
    };

    window.pkPendingCloudPresetLinks = '';
    window.pkOpenCloudTaskWithLinks = (links) => {
        const text = Array.isArray(links) ? links.map(x => String(x || '').trim()).filter(Boolean).join('\n') : String(links || '').trim();
        if (!text) return false;
        window.pkPendingCloudPresetLinks = text;
        if (btnCloud) {
            btnCloud.click();
            return true;
        }
        return false;
    };

    const initClipboardMagnetFocusWatcher = () => {
        if (window.__pkClipboardMagnetFocusWatcherBound) return;
        window.__pkClipboardMagnetFocusWatcherBound = true;

        const state = {
            lastCheckAt: 0,
            lastDeniedAt: 0,
            lastPromptAt: 0,
            lastSignature: '',
            prompting: false,
            processing: false,
            ignored: new Map(),
            queue: [],
            queued: new Set(),
            previewCache: new Map(),
            previewCircuitUntil: 0
        };

        const getClipText = () => getStrings();

        const normalizeHash = (link) => {
            const match = String(link || '').match(/urn:btih:([^&]+)/i);
            return match ? match[1].toUpperCase() : String(link || '').trim().toLowerCase();
        };

        const makeSignature = (links) => links.map(normalizeHash).filter(Boolean).sort().join('\n');

        const isIgnoredSignature = (signature) => {
            const until = state.ignored.get(signature) || 0;
            if (!until) return false;
            if (Date.now() < until) return true;
            state.ignored.delete(signature);
            return false;
        };

        const markIgnoredSignature = (signature) => {
            state.ignored.set(signature, Date.now() + CONF.clipboardMagnetIgnoreTTL);
        };

        const getDefaultMagnetSaveTarget = () => {
            const curFolder = S.path[S.path.length - 1] || { id: '', name: L.lbl_default_folder };
            const curId = curFolder.id || '';
            const isVirtual = curId.startsWith('virtual_') || curId.includes('_root') || curId === 'analyze_root';
            const isHomeSubDir = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.isFlattened && !S.dupMode && S.path.length > 1 && !isVirtual;
            return { id: isHomeSubDir ? curId : '', name: isHomeSubDir ? (curFolder.name || L.lbl_default_folder) : L.lbl_default_folder, path: isHomeSubDir ? S.path.filter(p => !p.id.startsWith('virtual_')) : null };
        };

        const createMagnetCloudTasks = async (links, targetId) => {
            const progressTask = FloatBarManager.create(L.msg_creating_cloud_task);
            let successCount = 0;
            let failCount = 0;

            for (let i = 0; i < links.length; i++) {
                progressTask.update(L.str_creating_task_n.replace('{n}', i + 1).replace('{t}', links.length));
                try {
                    let retry = 0;
                    let created = false;
                    while (retry < 3) {
                        try {
                            await apiAddOfflineTask(links[i], targetId || '', {});
                            successCount++;
                            created = true;
                            if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                            break;
                        } catch (reqErr) {
                            const isRateLimited = String((reqErr && (reqErr.message || reqErr.status || reqErr.code)) || '').includes('429');
                            if (isRateLimited) {
                                retry++;
                                if (retry >= 3) throw reqErr;
                                await sleep(2000 * retry);
                            } else {
                                throw reqErr;
                            }
                        }
                    }
                    if (!created) throw new Error('Magnet task create failed after retry');
                } catch (e) {
                    console.error(`Magnet Task Create Failed[${links[i]}]:`, e);
                    failCount++;
                }
                await sleep(300);
            }

            progressTask.destroy();

            if (failCount > 0) {
                showToast(L.msg_cloud_task_finish.replace('{s}', successCount).replace('{f}', failCount), 'warning');
            } else {
                showToast(L.msg_cloud_task_success.replace('{n}', successCount));
            }

            setTimeout(() => updateQuotaUI(), 1000);

            const curPathId = S.path[S.path.length - 1].id || '';
            if (S.offlineMode || (targetId && curPathId === targetId)) {
                load(false, true);
            } else if (!targetId && S.path.length === 1) {
                if (window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger(true);
            }
        };

        const extractMagnetLinks = (rawText) => {
            const text = String(rawText || '').trim();
            if (!text || text.length > CONF.clipboardMagnetMaxChars) return [];

            const unique = new Map();

            const addMagnet = (value) => {
                let link = String(value || '').trim();
                if (!link) return;

                const urnOnly = link.match(/^urn:btih:([a-fA-F0-9]{40}|[a-zA-Z2-7]{32})(?:[^\s"'<>`]*)?$/i);
                if (urnOnly) link = `magnet:?xt=urn:btih:${urnOnly[1].toUpperCase()}`;

                const pureHash = link.match(/^([a-fA-F0-9]{40}|[a-zA-Z2-7]{32})$/i);
                if (pureHash) link = `magnet:?xt=urn:btih:${pureHash[1].toUpperCase()}`;

                if (!/^magnet:\?/i.test(link)) return;
                const hashMatch = link.match(/[?&]xt=urn:btih:([a-fA-F0-9]{40}|[a-zA-Z2-7]{32})/i);
                if (!hashMatch) return;

                const signature = hashMatch[1].toUpperCase();
                if (!unique.has(signature)) unique.set(signature, link);
            };

            if (!/(magnet:\?|urn:btih:)/i.test(text)) {
                const lines = text.split(/\r?\n/).map(x => x.trim()).filter(Boolean);
                if (lines.length === 1) addMagnet(lines[0]);
                return Array.from(unique.values());
            }

            const magnetRegex = /magnet:\?[^\s"'<>`]+/gi;
            let match;
            while ((match = magnetRegex.exec(text))) addMagnet(match[0]);

            const urnRegex = /urn:btih:([a-fA-F0-9]{40}|[a-zA-Z2-7]{32})(?:[^\s"'<>`]*)?/gi;
            while ((match = urnRegex.exec(text))) addMagnet(match[0]);

            return Array.from(unique.values());
        };

        const requestMagnetPreview = (link) => {
            return new Promise((resolve) => {
                const hash = normalizeHash(link);
                const now = Date.now();
                const cached = state.previewCache.get(hash);
                if (cached && now - cached.at < cached.ttl) {
                    resolve(cached.data);
                    return;
                }

                const finish = (data) => {
                    const ttl = data && data.ok ? CONF.magnetPreviewCacheTTL : CONF.magnetPreviewErrorCacheTTL;
                    state.previewCache.set(hash, { at: Date.now(), ttl, data });
                    resolve(data);
                };

                if (state.previewCircuitUntil && now < state.previewCircuitUntil) {
                    finish({ ok: false, code: 'rate_limited' });
                    return;
                }

                const url = `${CONF.magnetPreviewApi}?url=${encodeURIComponent(link)}`;

                const handleStatus = (status) => {
                    if (status === 429) {
                        state.previewCircuitUntil = Date.now() + CONF.magnetPreviewCircuitTTL;
                        finish({ ok: false, code: 'rate_limited', status });
                        return true;
                    }
                    if (status >= 500) {
                        state.previewCircuitUntil = Date.now() + CONF.magnetPreviewCircuitTTL;
                        finish({ ok: false, code: 'network', status });
                        return true;
                    }
                    if (status && (status < 200 || status >= 300)) {
                        finish({ ok: false, code: 'network', status });
                        return true;
                    }
                    return false;
                };

                if (typeof GM_xmlhttpRequest !== 'function') {
                    fetch(url, { cache: 'no-store' }).then(r => {
                        if (handleStatus(r.status)) return null;
                        return r.json();
                    }).then(data => {
                        if (data) finish({ ok: true, code: 'ok', data });
                    }).catch(() => finish({ ok: false, code: 'network' }));
                    return;
                }

                GM_xmlhttpRequest({
                    method: 'GET',
                    url,
                    responseType: 'json',
                    timeout: CONF.magnetPreviewTimeout,
                    onload: (res) => {
                        if (handleStatus(res.status)) return;
                        try {
                            const data = res.response && typeof res.response === 'object' ? res.response : JSON.parse(res.responseText || '{}');
                            finish({ ok: true, code: 'ok', data });
                        } catch (e) {
                            finish({ ok: false, code: 'network' });
                        }
                    },
                    onerror: () => finish({ ok: false, code: 'network' }),
                    ontimeout: () => finish({ ok: false, code: 'timeout' })
                });
            });
        };

        const showMagnetPreviewModal = (links, preview) => {
            return new Promise((resolve) => {
                const TXT = getClipText();
                const data = preview && preview.ok && preview.data ? preview.data : {};
                let saveTarget = getDefaultMagnetSaveTarget();
                const toWhatslinkImageUrl = (value) => {
                    let url = String(value || '').trim();
                    if (!url) return '';
                    if (/^\/\//.test(url)) url = `https:${url}`;
                    else if (/^\//.test(url)) url = `https://whatslink.info${url}`;
                    else if (!/^https?:\/\//i.test(url) && /\.(webp|png|jpe?g|gif)(\?|#|$)/i.test(url)) url = `https://whatslink.info/${url.replace(/^\.?\//, '')}`;
                    return /^https?:\/\//i.test(url) ? url : '';
                };

                const pickShot = (item) => {
                    if (!item) return null;
                    if (typeof item === 'string') {
                        const src = toWhatslinkImageUrl(item);
                        return src ? { src, time: 0 } : null;
                    }
                    if (Array.isArray(item)) {
                        for (const sub of item) {
                            const shot = pickShot(sub);
                            if (shot) return shot;
                        }
                        return null;
                    }
                    if (typeof item === 'object') {
                        const officialSrc = toWhatslinkImageUrl(item.screenshot);
                        if (officialSrc) return { src: officialSrc, time: Number(item.time || 0) || 0 };
                        const keys = ['url', 'src', 'image', 'img', 'thumbnail', 'thumb', 'preview', 'poster', 'file', 'path'];
                        for (const key of keys) {
                            const shot = pickShot(item[key]);
                            if (shot) return { src: shot.src, time: Number(item.time || shot.time || 0) || 0 };
                        }
                        for (const val of Object.values(item)) {
                            const shot = pickShot(val);
                            if (shot) return { src: shot.src, time: Number(item.time || shot.time || 0) || 0 };
                        }
                    }
                    return null;
                };

                const collectShots = (source) => {
                    const list = [];
                    const seen = new Set();
                    const push = (value) => {
                        const shot = pickShot(value);
                        if (shot && shot.src && !seen.has(shot.src)) {
                            seen.add(shot.src);
                            list.push(shot);
                        }
                    };
                    if (Array.isArray(source.screenshots)) source.screenshots.forEach(push);
                    if (source.screenshot) push({ screenshot: source.screenshot, time: source.time });
                    if (Array.isArray(source.thumbnails)) source.thumbnails.forEach(push);
                    if (Array.isArray(source.images)) source.images.forEach(push);
                    if (Array.isArray(source.files)) source.files.forEach(file => {
                        if (file && (file.screenshots || file.screenshot || file.thumbnail || file.thumb || file.preview || file.poster || file.image || file.url || file.path)) push(file);
                    });
                    return list.slice(0, CONF.magnetPreviewMaxShots);
                };

                const fmtShotTime = (seconds) => {
                    const total = Math.floor(Number(seconds || 0));
                    if (!Number.isFinite(total) || total <= 0) return '';
                    const h = Math.floor(total / 3600);
                    const m = Math.floor((total % 3600) / 60);
                    const s = total % 60;
                    return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}` : `${m}:${String(s).padStart(2, '0')}`;
                };

                const shots = collectShots(data);
                const name = data.name || TXT.str_magnet_unknown_name;
                const size = Number(data.size || 0) > 0 ? fmtSize(data.size) : TXT.str_magnet_unknown_name;
                const count = Number(data.count || 0) > 0 ? String(data.count) : String(links.length);
                const type = data.file_type || data.type || TXT.str_magnet_unknown_name;
                const hashText = normalizeHash(links[0]);
                const heroHtml = shots[0] ? `<img src="${esc(shots[0].src)}" alt="" draggable="false" referrerpolicy="no-referrer" onerror="this.replaceWith(Object.assign(document.createElement('div'),{className:'pk-magnet-empty',textContent:'${esc(TXT.str_no_preview).replace(/'/g, '&#39;')}'}));">` : `<div class="pk-magnet-empty">${esc(TXT.str_no_preview)}</div>`;
                const shotsHtml = shots.length > 1 ? `<div class="pk-magnet-shots">${shots.map((shot, idx) => `<div class="pk-magnet-thumb-wrap${idx === 0 ? ' active' : ''}" data-shot-index="${idx}" data-shot-src="${esc(shot.src)}"><img class="pk-magnet-thumb" src="${esc(shot.src)}" alt="" draggable="false" referrerpolicy="no-referrer" onerror="this.closest('.pk-magnet-thumb-wrap')?.remove();">${fmtShotTime(shot.time) ? `<span class="pk-magnet-shot-time">${esc(fmtShotTime(shot.time))}</span>` : ''}</div>`).join('')}</div>` : '';
                const getPreviewFailText = () => {
                    if (preview && preview.ok) return '';
                    if (preview && preview.code === 'rate_limited') return TXT.msg_magnet_preview_rate_limited;
                    if (preview && preview.code === 'timeout') return TXT.msg_magnet_preview_timeout;
                    if (preview && preview.code === 'network') return TXT.msg_magnet_preview_network;
                    return TXT.msg_magnet_preview_fail;
                };
                const failText = getPreviewFailText();
                const warnHtml = failText ? `<div class="pk-magnet-warn">${esc(failText)}</div>` : '';

                const m = showModal(`
                    <div class="pk-magnet-preview-wrap">
                        <div class="pk-magnet-hero">${heroHtml}</div>
                        <div class="pk-magnet-body">
                            <div>
                                <div class="pk-magnet-title">${esc(name)}</div>
                                <div class="pk-magnet-desc">${esc(TXT.msg_magnet_preview_desc)}</div>
                            </div>
                            ${warnHtml}
                            ${shotsHtml}
                            <div class="pk-magnet-meta">
                                <div class="pk-magnet-meta-item"><div class="pk-magnet-meta-label">${esc(TXT.lbl_magnet_count)}</div><div class="pk-magnet-meta-value">${esc(count)}</div></div>
                                <div class="pk-magnet-meta-item"><div class="pk-magnet-meta-label">${esc(TXT.lbl_magnet_size)}</div><div class="pk-magnet-meta-value">${esc(size)}</div></div>
                                <div class="pk-magnet-meta-item"><div class="pk-magnet-meta-label">${esc(TXT.lbl_magnet_type)}</div><div class="pk-magnet-meta-value">${esc(type)}</div></div>
                            </div>
                            <div class="pk-magnet-hash">${esc(TXT.lbl_magnet_hash)}:${esc(hashText)}</div>
                            <div class="pk-magnet-save-row">
                                <span class="pk-magnet-save-label">${esc(L.lbl_save_to)}</span>
                                <span class="pk-magnet-save-icon">${CONF.typeIcons.folder}</span>
                                <span class="pk-magnet-save-name" id="pk_magnet_save_name">${esc(saveTarget.name)}</span>
                                ${L.tip_cloud_save_path ? `<span class="pk-magnet-save-help" data-pk-tip="${esc(L.tip_cloud_save_path)}"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" style="pointer-events:none;"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>` : ''}
                                <span class="pk-magnet-save-change" id="pk_magnet_change_dir">${esc(L.btn_modify)}</span>
                            </div>
                            <div class="pk-magnet-source">${esc(TXT.lbl_magnet_preview_source)} <a href="https://whatslink.info/" target="_blank" rel="noopener noreferrer">whatslink.info</a></div>
                            <div class="pk-magnet-actions">
                                <button class="pk-btn" id="pk_magnet_cancel" style="height:38px; min-width:86px; border-radius:9px;">${esc(TXT.btn_cancel)}</button>
                                <button class="pk-btn pri" id="pk_magnet_continue" style="height:38px; min-width:128px; border-radius:9px; background:var(--pk-pri); border:none; color:#fff; font-weight:800;">${esc(TXT.btn_magnet_continue)}</button>
                            </div>
                        </div>
                    </div>
                `);

                const box = m.querySelector('.pk-modal');
                if (box) {
                    box.style.width = '420px';
                    box.style.padding = '0';
                    box.style.borderRadius = '16px';
                }

                const heroBox = m.querySelector('.pk-magnet-hero');
                const setHeroShot = (src, thumb) => {
                    if (!heroBox || !src) return;
                    let heroImg = heroBox.querySelector('img');
                    if (!heroImg) {
                        heroBox.innerHTML = `<img src="${esc(src)}" alt="" draggable="false" referrerpolicy="no-referrer">`;
                        heroImg = heroBox.querySelector('img');
                    }
                    heroImg.src = src;
                    heroImg.setAttribute('draggable', 'false');
                    heroImg.setAttribute('referrerpolicy', 'no-referrer');
                    m.querySelectorAll('.pk-magnet-thumb-wrap').forEach(x => x.classList.remove('active'));
                    if (thumb) thumb.classList.add('active');
                };

                m.querySelectorAll('.pk-magnet-thumb-wrap').forEach(thumb => {
                    thumb.addEventListener('dragstart', e => e.preventDefault());
                    thumb.addEventListener('click', () => setHeroShot(thumb.dataset.shotSrc || thumb.querySelector('img')?.getAttribute('src'), thumb));
                });

                const heroImg = m.querySelector('.pk-magnet-hero img');
                if (heroImg) heroImg.addEventListener('dragstart', e => e.preventDefault());

                const saveNameEl = m.querySelector('#pk_magnet_save_name');
                const changeDirEl = m.querySelector('#pk_magnet_change_dir');

                if (changeDirEl) {
                    changeDirEl.onclick = () => {
                        showFolderSelector(saveTarget.id, (id, name, fullItem, selectedPathChain) => {
                            saveTarget.id = id || '';
                            saveTarget.name = name || L.lbl_default_folder;
                            saveTarget.path = selectedPathChain || null;
                            if (saveNameEl) saveNameEl.textContent = saveTarget.name;
                        }, saveTarget.path);
                    };
                }

                const close = () => {
                    m.remove();
                    resolve({ confirm: false });
                };

                m.querySelector('.pk-modal-close').onclick = close;
                m.querySelector('#pk_magnet_cancel').onclick = close;
                m.querySelector('#pk_magnet_continue').onclick = () => {
                    m.remove();
                    resolve({ confirm: true, targetId: saveTarget.id, targetName: saveTarget.name });
                };
            });
        };

        const processMagnetQueue = async () => {
            if (state.processing) return;
            state.processing = true;

            try {
                while (state.queue.length > 0) {
                    const task = state.queue.shift();
                    if (!task || !task.link || !task.signature) continue;

                    if (isIgnoredSignature(task.signature)) {
                        state.queued.delete(task.signature);
                        continue;
                    }

                    if (state.lastSignature === task.signature && Date.now() - state.lastPromptAt < CONF.clipboardMagnetPromptGap) {
                        state.queued.delete(task.signature);
                        continue;
                    }

                    state.prompting = true;
                    state.lastSignature = task.signature;
                    state.lastPromptAt = Date.now();

                    let result = null;

                    try {
                        const preview = await requestMagnetPreview(task.link);
                        result = await showMagnetPreviewModal([task.link], preview);
                    } catch (e) {
                        console.error('Magnet preview queue failed:', e);
                        result = { confirm: false };
                    } finally {
                        state.prompting = false;
                    }

                    if (!result || !result.confirm) {
                        markIgnoredSignature(task.signature);
                        state.queued.delete(task.signature);
                        continue;
                    }

                    try {
                        await createMagnetCloudTasks([task.link], result.targetId || '');
                    } catch (e) {
                        console.error('Magnet cloud task failed:', e);
                        if (L.err_operation_failed) showToast(L.err_operation_failed, 'error');
                    }

                    state.queued.delete(task.signature);
                    await sleep(80);
                }
            } finally {
                state.processing = false;
                if (state.queue.length > 0) processMagnetQueue();
            }
        };

        const handleMagnetLinks = (links) => {
            if (!links || links.length === 0) return;

            let added = false;
            const now = Date.now();

            links.forEach(link => {
                const cleanLink = String(link || '').trim();
                if (!/^magnet:\?/i.test(cleanLink)) return;

                const signature = normalizeHash(cleanLink);
                if (!signature) return;
                if (isIgnoredSignature(signature)) return;
                if (state.queued.has(signature)) return;
                if (state.lastSignature === signature && now - state.lastPromptAt < CONF.clipboardMagnetPromptGap) return;

                state.queue.push({ link: cleanLink, signature });
                state.queued.add(signature);
                added = true;
            });

            if (added) processMagnetQueue();
        };

        const handleRawText = (rawText) => {
            const magnetLinks = extractMagnetLinks(rawText);
            if (magnetLinks.length === 0) return;
            handleMagnetLinks(magnetLinks);
        };

        const shouldSkipFocusCheck = () => {
            if (gmGet('pk_clipboard_magnet_focus', false) !== true) return true;
            if (document.hidden) return true;
            if (!navigator.clipboard || typeof navigator.clipboard.readText !== 'function') return true;
            if (document.querySelector('#pk_cloud_input')) return true;
            if (document.querySelector('.pk-modal-ov') && !state.processing && !state.prompting) return true;
            const now = Date.now();
            if (now - state.lastCheckAt < CONF.clipboardMagnetFocusCooldown) return true;
            if (now - state.lastDeniedAt < CONF.clipboardMagnetDenyCooldown) return true;
            return false;
        };

        const checkClipboardMagnet = async () => {
            if (shouldSkipFocusCheck()) return;

            state.lastCheckAt = Date.now();

            let rawText = '';
            try {
                rawText = await navigator.clipboard.readText();
            } catch (e) {
                state.lastDeniedAt = Date.now();
                return;
            }

            handleRawText(rawText);
        };

        const scheduleClipboardMagnetCheck = () => {
            if (window.__pkClipboardMagnetFocusTimer) clearTimeout(window.__pkClipboardMagnetFocusTimer);
            window.__pkClipboardMagnetFocusTimer = setTimeout(checkClipboardMagnet, 350);
        };

        document.addEventListener('paste', (e) => {
            if (gmGet('pk_clipboard_magnet_paste', CONF.clipboardMagnetPaste) === false) return;
            if (document.querySelector('#pk_cloud_input')) return;
            const active = document.activeElement;
            if (active && (active.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(active.tagName))) return;
            const rawText = e.clipboardData ? e.clipboardData.getData('text/plain') : '';
            handleRawText(rawText);
        }, true);

        document.addEventListener('visibilitychange', () => {
            if (!document.hidden) scheduleClipboardMagnetCheck();
        });

        window.addEventListener('focus', scheduleClipboardMagnetCheck);
        window.addEventListener('pageshow', scheduleClipboardMagnetCheck);
    };

    initClipboardMagnetFocusWatcher();

    if (btnCloud) {
        btnCloud.onclick = () => {
            const curFolder = S.path[S.path.length - 1];
            const isVirtual = curFolder.id.startsWith('virtual_') || curFolder.id.includes('_root') || curFolder.id === 'analyze_root';
            const isHomeSubDir = !S.trashMode && !S.shareMode && !S.offlineMode && !S.starredMode && !S.recentMode && !S.isFlattened && !S.dupMode && S.path.length > 1 && !isVirtual;

            let saveToId = isHomeSubDir ? (curFolder.id || '') : '';
            let saveToName = isHomeSubDir ? (curFolder.name || L.lbl_default_folder) : L.lbl_default_folder;

            let currentSavePath = isHomeSubDir ? S.path.filter(p => !p.id.startsWith('virtual_')) : null;

            const m = showModal(`
                <div style="padding: 10px 5px 0 5px; display: flex; flex-direction: column; gap: 20px;">
                    <h3 style="border:none; margin:0; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_cloud_task}</h3>

                    <textarea class="pk-cloud-area pk-scroll" id="pk_cloud_input" placeholder="${L.ph_cloud_links}"></textarea>

                    <div style="display:flex; justify-content:space-between; align-items:center; margin-top:-8px; margin-bottom:-4px;">
                        <label style="display:flex; align-items:center; cursor:pointer; font-size:12px; color:var(--pk-fg); user-select:none; opacity:0.8; transition:opacity 0.2s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.8">
                            <input type="checkbox" id="pk_cloud_smart_fix" checked style="accent-color:var(--pk-pri); margin-right:6px; width:14px; height:14px; cursor:pointer;">
                            ${L.lbl_smart_fix}
                        </label>
                        <div id="pk_cloud_error" style="display:none; color:#d93025; font-size:13px; font-weight:600;">${L.err_invalid_links}</div>
                    </div>

                    <div id="pk_cloud_dest_row" style="display:flex; align-items:flex-end; gap:8px; font-size:14px; color:var(--pk-fg); line-height:1;">
                        <span style="opacity:0.9; margin-bottom:1px;">${L.lbl_save_to}</span>
                        <div style="display:flex; align-items:flex-end; gap:5px;">
                            <div style="line-height:0; transform:translateY(2px);">
                                ${CONF.typeIcons.folder.replace('width="30"', 'width="20"').replace('height="30"', 'height="20"')}
                            </div>
                            <span id="pk_cloud_dir_name" style="font-weight:600; max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-bottom:1px;">${saveToName}</span>
                            <div style="display:flex; color:#aaa; transition:color 0.2s; margin-bottom:1px;" data-pk-tip="${L.tip_cloud_save_path}" onmouseover="this.style.color=document.querySelector('.pk-ov').classList.contains('pk-dark')?'#ddd':'#666'" onmouseout="this.style.color='#aaa'">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="pointer-events:none;"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
                            </div>
                            <span id="pk_cloud_change_dir" style="color:var(--pk-pri); cursor:pointer; font-size:14px; margin-left:2px; margin-bottom:1px; display:inline-block;">${L.btn_modify}</span>
                        </div>
                    </div>

                    <div class="pk-modal-act" style="margin-top:10px; display:flex; align-items:center; justify-content:space-between; gap:20px;">
                        <div style="position:relative; flex-shrink:0;">
                            <span id="pk_cloud_torrent_trigger" style="color:var(--pk-pri); cursor:pointer; font-size:14px; font-weight:500; display:inline-block;">${L.btn_via_torrent}</span>
                            <input type="file" id="pk_cloud_torrent_file" accept=".torrent" multiple style="display:none;">
                        </div>
                        <div style="display:flex; gap:12px; align-items:center; flex-shrink:0;">
                            <button class="pk-btn" id="cloud_cancel" style="height:40px; width:110px; border-radius:8px; background:transparent; color:var(--pk-fg); font-weight:600; border:none; font-size:14px; flex-shrink:0; justify-content:center;">${L.btn_cancel}</button>
                            <button class="pk-btn pri" id="cloud_submit" disabled style="height:40px; width:110px; border-radius:8px; background:var(--pk-pri); border:none; color:#fff; font-weight:bold; font-size:14px; display:inline-flex; align-items:center; justify-content:center; gap:6px; transition:all 0.2s; white-space:nowrap; flex-shrink:0;">
                                <span style="display:block !important; line-height:1;">${L.btn_create_now}</span>
                            </button>
                        </div>
                    </div>
                </div>
            `);

            const modalBox = m.querySelector('.pk-modal');
            modalBox.style.width = "560px";
            modalBox.style.padding = "30px";

            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) { closeBtn.style.top = "36px"; closeBtn.style.right = "30px"; }

            const input = m.querySelector('#pk_cloud_input');
            const submit = m.querySelector('#cloud_submit');
            const dirLabel = m.querySelector('#pk_cloud_dir_name');

            m.querySelector('#pk_cloud_change_dir').onclick = () => {
                showFolderSelector(saveToId, (id, name, fullItem, selectedPathChain) => {
                    saveToId = id;
                    saveToName = name;
                    dirLabel.textContent = name;
                    if (selectedPathChain) {
                        currentSavePath = selectedPathChain;
                    }
                }, currentSavePath);
            };

            const smartFixCheckbox = m.querySelector('#pk_cloud_smart_fix');
            if (smartFixCheckbox) smartFixCheckbox.onchange = () => input.dispatchEvent(new Event('input'));

            const parseAndCleanLinks = parseCloudLinks;

            input.oninput = () => {
                const rawVal = input.value.trim();
                const isSmartFix = smartFixCheckbox ? smartFixCheckbox.checked : false;

                const linesRaw = rawVal.split('\n').map(l => l.trim()).filter(l => l);
                const hasInput = linesRaw.length > 0;

                const finalLinks = parseAndCleanLinks(rawVal, isSmartFix);

                const allValid = hasInput && (finalLinks.length === linesRaw.length || finalLinks.length > 0);

                const isError = hasInput && !allValid;
                m.querySelector('#pk_cloud_error').style.display = isError ? 'block' : 'none';

                submit.disabled = !allValid;
            };

            const presetLinks = String(window.pkPendingCloudPresetLinks || '').trim();
            window.pkPendingCloudPresetLinks = '';
            if (presetLinks) {
                input.value = presetLinks;
                if (smartFixCheckbox) smartFixCheckbox.checked = true;
                input.dispatchEvent(new Event('input'));
                setTimeout(() => input.focus(), 0);
            }

            m.querySelector('#cloud_cancel').onclick = () => m.remove();

            const torrentTrigger = m.querySelector('#pk_cloud_torrent_trigger');
            const torrentFile = m.querySelector('#pk_cloud_torrent_file');

            torrentTrigger.onclick = () => torrentFile.click();

            const showSnapshotModal = (urlList, defaultId, defaultName) => {
                return new Promise((resolve) => {
                    let currentSaveId = defaultId;
                    let currentSaveName = defaultName;

                    const displayUrl = urlList[0];
                    const countSuffix = urlList.length > 1 ? L.str_snap_link_count_suffix.replace('{n}', urlList.length) : '';

                    const snapIcon = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="3" width="20" height="18" rx="4" fill="#647EFF"/><circle cx="12" cy="12" r="5" stroke="white" stroke-width="2"/><path d="M16 8L18 6" stroke="white" stroke-width="2" stroke-linecap="round"/><path d="M6 18L8 16" stroke="white" stroke-width="2" stroke-linecap="round"/></svg>`;

                    const sm = showModal(`
                        <div style="padding: 10px 5px 0 5px; width: 480px; max-width: 90vw;">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                                <h3 style="margin: 0; font-size: 18px; font-weight: 700; border: none; color: var(--pk-fg);">${L.title_save_method}</h3>
                            </div>
                            <div style="font-size: 13px; color: #888; margin-bottom: 20px; display: flex; align-items: center; gap: 4px;">
                                ${L.msg_save_snapshot_desc}
                                <div style="display:flex; cursor:help; color:#888;" data-pk-tip="${L.tip_snapshot_details}">
                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
                                </div>
                            </div>

                            <div style="border: 1px solid var(--pk-pri); background: rgba(0, 103, 192, 0.05); border-radius: 8px; padding: 15px; display: flex; align-items: center; gap: 12px; margin-bottom: 25px;">
                                <div style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
                                    ${snapIcon}
                                </div>
                                <div style="flex: 1; overflow: hidden;">
                                    <div style="font-size: 14px; color: var(--pk-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500;">
                                        ${esc(displayUrl)}${countSuffix}
                                    </div>
                                </div>
                            </div>

                            <div style="display:flex; align-items:baseline; gap:10px; font-size:14px; color:var(--pk-fg); margin-bottom: 30px;">
                                <span style="opacity:0.9;">${L.lbl_save_to}</span>
                                <div style="display:flex; align-items:baseline; gap:6px;">
                                    <span style="display:inline-flex; align-items:center; transform:translateY(5px);">
                                        ${CONF.typeIcons.folder.replace('width="30"', 'width="20"').replace('height="30"', 'height="20"')}
                                    </span>
                                    <span id="snap_dir_name" style="font-weight:600; max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${esc(currentSaveName)}</span>
                                    <span id="snap_change_dir" style="color:var(--pk-pri); cursor:pointer; font-size:14px; margin-left:4px;">${L.btn_modify}</span>
                                </div>
                            </div>

                            <div class="pk-modal-act" style="display: flex; justify-content: flex-end; gap: 12px;">
                                <button class="pk-btn" id="snap_cancel" style="height:36px; padding:0 20px; border-radius:6px; background:transparent;">${L.btn_cancel}</button>
                                <button class="pk-btn pri" id="snap_save" style="height:36px; padding:0 20px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; border:none;">${L.btn_save_snapshot}</button>
                            </div>
                        </div>
                    `);

                    const mBox = sm.querySelector('.pk-modal');
                    if (mBox) { mBox.style.padding = '24px'; mBox.style.width = 'auto'; }
                    const closeBtn = sm.querySelector('.pk-modal-close');
                    if (closeBtn) { closeBtn.style.top = '30px'; closeBtn.style.right = '24px'; }

                    let snapCurrentPath = currentSavePath;

                    sm.querySelector('#snap_change_dir').onclick = () => {
                        showFolderSelector(currentSaveId, (id, name, fullItem, selectedPathChain) => {
                            currentSaveId = id;
                            currentSaveName = name;
                            sm.querySelector('#snap_dir_name').textContent = name;
                            if (selectedPathChain) {
                                snapCurrentPath = selectedPathChain;
                                currentSavePath = selectedPathChain;
                                saveToId = id;
                                saveToName = name;
                                dirLabel.textContent = name;
                            }
                        }, snapCurrentPath);
                    };

                    const doClose = (result) => {
                        sm.remove();
                        resolve(result ? { confirm: true, targetId: currentSaveId } : { confirm: false });
                    };

                    sm.querySelector('#snap_cancel').onclick = () => doClose(false);
                    if (closeBtn) closeBtn.onclick = () => doClose(false);
                    sm.querySelector('#snap_save').onclick = () => doClose(true);

                    sm.tabIndex = 0;
                    setTimeout(() => sm.focus(), 10);
                    sm.addEventListener('keydown', (e) => {
                        if (e.key === 'Enter') {
                            e.preventDefault(); e.stopPropagation();
                            sm.querySelector('#snap_save').click();
                        }
                    });
                });
            };

            torrentFile.onchange = async (e) => {
                const files = e.target.files;
                if (!files || files.length === 0) return;
                m.remove();

                const fb = FloatBarManager.create(L.str_parsing_torrent);
                let successCount = 0;
                let failCount = 0;

                for (let i = 0; i < files.length; i++) {
                    const file = files[i];
                    fb.update(`${L.str_parsing_torrent} (${i + 1}/${files.length})`);

                    try {
                        const magnetLink = await new Promise((resolve, reject) => {
                            const reader = new FileReader();
                            reader.onload = async (evt) => {
                                try {
                                    const buf = new Uint8Array(evt.target.result);
                                    let pos = 0;
                                    const decodeSkip = () => {
                                        if (pos >= buf.length) return;
                                        const c = buf[pos];
                                        if (c === 100 || c === 108) {
                                            pos++;
                                            while (pos < buf.length && buf[pos] !== 101) decodeSkip();
                                            pos++;
                                        } else if (c === 105) {
                                            pos++;
                                            while (pos < buf.length && buf[pos] !== 101) pos++;
                                            pos++;
                                        } else if (c >= 48 && c <= 57) {
                                            let colon = pos;
                                            while (colon < buf.length && buf[colon] !== 58) colon++;
                                            const lenStr = new TextDecoder().decode(buf.slice(pos, colon));
                                            const len = parseInt(lenStr, 10);
                                            pos = colon + 1 + len;
                                        }
                                    };

                                    if (buf[0] !== 100) throw new Error(L.err_invalid_torrent);
                                    pos = 1;
                                    let infoHash = null;

                                    while (pos < buf.length && buf[pos] !== 101) {
                                        const keyStart = pos;
                                        decodeSkip();

                                        let colon = keyStart;
                                        while (colon < buf.length && buf[colon] !== 58) colon++;
                                        const kLen = parseInt(new TextDecoder().decode(buf.slice(keyStart, colon)));
                                        const keyStr = new TextDecoder().decode(buf.slice(colon + 1, colon + 1 + kLen));

                                        if (keyStr === "info") {
                                            const infoStart = pos;
                                            decodeSkip();
                                            const infoEnd = pos;
                                            const infoBuf = buf.slice(infoStart, infoEnd);

                                            const hashBuf = await crypto.subtle.digest("SHA-1", infoBuf);
                                            infoHash = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('');
                                            break;
                                        } else {
                                            decodeSkip();
                                        }
                                    }

                                    if (infoHash) resolve(`magnet:?xt=urn:btih:${infoHash}&dn=${encodeURIComponent(file.name)}`);
                                    else reject(new Error(L.err_torrent_no_info));
                                } catch (err) { reject(err); }
                            };
                            reader.onerror = () => reject(new Error(L.err_file_read));
                            reader.readAsArrayBuffer(file);
                        });

                        fb.update(`${L.msg_creating_cloud_task} (${i + 1}/${files.length})`);

                        let retry = 0;
                        while (retry < 3) {
                            try {
                                await apiAddOfflineTask(magnetLink, saveToId);
                                successCount++;
                                break;
                            } catch (reqErr) {
                                if (reqErr.message && reqErr.message.includes('429')) {
                                    retry++;
                                    await sleep(2000 * retry);
                                } else {
                                    throw reqErr;
                                }
                            }
                        }
                    } catch (e) {
                        console.error(`Torrent parse/upload failed for ${file.name}:`, e);
                        failCount++;
                    }
                }

                fb.destroy();

                if (failCount > 0) {
                    showToast(L.msg_cloud_task_finish.replace('{s}', successCount).replace('{f}', failCount), 'warning');
                } else if (successCount > 0) {
                    showToast(L.msg_cloud_task_success.replace('{n}', successCount));
                }

                if (successCount > 0) {
                    if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                    setTimeout(() => updateQuotaUI(), 1000);
                    const curPathId = S.path[S.path.length - 1].id || '';
                    if (S.offlineMode || (saveToId && curPathId === saveToId)) {
                        load(false, true);
                    } else if (!saveToId && S.path.length === 1 && window.pkSmartRefreshTrigger) {
                         window.pkSmartRefreshTrigger(true);
                    }
                }
            };

            submit.onclick = async () => {
                if (submit.disabled) return;

                const rawVal = input.value.trim();
                if (!rawVal) return;

                const isSmartFix = smartFixCheckbox ? smartFixCheckbox.checked : false;

                m.style.display = 'none';

                const finalLinks = parseAndCleanLinks(rawVal, isSmartFix);

                const videoPlatformRegex = /(?:youtube\.com|youtu\.be|twitter\.com|x\.com|tiktok\.com|douyin\.com|facebook\.com|fb\.watch|instagram\.com|t\.me|bilibili\.com)/i;
                const snapshotLinks = finalLinks.filter(l => /^https?:\/\//i.test(l) && !videoPlatformRegex.test(l));
                const directLinks = finalLinks.filter(l => !/^https?:\/\//i.test(l) || videoPlatformRegex.test(l));

                let snapshotParams = null;
                const isSingleSnapshotTask = (finalLinks.length === 1 && snapshotLinks.length === 1);

                if (isSingleSnapshotTask) {
                    const result = await showSnapshotModal(snapshotLinks, saveToId, saveToName);
                    if (!result.confirm) {
                        m.style.display = 'flex';
                        return;
                    }
                    snapshotParams = {
                        save_as: 'snapshot',
                        targetId: result.targetId
                    };
                }

                m.remove();
                const progressTask = FloatBarManager.create(L.msg_creating_cloud_task);

                let successCount = 0;
                let failCount = 0;

                const processQueue =[
                    ...directLinks.map(u => ({ url: u, isSnap: false })),
                    ...snapshotLinks.map(u => ({ url: u, isSnap: true }))
                ];

                for (let i = 0; i < processQueue.length; i++) {
                    const item = processQueue[i];
                    progressTask.update(L.str_creating_task_n.replace('{n}', i + 1).replace('{t}', processQueue.length));

                    try {
                        const pid = (item.isSnap && snapshotParams) ? snapshotParams.targetId : saveToId;
                        const extras = item.isSnap ? { save_as: 'snapshot' } : {};

                        let retry = 0;
                        while (retry < 3) {
                            try {
                                await apiAddOfflineTask(item.url, pid, extras);
                                successCount++;
                                if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                                break;
                            } catch (reqErr) {
                                if (reqErr.message && reqErr.message.includes('429')) {
                                    retry++;
                                    await sleep(2000 * retry);
                                } else {
                                    throw reqErr;
                                }
                            }
                        }
                    } catch(e) {
                        console.error(`Task Create Failed[${item.url}]:`, e);
                        failCount++;
                    }
                    await sleep(300);
                }

                progressTask.destroy();

                if (failCount > 0) {
                    showToast(L.msg_cloud_task_finish.replace('{s}', successCount).replace('{f}', failCount), 'warning');
                } else {
                    showToast(L.msg_cloud_task_success.replace('{n}', successCount));
                }

                setTimeout(() => updateQuotaUI(), 1000);

                const curPathId = S.path[S.path.length - 1].id || '';
                if (S.offlineMode || (saveToId && curPathId === saveToId)) {
                    load(false, true);
                } else if (!saveToId && S.path.length === 1) {
                    if(window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger(true);
                }
            };
        };
    }

    UI.btnNavHome.onclick = () => switchTab('home');
    if(UI.btnNavStarred) UI.btnNavStarred.onclick = () => switchTab('starred');
    if(UI.btnNavRecent) UI.btnNavRecent.onclick = () => switchTab('recent');
    if(UI.btnNavHistory) UI.btnNavHistory.onclick = () => switchTab('history');
    if(UI.btnNavUpload) UI.btnNavUpload.onclick = () => switchTab('upload');
    if(UI.btnNavShare) UI.btnNavShare.onclick = () => switchTab('share');
    if (UI.btnNavOffline) {
        UI.btnNavOffline.onclick = () => {
            switchTab('offline');
        };
    }
    UI.btnNavTrash.onclick = () => switchTab('trash');

    if (UI.btnTrashRefresh) {
        UI.btnTrashRefresh.onclick = () => { updateQuotaUI(); load(false, true); };
    }

    UI.btnRestore.onclick = async () => {
        const ids = S.getSelectedIds();
        if (ids.length === 0) return;

        ensureItemMap();

        const progressTask = FloatBarManager.create(L.msg_prepare_restore);
        const updateFloat = progressTask.update;

        isGUISensitive = true;

        S.clearSelection();

        try {
            const BATCH_SIZE = 500;
            const total = ids.length;
            const taskIds =[];
            const affectedParentIds = new Set();
            const restoredFolders = [];

            ids.forEach(id => {
                const item = S.itemMap.get(id);
                if (item) {
                    if (item.kind === 'drive#folder') restoredFolders.push(item);
                    if (item.parent_id) affectedParentIds.add(item.parent_id);
                    else affectedParentIds.add('root');
                }
            });

            updateFloat(L.msg_submit_request.replace('{c}', 0).replace('{t}', total));

            for (let i = 0; i < total; i += BATCH_SIZE) {
                const chunk = ids.slice(i, i + BATCH_SIZE);

                const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/files:batchUntrash`, {
                    method: 'POST',
                    headers: getHeaders(),
                    body: JSON.stringify({ ids: chunk })
                });

                if (!res.ok) throw new Error(`Batch Untrash Error ${res.status}`);

                const data = await res.json();

                if (data.task_id) {
                    taskIds.push(data.task_id);
                }

                updateFloat(L.msg_submit_request.replace('{c}', Math.min(i + BATCH_SIZE, total)).replace('{t}', total));
                await sleep(50);
            }

            if (taskIds.length > 0) {
                updateFloat(L.msg_wait_server.replace('{c}', 0).replace('{t}', taskIds.length));

                const pendingTasks = new Set(taskIds);
                let pollRetries = 0;
                const maxPollRetries = 120;

                while (pendingTasks.size > 0 && pollRetries < maxPollRetries) {
                    await sleep(1000);
                    pollRetries++;

                    const currentIdsToCheck = Array.from(pendingTasks).join(',');
                    const filters = {
                        phase: { eq: "PHASE_TYPE_COMPLETE" },
                        id: { in: currentIdsToCheck }
                    };
                    const filterStr = encodeURIComponent(JSON.stringify(filters));

                    const pollUrl = `https://api-drive.mypikpak.com/drive/v1/tasks?with=reference_resource&type=&thumbnail_size=SIZE_SMALL&limit=100&filters=${filterStr}`;

                    try {
                        const tRes = await fetch(pollUrl, { headers: getHeaders() });
                        if (tRes.ok) {
                            const tData = await tRes.json();
                            const completedTasks = tData.tasks || [];

                            completedTasks.forEach(t => {
                                if (pendingTasks.has(t.id)) {
                                    pendingTasks.delete(t.id);
                                }
                            });

                            const doneCount = taskIds.length - pendingTasks.size;
                            updateFloat(L.msg_server_processing.replace('{c}', doneCount).replace('{t}', taskIds.length));
                        }
                    } catch (err) {
                        console.warn("[Restore] Poll failed, retrying...", err);
                    }
                }

                if (pendingTasks.size > 0) {
                    console.warn(`[Restore] Timeout waiting for tasks: ${Array.from(pendingTasks)}`);
                }
            }

            const allIdSet = new Set(ids);
            ids.forEach(id => S.itemMap.delete(id));
            S.items = S.items.filter(x => !allIdSet.has(x.id));

            ids.forEach(id => {
                const reviveFromTombstone = (fid) => {
                    if (globalTombstoneCache.has(fid)) {
                        const savedData = globalTombstoneCache.get(fid);
                        globalCache.set(fid, savedData);
                        globalTombstoneCache.delete(fid);

                        const list = Array.isArray(savedData) ? savedData : (savedData.items ||[]);
                        list.forEach(child => {
                            if (child.kind === 'drive#folder') {
                                reviveFromTombstone(child.id);
                            }
                        });
                    }
                };

                const wipeCrawlerMemory = (fid) => {
                    scannedFolderIds.delete(fid);
                    const children = [];
                    for (const [childId, parentInfo] of globalParentIndex.entries()) {
                        if (parentInfo.id === fid) {
                            children.push(childId);
                        }
                    }
                    children.forEach(childId => wipeCrawlerMemory(childId));
                };

                reviveFromTombstone(id);
                wipeCrawlerMemory(id);
            });


            if (typeof globalCache !== 'undefined') {
                const cleanListChunk = (raw) => {
                    if (Array.isArray(raw)) return raw.filter(f => !allIdSet.has(f.id));
                    if (raw && Array.isArray(raw.items)) {
                        raw.items = raw.items.filter(f => !allIdSet.has(f.id));
                        return raw;
                    }
                    return raw;
                };
                for (const key of globalCache.keys()) {
                    if (key && (key.endsWith('_root') || key === 'root_trashed')) {
                        globalCache.set(key, cleanListChunk(globalCache.get(key)));
                    }
                }
                for (const key of S.cache.keys()) {
                    if (key && (key.endsWith('_root') || key === 'root_trashed')) {
                        S.cache.set(key, cleanListChunk(S.cache.get(key)));
                    }
                }
            }

            refresh();

            affectedParentIds.forEach(pid => {
                if (pid && pid !== 'root') gmSet('pk_fmod_' + pid, new Date(getServerNow()).toISOString());
            });

            affectedParentIds.forEach(pid => {
                const keys = (pid === 'root' || pid === '') ? ['root', ''] : [pid];

                keys.forEach(k => {
                    if (typeof globalCache !== 'undefined') globalCache.delete(k);
                    if (S.cache) S.cache.delete(k);
                    if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.delete(k);
                    if (typeof globalDirtyFolders !== 'undefined') globalDirtyFolders.add(k);
                });

                const queueId = (pid === 'root') ? '' : pid;
                backgroundQueue.unshift({ id: queueId, name: "Refill_Restore", retryCount: 0 });
            });

            restoredFolders.forEach(folder => {
                scannedFolderIds.delete(folder.id);
                backgroundQueue.unshift({ id: folder.id, name: folder.name, retryCount: 0 });
            });

            runBackgroundCrawler();
            if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
            showToast(L.msg_restore_done.replace('{n}', total));

        } catch(e) {
            showAlert(`${L.str_error}: ${e.message}`);
            load(false, true);
        } finally {
            if (progressTask) progressTask.destroy();
            isGUISensitive = false;
        }
    };

    UI.btnDelForever.onclick = async () => {
        const ids = S.getSelectedIds();
        const count = ids.length;
        if (count === 0) return;

        ensureItemMap();

        if (!await showConfirm(L.msg_del_forever_confirm.replace('{n}', count))) return;

        await executeBatchDelete(ids, {
            hardDelete: true,
            silent: false
        });
    };

    UI.btnEmptyTrash.onclick = async () => {
        if (!await showConfirm(L.msg_empty_trash_confirm)) return;
        if (S.items.length === 0) return;

        S._isEmptyingTrash = true;
        setLoad(true);
        updateLoadTxt(L.str_deleting);

        try {
            const res = await fetch('https://api-drive.mypikpak.com/drive/v1/files/trash:empty', {
                method: 'PATCH',
                headers: getHeaders()
            });

            if (!res.ok) throw new Error(`API Error ${res.status}`);

            S.items = [];
            S.display = [];
            S.itemMap.clear();
            S.clearSelection();

            if (typeof globalCache !== 'undefined') globalCache.delete('root_trashed');
            if (S.cache) S.cache.delete('root_trashed');

            refresh();
            updateStat();
            showToast(L.msg_trash_emptied);

        } catch (e) {
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            setLoad(false);
            S._isEmptyingTrash = false;
        }
    };

    const openSettingsModal = () => {
        const inputStyle = `width:100%; height:44px; padding:0 15px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box;`;
        const areaStyle = `width:100%; min-height:60px; max-height:120px; padding:12px 15px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:13px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box; resize:vertical; line-height:1.5; font-family:inherit; cursor:auto;`;        const labelStyle = `position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; line-height:1; font-size:11px; color:var(--pk-pri); font-weight:bold; pointer-events:none; z-index:1;`;
        const curLang = gmGet('pk_lang', lang);
        const curEngine = gmGet('pk_search_engine', 'google');
        const curAriaUrl = gmGet('pk_aria2_url', '');
        const curAriaToken = gmGet('pk_aria2_token', '');
        const curBlur = gmGet('pk_blur_thumb', false);
        const curBlurScope = gmGet('pk_blur_scope', curBlur ? 'list' : 'off');

        let selectedLang = curLang;
        let selectedEngine = curEngine;

        let totalStorageBytes = 0;
        const keys = typeof GM_listValues !== 'undefined' ? GM_listValues() : Object.keys(localStorage);
        keys.forEach(k => {
            if (k.startsWith('pk_')) {
                const val = typeof GM_getValue !== 'undefined' ? GM_getValue(k) : localStorage.getItem(k);
                totalStorageBytes += (k.length + JSON.stringify(val || '').length);
            }
        });

        if (typeof globalCache !== 'undefined') {
            for (const [k, v] of globalCache.entries()) {
                try { totalStorageBytes += k.toString().length + JSON.stringify(v).length; } catch(e){}
            }
        }

        const storageDisplay = fmtSize(totalStorageBytes);

        const m = showModal(`
            <div style="display:flex; flex-direction:column; height:580px; max-height:75vh; width:420px; max-width:95vw; overflow:hidden; overscroll-behavior:none; position:relative;">
                <div style="padding: 30px 30px 15px 30px; flex-shrink:0; transform:translateZ(0);">
                    <h3 style="margin: 0; font-size: 18px; font-weight: 700; border: none; line-height: 1.2; color: var(--pk-fg);">${L.modal_settings_title}</h3>
                </div>

                <div class="pk-scroll pk-no-scrollbar" style="flex:1; overflow-y:auto; padding: 10px 30px 20px 30px; overscroll-behavior:contain; transform:translateZ(0);">
                    <div style="display:flex; flex-direction:column; gap:25px; padding-top:10px;">

                        <div class="pk-custom-select" id="cs_set_lang" style="position:relative; z-index:30;">
                            <div class="pk-select-label">${L.label_lang}</div>
                            <div class="pk-select-trigger"><span id="txt_set_lang"></span>${CONF.crumbIcons.down}</div>
                            <div class="pk-select-menu pk-scroll" style="height:auto; max-height:none; overflow-y:hidden; overscroll-behavior:contain;">
                                <div class="pk-select-item" data-val="zh">简体中文</div>
                                <div class="pk-select-item" data-val="tc">繁體中文</div>
                                <div class="pk-select-item" data-val="en">English</div>
                                <div class="pk-select-item" data-val="ko">한국어</div>
                                <div class="pk-select-item" data-val="ja">日本語</div>
                                <div class="pk-select-item" data-val="id">Indonesia</div>
                                <div class="pk-select-item" data-val="ms">Bahasa Melayu</div>
                            </div>
                        </div>

                        <div style="position:relative;">
                            <label for="set_turbo"
                                onmouseover="this.style.borderColor='var(--pk-pri)'"
                                onmouseout="this.style.borderColor='var(--pk-bd)'"
                                style="display:flex; align-items:center; justify-content:space-between; height:44px; border:2px solid var(--pk-bd); border-radius:8px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box; transform: translateZ(0);">
                                <span style="font-size:14px; color:var(--pk-fg);user-select:none;">${L.desc_turbo_mode}</span>
                                <input type="checkbox" id="set_turbo" ${gmGet('pk_turbo_mode', false)?'checked':''} style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer;">
                            </label>
                            <div class="pk-select-label">${L.label_turbo_mode}</div>
                        </div>

                        <div class="pk-custom-select" id="cs_thumb_scope" style="position:relative;">
                            <div class="pk-select-label">${L.label_privacy_mode}</div>
                            <input type="hidden" id="set_thumb_scope" value="${curBlurScope}">
                            <div class="pk-select-trigger" style="padding:0 12px; font-weight:400; transition:border-color 0.2s, background-color 0.2s;">
                                <span style="display:flex; align-items:center; justify-content:space-between; width:100%; gap:12px; min-width:0;">
                                    <span style="min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:14px; font-weight:400; color:var(--pk-fg); user-select:none;">${L.label_blur_cover}</span>
                                    <span id="txt_thumb_scope" style="flex-shrink:0; color:var(--pk-fg); opacity:0.92; font-size:14px; font-weight:500;">
                                        ${curBlurScope === 'off' ? L.opt_privacy_off : curBlurScope === 'list' ? L.opt_privacy_list : curBlurScope === 'grid' ? L.opt_privacy_grid : L.opt_privacy_both}
                                    </span>
                                </span>
                                <div style="display:flex; color:#999; margin-left:8px;">${CONF.crumbIcons.down}</div>
                            </div>
                            <div class="pk-select-menu">
                                <div class="pk-select-item ${curBlurScope === 'off' ? 'act' : ''}" data-val="off">${L.opt_privacy_off}</div>
                                <div class="pk-select-item ${curBlurScope === 'list' ? 'act' : ''}" data-val="list">${L.opt_privacy_list}</div>
                                <div class="pk-select-item ${curBlurScope === 'grid' ? 'act' : ''}" data-val="grid">${L.opt_privacy_grid}</div>
                                <div class="pk-select-item ${curBlurScope === 'both' ? 'act' : ''}" data-val="both">${L.opt_privacy_both}</div>
                            </div>
                        </div>

                        <div style="position:relative;">
                            <label for="set_skip_bl"
                                   onmouseover="this.style.borderColor='var(--pk-pri)'"
                                   onmouseout="this.style.borderColor='var(--pk-bd)'"
                                   style="display:flex; align-items:center; justify-content:space-between; height:44px; border:2px solid var(--pk-bd); border-radius:8px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                                <span style="font-size:14px; color:var(--pk-fg);user-select:none;">${L.lbl_skip_bl_on_del}</span>
                                <input type="checkbox" id="set_skip_bl" ${gmGet('pk_skip_bl_on_del', true)?'checked':''} style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer;">
                            </label>
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; pointer-events:none; line-height:1;">${L.title_blacklist}</div>
                        </div>

                        <div style="position:relative;">
                            <label for="set_clipboard_magnet_focus"
                                   onmouseover="this.style.borderColor='var(--pk-pri)'"
                                   onmouseout="this.style.borderColor='var(--pk-bd)'"
                                   style="display:flex; align-items:center; justify-content:space-between; height:44px; border:2px solid var(--pk-bd); border-radius:8px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                                <span style="font-size:14px; color:var(--pk-fg);user-select:none;">${L.desc_clipboard_magnet_focus}</span>
                                <input type="checkbox" id="set_clipboard_magnet_focus" ${gmGet('pk_clipboard_magnet_focus', false)?'checked':''} style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer;">
                            </label>
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; pointer-events:none; line-height:1;">${L.label_clipboard_magnet_focus}</div>
                        </div>

                        <div style="position:relative;">
                            <label for="set_keep_pos"
                                   onmouseover="this.style.borderColor='var(--pk-pri)'"
                                   onmouseout="this.style.borderColor='var(--pk-bd)'"
                                   style="display:flex; align-items:center; justify-content:space-between; height:44px; border:2px solid var(--pk-bd); border-radius:8px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                                <span style="font-size:14px; color:var(--pk-fg);user-select:none;">${L.label_keep_pos}</span>
                                <input type="checkbox" id="set_keep_pos" ${gmGet('pk_keep_pos', true)?'checked':''} style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer;">
                            </label>
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; pointer-events:none; line-height:1;">${L.lbl_browse_exp}</div>
                        </div>

                            <div onmouseover="this.style.borderColor='var(--pk-pri)'"
                                onmouseout="this.style.borderColor='var(--pk-bd)'"
                                style="position:relative; padding:15px; border:2px solid var(--pk-bd); border-radius:8px; transition:border-color 0.2s; cursor:default; transform: translateZ(0);">
                                <div class="pk-select-label">${L.label_sort_pref}</div>

                                <div style="display:flex; flex-direction:column; gap:15px;">
                                    <label style="display:flex; align-items:flex-start; gap:10px; cursor:pointer;">
                                        <input type="radio" name="set_sort_pref" value="indep" ${gmGet('pk_sort_independent', false)?'checked':''} style="margin-top:4px; accent-color:var(--pk-pri);">
                                        <div>
                                            <div style="font-size:14px; color:var(--pk-fg); font-weight:500; line-height:1.4;">${L.opt_sort_indep}</div>
                                            <div style="font-size:12px; color:#888; margin-top:2px;">${L.desc_sort_indep}</div>
                                        </div>
                                    </label>
                                    <label style="display:flex; align-items:flex-start; gap:10px; cursor:pointer;">
                                        <input type="radio" name="set_sort_pref" value="global" ${!gmGet('pk_sort_independent', false)?'checked':''} style="margin-top:4px; accent-color:var(--pk-pri);">
                                        <div>
                                            <div style="font-size:14px; color:var(--pk-fg); font-weight:500; line-height:1.4;">${L.opt_sort_global}</div>
                                            <div style="font-size:12px; color:#888; margin-top:2px;">${L.desc_sort_global}</div>
                                        </div>
                                    </label>
                                </div>
                            </div>

                            <div onmouseover="this.style.borderColor='var(--pk-pri)'"
                                onmouseout="this.style.borderColor='var(--pk-bd)'"
                                style="position:relative; padding:15px; border:2px solid var(--pk-bd); border-radius:8px; transition:border-color 0.2s; cursor:default; transform: translateZ(0);">
                                <div class="pk-select-label">${L.label_view_pref}</div>

                                <div style="display:flex; flex-direction:column; gap:15px;">
                                    <label style="display:flex; align-items:flex-start; gap:10px; cursor:pointer;">
                                        <input type="radio" name="set_view_pref" value="indep" ${gmGet('pk_view_independent', false)?'checked':''} style="margin-top:4px; accent-color:var(--pk-pri);">
                                        <div>
                                            <div style="font-size:14px; color:var(--pk-fg); font-weight:500; line-height:1.4;">${L.opt_view_indep}</div>
                                            <div style="font-size:12px; color:#888; margin-top:2px;">${L.desc_view_indep}</div>
                                        </div>
                                    </label>
                                    <label style="display:flex; align-items:flex-start; gap:10px; cursor:pointer;">
                                        <input type="radio" name="set_view_pref" value="global" ${!gmGet('pk_view_independent', false)?'checked':''} style="margin-top:4px; accent-color:var(--pk-pri);">
                                        <div>
                                            <div style="font-size:14px; color:var(--pk-fg); font-weight:500; line-height:1.4;">${L.opt_view_global}</div>
                                            <div style="font-size:12px; color:#888; margin-top:2px;">${L.desc_view_global}</div>
                                        </div>
                                    </label>
                                </div>
                            </div>

                            <div style="position:relative;">
                                <label for="set_comic_mode"
                                   onmouseover="this.style.borderColor='var(--pk-pri)'"
                                   onmouseout="this.style.borderColor='var(--pk-bd)'"
                                   style="display:flex; align-items:center; justify-content:space-between; height:44px; border:2px solid var(--pk-bd); border-radius:8px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:border-color 0.2s; box-sizing:border-box;">
                                <span style="font-size:14px; color:var(--pk-fg); user-select:none;">${L.desc_comic_mode}</span>
                                <input type="checkbox" id="set_comic_mode" ${gmGet('pk_comic_mode', true)?'checked':''} style="width:18px; height:18px; accent-color:var(--pk-pri); cursor:pointer;">
                            </label>
                            <div style="position:absolute; top:0; transform:translateY(-50%); left:10px; background:var(--pk-bg); padding:0 5px; font-size:11px; color:var(--pk-pri); font-weight:bold; pointer-events:none; line-height:1;">${L.label_comic_mode}</div>
                        </div>

                        <div class="pk-custom-select" id="cs_set_engine">
                            <div class="pk-select-label">${L.label_search_engine}</div>
                            <div class="pk-select-trigger"><span id="txt_set_engine"></span>${CONF.crumbIcons.down}</div>
                            <div class="pk-select-menu pk-scroll">
                                <div class="pk-select-item" data-val="google">${L.opt_engine_google}</div>
                                <div class="pk-select-item" data-val="yandex">${L.opt_engine_yandex}</div>
                                <div class="pk-select-item" data-val="saucenao">${L.opt_engine_saucenao}</div>
                                <div class="pk-select-item" data-val="tracemoe">${L.opt_engine_tracemoe}</div>
                            </div>
                        </div>

                        <div id="pk_dl_group" style="position:relative; padding:25px 15px 15px 15px; border:2px solid var(--pk-bd); border-radius:8px; transition:border-color 0.2s;">
                            <div class="pk-select-label">${L.lbl_dl_filter}</div>
                            <div style="display:flex; flex-direction:column; gap:15px;">
                                <div style="display:flex; align-items:center; gap:10px; width:100%;">
                                    <div style="flex:1; position:relative;">
                                        <style>#set_dl_filter_size_min::-webkit-inner-spin-button, #set_dl_filter_size_min::-webkit-outer-spin-button, #set_dl_filter_size_max::-webkit-inner-spin-button, #set_dl_filter_size_max::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }</style>
                                        <input type="number" id="set_dl_filter_size_min" value="${gmGet('pk_dl_filter_size_min', '')}" placeholder="0" min="0" step="1"
                                               style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace; -moz-appearance:textfield;">
                                        <div style="${labelStyle}">${L.lbl_ana_min}</div>
                                        <div class="pk-num-ctrl">
                                            <div class="pk-num-btn" id="dl_size_inc_min">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                            <div class="pk-num-btn" id="dl_size_dec_min">${CONF.crumbIcons.down}</div>
                                        </div>
                                    </div>
                                    <div style="color:#888; font-weight:bold; flex-shrink:0;">-</div>
                                    <div style="flex:1; position:relative;">
                                        <input type="number" id="set_dl_filter_size_max" value="${gmGet('pk_dl_filter_size_max', '')}" min="0" step="1" placeholder="∞"
                                               style="width:100%; height:42px; padding:0 30px 0 12px; border:2px solid var(--pk-bd); border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:16px; font-weight:700; outline:none; transition:border-color 0.2s; box-sizing:border-box; font-family:monospace; -moz-appearance:textfield;">
                                        <div style="${labelStyle}">${L.lbl_ana_max}</div>
                                        <div class="pk-num-ctrl">
                                            <div class="pk-num-btn" id="dl_size_inc_max">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                            <div class="pk-num-btn" id="dl_size_dec_max">${CONF.crumbIcons.down}</div>
                                        </div>
                                    </div>
                                    <div class="pk-custom-select" id="cs_set_dl_size_unit" style="margin-top:0; width:80px; flex-shrink:0;">
                                        <div class="pk-select-trigger" style="height:42px;"><span id="txt_set_dl_size_unit"></span>${CONF.crumbIcons.down}</div>
                                        <div class="pk-select-menu pk-scroll">
                                            <div class="pk-select-item" data-val="MB">MB</div>
                                            <div class="pk-select-item" data-val="GB">GB</div>
                                            <div class="pk-select-item" data-val="TB">TB</div>
                                        </div>
                                    </div>
                                </div>
                                <div style="position:relative;">
                                    <textarea id="set_dl_filter_ext" placeholder=".txt, .nfo" style="${areaStyle}">${esc(gmGet('pk_dl_filter_ext', ''))}</textarea>
                                    <div style="${labelStyle}">${L.label_dl_filter_ext}</div>
                                </div>
                                <div style="position:relative;">
                                    <textarea id="set_dl_filter_name" placeholder="ReadMe1, ReadMe2" style="${areaStyle}">${esc(gmGet('pk_dl_filter_name', ''))}</textarea>
                                    <div style="${labelStyle}">${L.label_dl_filter_name}</div>
                                </div>
                                <div style="font-size:11px; color:#888;">${L.desc_dl_filter}</div>
                            </div>
                        </div>

                        <div style="position:relative; transform: translateZ(0);">
                            <div style="position:relative;">
                                <input type="text" id="set_aria_url" value="${esc(curAriaUrl)}" placeholder="http://localhost:6800/jsonrpc"
                                       autocomplete="off" spellcheck="false" readonly onfocus="this.removeAttribute('readonly');"
                                       oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                                       style="width:100%; height:44px; padding:0 70px 0 12px; border:2px solid ${curAriaUrl ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box; transform: translateZ(0);">
                                <div class="pk-select-label">${L.label_aria2_url}</div>
                                <div id="btn_aria_default" style="position:absolute; right:10px; top:50%; transform:translateY(-50%); font-size:11px; color:var(--pk-pri); cursor:pointer; font-weight:bold; padding:4px 8px; border-radius:4px; background:rgba(0,103,192,0.1); border:1px solid rgba(0,103,192,0.2);" onmouseover="this.style.background='rgba(0,103,192,0.2)'" onmouseout="this.style.background='rgba(0,103,192,0.1)'">${L.btn_default}</div>
                            </div>
                            <div class="pk-aria-status-box" id="aria_test_res" style="margin-top: 8px;">
                                <div class="pk-aria-dot" id="aria_test_dot"></div>
                                <span id="aria_test_txt" style="color:#888; font-size: 11px;">${L.lbl_aria2_status}</span>
                            </div>
                        </div>

                        <div style="position:relative; transform: translateZ(0); -webkit-transform: translateZ(0);">
                            <input type="text" id="set_aria_token" value="${esc(curAriaToken)}" placeholder="${L.ph_aria2_secret}"
                                   autocomplete="off" spellcheck="false" data-lpignore="true" readonly onfocus="this.removeAttribute('readonly');"
                                   oninput="this.style.borderColor = this.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)'"
                                   style="width:100%; height:44px; padding:0 48px 0 12px; border:2px solid ${curAriaToken ? 'var(--pk-pri)' : 'var(--pk-bd)'}; border-radius:8px; background:var(--pk-bg); color:var(--pk-fg); font-size:14px; font-weight:600; outline:none; transition:border-color 0.2s; box-sizing:border-box; -webkit-text-security: disc; transform: translateZ(0);">
                            <button type="button" id="btn_aria_token_eye" class="pk-token-eye">${CONF.icons.eye}</button>
                            <div class="pk-select-label">${L.label_aria2_token}</div>
                        </div>

                        <div style="position:relative; padding:20px 15px 15px 15px; border:2px solid var(--pk-bd); border-radius:8px; transition:border-color 0.2s; transform:translateZ(0); backface-visibility:hidden;" onmouseover="this.style.borderColor='var(--pk-pri)'" onmouseout="this.style.borderColor='var(--pk-bd)'">
                            <div class="pk-select-label" style="transform:translateY(-50.5%);">${L.lbl_pwd_manage}</div>
                            <div id="btn_open_vault" style="display:flex; align-items:center; justify-content:center; gap:10px; height:44px; background:var(--pk-hl); border-radius:8px; cursor:pointer; transition:all 0.2s; color:var(--pk-fg); transform:translateZ(0);" onmouseover="this.style.background='var(--pk-sel-bg)'; this.style.color='var(--pk-pri)'" onmouseout="this.style.background='var(--pk-hl)'; this.style.color='var(--pk-fg)'">
                                <span style="width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:var(--pk-pri);">${CONF.icons.vault.replace('width="16"','width="20"').replace('height="16"','width="20"')}</span>
                                <span style="font-size:14px; font-weight:700;">${L.title_pwd_vault}</span>
                            </div>
                        </div>

                        <div style="position:relative; padding:20px 15px 15px 15px; border:2px solid var(--pk-bd); border-radius:8px; display:flex; flex-direction:column; gap:12px; transition:border-color 0.2s; transform:translateZ(0);" onmouseover="this.style.borderColor='var(--pk-pri)'" onmouseout="this.style.borderColor='var(--pk-bd)'">
                            <div class="pk-select-label" style="transform:translateY(-50.5%);">${L.lbl_config_manage}</div>

                            <button class="pk-btn" id="btn_cfg_clean" style="background:transparent; border:1px solid #d93025; color:#d93025; height:36px; border-radius:6px; font-weight:600; width:100%; display:flex; align-items:center; justify-content:center;">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="margin-right:6px; flex-shrink:0;"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
                                <div style="display:flex; align-items:baseline;">
                                    <span style="display:inline !important; font-size:13px; font-weight:600;">${L.btn_clean_data}</span>
                                    <span style="display:inline !important; font-size:13px; font-weight:600; margin-left:6px; opacity:0.8;">( ${storageDisplay} )</span>
                                </div>
                            </button>

                            <div style="display:grid; grid-template-columns: 1fr 1fr; gap:12px;">
                                <button class="pk-btn" id="btn_cfg_export" style="background:var(--pk-hl); border:1px solid var(--pk-bd); height:36px; border-radius:6px; font-weight:600;">
                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="margin-right:4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
                                    ${L.btn_export_data}
                                </button>
                                <button class="pk-btn" id="btn_cfg_import" style="background:var(--pk-hl); border:1px solid var(--pk-bd); height:36px; border-radius:6px; font-weight:600;">
                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="margin-right:4px;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
                                    ${L.btn_import_data}
                                </button>
                            </div>
                            <input type="file" id="cfg_import_input" accept=".json" style="display:none;">
                        </div>

                        </div>
                </div>

                <div style="padding: 20px 30px 30px 30px; flex-shrink:0; background:var(--pk-bg); border-top:1px solid var(--pk-bd);">
                    <div class="pk-modal-act" style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 0;">
                        <button class="pk-btn" id="set_cancel" style="height:44px; border-radius:10px; justify-content:center; background:transparent; font-weight:600; font-size:15px;">${L.btn_cancel}</button>
                        <button class="pk-btn pri" id="set_save" style="height:44px; border-radius:10px; background:var(--pk-pri); color:#fff; font-weight:bold; justify-content:center; border:none; font-size:15px;">${L.btn_save}</button>
                    </div>
                </div>
            </div>
        `);

        const modalBox = m.querySelector('.pk-modal');
        if (modalBox) {
            Object.assign(modalBox.style, { width: 'auto', padding: '0', overflow: 'hidden', height: 'auto', minHeight: 'auto' });
            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) Object.assign(closeBtn.style, { top: '26px', right: '26px' });
        }

        const bindSelect = (id, currentVal, onSelect) => {
            const container = m.querySelector(`#${id}`);
            const trigger = container.querySelector('.pk-select-trigger');
            const menu = container.querySelector('.pk-select-menu');
            const txt = container.querySelector('span');
            const items = container.querySelectorAll('.pk-select-item');

            items.forEach(item => {
                if (item.dataset.val === currentVal) {
                    item.classList.add('act');
                    txt.textContent = item.textContent;
                }
                item.onclick = (e) => {
                    e.stopPropagation();
                    items.forEach(i => i.classList.remove('act'));
                    item.classList.add('act');
                    txt.textContent = item.textContent;
                    menu.style.display = 'none';
                    onSelect(item.dataset.val);
                };
            });

            trigger.onclick = (e) => {
                e.stopPropagation();
                m.querySelectorAll('.pk-select-menu').forEach(om => { if(om !== menu) om.style.display = 'none'; });
                menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
            };
        };

        bindSelect('cs_set_lang', curLang, (val) => { selectedLang = val; });
        bindSelect('cs_set_engine', curEngine, (val) => { selectedEngine = val; });

        const ariaInp = m.querySelector('#set_aria_url');
        const ariaTok = m.querySelector('#set_aria_token');
        const ariaEye = m.querySelector('#btn_aria_token_eye');
        const ariaDot = m.querySelector('#aria_test_dot');
        const ariaTxt = m.querySelector('#aria_test_txt');
        const ariaBox = m.querySelector('#aria_test_res');
        let ariaTimer = null;
        let ariaTokenVisible = false;

        const runAriaTest = async () => {
            const url = ariaInp.value.trim();
            const token = ariaTok.value.trim();
            const showTip = () => showAlert(L.tip_mixed_content, L.lbl_aria2_status);

            if (!url) {
                ariaDot.className = 'pk-aria-dot';
                ariaTxt.textContent = L.lbl_aria2_status;
                ariaBox.onclick = showTip;
                ariaBox.style.cursor = 'pointer';
                return;
            }

            ariaDot.className = 'pk-aria-dot wait';
            ariaTxt.textContent = L.str_connecting;
            ariaBox.onclick = showTip;
            ariaBox.style.cursor = 'pointer';

            const testUrl = normalizeAriaRpcUrl(url);
            const payload = { jsonrpc: '2.0', method: 'aria2.getVersion', id: 'pk_live_test', params: buildAriaRpcParams(token) };

            try {
                await aria2RpcRequest(testUrl, payload, 3000);
                ariaDot.className = 'pk-aria-dot ok';
                ariaTxt.textContent = L.str_connected;
                ariaBox.onclick = null;
                ariaBox.style.cursor = 'default';
            } catch (e) {
                ariaDot.className = 'pk-aria-dot err';
                ariaTxt.textContent = L.str_conn_fail;
                ariaBox.onclick = showTip;
                ariaBox.style.cursor = 'pointer';
            }
        };

        const debouncedTest = () => {
            clearTimeout(ariaTimer);
            ariaTimer = setTimeout(runAriaTest, 600);
        };

        ariaInp.oninput = (e) => {
            if (e.target.oninput) e.target.style.borderColor = e.target.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)';
            debouncedTest();
        };
        ariaTok.oninput = (e) => {
            if (e.target.oninput) e.target.style.borderColor = e.target.value.trim() ? 'var(--pk-pri)' : 'var(--pk-bd)';
            debouncedTest();
        };
        if (ariaEye) {
            ariaEye.onclick = (e) => {
                e.preventDefault();
                e.stopPropagation();
                ariaTokenVisible = !ariaTokenVisible;
                ariaTok.style.webkitTextSecurity = ariaTokenVisible ? 'none' : 'disc';
                ariaEye.innerHTML = ariaTokenVisible ? CONF.icons.eyeOff : CONF.icons.eye;
            };
        }
        m.querySelector('#btn_aria_default').onclick = () => {
            ariaInp.value = 'http://localhost:6800/jsonrpc';
            ariaInp.style.borderColor = 'var(--pk-pri)';
            debouncedTest();
        };

        setTimeout(runAriaTest, 200);

        const clickAway = () => m.querySelectorAll('.pk-select-menu').forEach(menu => menu.style.display = 'none');
        setTimeout(() => document.addEventListener('click', clickAway), 0);

        const _orgRemove = m.remove.bind(m);
        m.remove = () => {
            document.removeEventListener('click', clickAway);
            _orgRemove();
        };

        const extInp = m.querySelector('#set_dl_filter_ext');
        const nameInp = m.querySelector('#set_dl_filter_name');
        const sizeMinInp = m.querySelector('#set_dl_filter_size_min');
        const sizeMaxInp = m.querySelector('#set_dl_filter_size_max');
        const groupEl = m.querySelector('#pk_dl_group');
        const updateDlBorders = () => {
            const hasE = extInp.value.trim() !== '';
            const hasN = nameInp.value.trim() !== '';
            const hasSMin = sizeMinInp.value.trim() !== '';
            const hasSMax = sizeMaxInp.value.trim() !== '';
            extInp.classList.toggle('pk-active-border', hasE);
            nameInp.classList.toggle('pk-active-border', hasN);
            sizeMinInp.classList.toggle('pk-active-border', hasSMin);
            sizeMaxInp.classList.toggle('pk-active-border', hasSMax);
            groupEl.classList.toggle('pk-typing-active', hasE || hasN || hasSMin || hasSMax);
        };
        extInp.oninput = nameInp.oninput = sizeMinInp.oninput = sizeMaxInp.oninput = updateDlBorders;
        updateDlBorders();

        const curDlSizeUnit = gmGet('pk_dl_filter_size_unit', 'MB');
        bindSelect('cs_set_dl_size_unit', curDlSizeUnit, (val) => {});

        m.querySelector('#dl_size_dec_min').onclick = (ev) => {
            ev.preventDefault(); ev.stopPropagation();
            let v = Math.floor(Number(sizeMinInp.value)) || 0;
            if (v > 0) sizeMinInp.value = v - 1;
            ensureDlSizeRange();
            updateDlBorders();
        };
        m.querySelector('#dl_size_inc_min').onclick = (ev) => {
            ev.preventDefault(); ev.stopPropagation();
            let v = Math.floor(Number(sizeMinInp.value)) || 0;
            sizeMinInp.value = v + 1;
            ensureDlSizeRange();
            updateDlBorders();
        };
        m.querySelector('#dl_size_dec_max').onclick = (ev) => {
            ev.preventDefault(); ev.stopPropagation();
            let v = Math.floor(Number(sizeMaxInp.value)) || 0;
            if (v > 0) sizeMaxInp.value = v - 1;
            ensureDlSizeRange();
            updateDlBorders();
        };
        m.querySelector('#dl_size_inc_max').onclick = (ev) => {
            ev.preventDefault(); ev.stopPropagation();
            let v = Math.floor(Number(sizeMaxInp.value)) || 0;
            sizeMaxInp.value = v + 1;
            ensureDlSizeRange();
            updateDlBorders();
        };

        const ensureDlSizeRange = () => {
            let minVal = sizeMinInp.value.trim() === '' ? -1 : Math.floor(Number(sizeMinInp.value));
            let maxVal = sizeMaxInp.value.trim() === '' ? -1 : Math.floor(Number(sizeMaxInp.value));

            if (minVal >= 0 && maxVal >= 0 && minVal > maxVal) {
                if (document.activeElement === sizeMinInp) {
                    sizeMaxInp.value = minVal;
                } else if (document.activeElement === sizeMaxInp) {
                    sizeMinInp.value = maxVal;
                } else {
                    sizeMaxInp.value = minVal;
                }
            }
        };
        sizeMinInp.addEventListener('blur', ensureDlSizeRange);
        sizeMaxInp.addEventListener('blur', ensureDlSizeRange);
        sizeMinInp.addEventListener('change', ensureDlSizeRange);
        sizeMaxInp.addEventListener('change', ensureDlSizeRange);

        m.querySelector('#btn_open_vault').onclick = (e) => {
            e.stopPropagation();
            const subM = document.createElement('div');
            subM.className = 'pk-modal-ov';
            subM.style.zIndex = (++modalZIndexCounter).toString();
            if (document.querySelector('.pk-ov').classList.contains('pk-dark')) subM.classList.add('pk-dark');

            const savedCount = gmGet('pk_pwd_try_count', 10);
            const savedPwds = (() => { try { return JSON.parse(gmGet('pk_pwd_vault', '[]')).map(x => typeof x === 'object' ? x.p : x).join('\n'); } catch { return ''; } })();

            subM.innerHTML = `
                <style>#vault_cnt_val::-webkit-outer-spin-button, #vault_cnt_val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .pk-dark .pk-sub-ctrl-btn:hover { background: #444 !important; }</style>
                <div class="pk-modal" style="width:380px; padding:24px; border-radius:12px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); background:var(--pk-bg); border:1px solid var(--pk-bd);">
                    <div class="pk-modal-close" style="top:26px; right:24px; color:var(--pk-icon-c); opacity:0.7;">${CONF.icons.close}</div>
                    <h3 style="border:none; margin:0 0 10px 0; font-size:17px; font-weight:700; color:var(--pk-fg); display:flex; align-items:center; gap:10px;">
                        <span style="width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:var(--pk-pri);">${CONF.icons.vault.replace('width="16"','width="22"').replace('height="16"','width="22"')}</span>
                        ${L.title_pwd_vault}
                    </h3>

                    <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:4px;">
                        <span style="font-size:14px; color:var(--pk-fg); font-weight:600; opacity:0.9;">${L.lbl_pwd_try_count}</span>
                        <div class="pk-sub-ctrl" style="width:110px; height:34px; display:flex; align-items:center; border:1.5px solid var(--pk-bd); border-radius:6px; overflow:hidden; background:var(--pk-hl);">
                            <div class="pk-sub-ctrl-btn" id="vault_cnt_dec" style="width:32px; height:100%; display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--pk-fg); font-weight:bold; border-right:1px solid var(--pk-bd); transition:background 0.2s;">-</div>
                            <input type="number" id="vault_cnt_val" value="${savedCount}" min="10" max="50" style="flex:1; width:30px; height:100%; text-align:center; border:none; background:transparent; color:var(--pk-fg); outline:none; font-size:15px; font-weight:700; font-family:inherit; -moz-appearance:textfield;">

                            <div class="pk-sub-ctrl-btn" id="vault_cnt_inc" style="width:32px; height:100%; display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--pk-fg); font-weight:bold; border-left:1px solid var(--pk-bd); transition:background 0.2s;">+</div>
                        </div>
                    </div>
                    <div style="font-size:12px; color:#888; margin-bottom:8px; padding-left:2px;">${L.tip_pwd_manual}</div>

                    <div style="display:flex; flex-direction:column; gap:8px; position:relative;">
                        <textarea id="vault_pwd_area" placeholder="" spellcheck="false" wrap="off"
                                style="width:100%; height:242px; padding:0 15px; border:2px solid var(--pk-bd); border-radius:8px; background-color:var(--pk-hl); background-image:linear-gradient(to right, var(--pk-hl) 4px, transparent 4px), linear-gradient(to bottom, transparent 31px, var(--pk-v-line) 31px); background-size:8px 32px, 100% 32px; background-attachment:local; color:var(--pk-fg); font-size:13px; font-family:inherit; line-height:32px; resize:none; outline:none; box-sizing:border-box; white-space:pre; transition:border-color 0.2s, box-shadow 0.2s; overflow-y:auto; cursor:auto;"></textarea>
                    </div>

                    <div class="pk-modal-act" style="display:flex; justify-content:flex-end; align-items:center; margin-top:28px; gap:20px; border-top:1px solid var(--pk-bd); padding-top:20px;">
                        <span id="vault_cancel" style="cursor:pointer; color:var(--pk-icon-c); font-size:14px; font-weight:600; transition:color 0.2s;" onmouseover="this.style.color='var(--pk-fg)'" onmouseout="this.style.color='var(--pk-icon-c)'">${L.btn_cancel}</span>
                        <button class="pk-btn pri" id="vault_save"
                                style="height:40px; padding:0 30px; border-radius:8px; background:var(--pk-pri); border:none; color:#fff; font-weight:bold; font-size:14px; box-shadow:0 4px 12px rgba(0,0,0,0.2); transition:transform 0.1s, filter 0.2s;">${L.btn_save}</button>
                    </div>
                </div>
            `;
            document.body.appendChild(subM);

            const cntInp = subM.querySelector('#vault_cnt_val');
            subM.querySelector('#vault_cnt_dec').onclick = (ev) => {
                ev.preventDefault(); ev.stopPropagation();
                let v = Math.floor(Number(cntInp.value)) || 10;
                cntInp.value = Math.max(10, v - 1);
            };
            subM.querySelector('#vault_cnt_inc').onclick = (ev) => {
                ev.preventDefault(); ev.stopPropagation();
                let v = Math.floor(Number(cntInp.value)) || 0;
                cntInp.value = Math.min(50, v + 1);
            };

            cntInp.oninput = () => {
                let raw = cntInp.value;
                if (raw === "") return;
                let v = parseInt(raw);
                if (isNaN(v)) { cntInp.value = 10; return; }
                if (v > 50) cntInp.value = 50;
            };
            cntInp.onblur = () => {
                let v = parseInt(cntInp.value);
                if (isNaN(v) || v < 10) cntInp.value = 10;
            };
            const area = subM.querySelector('#vault_pwd_area');
            area.onfocus = () => area.style.borderColor = 'var(--pk-pri)';
            area.onblur = () => area.style.borderColor = 'var(--pk-bd)';
            area.value = savedPwds;
            area.oninput = () => {
                let lines = area.value.split('\n');
                let changed = false;
                let msg = "";
                if (lines.length > 50) {
                    lines = lines.slice(0, 50);
                    changed = true;
                    msg = L.err_vault_max;
                }
                for (let i = 0; i < lines.length; i++) {
                    if (lines[i].length > 127) {
                        lines[i] = lines[i].substring(0, 127);
                        changed = true;
                        msg = L.err_pwd_len;
                    }
                }
                if (changed) {
                    const cursor = area.selectionStart;
                    area.value = lines.join('\n');
                    area.setSelectionRange(cursor, cursor);
                    showToast(msg, 'error');
                    area.style.borderColor = '#d93025';
                    setTimeout(() => { if(area) area.style.borderColor = 'var(--pk-pri)'; }, 1000);
                }
            };

            const doSave = () => {
                let cnt = Math.floor(Number(cntInp.value));
                if (isNaN(cnt) || cnt < 10) cnt = 10;
                if (cnt > 50) cnt = 50;

                const inputPwds = area.value.split('\n').map(s => s.trim()).filter(s => s);
                if (inputPwds.length > 50) {
                    showToast(L.err_vault_max, 'error');
                    area.style.borderColor = '#d93025';
                    return;
                }

                try {
                    const oldList = JSON.parse(gmGet('pk_pwd_vault', '[]')).map(x => typeof x === 'object' ? x : {p: x, h: 0});
                    const hitMap = new Map(oldList.map(x => [x.p, x.h]));
                    const newList = [...new Set(inputPwds)].map(p => ({ p: p, h: hitMap.get(p) || 0 }));
                    newList.sort((a, b) => b.h - a.h);

                    gmSet('pk_pwd_try_count', cnt);
                    gmSet('pk_pwd_vault', JSON.stringify(newList));
                } catch(e) {
                    gmSet('pk_pwd_vault', JSON.stringify([...new Set(inputPwds)]));
                }

                subM.remove();
                showToast(L.msg_settings_saved);
            };

            subM.querySelector('#vault_save').onclick = doSave;
            subM.querySelector('#vault_cancel').onclick = () => subM.remove();
            subM.querySelector('.pk-modal-close').onclick = () => subM.remove();
        };

        m.querySelector('#btn_cfg_clean').onclick = async () => {
            const keys = typeof GM_listValues !== 'undefined' ? GM_listValues() : Object.keys(localStorage);

            const sizes = { index: 0, pref: 0, rules: 0, vault: 0, history: 0, cache: 0 };

            if (typeof globalCache !== 'undefined') {
                for (const [k, v] of globalCache.entries()) {
                    try { sizes.index += k.toString().length + JSON.stringify(v).length; } catch(e){}
                }
            }

            const getCat = (k) => {
                if (!k.startsWith('pk_')) return null;
                if (k.startsWith('pk_archive_pwd_') || k === 'pk_pwd_vault') return 'vault';
                if (k.startsWith('pk_progress_') || k.startsWith('pk_duration_')) return 'history';
                if (k.startsWith('pk_fmod_') || k === 'pk_captured_captcha') return 'cache';

                const ruleKeys =['pk_blacklist', 'pk_blacklist_folders', 'pk_aria2_url', 'pk_aria2_token', 'pk_dl_filter_ext', 'pk_dl_filter_name', 'pk_dl_filter_size_min', 'pk_dl_filter_size_max', 'pk_dl_filter_size_unit', 'pk_search_engine', 'pk_search_history', 'pk_expired_shares', 'pk_share_limits', 'pk_bn_find_hist', 'pk_bn_rep_hist'];
                if (ruleKeys.includes(k) || k.startsWith('pk_scan_last_') || k.startsWith('pk_analyze_last_') || k === 'pk_dup_strictness') return 'rules';

                return 'pref';
            };

            keys.forEach(k => {
                const cat = getCat(k);
                if (cat) {
                    const val = typeof GM_getValue !== 'undefined' ? GM_getValue(k) : localStorage.getItem(k);
                    sizes[cat] += (k.length + (val ? JSON.stringify(val).length : 0));
                }
            });

            const renderLbl = (cat, txt, isChecked = false, isMandatory = false) => {
                const sz = sizes[cat];
                if (sz === 0 && cat !== 'index') return '';
                const szStr = fmtSize(sz);
                const checkAttr = (isChecked || isMandatory) ? 'checked' : '';
                const disAttr = isMandatory ? 'disabled' : '';
                const cursor = isMandatory ? 'not-allowed' : 'pointer';
                const opacity = isMandatory ? '0.7' : '1';
                return `<label style="display:flex; align-items:flex-start; gap:12px; cursor:${cursor}; color:var(--pk-fg); font-size:14px; opacity:${opacity};">
                            <input type="checkbox" class="clean-opt" value="${cat}" ${checkAttr} ${disAttr} style="width:18px; height:18px; accent-color:#d93025; cursor:inherit; margin-top:2px;">
                            <div style="display:flex; flex-direction:column;">
                                <span>${txt}</span>
                                <span style="font-size:12px; color:#888; font-family:monospace; margin-top:2px;">${szStr}</span>
                            </div>
                        </label>`;
            };

            const htmlOptions =[
                renderLbl('index', L.opt_cfg_index, true, true),
                renderLbl('pref', L.opt_cfg_pref),
                renderLbl('rules', L.opt_cfg_rules),
                renderLbl('vault', L.opt_cfg_vault),
                renderLbl('history', L.opt_cfg_history),
                renderLbl('cache', L.opt_cfg_cache)
            ].filter(Boolean).join('');

            if (!htmlOptions) return;

            const cleanM = showModal(`
                <h3 style="border:none; margin-bottom:20px; font-size:18px; font-weight:700; color:var(--pk-fg);">${L.title_clean_data}</h3>
                <div style="display:flex; flex-direction:column; gap:16px; margin-bottom:25px;">
                    ${htmlOptions}
                </div>
                <div class="pk-modal-act">
                    <button class="pk-btn" id="clean_cancel">${L.btn_cancel}</button>
                    <button class="pk-btn pri pk-btn-danger" id="clean_confirm">${L.btn_del}</button>
                </div>
            `);
            cleanM.querySelector('#clean_cancel').onclick = () => cleanM.remove();
            cleanM.querySelector('#clean_confirm').onclick = async () => {
                const selected = Array.from(cleanM.querySelectorAll('.clean-opt:checked')).map(el => el.value);
                if (selected.length === 0) { cleanM.remove(); return; }
                if (!await showConfirm(L.msg_clean_confirm)) return;

                if (selected.includes('index')) {
                    if (typeof globalCache !== 'undefined') globalCache.clear();
                    if (typeof S !== 'undefined' && S.cache) S.cache.clear();
                    if (typeof globalLineageMap !== 'undefined') globalLineageMap.clear();
                    if (typeof globalParentIndex !== 'undefined') globalParentIndex.clear();
                    if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.clear();
                }

                keys.forEach(k => {
                    const cat = getCat(k);
                    if (cat && selected.includes(cat)) {
                        try {
                            if (typeof GM_deleteValue !== 'undefined') {
                                GM_deleteValue(k);
                            } else if (typeof GM_setValue !== 'undefined') {
                                GM_setValue(k, '');
                            }
                            localStorage.removeItem(k);
                        } catch (e) {
                            console.warn("Delete config error:", e);
                        }
                    }
                });

                showToast(L.msg_clean_success);
                setTimeout(() => location.reload(), 1500);
            };
        };

        m.querySelector('#btn_cfg_export').onclick = () => {
            const gmKeys = typeof GM_listValues !== 'undefined' ? GM_listValues() : [];
            const lsKeys = Object.keys(localStorage).filter(k => typeof k === 'string' && k.startsWith('pk_'));
            const keys = Array.from(new Set([...(Array.isArray(gmKeys) ? gmKeys : []), ...lsKeys]));

            const config = {
                "_pk_metadata": {
                    "signature": "PIKPAK_ENHANCEMENT_MASTER",
                    "version": version,
                    "export_at": new Date().toISOString(),
                    "author": "digbug82",
                    "scope": "whitelist"
                }
            };

            const exportExactKeys = new Set([
                'pk_lang',
                'pk_theme',
                'pk_turbo_mode',
                'pk_file_view_mode',
                'pk_view_independent',
                'pk_folder_view_prefs',
                'pk_keep_pos',
                'pk_pos_left',
                'pk_pos_top',
                'pk_blur_thumb',
                'pk_blur_scope',
                'pk_comic_mode',
                'pk_clipboard_magnet_focus',
                'pk_sort_independent',
                'pk_folder_first',
                'pk_folder_sort_prefs',
                'pk_global_sort_pref',
                'pk_ext_player',
                'pk_play_mode',
                'pk_skip_intro',
                'pk_skip_outro',
                'pk_vol_muted',
                'pk_vol_level',
                'pk_suppress_global_warn',
                'pk_blacklist',
                'pk_blacklist_folders',
                'pk_aria2_url',
                'pk_aria2_token',
                'pk_dl_filter_ext',
                'pk_dl_filter_name',
                'pk_dl_filter_size_min',
                'pk_dl_filter_size_max',
                'pk_dl_filter_size_unit',
                'pk_search_engine',
                'pk_search_history',
                'pk_expired_shares',
                'pk_share_limits',
                'pk_bn_find_hist',
                'pk_bn_rep_hist',
                'pk_dup_strictness',
                'pk_skip_bl_on_del',
                'pk_pwd_vault',
                'pk_pwd_try_count'
            ]);

            const exportPrefixKeys = [
                'pk_scan_last_',
                'pk_analyze_last_',
                'pk_archive_pwd_',
                'pk_progress_',
                'pk_duration_'
            ];

            const isWhitelistedExportKey = (k) => exportExactKeys.has(k) || exportPrefixKeys.some(prefix => k.startsWith(prefix));

            const readStoredValue = (k) => {
                let v = typeof GM_getValue !== 'undefined' ? GM_getValue(k, null) : null;
                if ((v === null || v === undefined || v === '') && localStorage.getItem(k) !== null) v = localStorage.getItem(k);
                return v;
            };

            const pkKeys = keys.filter(k => isWhitelistedExportKey(k));

            const getCatWeight = (k) => {
                if (k.startsWith('pk_archive_pwd_') || k === 'pk_pwd_vault' || k === 'pk_pwd_try_count' || k === 'pk_share_limits') return 3;
                if (k.startsWith('pk_progress_') || k.startsWith('pk_duration_')) return 4;

                const ruleKeys = ['pk_blacklist', 'pk_blacklist_folders', 'pk_aria2_url', 'pk_aria2_token', 'pk_dl_filter_ext', 'pk_dl_filter_name', 'pk_dl_filter_size_min', 'pk_dl_filter_size_max', 'pk_dl_filter_size_unit', 'pk_search_engine', 'pk_search_history', 'pk_expired_shares', 'pk_share_limits', 'pk_bn_find_hist', 'pk_bn_rep_hist', 'pk_dup_strictness', 'pk_skip_bl_on_del', 'pk_clipboard_magnet_focus'];
                if (ruleKeys.includes(k) || k.startsWith('pk_scan_last_') || k.startsWith('pk_analyze_last_')) return 2;

                return 1;
            };

            pkKeys.sort((a, b) => {
                const wA = getCatWeight(a);
                const wB = getCatWeight(b);
                if (wA !== wB) return wA - wB;
                return a.localeCompare(b);
            });

            pkKeys.forEach(k => {
                const v = readStoredValue(k);
                if (v !== null && v !== undefined && v !== '') config[k] = v;
            });

            config._pk_metadata.exported_keys = pkKeys.length;

            const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
            const url = URL.createObjectURL(blob);

            const domEl = document.querySelector('.name.ellipsis');
            let rawUserName = domEl ? (domEl.title || domEl.innerText) : 'Default';
            const userName = rawUserName.trim().replace(/[\\/:*?"<>|]/g, '_');

            const now = new Date();
            const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
            const timeStr = now.getHours().toString().padStart(2, '0') + now.getMinutes().toString().padStart(2, '0');

            const fileName = `PKM_Backup_${userName}_${dateStr}_${timeStr}.json`;

            const a = document.createElement('a');
            a.href = url;
            a.download = fileName;
            a.click();
            URL.revokeObjectURL(url);
        };

        const fileInput = m.querySelector('#cfg_import_input');
        m.querySelector('#btn_cfg_import').onclick = () => fileInput.click();
        fileInput.onchange = async (e) => {
            const file = e.target.files[0];
            if (!file) return;
            if (!await showConfirm(L.msg_import_confirm)) { fileInput.value = ''; return; }

            const reader = new FileReader();
            reader.onload = (ev) => {
                try {
                    const config = JSON.parse(ev.target.result);

                    if (!config._pk_metadata || config._pk_metadata.signature !== "PIKPAK_ENHANCEMENT_MASTER") {
                        throw new Error("INVALID_SIGNATURE");
                    }

                    const importExactKeys = new Set([
                        'pk_lang',
                        'pk_theme',
                        'pk_turbo_mode',
                        'pk_file_view_mode',
                        'pk_view_independent',
                        'pk_folder_view_prefs',
                        'pk_keep_pos',
                        'pk_pos_left',
                        'pk_pos_top',
                        'pk_blur_thumb',
                        'pk_blur_scope',
                        'pk_comic_mode',
                        'pk_clipboard_magnet_focus',
                        'pk_sort_independent',
                        'pk_folder_first',
                        'pk_folder_sort_prefs',
                        'pk_global_sort_pref',
                        'pk_ext_player',
                        'pk_play_mode',
                        'pk_skip_intro',
                        'pk_skip_outro',
                        'pk_vol_muted',
                        'pk_vol_level',
                        'pk_suppress_global_warn',
                        'pk_blacklist',
                        'pk_blacklist_folders',
                        'pk_aria2_url',
                        'pk_aria2_token',
                        'pk_dl_filter_ext',
                        'pk_dl_filter_name',
                        'pk_dl_filter_size_min',
                        'pk_dl_filter_size_max',
                        'pk_dl_filter_size_unit',
                        'pk_search_engine',
                        'pk_search_history',
                        'pk_expired_shares',
                        'pk_share_limits',
                        'pk_bn_find_hist',
                        'pk_bn_rep_hist',
                        'pk_dup_strictness',
                        'pk_skip_bl_on_del',
                        'pk_pwd_vault',
                        'pk_pwd_try_count'
                    ]);

                    const importPrefixKeys = [
                        'pk_scan_last_',
                        'pk_analyze_last_',
                        'pk_archive_pwd_',
                        'pk_progress_',
                        'pk_duration_'
                    ];

                    const isWhitelistedImportKey = (k) => importExactKeys.has(k) || importPrefixKeys.some(prefix => k.startsWith(prefix));

                    const readStoredValue = (k) => {
                        let v = typeof GM_getValue !== 'undefined' ? GM_getValue(k, null) : null;
                        if ((v === null || v === undefined || v === '') && localStorage.getItem(k) !== null) v = localStorage.getItem(k);
                        return v;
                    };

                    const writeStoredValue = (k, v) => {
                        if (typeof GM_setValue !== 'undefined') GM_setValue(k, v);
                        else localStorage.setItem(k, typeof v === 'string' ? v : JSON.stringify(v));
                        if (k === 'pk_turbo_mode') localStorage.setItem(k, String(v));
                    };

                    const parseMaybeJson = (v) => {
                        if (typeof v === 'string') {
                            const s = v.trim();
                            if ((s.startsWith('[') && s.endsWith(']')) || (s.startsWith('{') && s.endsWith('}'))) {
                                try { return JSON.parse(s); } catch {}
                            }
                        }
                        return v;
                    };

                    const isPlainObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);

                    const mergeArrayUnique = (localArr, importedArr, limit = 100) => {
                        const seen = new Set();
                        const merged = [];
                        [...localArr, ...importedArr].forEach(item => {
                            const key = (item && typeof item === 'object') ? JSON.stringify(item) : `v:${String(item)}`;
                            if (seen.has(key)) return;
                            seen.add(key);
                            merged.push(item);
                        });
                        return merged.slice(0, limit);
                    };

                    const importKeys = Object.keys(config).filter(k => isWhitelistedImportKey(k));

                    importKeys.forEach(k => {
                        const importedVal = config[k];
                        const localVal = readStoredValue(k);

                        if (localVal === null || localVal === undefined || localVal === '') {
                            writeStoredValue(k, importedVal);
                            return;
                        }

                        try {
                            if (k === 'pk_blacklist' || k === 'pk_blacklist_folders') {
                                const localSet = new Set(String(localVal || '').split('\n').map(s => s.trim()).filter(s => s));
                                const importedSet = new Set(String(importedVal || '').split('\n').map(s => s.trim()).filter(s => s));
                                importedSet.forEach(v => localSet.add(v));
                                writeStoredValue(k, Array.from(localSet).join('\n'));
                                return;
                            }

                            if (k === 'pk_dl_filter_ext' || k === 'pk_dl_filter_name') {
                                const localSet = new Set(String(localVal || '').split(/[,,\n]/).map(s => s.trim()).filter(s => s));
                                const importedSet = new Set(String(importedVal || '').split(/[,,\n]/).map(s => s.trim()).filter(s => s));
                                importedSet.forEach(v => localSet.add(v));
                                writeStoredValue(k, Array.from(localSet).join(', '));
                                return;
                            }

                            const localObj = parseMaybeJson(localVal);
                            const importedObj = parseMaybeJson(importedVal);

                            if (k === 'pk_pwd_vault') {
                                const localArr = Array.isArray(localObj) ? localObj : [];
                                const importedArr = Array.isArray(importedObj) ? importedObj : [];
                                const map = new Map();

                                localArr.forEach(x => {
                                    const p = typeof x === 'object' ? x.p : x;
                                    const h = typeof x === 'object' ? (Number(x.h) || 0) : 0;
                                    if (p) map.set(p, { p, h });
                                });

                                importedArr.forEach(x => {
                                    const p = typeof x === 'object' ? x.p : x;
                                    const h = typeof x === 'object' ? (Number(x.h) || 0) : 0;
                                    if (!p) return;
                                    if (map.has(p)) map.get(p).h += h;
                                    else map.set(p, { p, h });
                                });

                                writeStoredValue(k, JSON.stringify(Array.from(map.values()).sort((a, b) => b.h - a.h).slice(0, 50)));
                                return;
                            }

                            if (k === 'pk_expired_shares') {
                                const localArr = Array.isArray(localObj) ? localObj : [];
                                const importedArr = Array.isArray(importedObj) ? importedObj : [];
                                const map = new Map();

                                localArr.forEach(x => { if (x && x.id) map.set(x.id, x); });
                                importedArr.forEach(x => { if (x && x.id) map.set(x.id, x); });

                                writeStoredValue(k, JSON.stringify(Array.from(map.values())));
                                return;
                            }

                            if (Array.isArray(localObj) && Array.isArray(importedObj)) {
                                writeStoredValue(k, JSON.stringify(mergeArrayUnique(localObj, importedObj, 100)));
                                return;
                            }

                            if (isPlainObject(localObj) && isPlainObject(importedObj)) {
                                writeStoredValue(k, JSON.stringify(Object.assign({}, localObj, importedObj)));
                                return;
                            }

                            writeStoredValue(k, importedVal);
                        } catch (e2) {
                            writeStoredValue(k, importedVal);
                        }
                    });

                    showToast(L.msg_import_success);
                    fileInput.value = '';
                    setTimeout(() => location.reload(), 1500);
                } catch (err) {
                    let errorTip = "";
                    if (err.message === "INVALID_SIGNATURE") {
                        errorTip = L.err_invalid_config;
                    } else {
                        errorTip = L.err_json_format;
                        console.error("[Config Import]", err);
                    }

                    showAlert(errorTip, L.str_error);
                    fileInput.value = '';
                }
            };
            reader.readAsText(file);
        };

        const thumbScopeWrap = m.querySelector('#cs_thumb_scope');
        const thumbScopeInput = m.querySelector('#set_thumb_scope');
        const thumbScopeTrigger = m.querySelector('#cs_thumb_scope .pk-select-trigger');
        const thumbScopeMenu = m.querySelector('#cs_thumb_scope .pk-select-menu');
        const thumbScopeTxt = m.querySelector('#txt_thumb_scope');

        if (thumbScopeWrap && thumbScopeInput && thumbScopeTrigger && thumbScopeMenu && thumbScopeTxt) {
            thumbScopeTrigger.onclick = (e) => {
                e.stopPropagation();
                thumbScopeMenu.style.display = thumbScopeMenu.style.display === 'block' ? 'none' : 'block';
            };

            m.querySelectorAll('#cs_thumb_scope .pk-select-item').forEach(item => {
                item.onclick = (e) => {
                    e.stopPropagation();
                    m.querySelectorAll('#cs_thumb_scope .pk-select-item').forEach(i => i.classList.remove('act'));
                    item.classList.add('act');
                    thumbScopeInput.value = item.dataset.val;
                    thumbScopeTxt.textContent = item.textContent;
                    thumbScopeMenu.style.display = 'none';
                };
            });

            m.addEventListener('click', () => {
                thumbScopeMenu.style.display = 'none';
            });
        }

        m.querySelector('#set_cancel').onclick = () => m.remove();
        m.tabIndex = 0;
        setTimeout(() => m.focus(), 10);
        m.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault(); e.stopPropagation();
                m.querySelector('#set_save').click();
            }
        });

        m.querySelector('#set_save').onclick = async () => {
            const newTurbo = m.querySelector('#set_turbo').checked;
            const oldTurbo = gmGet('pk_turbo_mode', false);
            const newUrl = m.querySelector('#set_aria_url').value.trim();
            const newToken = m.querySelector('#set_aria_token').value.trim();
            const newBlurScope = m.querySelector('#set_thumb_scope').value;
            const newKeepPos = m.querySelector('#set_keep_pos').checked;
            const newSkipBl = m.querySelector('#set_skip_bl').checked;
            const newClipboardMagnetFocus = m.querySelector('#set_clipboard_magnet_focus').checked;
            const newComicMode = m.querySelector('#set_comic_mode').checked;
            const sortPref = m.querySelector('input[name="set_sort_pref"]:checked').value;
            const viewPref = m.querySelector('input[name="set_view_pref"]:checked').value;
            const saveBtn = m.querySelector('#set_save');

            const applyChangesAndClose = async () => {
                gmSet('pk_blur_scope', newBlurScope);
                gmSet('pk_blur_thumb', newBlurScope !== 'off');
                gmSet('pk_keep_pos', newKeepPos);
                gmSet('pk_comic_mode', newComicMode);
                gmSet('pk_clipboard_magnet_focus', newClipboardMagnetFocus);
                gmSet('pk_skip_bl_on_del', newSkipBl);

                const wasIndep = gmGet('pk_sort_independent', false);
                const isIndep = (sortPref === 'indep');
                gmSet('pk_sort_independent', isIndep);

                if (wasIndep && !isIndep) {
                    const keepFolderFirst = gmGet('pk_folder_first', false);
                    gmSet('pk_folder_sort_prefs', '{}');
                    gmSet('pk_global_sort_pref', JSON.stringify({ sort: 'modified_time', dir: 1, folderFirst: keepFolderFirst }));
                    S.sort = 'modified_time';
                    S.dir = 1;
                    S.folderFirst = keepFolderFirst;
                }

                const isViewIndep = (viewPref === 'indep');
                gmSet('pk_view_independent', isViewIndep);
                persistViewPreference();

                gmSet('pk_lang', selectedLang);
                gmSet('pk_search_engine', selectedEngine);
                gmSet('pk_turbo_mode', newTurbo);
                gmSet('pk_dl_filter_ext', m.querySelector('#set_dl_filter_ext').value.trim());
                gmSet('pk_dl_filter_size_min', m.querySelector('#set_dl_filter_size_min').value.trim());
                gmSet('pk_dl_filter_size_max', m.querySelector('#set_dl_filter_size_max').value.trim());
                gmSet('pk_dl_filter_size_unit', m.querySelector('#cs_set_dl_size_unit .pk-select-item.act') ? m.querySelector('#cs_set_dl_size_unit .pk-select-item.act').dataset.val : 'MB');
                gmSet('pk_dl_filter_name', m.querySelector('#set_dl_filter_name').value.trim());
                gmSet('pk_aria2_url', newUrl);
                gmSet('pk_aria2_token', newToken);

                if (curLang !== selectedLang) {
                    await ensureI18nReady(true, selectedLang);
                }

                m.remove();

                const savedPack = (pkRemoteI18n && pkRemoteI18n.lang === selectedLang) ? pkRemoteI18n.data : T_LOCAL.zh;
                showToast((savedPack && savedPack.msg_settings_saved) || T_LOCAL.zh.msg_settings_saved);

                if (newTurbo !== oldTurbo) {
                    setTimeout(() => location.reload(), 300);
                    return;
                }

                if (curLang !== selectedLang) {
                    let safePath = [...S.path];
                    if (safePath.some(n => n.id === 'virtual_search_root' || n.id === 'analyze_root')) {
                        safePath = S.preSearchPath || [{ id: '', name: (savedPack && savedPack.btn_nav_home) || L.btn_nav_home }];
                    }

                    globalSavedState = {
                        path: safePath,
                        trashMode: S.trashMode,
                        shareMode: S.shareMode,
                        starredMode: S.starredMode,
                        recentMode: S.recentMode,
                        historyMode: S.historyMode,
                        offlineMode: S.offlineMode,
                        uploadMode: S.uploadMode,
                        isMaximized: UI.win.classList.contains('pk-maximized'),
                        scrollTop: UI.vp ? UI.vp.scrollTop : 0,
                        uploadTasks: S.uploadTasks
                    };

                    document.removeEventListener('keydown', keyHandler);
                    document.removeEventListener('mouseup', mouseHandler);

                    if (typeof destroyTooltip === 'function') destroyTooltip();

                    if (visibilityListener && visibilityListener.abort) visibilityListener.abort();
                    el.remove();

                    await ensureI18nReadyBeforeOpen(selectedLang);
                    await openManager(S.cache, S.preLoadPromise);
                } else {
                    renderVisible();
                }
            };

            if (!newUrl && !newToken) {
                await applyChangesAndClose();
                return;
            }

            saveBtn.disabled = true;
            saveBtn.textContent = L.str_saving_dots;

            try {
                const fetchUrl = normalizeAriaRpcUrl(newUrl || 'http://localhost:6800/jsonrpc');
                const payload = { jsonrpc: '2.0', method: 'aria2.getVersion', id: 'pk_test', params: buildAriaRpcParams(newToken) };
                await aria2RpcRequest(fetchUrl, payload, 5000);
                await applyChangesAndClose();
            } catch (e) {
                if (await showConfirm(L.msg_aria2_test_fail, L.title_aria2_fail)) {
                    await applyChangesAndClose();
                } else {
                    saveBtn.disabled = false;
                    saveBtn.textContent = L.btn_save;
                }
            }
        };
    };

    UI.btnSettings.onclick = (e) => {
        if (e) e.stopPropagation();
        const existing = document.getElementById('pk-settings-pop');
        if (existing) { existing.remove(); return; }

        const isMax = UI.win.classList.contains('pk-maximized');
        const pop = document.createElement('div');
        pop.id = 'pk-settings-pop';
        if (isMax) pop.className = 'pk-pop-max';
        pop.style.cssText = `
            position: absolute; background: var(--pk-bg); border: 1px solid var(--pk-bd);
            border-radius: 8px; padding: 4px 0; box-shadow: 0 4px 15px rgba(0,0,0,0.15);
            z-index: 2147483647; min-width: 140px; display: flex; flex-direction: column;
            zoom: var(--pk-zoom, 1);
        `;
        if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) pop.classList.add('pk-dark');

        pop.innerHTML = `
            <div class="pk-dropdown-item" id="pk-set-menu-settings" style="padding:10px 16px;">${CONF.icons.settings} <span>${L.btn_settings}</span></div>
            <div style="height:1px; background:var(--pk-bd); margin:4px 0;"></div>
            <div class="pk-dropdown-item" id="pk-set-menu-logout" style="padding:10px 16px; color:#d93025;">${CONF.icons.logout} <span>${L.btn_logout}</span></div>
        `;
        document.body.appendChild(pop);

        const updatePosition = () => {
            if (!pop.isConnected) return;
            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const rect = getLogicalRect(UI.btnSettings);
            const winEl = document.querySelector('.pk-win');
            const isMax = winEl && winEl.classList.contains('pk-maximized');

            let popLeft, popBottom;
            if (isMax) {
                popLeft = rect.left;
                popBottom = (window.innerHeight / scale) - rect.top + 8;
            } else {
                popLeft = rect.right + 10;
                popBottom = (window.innerHeight / scale) - rect.bottom;
            }

            pop.style.bottom = popBottom + 'px';
            pop.style.left = popLeft + 'px';
        };
        updatePosition();
        window.addEventListener('resize', updatePosition);

        const cleanup = () => {
            window.removeEventListener('resize', updatePosition);
            document.removeEventListener('mousedown', closer);
            pop.remove();
        };

        const closer = (ev) => {
            if (!pop.contains(ev.target) && !UI.btnSettings.contains(ev.target)) cleanup();
        };
        setTimeout(() => document.addEventListener('mousedown', closer), 10);

        pop.querySelector('#pk-set-menu-settings').onclick = (ev) => {
            ev.stopPropagation();
            cleanup();
            openSettingsModal();
        };

        pop.querySelector('#pk-set-menu-logout').onclick = async (ev) => {
            ev.stopPropagation();
            cleanup();
            if (await showConfirm(L.msg_logout_confirm)) {
                const keysToRemove = [];
                for (let i = 0; i < localStorage.length; i++) {
                    const k = localStorage.key(i);
                    if (k && (k.startsWith('credentials') || k.startsWith('captcha') || k === 'pk_captured_captcha')) {
                        keysToRemove.push(k);
                    }
                }
                keysToRemove.forEach(k => localStorage.removeItem(k));
                if (typeof purgeAllCachesOnLogout === 'function') purgeAllCachesOnLogout();
                if (!location.href.includes('/login')) window.location.href = 'https://mypikpak.com/drive/login';
            }
        };
    };

    const ctx = el.querySelector('#pk-ctx');

    if (!window.pkPropCache) window.pkPropCache = new Map();

    ctx.querySelector('#ctx-property').onclick = async () => {
        ctx.style.display = 'none';
        const id = S.getSelectedIds()[0];
        if (!id) return;

        let item = S.itemMap.get(id);
        if (!item) return;

        setLoad(true);
        updateLoadTxt(L.loading_detail);

        const isFolder = item.kind === 'drive#folder';

        try {
            const freshData = await apiGet(id);
            item = { ...item, ...freshData };
        } catch(e) {
            console.warn("Fetch detail failed, using cached info");
        }

        if (isFolder) {
            const localFMod = gmGet('pk_fmod_' + item.id);
            if (localFMod) item.modified_time = localFMod;
        }

        setLoad(false);

        let cachedProp = window.pkPropCache.get(item.id);
        if (!cachedProp) {
            cachedProp = { size: BigInt(0), fileCount: 0, folderCount: 0, isDone: false, scanned: new Set(), isRunning: false };
            window.pkPropCache.set(item.id, cachedProp);
        }

        let realSize = isFolder ? cachedProp.size : BigInt(item.size || 0);
        let fileCount = isFolder ? cachedProp.fileCount : 0;
        let folderCount = isFolder ? cachedProp.folderCount : 0;

        if (isFolder && item.usage && item.usage.size && item.usage.size !== "0") {
            realSize = BigInt(item.usage.size);
            fileCount = item.usage.file_count;
            folderCount = item.usage.folder_count;

            cachedProp.size = realSize;
            cachedProp.fileCount = fileCount;
            cachedProp.folderCount = folderCount;
            cachedProp.isDone = true;
        }

        const countStr = isFolder ? L.fmt_prop_count.replace('{f}', fileCount).replace('{d}', folderCount) : "-";

        const formatTime = (iso) => fmtDate ? fmtDate(iso) : iso;

        let sourceStr = L.str_prop_unknown;
        let magnetLink = item.params?.url || item.audit?.source_url || "";

        if (magnetLink) {
            sourceStr = L.str_prop_cloud;
        } else {
            sourceStr = L.str_prop_user;
        }

        const btnStyle = "margin-left:10px; padding:2px 8px; font-size:12px; background:var(--pk-pri); color:#fff; border:none; border-radius:4px; cursor:pointer; height:24px; white-space:nowrap;";
        const rowStyle = "display:flex; align-items:center; margin-bottom:12px; font-size:13px; line-height:1.5;";
        const labelStyle = "width:80px; color:#888; flex-shrink:0;";
        const valStyle = "color:var(--pk-fg); flex:1; word-break:break-all;";

        const mkRow = (lbl, val, copyVal = null, isHtml = false, valId = null) => {
            if (!val && val !== 0 && val !== "-") return "";
            let btnHtml = copyVal ? `<button class="pk-btn-copy-prop" data-val="${esc(copyVal)}" style="${btnStyle}">${L.btn_copy_text}</button>` : "";
            return `<div style="${rowStyle}"><div style="${labelStyle}">${lbl}:</div><div style="${valStyle}" ${valId ? `id="${valId}"` : ''}>${isHtml ? val : esc(val)}</div>${btnHtml}</div>`;
        };

        let pathRowHtml = "";
        const isSpecialView = S.shareMode || S.offlineMode || S.recentMode || S.historyMode || S.trashMode || S.starredMode;
        const curPathNode = S.path[S.path.length - 1];
        const shouldHidePath = isSpecialView || (S.analyzeMode && curPathNode && curPathNode.id !== 'analyze_root');

        if (!shouldHidePath) {
            const homeText = L.btn_nav_home;
            let parts = (item._lineage && Array.isArray(item._lineage))
            ? item._lineage.filter(p => p.id !== item.id).map(p => p.name)
            : S.path.map(p => p.name);

            parts = parts.filter(n => n && n !== 'Root' && n !== L.str_root_dir_cn);
            if (parts[0] !== homeText) parts.unshift(homeText);

            const pathStr = parts.join('/');
            let pathDisplayHtml = esc(pathStr);

            if (pathStr.startsWith(homeText)) {
                const homeSvg = CONF.icons.home.replace('width="24"','width="15"').replace('height="24"','height="15"').replace('viewBox="0 0 24 24"','viewBox="0 0 24 24" style="margin-right:4px;flex-shrink:0;vertical-align:-2.5px;"');
                const restPath = pathStr.substring(homeText.length);
                const homeGroup = `<span style="display:inline-flex;align-items:center;vertical-align:bottom;margin-right:2px;">${homeSvg}${esc(homeText)}</span>`;
                pathDisplayHtml = `<div style="line-height:1.6;word-break:break-all;">${homeGroup}${esc(restPath)}</div>`;
            }
            pathRowHtml = mkRow(L.lbl_prop_path, pathDisplayHtml, pathStr, true);
        }

        const html = `
    <div style="padding:10px 0;">
        ${mkRow(L.lbl_prop_name, item.name)}
        ${mkRow(L.lbl_prop_size, fmtSize(realSize.toString()) || "0 B", null, false, 'pk_prop_size_val')}
        ${isFolder ? mkRow(L.lbl_prop_count, countStr, null, false, 'pk_prop_count_val') : ''}
        ${mkRow(L.lbl_prop_ctime, formatTime(item.created_time))}
        ${mkRow(L.lbl_prop_mtime, formatTime(item.modified_time))}
        ${mkRow(L.lbl_prop_source, sourceStr)}
        ${magnetLink ? mkRow(L.lbl_prop_link, magnetLink, magnetLink) : ''}
        ${pathRowHtml}
    </div>
`;

        const m = showModal(`
            <h3 style="border-bottom:1px solid var(--pk-bd); padding-bottom:10px; margin-bottom:15px;">${L.title_property}</h3>
            ${html}
        `);

        m.querySelectorAll('.pk-btn-copy-prop').forEach(btn => {
            btn.onclick = (e) => {
                const txt = e.target.getAttribute('data-val');
                GM_setClipboard(txt);
                const oldTxt = e.target.textContent;
                e.target.textContent = "OK";
                e.target.style.background = "#4CAF50";
                setTimeout(() => {
                    e.target.textContent = oldTxt;
                    e.target.style.background = "var(--pk-pri)";
                }, 1500);
            };
        });

        if (isFolder && !cachedProp.isDone) {
            const sizeEl = m.querySelector('#pk_prop_size_val');
            const countEl = m.querySelector('#pk_prop_count_val');

            let lastUiUpdateTime = performance.now();
            const updateUI = () => {
                const now = performance.now();
                if (now - lastUiUpdateTime > 80) {
                    if (sizeEl && sizeEl.isConnected) sizeEl.textContent = fmtSize(cachedProp.size.toString()) || "0 B";
                    if (countEl && countEl.isConnected) countEl.textContent = L.fmt_prop_count.replace('{f}', cachedProp.fileCount).replace('{d}', cachedProp.folderCount);
                    lastUiUpdateTime = now;
                }
            };

            if (!cachedProp.isRunning) {
                cachedProp.isRunning = true;

                const localAbortCtrl = new AbortController();
                const rootNodes =[{ id: item.id, name: item.name, lineage: [], retryCount: 0 }];

                coreRecursiveEngine(rootNodes, {
                    signal: localAbortCtrl.signal,
                    onFolder: (f) => {
                        if (f.id !== item.id && !cachedProp.scanned.has(f.id)) {
                            cachedProp.folderCount++;
                            cachedProp.scanned.add(f.id);
                        }
                        updateUI();
                    },
                    onFile: (f) => {
                        if (!cachedProp.scanned.has(f.id)) {
                            cachedProp.fileCount++;
                            cachedProp.size += BigInt(f.size || 0);
                            cachedProp.scanned.add(f.id);
                        }
                        updateUI();
                    },
                    onProgress: () => {}
                }).then(() => {
                    cachedProp.isDone = true;
                    cachedProp.isRunning = false;
                    if (sizeEl && sizeEl.isConnected) sizeEl.textContent = fmtSize(cachedProp.size.toString()) || "0 B";
                    if (countEl && countEl.isConnected) countEl.textContent = L.fmt_prop_count.replace('{f}', cachedProp.fileCount).replace('{d}', cachedProp.folderCount);
                }).catch(e => {
                    cachedProp.isRunning = false;
                });
            } else {
                const timer = setInterval(() => {
                    if (!document.contains(m)) {
                        clearInterval(timer);
                        return;
                    }
                    if (sizeEl) sizeEl.textContent = fmtSize(cachedProp.size.toString()) || "0 B";
                    if (countEl) countEl.textContent = L.fmt_prop_count.replace('{f}', cachedProp.fileCount).replace('{d}', cachedProp.folderCount);
                    if (cachedProp.isDone) clearInterval(timer);
                }, 200);
            }
        }
    };

    const btnLocate = ctx.querySelector('#ctx-locate');
    if (btnLocate) {
        btnLocate.onclick = async () => {
            ctx.style.display = 'none';
            const id = S.getSelectedIds()[0];
            if (!id) return;

            let item = S.itemMap.get(id);
            if (!item) return;

            setLoad(true);
            updateLoadTxt(L.str_loc_tracing);

            try {
                if (item.kind === 'drive#task' || S.recentMode || S.uploadMode) {
                    const lookupId = (item.kind === 'drive#task' || S.uploadMode) ? item.file_id : item.id;

                    if (!lookupId) {
                        showToast(L.err_folder_not_ready, 'error');
                        setLoad(false); return;
                    }

                    try {
                        item = await apiGet(lookupId);
                    } catch (e) {
                        const errText = e.message || "";
                        if (errText.includes('404') || errText.includes('400')) {
                            showToast(L.err_item_deleted, 'error');
                        } else {
                            showToast(`${L.str_error}: ${e.message}`, 'error');
                        }
                        setLoad(false); return;
                    }
                }

                let pathChain = [];
                if (item._lineage && Array.isArray(item._lineage)) {
                    pathChain = item._lineage.filter(p =>
                        p.id !== item.id &&
                        p.id !== 'virtual_search_root' &&
                        p.id !== 'analyze_root' &&
                        p.id !== 'recent_root'
                    );
                } else if (item.parent_id && typeof globalLineageMap !== 'undefined' && globalLineageMap.has(item.parent_id)) {
                    pathChain = [...globalLineageMap.get(item.parent_id)];
                } else {
                    let currParentId = item.parent_id;
                    for (let i = 0; i < 15; i++) {
                        if (!currParentId || currParentId === 'root' || currParentId === '') break;

                        if (typeof globalLineageMap !== 'undefined' && globalLineageMap.has(currParentId)) {
                            const cachedLineage = globalLineageMap.get(currParentId);
                            pathChain.unshift(...cachedLineage);
                            break;
                        }

                        try {
                            const res = await apiGet(currParentId);
                            pathChain.unshift({ id: res.id, name: res.name });
                            currParentId = res.parent_id;
                        } catch (e) {
                            console.warn("Trace broken:", e);
                            break;
                        }
                    }
                }

                if (pathChain.length > 0 && (pathChain[0].id === '' || pathChain[0].id === 'root')) {
                    pathChain[0].id = '';
                    pathChain[0].name = L.btn_nav_home;
                } else {
                    pathChain.unshift({ id: '', name: L.btn_nav_home });
                }

                const targetContextId = (item.parent_id === 'root' || !item.parent_id) ? '' : item.parent_id;

                const needsRestoreGlobalCheck = S.starredMode || S.recentMode || S.historyMode || S.offlineMode || S.uploadMode || S.shareMode || S.isFlattened || S.dupMode || S.analyzeMode;

                S.starredMode = false; S.trashMode = false; S.shareMode = false; S.offlineMode = false;
                S.recentMode = false;
                S.historyMode = false;
                S.uploadMode = false;
                S.dupMode = false; S.isFlattened = false; S.analyzeMode = false;
                if (UI.chkSearchPath) UI.chkSearchPath.checked = false;

                if (UI.btnNavStarred) UI.btnNavStarred.classList.remove('act');
                if (UI.btnNavRecent) UI.btnNavRecent.classList.remove('act');
                if (UI.btnNavHistory) UI.btnNavHistory.classList.remove('act');
                if (UI.btnNavShare) UI.btnNavShare.classList.remove('act');
                if (UI.btnNavOffline) UI.btnNavOffline.classList.remove('act');
                if (UI.btnNavUpload) UI.btnNavUpload.classList.remove('act');
                if (UI.btnNavHome) UI.btnNavHome.classList.add('act');

                [UI.btnAria2, UI.btnDown, UI.btnExt, UI.btnImgSearch].forEach(b => { if(b) b.style.display = 'inline-flex'; });

                [UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll].forEach(b => { if(b) b.style.display = 'none'; });
                const upSep = document.getElementById('pk-up-sep');
                if(upSep) upSep.style.display = 'none';

                [UI.btnNewFolder, UI.btnDel, UI.btnCopy, UI.btnCut, UI.btnPaste, UI.btnRename, UI.btnBulkRename, UI.btnPrune, UI.btnUnzip, UI.btnMigrate, UI.btnRefresh, UI.btnBlacklistManager].forEach(b => { if(b) b.style.display = 'inline-flex'; });
                if(UI.uploadWrap) UI.uploadWrap.style.display = 'inline-flex';

                S.path = pathChain;

                if (UI.lblGlobal) UI.lblGlobal.style.display = 'flex';

                if (UI.chkGlobal && needsRestoreGlobalCheck && typeof S.wasGlobalChecked !== 'undefined') {
                    UI.chkGlobal.checked = S.wasGlobalChecked;
                }

                if (UI.scan) UI.scan.style.display = 'flex';
                if (UI.btnAnalyze) UI.btnAnalyze.style.display = 'flex';
                if (UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
                if (UI.cntFolderFirst) UI.cntFolderFirst.style.display = 'flex';
                if (UI.btnNewFolder) UI.btnNewFolder.style.display = 'inline-flex';
                if (UI.btnPaste) UI.btnPaste.style.display = S.clipItems && S.clipItems.length > 0 ? 'inline-flex' : 'inline-flex';

                S.sort = 'modified_time'; S.dir = 1;

                const locateTargetViewMode = (typeof resolvePreferredViewMode === 'function')
                ? resolvePreferredViewMode(targetContextId || 'root')
                : (gmGet('pk_file_view_mode', 'grid') === 'list' ? 'list' : 'grid');
                const shouldHoldLocateGridSync = locateTargetViewMode === 'grid';
                if (shouldHoldLocateGridSync) {
                    S._folderViewSyncHold = true;
                    beginFolderViewSync();
                }

                await load();

                let trackCount = 0;
                const maxTracks = 10;
                let trackerInterval = null;

                const releaseLocateViewSync = () => {
                    if (!S._folderViewSyncHold) return;
                    S._folderViewSyncHold = false;
                    endFolderViewSync(true);
                };

                const stopTracking = (releaseViewSync = true) => {
                    if (trackerInterval) { clearInterval(trackerInterval); trackerInterval = null; }
                    if (releaseViewSync) releaseLocateViewSync();
                };

                let hasTriedRecovery = false;

                const performLocate = () => {
                    const currentPathNode = S.path[S.path.length - 1];
                    const currentContextId = currentPathNode ? (currentPathNode.id || '') : '';

                    if (currentContextId !== targetContextId) {
                        stopTracking(); return;
                    }

                    if (!S.loading && !S.itemMap.has(item.id) && !hasTriedRecovery) {
                        hasTriedRecovery = true;
                        console.warn(`[Locate] Target item ${item.id} not found in cache. Forcing network sync...`);

                        const cacheKey = S.getRealCacheKey(currentContextId);
                        S.cache.delete(cacheKey);
                        if (typeof globalCache !== 'undefined') globalCache.delete(cacheKey);

                        globalDirtyFolders.add(currentContextId || 'root');

                        updateLoadTxt(L.str_loc_stale);
                        load(false, true);
                        return;
                    }

                    if (S.loading || !S.itemMap.has(item.id)) return;

                    S.sel.clear();
                    S.sel.add(item.id);
                    S.activeId = item.id;

                    const targetIdx = S.display.findIndex(x => x.id === item.id);

                    if (targetIdx !== -1) {
                        const vpHeight = UI.vp.clientHeight;
                        let rowTop = targetIdx * CONF.rowHeight;

                        if (isGridView()) {
                            syncLayoutMetrics();
                            const gridLayout = getGridLayout();
                            const cols = Math.max(1, gridLayout.cols || 1);
                            rowTop = Math.floor(targetIdx / cols) * CONF.rowHeight;
                        }

                        const centerScroll = Math.max(0, rowTop - (vpHeight / 2) + (CONF.rowHeight / 2));

                        if (Math.abs(UI.vp.scrollTop - centerScroll) > (CONF.rowHeight / 2)) {
                            UI.vp.scrollTop = centerScroll;
                        }

                        renderVisible();

                        const row = UI.in.querySelector(`.pk-row[data-id="${item.id}"]`);
                        if (row) {
                            if (isGridView()) {
                                delete row.dataset.flashing;
                                row.style.transition = '';
                                row.style.backgroundColor = '';
                                row.style.border = '';
                                row.style.borderRadius = '';
                                row.style.boxShadow = '';
                                row.style.outline = '';
                                row.className = getRowClassName(true, true, S.movingIds.has(item.id));
                                const chk = row.querySelector('input[type="checkbox"]');
                                if (chk && !chk.checked) chk.checked = true;
                            } else if (!row.dataset.flashing) {
                                row.dataset.flashing = "true";
                                row.style.transition = "none";
                                row.style.backgroundColor = "rgba(255, 193, 7, 0.5)";
                                void row.offsetWidth;
                                requestAnimationFrame(() => {
                                    row.style.transition = "background-color 1.5s ease-out";
                                    row.style.backgroundColor = "";
                                    setTimeout(() => { if(row) delete row.dataset.flashing; }, 1500);
                                });
                            }
                        }
                    }

                    requestAnimationFrame(() => {
                        releaseLocateViewSync();
                    });
                };

                performLocate();

                trackerInterval = setInterval(() => {
                    trackCount++;
                    const limit = hasTriedRecovery ? 25 : 10;
                    if (S.loading || trackCount <= limit) performLocate();
                    else stopTracking();
                }, 200);

            } catch (e) {
                console.error(e);
                showAlert(`${L.str_error}: ${e.message}`);
                if (UI.vp) UI.vp.style.opacity = '1';
            } finally {
                setLoad(false);
                setTimeout(() => { if (UI.vp) UI.vp.style.opacity = '1'; }, 100);
            }
        };
    }

    ctx.querySelector('#ctx-ext-play').onclick = () => {
        ctx.style.display = 'none';
        UI.btnExt.click();
    };

    ctx.querySelector('#ctx-open').onclick = () => {
        ctx.style.display = 'none';

        const id = S.getSelectedIds()[0];
        if (!id) return;
        const item = S.items.find(x => x.id === id);
        if (!item) return;

        if (S.offlineMode && item.phase !== 'PHASE_TYPE_COMPLETE') {
            const locateBtn = document.getElementById('ctx-locate');
            if (locateBtn) locateBtn.click();
            return;
        }
        if (S.uploadMode && item.status !== 'DONE') {
            if (item.file_id) {
                const locateBtn = document.getElementById('ctx-locate');
                if (locateBtn) locateBtn.click();
            }
            return;
        }

        if (item.kind === 'drive#folder') {
            if (S.loading) return;
            S.path.push({ id: item.id, name: item.name });
            load();
        } else {
            const mime = (item.mime_type || "").toLowerCase();
            const name = (item.name || "").toLowerCase();

            if (name.endsWith('.torrent')) {
                handleTorrentFile(item);
            }
            else if (mime.includes('zip') || mime.includes('rar') || mime.includes('7z') ||
                mime.includes('compressed') || mime.includes('archive') ||
                name.endsWith('.zip') || name.endsWith('.rar') || name.endsWith('.7z') || name.endsWith('.tar') || name.endsWith('.gz')) {
                handleOpenArchive(item);
            }
            else if (mime.startsWith('video')) {
                playVideo(item);
            }
            else if (mime.startsWith('image')) {
                showImage(item);
            }
            else {
                UI.btnExt.click();
            }
        }
    };

    const starBtnCtx = ctx.querySelector('#ctx-star');
    if (starBtnCtx) {
        starBtnCtx.onclick = async (e) => {
            ctx.style.display = 'none';

            const action = e.target.getAttribute('data-action');
            const isStar = (action === 'star');

            const rawIds = S.getSelectedIds();
            const ids = rawIds.filter(id => {
                const it = S.itemMap.get(id);
                if (!it) return false;

                if (!S.trashMode && isSystemItem(it)) return false;

                const isCurrentlyStarred = !!(it.starred || (it.tags && it.tags.some(t => t.name === 'STAR')));
                if (isStar && isCurrentlyStarred) return false;
                if (!isStar && !isCurrentlyStarred) return false;

                return true;
            });

            if (ids.length === 0) {
                showToast(isStar ? L.msg_star_added : L.msg_unstar_done);
                return;
            }

            const starTask = FloatBarManager.create(isStar ? L.msg_starring : L.msg_unstarring);
            const total = ids.length;
            let successCount = 0;

            try {
                const url = `https://api-drive.mypikpak.com/drive/v1/files:${action}`;
                const headers = getHeaders();

                const BATCH_SIZE = 100;

                for (let i = 0; i < total; i += BATCH_SIZE) {
                    const chunk = ids.slice(i, i + BATCH_SIZE);
                    const chunkSet = new Set(chunk);

                    starTask.update(`${isStar ? L.msg_starring : L.msg_unstarring} ${Math.min(i + BATCH_SIZE, total)} / ${total}`);

                    const res = await fetch(url, {
                        method: 'POST', headers: headers,
                        body: JSON.stringify({ "ids": chunk })
                    });

                    if (!res.ok) {
                        const errText = await res.text();
                        if (res.status === 400 && errText.includes('captcha')) throw new Error(L.err_captcha_simple);
                        throw new Error(`API ${res.status}`);
                    }

                    chunk.forEach(id => {
                        if (isStar) S.starredSet.add(id); else S.starredSet.delete(id);

                        const syncObject = (o) => {
                            if (!o) return;
                            o.starred = isStar;
                            if (!o.tags) o.tags = [];
                            if (isStar) {
                                if (!o.tags.some(t => t.name === 'STAR')) o.tags.push({name: 'STAR', type: 0});
                            } else {
                                o.tags = o.tags.filter(t => t.name !== 'STAR');
                            }
                        };

                        syncObject(S.itemMap.get(id));

                        const deepSync = (cacheMap) => {
                            if (!cacheMap) return;
                            cacheMap.forEach((data) => {
                                const list = Array.isArray(data) ? data : (data?.items || []);
                                const target = list.find(f => f.id === id);
                                if (target) syncObject(target);
                            });
                        };

                        deepSync(globalCache);
                        deepSync(S.cache);
                    });

                    if (!isStar && S.starredMode && S.path.length === 1) {
                        S.items = S.items.filter(it => !chunkSet.has(it.id));
                        S.display = S.display.filter(d => !d.isHeader && !chunkSet.has(d.id));
                        renderVisible();
                        updateStat();
                    } else {
                        renderVisible();
                    }

                    successCount += chunk.length;
                    if (total > BATCH_SIZE) await sleep(50);
                }

                starTask.destroy();
                showToast(isStar ? L.msg_star_added : L.msg_unstar_done);

            } catch (err) {
                console.error(err);
                if (starTask) starTask.destroy();

                ids.forEach(id => {
                    const revertStatus = !isStar;
                    if (revertStatus) S.starredSet.add(id); else S.starredSet.delete(id);
                    const item = S.itemMap.get(id);
                    if (item) {
                        item.starred = revertStatus;
                        if (!item.tags) item.tags = [];
                        if (revertStatus) {
                            if (!item.tags.some(t => t.name === 'STAR')) item.tags.push({name: 'STAR', type: 0});
                        } else {
                            item.tags = item.tags.filter(t => t.name !== 'STAR');
                        }
                    }
                });
                renderVisible();
                showAlert(err.message);
            }
        };
    }

    const observeUnzipTask = (taskId, folderId, fileId, skipUiRefresh = false) => {
        const checkStatus = async () => {
            try {
                const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/progress?task_id=${taskId}`, { headers: getHeaders() });
                if (!res.ok) return;
                const data = await res.json();

                if (data.phase === 'PHASE_TYPE_COMPLETE') {
                    console.log(`[Unzip] Task finished: ${taskId}`);

                    if (S) S.clearSelection();

                    let physicalParentId = folderId || 'root';
                    if (fileId && S.itemMap.has(fileId)) {
                        const it = S.itemMap.get(fileId);
                        if (!it.params) it.params = {};
                        it.params.global_file_kind = '1';
                        if (it.parent_id) physicalParentId = it.parent_id;
                        else if (it.parent_id === '') physicalParentId = 'root';
                    }

                    S.items.forEach(it => {
                        if ((it.kind === 'drive#task' || S.offlineMode || S.uploadMode) && it.file_id === fileId) {
                            if (!it.params) it.params = {};
                            it.params.global_file_kind = '1';
                        }
                    });

                    if (skipUiRefresh) return;

                    if (S && S.getSelectedCount() > 0) S.clearSelection();

                    refresh();
                    updateStat();

                    const dirtyTargets = new Set();
                    if (folderId) dirtyTargets.add(folderId); else dirtyTargets.add('root');
                    dirtyTargets.add(physicalParentId);

                    dirtyTargets.forEach(target => {
                        globalDirtyFolders.add(target);
                        if (target === 'root') globalDirtyFolders.add('');

                        if (typeof globalCache !== 'undefined') globalCache.delete(target);
                        if (typeof pkState !== 'undefined' && pkState && pkState.cache) pkState.cache.delete(target);
                        if (typeof scannedFolderIds !== 'undefined') scannedFolderIds.delete(target === 'root' ? '' : target);

                        backgroundQueue.unshift({ id: target === 'root' ? '' : target, name: 'Unzipped_Update', retryCount: 0 });
                    });

                    runBackgroundCrawler();

                    const curPathNode = S.path[S.path.length - 1];
                    const curId = curPathNode.id || 'root';

                    if (dirtyTargets.has(curId) || curId === 'virtual_search_root' || S.isFlattened || S.dupMode) {
                        if (window.pkSmartRefreshTrigger) {
                            console.log(`[Unzip] Triggering force sync for ${curId}`);
                            setTimeout(() => window.pkSmartRefreshTrigger(true), 1200);
                        }
                    } else {
                        dirtyTargets.forEach(target => {
                            apiList(target === 'root' ? '' : target, 500, null, null, false, true).then(newFiles => {
                                if (typeof globalCache !== 'undefined') globalCache.set(target, newFiles);
                            }).catch(()=>{});
                        });
                    }
                    return;
                }

                if (data.phase === 'PHASE_TYPE_RUNNING' || data.phase === 'PHASE_TYPE_PENDING') {
                    setTimeout(checkStatus, 4000);
                }
            } catch (e) {
                setTimeout(checkStatus, 8000);
            }
        };
        checkStatus();
    };

    const askForPassword = (fileName, errorMsg, isBatch = false) => {
        return new Promise((resolve) => {
            const txtCancel = isBatch ? L.btn_skip : L.btn_cancel;
            const txtConfirm = isBatch ? L.btn_ok : L.btn_view_file;

            const m = showModal(`
                <div style="display:flex; flex-direction:column; height:100%; overflow:hidden;">
                    <div style="padding: 24px 50px 0 24px; flex-shrink:0;">
                         <div style="font-size:16px; font-weight:bold; color:var(--pk-fg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; line-height:1.4;" title="${esc(fileName)}">
                            ${esc(fileName)}
                        </div>
                    </div>

                    <div style="flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; padding: 0 40px;">
                        <div style="margin-bottom:16px; color:#FFC107;">
                            ${CONF.typeIcons.archive.replace(/width="\d+"/, 'width="64"').replace(/height="\d+"/, 'height="64"')}
                        </div>

                        <div style="font-size:14px; color:var(--pk-fg); margin-bottom:8px; font-weight:500;">
                            ${L.title_input_pwd}
                        </div>

                        <div style="font-size:12px; color:#ff4d4f; height:20px; margin-bottom:10px; opacity:${errorMsg?1:0}; font-weight:bold;">
                            ${esc(errorMsg) || L.err_pwd_simple}
                        </div>

                        <div style="position:relative; width:100%;">
                            <input type="text" id="retry_pwd" placeholder="${L.lbl_pwd_prompt}"
                                   style="width:100%; height:44px; background:var(--pk-hl); border:none; border-radius:6px; padding:0 36px 0 12px; font-size:14px; color:var(--pk-fg); outline:none; box-sizing:border-box; transition:background 0.2s;"
                                   autocomplete="off" name="pk_pwd_no_fill_${Date.now()}">

                            <div id="pwd_clear_btn" style="position:absolute; right:0; top:0; bottom:0; width:36px; display:none; align-items:center; justify-content:center; cursor:pointer; color:#999; transition:color 0.2s;">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                            </div>
                        </div>
                    </div>

                    <div style="background:var(--pk-hl); padding:16px 24px; display:flex; justify-content:flex-end; gap:12px; flex-shrink:0;">
                        <button class="pk-btn" id="ask_skip" style="height:36px; padding:0 16px; font-size:14px; color:#666; background:transparent; border:none; font-weight:500;">
                            ${txtCancel}
                        </button>
                        <button class="pk-btn pri" id="ask_retry" style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; font-size:14px; border:none; box-shadow:0 2px 5px rgba(0,0,0,0.1);">
                            ${txtConfirm}
                        </button>
                    </div>
                </div>
            `);

            const modalBox = m.querySelector('.pk-modal');
            if (modalBox) {
                modalBox.style.padding = '0';
                modalBox.style.width = "420px";
                modalBox.style.height = "420px";
                modalBox.style.display = "flex";
                modalBox.style.flexDirection = "column";
                modalBox.style.overflow = "hidden";
            }

            const closeBtn = m.querySelector('.pk-modal-close');
            if(closeBtn) {
                closeBtn.style.top = "21px";
                closeBtn.style.right = "24px";
                closeBtn.style.color = "#999";
            }

            const inp = m.querySelector('#retry_pwd');
            const clearBtn = m.querySelector('#pwd_clear_btn');

            setTimeout(() => inp.focus(), 50);

            inp.addEventListener('input', () => {
                clearBtn.style.display = inp.value ? 'flex' : 'none';
            });
            clearBtn.onclick = () => {
                inp.value = '';
                clearBtn.style.display = 'none';
                inp.focus();
            };
            clearBtn.onmouseover = () => clearBtn.style.color = '#666';
            clearBtn.onmouseout = () => clearBtn.style.color = '#999';

            const doResolve = (val) => { m.remove(); resolve(val); };

            m.querySelector('#ask_skip').onclick = () => doResolve(null);
            m.querySelector('.pk-modal-close').onclick = () => doResolve(null);
            m.querySelector('#ask_retry').onclick = () => doResolve(inp.value);
            inp.onkeydown = (e) => { if(e.key === 'Enter') doResolve(inp.value); };
        });
    };

    const handleOpenArchive = async (file) => {
        if (S.trashMode) return;

        setLoad(true);
        updateLoadTxt(L.loading);

        const Vault = {
            get: () => {
                try {
                    const raw = JSON.parse(gmGet('pk_pwd_vault', '[]'));
                    return raw.map(x => typeof x === 'object' ? x.p : x);
                } catch { return []; }
            },
            save: (p) => {
                if (!p) return;
                try {
                    let list = JSON.parse(gmGet('pk_pwd_vault', '[]')).map(x => typeof x === 'object' ? x : {p: x, h: 0});
                    let item = list.find(x => x.p === p);
                    if (item) item.h = (item.h || 0) + 1; else list.push({p: p, h: 1});
                    list.sort((a, b) => b.h - a.h);
                    if (list.length > 50) list = list.slice(0, 50);
                    gmSet('pk_pwd_vault', JSON.stringify(list));
                } catch(e) {}
            }
        };

        try {
            let detail = file;
            if (!detail.gcid && !detail.hash) detail = await apiGet(file.id);
            const gcid = detail.gcid || detail.hash || detail.md5_checksum || "";

            let currentPwd = "";
            let isVerified = false;
            let hasTriedAuto = false;
            let serverBusyRetry = 0;

            while (!isVerified) {
                const payload = { gcid, file_id: file.id, password: currentPwd, path: "" };
                const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                    method: 'POST', headers: getHeaders(), body: JSON.stringify(payload)
                });

                const data = await res.json().catch(() => ({}));
                const errStr = (data.status_text || data.error_description || "").toLowerCase();

                const isPwdRequired = data.error_code === 10023 || data.status === 'PASS_WORD_ERROR' || errStr.includes('password') || errStr.includes('密码');

                if (data.status === 'OK' && res.ok) {
                    isVerified = true;
                    if (currentPwd) Vault.save(currentPwd);
                }
                else if (isPwdRequired) {

                    if (!currentPwd && !hasTriedAuto) {
                        hasTriedAuto = true;
                        const tryLimit = gmGet('pk_pwd_try_count', 10);
                        const candidates = Vault.get().slice(0, tryLimit);

                        if (candidates.length > 0) {
                            console.log(`[Archive] Parallel-Bruteforce starting: ${candidates.length} candidates`);
                            setLoad(false);
                            const matchingTask = FloatBarManager.create(L.msg_smart_matching_n.replace('{n}', candidates.length));

                            const checkTask = async (pwd, idx) => {
                                const tieredDelay = Math.floor(idx / 5) * 1200;
                                await sleep((idx * 150) + tieredDelay);
                                if (isVerified) return Promise.reject("Aborted");
                                try {
                                    const autoRes = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                                        method: 'POST', headers: getHeaders(), body: JSON.stringify({ gcid, file_id: file.id, password: pwd, path: "" })
                                    });
                                    const autoData = await autoRes.json();
                                    if (autoData.status === 'OK') return pwd;
                                } catch(e) {}
                                throw new Error("Wrong Pwd");
                            };

                            try {
                                const correctPwd = await Promise.any(candidates.map((p, i) => checkTask(p, i)));
                                if (correctPwd) {
                                    currentPwd = correctPwd;
                                    isVerified = true;
                                    Vault.save(correctPwd);
                                }
                            } catch (e) {
                                console.log("[Archive] All cached passwords failed.");
                            } finally {
                                matchingTask.destroy();
                                if (!isVerified) setLoad(true);
                            }

                            if (isVerified) break;
                        }
                    }

                    setLoad(false);
                    const promptMsg = currentPwd ? L.err_pwd_simple : "";

                    const userPwd = await askForPassword(file.name, promptMsg);
                    if (userPwd === null) { setLoad(false); return; }

                    currentPwd = userPwd;
                    setLoad(true);
                    updateLoadTxt(L.str_verifying);
                }
                else {
                    if (res.status === 500 || res.status === 502) {
                        if (serverBusyRetry < 5) {
                            serverBusyRetry++;
                            console.warn(`[Archive] Server 500 Error. Retry ${serverBusyRetry}/5...`);
                            updateLoadTxt(L.str_server_indexing.replace('{n}', serverBusyRetry));
                            await sleep(1500);
                            continue;
                        }
                    }

                    throw new Error(errStr || `API Error ${res.status}`);
                }
            }

            setLoad(false);

            const result = await showArchivePreview(file, currentPwd);
            if (result && result.confirm) {
                if (result.password) Vault.save(result.password);
                handleUnzip([file], result.password, true, result.taskId);
            }

        } catch (e) {
            setLoad(false);
            showAlert(`${L.str_error}: ${e.message}`);
        }
    };

    const handleTorrentFile = async (file) => {
        if (parseInt(file.size) > 10 * 1024 * 1024) {
            showAlert(`${L.str_error_crit}: ${L.err_invalid_links}`);
            return;
        }

        const fb = FloatBarManager.create(L.str_processing);
        try {
            const physicalId = (file.file_id || (file.params && file.params.file_id)) || file.id;

            let detail = file;
            if (!detail.web_content_link) detail = await apiGet(physicalId);

            const res = await fetch(detail.web_content_link);
            const buffer = await res.arrayBuffer();
            const buf = new Uint8Array(buffer);

            if (buf[0] !== 100) throw new Error(L.err_invalid_torrent);

            let pos = 0;
            let safetyCounter = 0;
            const MAX_ITERATIONS = 100000;

            const decodeSkip = () => {
                if (++safetyCounter > MAX_ITERATIONS) throw new Error(L.err_torrent_complex);
                if (pos >= buf.length) return;
                const c = buf[pos];
                if (c === 100 || c === 108) {
                    pos++;
                    while (pos < buf.length && buf[pos] !== 101) {
                        decodeSkip();
                        if (pos > buf.length) break;
                    }
                    pos++;
                } else if (c === 105) {
                    pos++;
                    while (pos < buf.length && buf[pos] !== 101) pos++;
                    pos++;
                } else if (c >= 48 && c <= 57) {
                    let colon = pos;
                    while (colon < buf.length && buf[colon] !== 58) colon++;
                    if (colon >= buf.length) throw new Error(L.err_torrent_format);
                    const len = parseInt(new TextDecoder().decode(buf.slice(pos, colon)));
                    if (isNaN(len)) throw new Error(L.err_torrent_len);
                    pos = colon + 1 + len;
                } else {
                    throw new Error(L.err_torrent_char);
                }
            };

            let infoHash = null;
            pos = 1;
            while (pos < buf.length && buf[pos] !== 101) {
                if (++safetyCounter > MAX_ITERATIONS) break;
                const keyStart = pos;
                decodeSkip();
                let colon = keyStart;
                while (buf[colon] !== 58 && colon < buf.length) colon++;
                const kLen = parseInt(new TextDecoder().decode(buf.slice(keyStart, colon)));
                const keyStr = new TextDecoder().decode(buf.slice(colon + 1, colon + 1 + kLen));

                if (keyStr === "info") {
                    const infoStart = pos;
                    decodeSkip();
                    const infoEnd = pos;
                    const infoBuf = buf.slice(infoStart, infoEnd);
                    const hashBuf = await crypto.subtle.digest("SHA-1", infoBuf);
                    infoHash = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('');
                    break;
                }
            }

            if (!infoHash) throw new Error("Invalid Torrent File");

            const magnet = `magnet:?xt=urn:btih:${infoHash}&dn=${encodeURIComponent(file.name)}`;
            fb.update(L.msg_creating_cloud_task);

            await apiAddOfflineTask(magnet, file.parent_id || "");

            showToast(L.msg_cloud_task_success.replace('{n}', 1));
            if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;

            if (S.offlineMode) load(false, true);

        } catch (e) {
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            fb.destroy();
        }
    };

    const sendUnzipRequest = async (file, password) => {
        let detail = file;
        if (!detail.gcid && !detail.hash) {
            try { detail = await apiGet(file.id); } catch(e) {}
        }
        const gcid = detail.gcid || detail.hash || detail.md5_checksum || "";
        const payload = { file_id: file.id, files: [], password: password || "", default_parent: true, gcid: gcid };

        const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/decompress`, {
            method: 'POST', headers: getHeaders(), body: JSON.stringify(payload)
        });

        const data = await res.json().catch(() => ({}));
        const rawMsg = data.msg || data.error || data.error_description || data.status_text || "";
        const errStr = rawMsg.toLowerCase();

        const isPwdError = data.error_code === 10023 ||
                           data.status === 'PASS_WORD_ERROR' ||
                           errStr.includes('password') ||
                           errStr.includes('密码');

        if (isPwdError) {
            throw { isError: true, isPwd: true, code: 10023, msg: L.err_pwd_simple };
        }

        if (!res.ok) {
            throw { isError: true, code: res.status, msg: rawMsg || `HTTP ${res.status}` };
        }

        if (data.status && data.status !== 'OK' && data.code !== 0) {
            throw { isError: true, code: data.code || -1, msg: rawMsg || data.status };
        }

        return data;
    };

    const showArchivePreview = async (file, initialPassword = "") => {
        let currentPath = "";
        let pathNodes = [{ name: L.picker_all, path: '' }];
        let currentPwd = initialPassword;

        let preCheckData = null;
        setLoad(true);
        updateLoadTxt(L.loading);

        try {
            let detail = file;
            if (!detail.gcid && !detail.hash) {
                try { detail = await apiGet(file.id); } catch(e) {}
            }
            const gcid = detail.gcid || detail.hash || detail.md5_checksum || "";

            let isVerified = false;
            let isFirstTry = true;

            while(!isVerified) {
                const payload = { gcid, file_id: file.id, password: currentPwd, path: "" };
                const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                    method: 'POST', headers: getHeaders(), body: JSON.stringify(payload)
                });

                const data = await res.json().catch(() => ({}));
                const errStr = (data.status_text || data.error_description || "").toLowerCase();
                const isPwdErr = data.error_code === 10023 || data.status === 'PASS_WORD_ERROR' || errStr.includes('password') || errStr.includes('密码');

                if (isPwdErr) {
                    setLoad(false);
                    const newPwd = await askForPassword(file.name, isFirstTry ? "" : L.err_pwd_simple);
                    if (newPwd !== null) {
                        currentPwd = newPwd;
                        isFirstTry = false;
                        setLoad(true);
                    } else {
                        return { confirm: false };
                    }
                } else if (!res.ok || (data.status && data.status !== 'OK')) {
                    throw new Error(data.status_text || `API Error ${res.status}`);
                } else {
                    if (currentPwd) {
                        if (gcid) gmSet('pk_archive_pwd_' + gcid, currentPwd);
                        if (typeof updateGlobalPool === 'function') updateGlobalPool(currentPwd);
                    }
                    preCheckData = data;
                    isVerified = true;
                }
            }
        } catch (e) {
            setLoad(false);
            showAlert(`${L.str_error}: ${e.message}`);
            return { confirm: false };
        } finally {
            setLoad(false);
        }

        return new Promise(async (resolve) => {
            const m = showModal(`
                <div style="display:flex; flex-direction:column; height:100%; overflow:hidden;">
                    <div style="padding: 20px 24px 0 24px; flex-shrink:0;">
                         <div style="font-size:16px; font-weight:700; color:var(--pk-fg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:20px; line-height:1.4;" data-pk-tip="${esc(file.name)}">
                            ${esc(file.name)}
                        </div>
                    </div>

                    <div id="arc_crumb" class="pk-no-scrollbar" style="display:flex; align-items:center; padding: 10px 24px; overflow-x:auto; white-space:nowrap; flex-shrink:0; font-size:14px; color:var(--pk-fg);">
                    </div>

                    <div id="arc_list_container" class="pk-scroll" style="flex:1; overflow-y:auto; overflow-x:hidden; position:relative;">
                        <div id="arc_loading" style="position:absolute; inset:0; background:var(--pk-tip-bg); backdrop-filter:blur(4px); display:none; align-items:center; justify-content:center; z-index:3;">
                            <div class="pk-spin-lg" style="width:30px; height:30px; border-width:3px;"></div>
                        </div>
                    </div>

                    <div style="background:var(--pk-hl); padding:16px 24px; display:flex; align-items:center; justify-content:space-between; flex-shrink:0;">
                        <span id="unzip_progress_text" style="font-size:13px; font-weight:bold; color:var(--pk-pri); opacity:0; transition:opacity 0.2s;"></span>

                        <div style="display:flex; gap:12px; margin-left:auto;">
                            <button class="pk-btn" id="unzip_cancel" style="height:36px; padding:0 16px; font-size:14px; color:#666; background:transparent; border:none; font-weight:500;">

                            </button>
                            <button class="pk-btn pri" id="unzip_confirm" style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); color:#fff; font-weight:600; font-size:14px; border:none; box-shadow:0 2px 5px rgba(0,0,0,0.1);">
                                ${L.btn_unzip_all}
                            </button>
                        </div>
                    </div>
                </div>
            `);

            const modalBox = m.querySelector('.pk-modal');
            modalBox.style.padding = '0';
            modalBox.style.width = "600px";
            modalBox.style.height = "500px";
            modalBox.style.maxHeight = "85vh";
            modalBox.style.display = "flex";
            modalBox.style.flexDirection = "column";
            modalBox.style.overflow = "hidden";

            const closeBtn = m.querySelector('.pk-modal-close');
            if (closeBtn) {
                closeBtn.style.top = "17px";
                closeBtn.style.right = "20px";
                closeBtn.style.color = "#999";
                closeBtn.style.zIndex = "10";
            }

            const listContainer = m.querySelector('#arc_list_container');
            const crumbContainer = m.querySelector('#arc_crumb');
            const loading = m.querySelector('#arc_loading');

            let arcCrumbIdx = 0;
            let lastArcScroll = 0;

            crumbContainer.onwheel = (e) => {
                e.preventDefault();
                const now = Date.now();
                if (now - lastArcScroll < 120) return;
                lastArcScroll = now;

                const nodes = [...crumbContainer.children].filter(c => c.textContent.trim() !== "");
                if (!nodes.length) return;

                if (e.deltaY < 0) arcCrumbIdx = Math.max(0, arcCrumbIdx - 1);
                else arcCrumbIdx = Math.min(nodes.length - 1, arcCrumbIdx + 1);

                const target = nodes[arcCrumbIdx];
                const containerWidth = crumbContainer.offsetWidth;
                const centerOffset = target.offsetLeft + (target.offsetWidth / 2) - (containerWidth / 2);

                crumbContainer.scrollTo({ left: centerOffset, behavior: 'smooth' });
            };

            const renderData = (data, path) => {
                crumbContainer.innerHTML = '';
                pathNodes.forEach((node, idx) => {
                    const isLast = idx === pathNodes.length - 1;
                    const span = document.createElement('span');
                    span.textContent = node.name;
                    span.style.cssText = isLast
                        ? "font-weight:bold; color:var(--pk-fg); cursor:default;"
                        : "color:#888; cursor:pointer; transition:color 0.2s;";

                    if (!isLast) {
                        span.onmouseover = () => span.style.color = 'var(--pk-pri)';
                        span.onmouseout = () => span.style.color = '#888';
                        span.onclick = () => {
                            pathNodes = pathNodes.slice(0, idx + 1);
                            updateView(node.path);
                        };
                    }
                    crumbContainer.appendChild(span);

                    if (!isLast) {
                        const sep = document.createElement('span');
                        sep.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:block; opacity:0.5;"><polyline points="9 18 15 12 9 6"></polyline></svg>`;
                        sep.style.margin = "0 6px";
                        crumbContainer.appendChild(sep);
                    }
                });

                arcCrumbIdx = pathNodes.length - 1;
                requestAnimationFrame(() => {
                    crumbContainer.scrollTo({ left: crumbContainer.scrollWidth, behavior: 'smooth' });
                });

                listContainer.innerHTML = '';
                listContainer.appendChild(loading);

                const items = data.files || data.list || [];
                if (items.length === 0) {
                    const emptyMsg = document.createElement('div');
                    emptyMsg.style.cssText = "padding:50px; text-align:center; color:#999; font-size:13px;";
                    emptyMsg.textContent = L.str_empty_dir;
                    listContainer.appendChild(emptyMsg);
                } else {
                    items.forEach(f => {
                        const itemName = f.filename || f.file_name || f.name || L.str_unknown_name;
                        const itemSize = f.filesize || f.size || 0;
                        const isDir = f.kind === 'drive#folder' || (itemSize == 0 && !f.mime_type);

                        const fullPath = (path || "") + itemName + (isDir ? "/" : "");

                        const row = document.createElement('div');
                        row.style.cssText = "display:flex; align-items:center; height:48px; padding:0 24px; cursor:default; transition:background 0.1s; border-bottom:1px solid rgba(0,0,0,0.03);";

                        if (isDir) {
                            row.style.cursor = "pointer";
                            row.onmouseover = () => row.style.background = 'var(--pk-hl)';
                            row.onmouseout = () => row.style.background = 'transparent';
                            row.onclick = (e) => {
                                e.stopPropagation();
                                pathNodes.push({ name: itemName, path: fullPath });
                                updateView(fullPath);
                            };
                        } else {
                            row.onmouseover = () => row.style.background = 'var(--pk-hl)';
                            row.onmouseout = () => row.style.background = 'transparent';
                        }

                        const iconSrc = f.icon_link || '';
                        const fallbackSvg = (isDir ? CONF.typeIcons.folder : getIcon({ name: itemName, mime_type: f.mime_type }))
                            .replace(/width="\d+"/, 'width="28"').replace(/height="\d+"/, 'height="28"');

                        const iconHtml = iconSrc
                            ? `<img src="${iconSrc}" style="width:28px;height:28px;object-fit:contain;flex-shrink:0;" onerror="this.style.display='none';if(this.nextElementSibling)this.nextElementSibling.style.display='inline-flex';"><span style="display:none;align-items:center;flex-shrink:0;">${fallbackSvg}</span>`
                            : fallbackSvg;

                        row.innerHTML = `
                            <div style="margin-right:12px; display:flex; align-items:center; justify-content:center; width:28px;">${iconHtml}</div>
                            <div style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:14px; color:var(--pk-fg); line-height:1.5;">${esc(itemName)}</div>
                            <div style="color:var(--pk-fg); font-size:14px; line-height:1.5; margin-left:15px; font-weight:normal;">${isDir ? '-' : fmtSize(itemSize)}</div>
                        `;
                        listContainer.appendChild(row);
                    });
                }
            };

            const updateView = async (path) => {
                loading.style.display = 'flex';
                try {
                    let detail = file;
                    if (!detail.gcid && !detail.hash) detail = await apiGet(file.id);
                    const gcid = detail.gcid || detail.hash || detail.md5_checksum || "";
                    const payload = { gcid, file_id: file.id, password: currentPwd, path: path };

                    const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                        method: 'POST', headers: getHeaders(), body: JSON.stringify(payload)
                    });
                    const data = await res.json().catch(() => ({}));
                    if (res.ok && data.status === 'OK') renderData(data, path);
                    else throw new Error(data.status_text || L.str_load_failed_simple);
                } catch (e) {
                    const errDiv = document.createElement('div');
                    errDiv.style.cssText = "padding:50px; text-align:center; color:#d93025; font-size:13px;";
                    errDiv.textContent = esc(e.message);
                    listContainer.innerHTML = '';
                    listContainer.appendChild(loading);
                    listContainer.appendChild(errDiv);
                } finally {
                    loading.style.display = 'none';
                }
            };

            m.querySelector('.pk-modal-close').onclick = () => { m.remove(); resolve({ confirm: false }); };
            m.querySelector('#unzip_cancel').onclick = () => { m.remove(); resolve({ confirm: false }); };

            m.tabIndex = 0;
            setTimeout(() => m.focus(), 10);
            m.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.preventDefault(); e.stopPropagation();
                    m.querySelector('#unzip_confirm').click();
                }
            });

            m.querySelector('#unzip_confirm').onclick = async () => {
                const cur = S.path[S.path.length - 1];
                const isStarredRoot = S.starredMode && S.path.length === 1;
                const isRecentRoot = S.recentMode && S.path.length === 1;
                const isVirtual = S.isFlattened || S.dupMode || isStarredRoot || isRecentRoot || cur.id === 'analyze_root' || cur.id === 'virtual_search_root';

                if (isVirtual) {
                    const userConfirmed = await new Promise(res => {
                        const vm = showModal(`
                            <h3>${L.title_confirm}</h3>
                            <div style="margin:20px 0;line-height:1.5;">${L.msg_unzip_virtual_view_warn}</div>
                            <div class="pk-modal-act">
                                <button class="pk-btn" id="vc_cancel">${L.btn_cancel}</button>
                                <button class="pk-btn pri" id="vc_ok">${L.btn_understand_unzip}</button>
                            </div>
                        `);
                        vm.querySelector('#vc_cancel').onclick = () => { vm.remove(); res(false); };
                        vm.querySelector('.pk-modal-close').onclick = () => { vm.remove(); res(false); };
                        vm.querySelector('#vc_ok').onclick = () => { vm.remove(); res(true); };

                        vm.tabIndex = 0;
                        setTimeout(() => vm.focus(), 10);
                        vm.addEventListener('keydown', (e) => {
                            if (e.key === 'Enter') {
                                e.preventDefault(); e.stopPropagation();
                                vm.querySelector('#vc_ok').click();
                            }
                        });
                    });
                    if (!userConfirmed) return;
                }

                const btn = m.querySelector('#unzip_confirm');
                const progTxt = m.querySelector('#unzip_progress_text');

                btn.disabled = true;
                btn.style.opacity = '0.6';
                btn.style.cursor = 'not-allowed';
                btn.textContent = L.str_unzipping_state;

                progTxt.textContent = L.str_unzipping_prog_0;
                progTxt.style.opacity = '1';

                try {
                    const resp = await sendUnzipRequest(file, currentPwd);

                    if (resp && resp.task_id) {
                        const taskId = resp.task_id;
                        let isPolling = true;
                        let progressTask = null;

                        const currentFolderId = S.path[S.path.length - 1].id || '';
                        observeUnzipTask(taskId, currentFolderId, file.id);

                        const pollProgress = async () => {
                            if (!isPolling) return;

                            if (!document.contains(m) && !progressTask) {
                                progressTask = FloatBarManager.create(L.str_unzipping.replace('{n}', file.name));
                            }

                            try {
                                const tRes = await fetch(`https://api-drive.mypikpak.com/decompress/v1/progress?task_id=${taskId}`, { headers: getHeaders() });
                                if (!tRes.ok) { finishUnzip(); return; }
                                const tData = await tRes.json();

                                if (tData.phase === 'PHASE_TYPE_COMPLETE') {
                                    finishUnzip();
                                    return;
                                }

                                if (tData.progress !== undefined) {
                                    const text = L.str_unzipping_prog_fmt.replace('{n}', tData.progress);
                                    if (progressTask) progressTask.update(`${L.str_unzipping_state} ${tData.progress}%`);
                                    else progTxt.textContent = text;

                                    if (tData.progress >= 100) { finishUnzip(); return; }
                                }
                            } catch(e) {
                                if (progressTask) progressTask.destroy();
                                isPolling = false;
                                resolve({ confirm: true, password: currentPwd, taskId: taskId, alreadyStarted: true });
                                return;
                            }

                            if (isPolling) setTimeout(pollProgress, 800);
                        };

                        const finishUnzip = async () => {
                            isPolling = false;
                            if (progressTask) progressTask.destroy();
                            if (document.contains(m)) {
                                progTxt.textContent = L.str_unzipping_prog_100;
                                await sleep(500);
                                m.remove();
                            }
                        };

                        resolve({ confirm: true, password: currentPwd, taskId: taskId, alreadyStarted: true, alreadyObserved: true });
                        pollProgress();

                    } else {
                        m.remove();
                        resolve({ confirm: true, password: currentPwd });
                    }
                } catch (e) {
                    btn.disabled = false;
                    btn.style.opacity = '1';
                    btn.style.cursor = 'pointer';
                    btn.textContent = L.btn_unzip_all;
                    progTxt.style.color = '#d93025';
                    progTxt.textContent = e.msg || e.message || L.str_action_failed;
                    console.error(e);
                }
            };

            renderData(preCheckData, "");
        });
    };

    const handleUnzip = async (items, verifiedPwd = "", skipPreview = false, externalTaskId = null) => {
        if (!items || items.length === 0) return;
        const currentFolderId = S.path[S.path.length - 1].id || '';

        const Vault = {
            get: () => {
                try {
                    const raw = JSON.parse(gmGet('pk_pwd_vault', '[]'));
                    return raw.map(x => typeof x === 'object' ? x.p : x);
                } catch { return []; }
            },
            save: (p) => {
                if (!p) return;
                try {
                    let list = JSON.parse(gmGet('pk_pwd_vault', '[]')).map(x => typeof x === 'object' ? x : {p: x, h: 0});
                    let item = list.find(x => x.p === p);
                    if (item) item.h = (item.h || 0) + 1; else list.push({p: p, h: 1});
                    list.sort((a, b) => b.h - a.h);
                    if (list.length > 50) list = list.slice(0, 50);
                    gmSet('pk_pwd_vault', JSON.stringify(list));
                } catch(e) {}
            }
        };

        const curPathNode = S.path[S.path.length - 1];
        const isStarredRoot = S.starredMode && S.path.length === 1;
        const isRecentRoot = S.recentMode && S.path.length === 1;
        const isVirtual = S.isFlattened || S.dupMode || isStarredRoot || isRecentRoot || curPathNode.id === 'analyze_root' || curPathNode.id === 'virtual_search_root';

        let startFromPreviewTaskId = externalTaskId;
        let previewResult = null;

        if (!skipPreview && !verifiedPwd) {
            if (items.length === 1) {
                previewResult = await showArchivePreview(items[0], "");
                if (!previewResult || !previewResult.confirm) return;

                verifiedPwd = previewResult.password;
                if (previewResult.alreadyStarted && previewResult.taskId) {
                    startFromPreviewTaskId = previewResult.taskId;
                }
            } else {
                if (isVirtual) {
                    const userConfirmed = await new Promise(res => {
                        const vm = showModal(`
                            <h3>${L.title_confirm}</h3>
                            <div style="margin:20px 0;line-height:1.5;">${L.msg_unzip_virtual_view_warn}</div>
                            <div class="pk-modal-act">
                                <button class="pk-btn" id="vc_cancel">${L.btn_cancel}</button>
                                <button class="pk-btn pri" id="vc_ok">${L.btn_understand_unzip}</button>
                            </div>
                        `);
                        vm.querySelector('#vc_cancel').onclick = () => { vm.remove(); res(false); };
                        vm.querySelector('.pk-modal-close').onclick = () => { vm.remove(); res(false); };
                        vm.querySelector('#vc_ok').onclick = () => { vm.remove(); res(true); };
                    });
                    if (!userConfirmed) return;
                } else {
                    if (!await showConfirm(L.msg_unzip_confirm_n.replace('{n}', items.length))) return;
                }
            }
        }

        if (S.sel.size > 0) {
            S.clearSelection();
            refresh();
        }

        const isSilentMode = (items.length === 1 && !!startFromPreviewTaskId);
        let progressTask = null;

        if (!isSilentMode) {
            progressTask = FloatBarManager.create(L.str_preparing);
        }

        let successCount = 0;
        let failCount = 0;
        let lastBatchPwd = "";

        const MAX_CONCURRENCY = 3;
        const activePromises = [];
        let promptMutex = Promise.resolve();

        const waitForTaskDone = async (taskId) => {
            for (let i = 0; i < 300; i++) {
                try {
                    const res = await fetch(`https://api-drive.mypikpak.com/decompress/v1/progress?task_id=${taskId}`, { headers: getHeaders() });
                    if (res.ok) {
                        const d = await res.json();
                        if (d.phase === 'PHASE_TYPE_COMPLETE' || d.phase === 'PHASE_TYPE_ERROR') return;
                    } else if (res.status === 404) {
                        return;
                    }
                } catch(e) {}
                await sleep(2000);
            }
        };

        const processSingleItem = async (file, index) => {
            try {
                let detail = file;
                if (!detail.gcid && !detail.hash) {
                    for(let w = 0; w < 5; w++) {
                         try {
                             detail = await Promise.race([
                                 apiGet(file.id),
                                 new Promise((_, r) => setTimeout(() => r(new Error("Timeout")), 2000))
                             ]);
                         } catch(e) {}
                         if (detail.gcid || detail.hash || detail.md5_checksum) break;
                         await sleep(1000);
                    }
                }
                const gcid = detail.gcid || detail.hash || detail.md5_checksum || "";

                let isDone = false;
                let triedVerified = false;
                let triedBatch = false;
                let triedEmpty = false;
                let hasTriedVault = false;
                let isFirstManualInput = true;
                let systemRetry = 0;

                while (!isDone) {
                    let pwdToTry = "";
                    let currentSource = "";

                    if (verifiedPwd && !triedVerified) {
                        pwdToTry = verifiedPwd; currentSource = "VERIFIED";
                    } else if (lastBatchPwd && !triedBatch) {
                        pwdToTry = lastBatchPwd; currentSource = "BATCH";
                    } else if (!triedEmpty) {
                        pwdToTry = ""; currentSource = "EMPTY";
                    } else if (!hasTriedVault) {
                        hasTriedVault = true;
                        const tryLimit = gmGet('pk_pwd_try_count', 10);
                        const candidates = Vault.get().slice(0, tryLimit);

                        if (candidates.length > 0) {
                            if (progressTask) progressTask.update(L.msg_smart_matching_file.replace('{n}', file.name));

                            const checkTask = async (pwd, idx) => {
                                const tieredDelay = Math.floor(idx / 5) * 1200;
                                await sleep((idx * 150) + tieredDelay);
                                try {
                                    const autoRes = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                                        method: 'POST',
                                        headers: getHeaders(),
                                        body: JSON.stringify({ gcid, file_id: file.id, password: pwd, path: "" }),
                                        signal: AbortSignal.timeout(5000)
                                    });
                                    const autoData = await autoRes.json();
                                    if (autoData.status === 'OK') return pwd;
                                } catch(e) {}
                                throw new Error("Wrong");
                            };

                            try {
                                const correctPwd = await Promise.any(candidates.map((p, i) => checkTask(p, i)));
                                if (correctPwd) {
                                    lastBatchPwd = correctPwd;
                                    Vault.save(correctPwd);
                                    triedBatch = false;
                                    continue;
                                }
                            } catch (e) {}
                        }
                        continue;
                    } else {
                        currentSource = "MANUAL";
                    }

                    if (currentSource === "MANUAL") {
                        const pwdBeforeWait = lastBatchPwd;
                        const previousMutex = promptMutex;
                        let releaseMutex;
                        promptMutex = new Promise(r => releaseMutex = r);
                        await previousMutex;

                        try {
                            if (lastBatchPwd !== pwdBeforeWait && lastBatchPwd !== "") {
                                continue;
                            }

                            setLoad(false);
                            const errorMsg = isFirstManualInput ? "" : L.err_pwd_simple;
                            const userPwd = await askForPassword(file.name, errorMsg, true);
                            isFirstManualInput = false;
                            setLoad(true);

                            if (userPwd !== null) {
                                lastBatchPwd = userPwd;
                                Vault.save(userPwd);
                                verifiedPwd = userPwd;
                                triedVerified = false;
                                continue;
                            } else {
                                failCount++; isDone = true; continue;
                            }
                        } finally {
                            releaseMutex();
                        }
                    }

                    try {
                        if (progressTask) {
                            const doneCount = successCount + failCount;
                            progressTask.update(`${L.str_unzipping.replace('{n}', file.name)} (${doneCount + 1} / ${items.length})`);
                        }

                        let taskId = null;
                        let needsObserve = true;

                        if (index === 0 && startFromPreviewTaskId) {
                            taskId = startFromPreviewTaskId;
                            startFromPreviewTaskId = null;
                            if (items.length === 1 && previewResult && previewResult.alreadyObserved) {
                                needsObserve = false;
                            }
                        } else {
                            const preRes = await fetch(`https://api-drive.mypikpak.com/decompress/v1/list`, {
                                method: 'POST',
                                headers: getHeaders(),
                                body: JSON.stringify({ gcid, file_id: file.id, password: pwdToTry, path: "" }),
                                signal: AbortSignal.timeout(8000)
                            });
                            const preData = await preRes.json().catch(() => ({}));
                            const preErrStr = (preData.status_text || preData.error_description || "").toLowerCase();

                            if (preData.error_code === 10023 || preData.status === 'PASS_WORD_ERROR' || preErrStr.includes('password') || preErrStr.includes('密码')) {
                                throw { isPwd: true, code: 10023, msg: L.err_pwd_simple };
                            }

                            const resp = await sendUnzipRequest(file, pwdToTry);
                            taskId = resp?.task_id;
                        }

                        if (taskId) {
                            if (needsObserve) observeUnzipTask(taskId, currentFolderId, file.id, true);
                            await waitForTaskDone(taskId);
                        }

                        if (pwdToTry) {
                            lastBatchPwd = pwdToTry;
                            Vault.save(pwdToTry);
                        }
                        successCount++;
                        isDone = true;

                        if (!isSilentMode) {
                            const doneCount = successCount + failCount;
                            updateLoadTxt(`${L.str_unzipping.replace('{n}', file.name)}\n(${doneCount} / ${items.length})`);
                        }

                    } catch (err) {
                        const errText = (err.msg || "").toLowerCase();

                        const isDefinitePwdErr = err.isPwd || err.code === 10023 || err.status === 'PASS_WORD_ERROR' || errText.includes('password') || errText.includes('密码');
                        const isAlreadyRunning = errText.includes('正在解压') || errText.includes('decompressing');
                        const isSystemErr = !isDefinitePwdErr && !isAlreadyRunning && (err.name === 'TimeoutError' || err.code === 400 || err.code === 429 || err.code >= 500 || errText.includes('limit') || errText.includes('busy') || errText.includes('task creation failed'));

                        if (isAlreadyRunning) {
                            console.warn(`[Unzip] Server Lock detected for: ${file.name}. Task is running in background or zombie.`);
                            if (!isSilentMode) {
                                showToast(L.msg_unzip_running_bg.replace('{n}', esc(file.name)), 'warning', 6000);
                            }
                            successCount++;
                            isDone = true;
                        }
                        else if (isSystemErr) {
                            systemRetry++;
                            if (systemRetry < 6) {
                                const delay = 1500 * systemRetry + (Math.random() * 500);
                                updateLoadTxt(L.msg_system_busy_retry.replace('{n}', systemRetry));
                                await sleep(delay);
                                continue;
                            }
                            failCount++; isDone = true;
                            if (progressTask) progressTask.update(`${L.str_unzipping.replace('{n}', file.name)} (${successCount + failCount} / ${items.length})`);
                        }
                        else if (isDefinitePwdErr) {
                            if (currentSource === "VERIFIED") { triedVerified = true; verifiedPwd = ""; }
                            else if (currentSource === "BATCH") { triedBatch = true; }
                            else if (currentSource === "EMPTY") { triedEmpty = true; }
                        } else {
                            console.error(err);
                            failCount++; isDone = true;
                            if (progressTask) progressTask.update(`${L.str_unzipping.replace('{n}', file.name)} (${successCount + failCount} / ${items.length})`);
                        }
                    }
                }
            } catch (fatal) {
                console.error("Fatal Worker Error:", fatal);
                failCount++;
            }
        };

        for (let i = 0; i < items.length; i++) {
            while (activePromises.length >= MAX_CONCURRENCY) {
                await Promise.race(activePromises);
            }
            const p = processSingleItem(items[i], i);
            const wrappedP = p.then(() => {
                const idx = activePromises.indexOf(wrappedP);
                if (idx > -1) activePromises.splice(idx, 1);
            });
            activePromises.push(wrappedP);
        }

        await Promise.all(activePromises);

        if (successCount > 0 && !isSilentMode) {
            S.clearSelection();
            refresh();
            updateStat();
            if (window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger(true);
        }

        if (progressTask) progressTask.destroy();

        if (!isSilentMode) {
            setLoad(false);
            if (successCount > 0) {
                let msg = L.msg_unzip_batch_submitted.replace('{n}', successCount);
                if (failCount > 0) msg += " " + L.msg_unzip_batch_skipped.replace('{n}', failCount);
                if (isVirtual) msg += L.msg_unzip_check_source;
                showToast(msg);
            }
            else if (failCount > 0) {
                showToast(L.msg_unzip_fail, 'error');
            }
        }
    };

    const renderCalendar = (anchorEl, onSelect) => {
        const old = document.querySelector('.pk-cal-pop');
        if (old) old.remove();

        const pop = document.createElement('div');
        pop.className = 'pk-cal-pop';

        if (document.querySelector('.pk-ov')?.classList.contains('pk-dark')) {
            pop.classList.add('pk-dark');
        }

        pop.style.cssText = "opacity: 0; animation: none; visibility: hidden; transition: opacity 0.15s ease; z-index: 2147483647 !important; zoom: var(--pk-zoom, 1);";

        const now = new Date(getServerNow());
        const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();

        let viewYear = now.getFullYear();
        let viewMonth = now.getMonth();

        const sideOpts = [
            { lbl: L.share_perm, val: -1 },
            { lbl: `7 ${L.share_days}`, val: 7 },
            { lbl: `14 ${L.share_days}`, val: 14 },
            { lbl: `30 ${L.share_days}`, val: 30 }
        ];

        const buildHTML = () => {
            const navLeft = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`;
            const navRight = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`;

            let sideHtml = sideOpts.map(o =>
                                        `<div class="pk-cal-side-item" data-val="${o.val}">${o.lbl}</div>`
                                       ).join('');

            const monthStr = (L.cal_months || [])[viewMonth] || (viewMonth + 1 + L.unit_month);
            const headerHtml = `
                    <div class="pk-cal-nav-btn" id="cal_prev_y" title="-1Y">${navLeft}</div>
                    <div class="pk-cal-nav-btn" id="cal_prev_m" title="-1M" style="margin-right:auto;">${navLeft}</div>
                    <div style="font-weight:bold; font-size:14px;">${viewYear} ${monthStr}</div>
                    <div class="pk-cal-nav-btn" id="cal_next_m" title="+1M" style="margin-left:auto;">${navRight}</div>
                    <div class="pk-cal-nav-btn" id="cal_next_y" title="+1Y">${navRight}</div>
                `;

            const weeksHtml = (L.cal_week_days || ["S","M","T","W","T","F","S"]).map(w =>
                                                                                     `<div class="pk-cal-th">${w}</div>`
                                                                                    ).join('');

            const firstDay = new Date(viewYear, viewMonth, 1).getDay();
            const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();

            let daysHtml = '';
            for(let i=0; i<firstDay; i++) daysHtml += `<div></div>`;
            for(let d=1; d<=daysInMonth; d++) {
                const currentTs = new Date(viewYear, viewMonth, d).getTime();
                const isToday = currentTs === todayStart;
                const isPast = currentTs < todayStart;
                const isDisabled = isPast;

                let cls = 'pk-cal-td';
                if (isDisabled) cls += ' disabled';
                if (isToday) cls += ' today';

                daysHtml += `<div class="${cls}" data-ts="${currentTs}">${d}</div>`;
            }

            return `
                    <div class="pk-cal-side">${sideHtml}</div>
                    <div class="pk-cal-main">
                        <div style="font-weight:bold; margin-bottom:10px; font-size:14px;">${L.cal_custom_title}</div>
                        <div class="pk-cal-hd">${headerHtml}</div>
                        <div class="pk-cal-grid">
                            ${weeksHtml}
                            ${daysHtml}
                        </div>
                    </div>
                `;
        };

        const refresh = () => {
            pop.innerHTML = buildHTML();

            pop.querySelectorAll('.pk-cal-side-item').forEach(el => {
                el.onclick = (e) => {
                    e.stopPropagation();
                    const days = parseInt(el.dataset.val);
                    onSelect({ days: days, ts: null });
                    pop.remove();
                    document.removeEventListener('mousedown', onClickOutside);
                };
            });

            pop.querySelectorAll('.pk-cal-td:not(.disabled)').forEach(el => {
                el.onclick = (e) => {
                    e.stopPropagation();
                    const ts = parseInt(el.dataset.ts);
                    const diff = (ts - todayStart) / (1000 * 3600 * 24);
                    const days = Math.max(1, Math.ceil(diff));

                    onSelect({ days: days, ts: ts });
                    pop.remove();
                    document.removeEventListener('mousedown', onClickOutside);
                };
            });

            pop.querySelector('#cal_prev_y').onclick = (e) => { e.stopPropagation(); viewYear--; refresh(); };
            pop.querySelector('#cal_next_y').onclick = (e) => { e.stopPropagation(); viewYear++; refresh(); };
            pop.querySelector('#cal_prev_m').onclick = (e) => { e.stopPropagation(); viewMonth--; if(viewMonth<0){viewMonth=11;viewYear--;} refresh(); };
            pop.querySelector('#cal_next_m').onclick = (e) => { e.stopPropagation(); viewMonth++; if(viewMonth>11){viewMonth=0;viewYear++;} refresh(); };
        };

        refresh();
        document.body.appendChild(pop);

        const updatePosition = () => {
            if (!pop.isConnected) return;

            if (anchorEl.offsetParent === null) {
                cleanup();
                return;
            }

            const scale = parseFloat(document.documentElement.style.getPropertyValue('--pk-zoom')) || 1;
            const rect = getLogicalRect(anchorEl);
            let top = rect.bottom + 5;
            let left = rect.left;

            if (top + 320 > window.innerHeight / scale) top = rect.top - 320 - 5;
            if (left + 400 > window.innerWidth / scale) left = (window.innerWidth / scale) - 400 - 10;
            if (left < 10) left = 10;

            pop.style.top = top + 'px';
            pop.style.left = left + 'px';
        };

        updatePosition();

        requestAnimationFrame(() => {
            pop.style.visibility = 'visible';
            void pop.offsetHeight;
            pop.style.opacity = '1';
        });

        window.addEventListener('resize', updatePosition);
        window.addEventListener('scroll', updatePosition, true);

        const onClickOutside = (e) => {
            if (!pop || !anchorEl || !pop.parentNode) return;
            if (!pop.contains(e.target) && !anchorEl.contains(e.target)) {
                cleanup();
            }
        };

        const cleanup = () => {
            window.removeEventListener('resize', updatePosition);
            window.removeEventListener('scroll', updatePosition, true);
            document.removeEventListener('mousedown', onClickOutside);
            pop.remove();
        };

        setTimeout(() => document.addEventListener('mousedown', onClickOutside), 10);
    };

    const showShareDetail = (item) => {
        const checkIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#52c41a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="animation: pkPlayPop 0.3s ease-out;"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
        const copyIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="cursor:pointer;color:#888;"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;

        let statusColor = '#52c41a';
        let statusText = L.share_perm;

        if (item.share_status === 'EXPIRED') {
            statusColor = '#faad14'; statusText = L.str_share_expired;
        } else if (item.share_status === 'DELETED') {
            statusColor = '#ff4d4f'; statusText = L.str_share_deleted;
        } else if (item.expiration_at && item.expiration_at !== "-1") {
            statusColor = 'inherit';
            const d = new Date(item.expiration_at);
            const pad = n => String(n).padStart(2, '0');
            statusText = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
        } else if (item.share_status_text) {
            statusText = item.share_status_text;
        }

        const labelStyle = "font-size:14px; font-weight:bold; color:var(--pk-fg); margin-bottom:8px;";
        const inputWrapStyle = "display:flex; align-items:center; border:1px solid var(--pk-bd); border-radius:4px; padding:0 12px; background:var(--pk-bg); height:40px; transition:border-color 0.2s;";
        const inputStyle = "flex:1; border:none; background:transparent; outline:none; font-size:14px; color:var(--pk-fg);";
        const copyBtnStyle = "margin-left:10px; display:flex; align-items:center; justify-content:center; transition:color 0.2s;";

        const m = showModal(`
            <div class="pk-share-modal-root" style="width:420px; max-width:90vw; display:flex; flex-direction:column; overflow:hidden;">
                <div style="display: flex; align-items: center; padding: 16px 50px 16px 24px; flex-shrink:0; border-bottom: 1px solid transparent;">
                    <h3 style="margin: 0; font-size: 18px; font-weight: 700; border: none; padding: 0; line-height: 1.2; color: var(--pk-fg);">${L.title_share_detail}</h3>
                </div>

                <div class="pk-scroll" style="flex:1; overflow-y:auto; overflow-x:hidden; padding: 0 24px;">
                    <div style="margin-bottom:16px; text-align:center; padding: 0 10px;">
                        <div style="font-size:15px; font-weight:700; color:var(--pk-fg); word-break:break-all; line-height:1.4; opacity:0.9;">${esc(item.name || item.title)}</div>
                    </div>

                    <div class="pk-share-stat-box">
                        <div class="pk-share-stat-item">
                            <div class="pk-share-stat-val">${item.view_count || 0}</div>
                            <div class="pk-share-stat-lbl">${L.lbl_share_view}</div>
                        </div>
                        <div class="pk-share-stat-item">
                            <div class="pk-share-stat-val">${item.save_count || 0}</div>
                            <div class="pk-share-stat-lbl">${L.lbl_share_save}</div>
                        </div>
                    </div>

                <div style="margin-bottom:20px;">
                    <div style="${labelStyle}">${L.lbl_share_link_title}</div>
                    <div class="pk-share-input-wrap" style="${inputWrapStyle}">
                        <a href="${item.share_url}" target="_blank" style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-decoration:underline; color:var(--pk-fg); font-size:14px;">${item.share_url}</a>
                        <div class="pk-copy-btn" data-val="${item.share_url}" style="${copyBtnStyle}" data-pk-tip="${L.btn_copy}">${copyIcon}</div>
                    </div>
                </div>

                <div style="margin-bottom:20px;">
                    <div style="${labelStyle}">${L.lbl_share_pwd_title}</div>
                    <div class="pk-share-input-wrap" style="${inputWrapStyle}">
                        <input type="text" id="pk_share_pwd_edit" value="${item.pass_code || ''}" style="${inputStyle}" placeholder="${L.str_no_pwd}">
                        <div class="pk-copy-btn" id="pk_copy_pwd" data-val="${item.pass_code || ''}"
                             style="${copyBtnStyle}; display: ${item.pass_code ? 'flex' : 'none'};"
                             data-pk-tip="${L.btn_copy}">${copyIcon}</div>
                    </div>
                </div>

                <div style="margin-bottom:20px;">
                    <div style="${labelStyle}">${L.share_count_ed}</div>
                    <div class="pk-share-input-wrap" id="pk_share_limit_edit" style="${inputWrapStyle}; cursor:pointer;">
                        <input type="text" id="pk_share_limit_val" readonly
                               style="${inputStyle}; cursor:pointer; color:var(--pk-fg);"
                               value="${item.limit_count > 0 ? (item.limit_count + ' ' + L.share_times) : L.share_unlimit}">
                        <div style="${copyBtnStyle}">${CONF.crumbIcons.right}</div>
                    </div>
                </div>

                <div style="margin-bottom:20px;">
                    <div style="${labelStyle}">${L.lbl_share_expire_title}</div>
                    <div class="pk-share-input-wrap" id="pk_share_exp_edit" style="${inputWrapStyle}; cursor:pointer;">
                        <input type="text" id="pk_share_exp_val" readonly
                               style="${inputStyle}; cursor:pointer; color:var(--pk-fg);"
                               value="${statusText}">
                        <div style="${copyBtnStyle}">${CONF.crumbIcons.right}</div>
                    </div>
                </div>

                <div style="margin-bottom:30px;">
                    <div style="${labelStyle}">${L.lbl_share_code_title}</div>
                    <div id="pk_phrase_list_container" style="border:1px solid var(--pk-bd); border-radius:6px; background:var(--pk-bg); overflow:hidden;">
                        <div id="pk_phrase_rows"></div>
                        <div id="pk_btn_add_phrase" style="display:flex; align-items:center; gap:8px; color:var(--pk-pri); font-size:14px; height:44px; padding:0 12px; cursor:pointer; background:var(--pk-bg); transition:background 0.2s;" onmouseover="this.style.background='var(--pk-hl)'" onmouseout="this.style.background='var(--pk-bg)'">
                            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></svg>
                            <span>${L.btn_add_share_code}</span>
                        </div>
                    </div>
                </div>

                <div class="pk-share-footer" style="flex-shrink:0; margin-top:0; padding: 20px 24px 24px 24px; display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                    <button id="pk_detail_cancel" class="pk-btn-quiet-red" style="width: 100%; height: 40px; display: flex; align-items: center; justify-content: center; margin: 0; border: none; background: rgba(217, 48, 37, 0.08); font-size: 14px; font-weight: 600; border-radius: 8px; cursor: pointer; transition: background 0.2s;">
                        ${L.btn_cancel_share}
                    </button>
                    <button id="pk_detail_copy_all" class="pk-btn-primary-action" style="width: 100%; height: 40px; display: flex; align-items: center; justify-content: center; margin: 0; border-radius: 8px;">
                        ${item.pass_code ? L.btn_copy_link_pwd : L.btn_copy_link}
                    </button>
                </div>
            </div>
        `);

        const modalContainer = m.querySelector('.pk-modal');
        if (modalContainer) {
            modalContainer.style.padding = '0';
            modalContainer.style.width = 'fit-content';
            modalContainer.style.display = 'flex';
            modalContainer.style.flexDirection = 'column';
            modalContainer.style.overflow = 'hidden';
            modalContainer.style.maxHeight = '600px';
        }

        m.querySelectorAll('.pk-copy-btn').forEach(btn => {
            btn.onclick = (e) => {
                const val = btn.getAttribute('data-val');
                if(!val) return;
                GM_setClipboard(val);
                const originalSvg = btn.innerHTML;
                btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#52c41a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
                setTimeout(() => btn.innerHTML = originalSvg, 1500);
            };
        });

        const pwdField = m.querySelector('#pk_share_pwd_edit');
        const pwdCopyBtn = m.querySelector('#pk_copy_pwd');

        pwdField.readOnly = true;
        pwdField.style.cursor = 'pointer';

        pwdField.onclick = () => {
            const subM = document.createElement('div');
            subM.className = 'pk-modal-ov';
            subM.style.zIndex = (++modalZIndexCounter).toString();
            if (document.querySelector('.pk-ov').classList.contains('pk-dark')) subM.classList.add('pk-dark');
            subM.innerHTML = `
                <div class="pk-modal" style="width:340px; padding:24px; border-radius:12px; box-shadow: 0 8px 30px rgba(0,0,0,0.3);">
                    <div class="pk-modal-close">${CONF.icons.close}</div>
                    <h3 style="border:none; margin:0 0 16px 0; font-size:16px; font-weight:700; color:var(--pk-fg);">${L.title_edit_pwd}</h3>
                    <div class="pk-field">
                        <input type="text" id="pk_new_pwd_input" placeholder="${L.ph_edit_pwd}"
                               style="height:44px; font-size:14px; border:1px solid var(--pk-pri); border-radius:6px; background:var(--pk-bg); color:var(--pk-fg); padding:0 12px; outline:none;"
                               value="${item.pass_code || ''}" autocomplete="off">
                    </div>
                    <div id="pk_close_pwd_row" style="display:${item.pass_code ? 'flex' : 'none'}; align-items:center; gap:8px; margin-top:12px; cursor:pointer; color:var(--pk-pri); font-size:14px; width:fit-content; transition:opacity 0.2s;" onmouseover="this.style.opacity=0.8" onmouseout="this.style.opacity=1">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
                        <span>${L.btn_close_pwd}</span>
                    </div>
                    <div style="display:flex; justify-content:flex-end; align-items:center; margin-top:24px; gap:20px;">
                        <span id="pk_edit_pwd_cancel" style="cursor:pointer; color:#888; font-size:14px; font-weight:500;">${L.btn_cancel}</span>
                        <button class="pk-btn pri" id="pk_edit_pwd_save" disabled
                                style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); border:none; color:#fff; font-weight:600; transition:all 0.2s; opacity: 0.4; cursor: not-allowed;">${L.btn_save}</button>
                    </div>
                </div>
            `;
            document.body.appendChild(subM);

            const input = subM.querySelector('#pk_new_pwd_input');
            const saveBtn = subM.querySelector('#pk_edit_pwd_save');
            input.focus();

            const validate = () => {
                const val = input.value.trim();
                const isValid = /^[a-zA-Z0-9]{4,10}$/.test(val);
                saveBtn.disabled = !isValid;
                saveBtn.style.opacity = isValid ? '1' : '0.4';
                saveBtn.style.cursor = isValid ? 'pointer' : 'not-allowed';
                return isValid;
            };

            input.oninput = validate;
            validate();

            const closeBtn = subM.querySelector('#pk_close_pwd_row');
            closeBtn.onclick = async (e) => {
                e.stopPropagation();
                closeBtn.style.pointerEvents = 'none';
                closeBtn.style.opacity = '0.5';
                try {
                    await apiUpdateShare(item.id, {
                        pass_code_option: 'NOT_REQUIRED'
                    });

                    item.pass_code = "";

                    pwdField.value = L.str_no_pwd;
                    pwdCopyBtn.setAttribute('data-val', "");

                    pwdCopyBtn.style.display = 'none';
                    const mainCopyBtn = m.querySelector('#pk_detail_copy_all');
                    if (mainCopyBtn) mainCopyBtn.textContent = L.btn_copy_link;

                    renderVisible();

                    subM.remove();

                    const toast = document.createElement('div');
                    toast.style.cssText = "position:absolute; top:180px; left:50%; transform:translateX(-50%); width:max-content; background:var(--pk-toast-bg); color:var(--pk-toast-fg); padding:10px 24px; border-radius:8px; font-size:14px; font-weight:600; z-index:10007; pointer-events:none; box-shadow:0 8px 24px var(--pk-tip-sd); border:1px solid var(--pk-toast-bd);";
                    toast.textContent = L.msg_pwd_updated;
                    m.querySelector('.pk-modal').appendChild(toast);
                    setTimeout(() => toast.remove(), 1200);
                } catch (err) {
                    showAlert(err.message);
                    closeBtn.style.pointerEvents = 'auto';
                    closeBtn.style.opacity = '1';
                }
            };

            const doSave = async () => {
                const newPwd = input.value.trim();
                const oldPwd = item.pass_code || '';
                if (!validate() || newPwd === oldPwd) { if(newPwd === oldPwd) subM.remove(); return; }

                saveBtn.disabled = true;
                saveBtn.style.opacity = '0.7';
                saveBtn.textContent = "...";

                try {
                    await apiUpdateShare(item.id, {
                        pass_code_option: 'REQUIRED',
                        custom_pass_code: newPwd
                    });

                    item.pass_code = newPwd;

                    pwdField.value = newPwd;
                    pwdCopyBtn.setAttribute('data-val', newPwd);

                    pwdCopyBtn.style.setProperty('display', 'flex', 'important');
                    const mainCopyBtn = m.querySelector('#pk_detail_copy_all');
                    if (mainCopyBtn) mainCopyBtn.textContent = L.btn_copy_link_pwd;

                    renderVisible();

                    subM.remove();

                    const toast = document.createElement('div');
                    toast.style.cssText = "position:absolute; top:180px; left:50%; transform:translateX(-50%); width:max-content; background:var(--pk-toast-bg); color:var(--pk-toast-fg); padding:10px 24px; border-radius:8px; font-size:14px; font-weight:600; z-index:10007; pointer-events:none; box-shadow:0 8px 24px var(--pk-tip-sd); border:1px solid var(--pk-toast-bd);";
                    toast.textContent = L.msg_pwd_updated;
                    m.querySelector('.pk-modal').appendChild(toast);
                    setTimeout(() => toast.remove(), 1200);
                } catch (e) {
                    showAlert(`${L.str_error}: ${e.message}`);
                    saveBtn.disabled = false;
                    saveBtn.style.opacity = '1';
                    saveBtn.textContent = L.btn_save;
                }
            };

            saveBtn.onclick = doSave;
            subM.querySelector('#pk_edit_pwd_cancel').onclick = () => subM.remove();
            subM.querySelector('.pk-modal-close').onclick = () => subM.remove();
            input.onkeydown = (e) => { if(e.key === 'Enter') doSave(); if(e.key === 'Escape') { e.stopPropagation(); subM.remove(); } };
        };

        const phraseRowsContainer = m.querySelector('#pk_phrase_rows');
        const btnAddPhrase = m.querySelector('#pk_btn_add_phrase');
        let localPhrases = [];

        const renderPhraseRows = () => {
            if (!phraseRowsContainer) return;
            phraseRowsContainer.innerHTML = localPhrases.map(p => `
                <div class="pk-phrase-row" data-val="${esc(p)}" style="display:flex; align-items:center; width:100%; height:44px; padding:0 12px; border-bottom:1px solid var(--pk-bd); cursor:pointer; transition:background 0.1s; background:var(--pk-bg);">
                    <div style="flex:1; border:none; background:transparent; outline:none; font-size:14px; color:var(--pk-fg); font-weight:bold; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${esc(p)}</div>
                    <div class="pk-copy-btn" data-val="${esc(p)}" style="margin-left:10px; display:flex; align-items:center; justify-content:center; cursor:pointer; color:#888;" data-pk-tip="${L.btn_copy}">
                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
                    </div>
                </div>
            `).join('');

            phraseRowsContainer.querySelectorAll('.pk-phrase-row').forEach(row => {
                row.onclick = (e) => {
                    const copyBtn = e.target.closest('.pk-copy-btn');
                    if (copyBtn) {
                        const val = copyBtn.dataset.val;
                        GM_setClipboard(val);

                        const originalHtml = copyBtn.innerHTML;
                        copyBtn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#52c41a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;

                        setTimeout(() => {
                            if (copyBtn.isConnected) {
                                copyBtn.innerHTML = originalHtml;
                            }
                        }, 1500);
                        return;
                    }
                    openPhraseEditModal(row.dataset.val);
                };

                row.onmouseenter = () => row.style.background = 'var(--pk-hl)';
                row.onmouseleave = () => row.style.background = 'var(--pk-bg)';
            });
        };

        const openPhraseEditModal = (oldVal = "") => {
            const subM = document.createElement('div');
            subM.className = 'pk-modal-ov';
            subM.style.zIndex = (++modalZIndexCounter).toString();
            if (document.querySelector('.pk-ov').classList.contains('pk-dark')) subM.classList.add('pk-dark');
            subM.innerHTML = `
                <div class="pk-modal" style="width:340px; padding:24px; border-radius:12px; box-shadow: 0 8px 30px rgba(0,0,0,0.3);">
                    <div class="pk-modal-close">${CONF.icons.close}</div>
                    <h3 style="border:none; margin:0 0 16px 0; font-size:16px; font-weight:700; color:var(--pk-fg);">${oldVal ? L.title_edit_share_code : L.btn_add_share_code}</h3>
                    <div class="pk-field">
                        <input type="text" id="pk_new_phrase_input" placeholder="${L.ph_edit_share_code}"
                               style="height:44px; font-size:14px; border:1px solid var(--pk-pri); border-radius:6px; background:var(--pk-bg); color:var(--pk-fg); padding:0 12px; outline:none;"
                               value="${oldVal || ''}" autocomplete="off">
                    </div>
                    <div id="pk_del_phrase_row" style="display:${oldVal ? 'flex' : 'none'}; align-items:center; gap:8px; margin-top:12px; cursor:pointer; color:var(--pk-pri); font-size:14px; width:fit-content; transition:opacity 0.2s;" onmouseover="this.style.opacity=0.8" onmouseout="this.style.opacity=1">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
                        <span>${L.btn_del_share_code}</span>
                    </div>
                    <div style="display:flex; justify-content:flex-end; align-items:center; margin-top:24px; gap:20px;">
                        <span id="pk_edit_phrase_cancel" style="cursor:pointer; color:#888; font-size:14px; font-weight:500;">${L.btn_cancel}</span>
                        <button class="pk-btn pri" id="pk_edit_phrase_save" disabled
                                style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); border:none; color:#fff; font-weight:600; transition:all 0.2s; opacity: 0.4; cursor: not-allowed;">${L.btn_save}</button>
                    </div>
                </div>
            `;
            document.body.appendChild(subM);

            const input = subM.querySelector('#pk_new_phrase_input');
            const saveBtn = subM.querySelector('#pk_edit_phrase_save');
            input.focus();

            const validate = () => {
                const val = input.value.trim();
                const isValid = val.length >= 5 && val.length <= 18;
                saveBtn.disabled = !isValid;
                saveBtn.style.opacity = isValid ? '1' : '0.4';
                saveBtn.style.cursor = isValid ? 'pointer' : 'not-allowed';
                return isValid;
            };

            input.oninput = validate;
            validate();

            const doRequest = async (newVal) => {
                saveBtn.disabled = true;
                saveBtn.style.opacity = '0.7';
                saveBtn.textContent = "...";

                try {
                    await apiUpdateSharePhrase(item.id, newVal, oldVal);

                    if (!newVal) {
                        localPhrases = localPhrases.filter(x => x !== oldVal);
                    } else if (!oldVal) {
                        localPhrases.push(newVal);
                    } else {
                        localPhrases = localPhrases.map(x => x === oldVal ? newVal : x);
                    }

                    subM.remove();
                    renderPhraseRows();
                    renderVisible();

                    const toast = document.createElement('div');
                    toast.style.cssText = "position:absolute; top:180px; left:50%; transform:translateX(-50%); width:max-content; background:var(--pk-toast-bg); color:var(--pk-toast-fg); padding:10px 24px; border-radius:8px; font-size:14px; font-weight:600; z-index:10007; pointer-events:none; box-shadow:0 8px 24px var(--pk-tip-sd); border:1px solid var(--pk-toast-bd);";
                    toast.textContent = L.msg_share_code_updated;
                    m.querySelector('.pk-modal').appendChild(toast);
                    setTimeout(() => toast.remove(), 1200);

                } catch (err) {
                    showAlert(err.message);
                    saveBtn.disabled = false;
                    saveBtn.style.opacity = '1';
                    saveBtn.textContent = L.btn_save;
                }
            };

            saveBtn.onclick = () => { if(validate()) doRequest(input.value.trim()); };
            const delBtn = subM.querySelector('#pk_del_phrase_row');
            if (delBtn) delBtn.onclick = () => doRequest("");
            subM.querySelector('#pk_edit_phrase_cancel').onclick = () => subM.remove();
            subM.querySelector('.pk-modal-close').onclick = () => subM.remove();
            input.onkeydown = (ev) => {
                if(ev.key === 'Enter') { if(validate()) saveBtn.onclick(); }
                if(ev.key === 'Escape') { ev.stopPropagation(); subM.remove(); }
            };
        };

        if (btnAddPhrase) btnAddPhrase.onclick = () => openPhraseEditModal("");

        apiGetSharePhrases(item.id).then(list => {
            localPhrases = list;
            renderPhraseRows();
        });



        const limitField = m.querySelector('#pk_share_limit_edit');
        const limitInput = m.querySelector('#pk_share_limit_val');

        limitField.onclick = () => {
            const currentSave = parseInt(item.save_count || 0);
            const currentLimit = parseInt(item.limit_count || 0);
            const subM = document.createElement('div');
            subM.className = 'pk-modal-ov';
            subM.style.zIndex = (++modalZIndexCounter).toString();
            if (document.querySelector('.pk-ov').classList.contains('pk-dark')) subM.classList.add('pk-dark');
            subM.innerHTML = `
                <div class="pk-modal" style="width:340px; padding:24px; border-radius:12px; box-shadow: 0 8px 30px rgba(0,0,0,0.3);">
                    <div class="pk-modal-close">${CONF.icons.close}</div>
                    <h3 style="border:none; margin:0 0 24px 0; font-size:16px; font-weight:700; color:var(--pk-fg);">${L.share_count_ed}</h3>

                    <div class="pk-s-opts" style="flex-direction:column; align-items:flex-start; gap:16px;">
                        <div style="display:flex; gap:15px; width:100%;">
                            <label class="pk-s-opt"><input type="radio" name="sh_mod_cnt" value="-1" ${currentLimit===0?'checked':''}> ${L.share_unlimit}</label>
                            <label class="pk-s-opt"><input type="radio" name="sh_mod_cnt" value="100" ${currentLimit===100?'checked':''}> 100${L.share_times}</label>
                        </div>
                        <div style="display:flex; align-items:center; gap:10px; width:100%;">
                            <label class="pk-s-opt" style="flex-shrink:0;"><input type="radio" name="sh_mod_cnt" value="custom" ${![0,100].includes(currentLimit)?'checked':''}> ${L.share_custom}</label>
                            <div style="position:relative; flex:1;">
                                <input type="number" id="sh_mod_cnt_val" class="pk-s-input"
                                       value="${currentLimit > 0 ? currentLimit : ''}"
                                       placeholder="> ${currentSave}"
                                       style="width:100%; height:36px; padding-right:32px;" ${![0,100].includes(currentLimit)?'':'disabled'}>
                                <div class="pk-num-ctrl">
                                    <div class="pk-num-btn" id="sh_cnt_inc">${CONF.crumbIcons.down.replace('points="6 9 12 15 18 9"', 'points="18 15 12 9 6 15"')}</div>
                                    <div class="pk-num-btn" id="sh_cnt_dec">${CONF.crumbIcons.down}</div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div style="display:flex; justify-content:flex-end; align-items:center; margin-top:24px; gap:20px;">
                        <span id="pk_mod_cnt_cancel" style="cursor:pointer; color:#888; font-size:14px; font-weight:500;">${L.btn_cancel}</span>
                        <button class="pk-btn pri" id="pk_mod_cnt_save"
                                style="height:36px; padding:0 24px; border-radius:6px; background:var(--pk-pri); border:none; color:#fff; font-weight:600; transition:all 0.2s;">${L.btn_save}</button>
                    </div>
                </div>
            `;
            document.body.appendChild(subM);

            const radios = subM.querySelectorAll('input[name="sh_mod_cnt"]');
            const input = subM.querySelector('#sh_mod_cnt_val');
            const saveBtn = subM.querySelector('#pk_mod_cnt_save');

            const ctrl = subM.querySelector('.pk-num-ctrl');
            const updateCtrlState = () => {
                ctrl.style.opacity = input.disabled ? '0.3' : '1';
                ctrl.style.pointerEvents = input.disabled ? 'none' : 'auto';
                ctrl.style.cursor = input.disabled ? 'not-allowed' : 'default';
            };

            radios.forEach(r => r.onchange = () => {
                input.disabled = r.value !== 'custom';
                if (!input.disabled) input.focus();
                updateCtrlState();
            });

            updateCtrlState();

            subM.querySelector('#sh_cnt_inc').onclick = (e) => {
                if (input.disabled) return;
                e.stopPropagation();
                input.value = (parseInt(input.value) || currentSave) + 1;
                validate();
            };
            subM.querySelector('#sh_cnt_dec').onclick = (e) => {
                if (input.disabled) return;
                e.stopPropagation();
                input.value = Math.max(currentSave + 1, (parseInt(input.value) || (currentSave + 2)) - 1);
                validate();
            };

            const doSave = async () => {
                const rVal = subM.querySelector('input[name="sh_mod_cnt"]:checked').value;
                let newLimit = 0;
                if (rVal === 'custom') {
                    const val = parseInt(input.value);
                    if (isNaN(val) || val <= 0) {
                        input.style.borderColor = '#d93025'; return;
                    }
                    newLimit = val;
                } else {
                    newLimit = parseInt(rVal);
                    if (newLimit === -1) newLimit = 0;
                }

                if (newLimit > 0 && newLimit <= currentSave) {
                    showToast(L.err_limit_too_low.replace('{n}', newLimit).replace('{s}', currentSave), 'error');
                    if(input.disabled) input.value = currentSave + 1;
                    return;
                }

                saveBtn.textContent = "...";
                saveBtn.disabled = true;

                item.limit_count = newLimit;

                const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                if (newLimit > 0) {
                    store[item.id] = newLimit;
                } else {
                    delete store[item.id];
                }
                gmSet('pk_share_limits', JSON.stringify(store));

                if (newLimit === 0) {
                    limitInput.value = L.share_unlimit;
                } else {
                    limitInput.value = `${newLimit} ${L.share_times}`;
                }
                limitInput.style.color = 'var(--pk-fg)';

                renderVisible();

                if (window.pkSmartRefreshTrigger) {
                    window.pkSmartRefreshTrigger(true);
                }

                setTimeout(() => {
                    subM.remove();
                    showToast(L.msg_limit_updated);
                }, 200);
            };

            subM.querySelector('#pk_mod_cnt_cancel').onclick = () => subM.remove();
            subM.querySelector('.pk-modal-close').onclick = () => subM.remove();
            saveBtn.onclick = doSave;
            input.onkeydown = (e) => { if(e.key === 'Enter') doSave(); };
        };

        const expField = m.querySelector('#pk_share_exp_edit');
        const expInput = m.querySelector('#pk_share_exp_val');

        expField.onclick = (e) => {
            e.stopPropagation();
            const existing = document.querySelector('.pk-cal-pop');
            if (existing) { existing.remove(); return; }

            renderCalendar(expField, async (res) => {
                const selectedDays = res.days;
                if (selectedDays === null) return;

                const originalText = expInput.value;
                expInput.value = "...";

                try {
                    const payload = {};
                    let newText = "";
                    let newStatusLeft = "";

                    if (selectedDays === -1) {
                        payload.expiration_at = "-1";
                        newText = L.share_perm;
                        newStatusLeft = "-1";
                    } else {
                        const d = res.ts ? new Date(res.ts) : new Date(getServerNow() + selectedDays * 24 * 3600 * 1000);
                        const pad = n => String(n).padStart(2, '0');
                        const ds = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
                        payload.expiration_at = `${ds}T23:59:59.000+08:00`;
                        newText = res.ts ? ds : (selectedDays + L.share_days);

                        if (res.ts) {
                            const diff = Math.max(1, Math.ceil((res.ts - new Date(getServerNow()).setHours(0,0,0,0)) / (1000 * 3600 * 24)));
                            newStatusLeft = diff + L.unit_days.trim();
                        } else {
                            newStatusLeft = selectedDays + L.share_days;
                        }
                    }

                    await apiUpdateShare(item.id, payload);

                    item.expiration_days = selectedDays;
                    item.expiration_at = payload.expiration_at;
                    item.expiration_left = newStatusLeft;

                    expInput.value = newText;
                    expInput.style.color = 'var(--pk-fg)';

                    renderVisible();

                    const toast = document.createElement('div');
                    toast.style.cssText = "position:absolute; top:180px; left:50%; transform:translateX(-50%); width:max-content; background:var(--pk-toast-bg); color:var(--pk-toast-fg); padding:10px 24px; border-radius:8px; font-size:14px; font-weight:600; z-index:10007; pointer-events:none; box-shadow:0 8px 24px var(--pk-tip-sd); border:1px solid var(--pk-toast-bd);";
                    toast.textContent = L.msg_exp_updated;
                    m.querySelector('.pk-modal').appendChild(toast);
                    setTimeout(() => toast.remove(), 1200);
                } catch (err) {
                    showAlert(`${L.str_error}: ${err.message}`);
                    expInput.value = originalText;
                }
            });
        };

        m.querySelector('#pk_detail_cancel').onclick = async () => {
            if (await showConfirm(L.msg_cancel_share_confirm.replace('{n}', 1))) {
                setLoad(true);
                try {
                    await apiCancelShare([item.id]);
                    m.remove();
                    showAlert(L.msg_cancel_share_done.replace('{n}', 1));
                    load(false, true);
                } catch (e) {
                    setLoad(false);
                    showAlert(`${L.str_error}: ${e.message}`);
                }
            }
        };

        m.querySelector('#pk_detail_copy_all').onclick = () => {
            const url = item.share_url;
            const pwd = item.pass_code || '';
            const title = item.name || item.title;

            let text = url + '\n';
            if (pwd) text += `${L.share_copy_pwd}: ${pwd}\n`;
            text += `${title}\n${L.share_copy_suffix}`;

            GM_setClipboard(text);

            const btn = m.querySelector('#pk_detail_copy_all');
            const orgTxt = btn.textContent;
            btn.textContent = L.msg_copy_success;
            const originalColor = btn.style.backgroundColor;
            const originalBorder = btn.style.borderColor;
            btn.style.backgroundColor = "#52c41a";
            btn.style.borderColor = "#52c41a";

            setTimeout(() => {
                btn.textContent = orgTxt;
                btn.style.backgroundColor = originalColor;
                btn.style.borderColor = originalBorder;
            }, 1500);
        };
    };

    UI.btnCancelShare.onclick = async () => {
        const selectedIds = S.getSelectedIds();
        const n = selectedIds.length;
        if (n === 0) return;

        if (!await showConfirm(L.msg_cancel_share_confirm.replace('{n}', n))) return;

        setLoad(true);
        updateLoadTxt(L.msg_cancel_share_ing);

        try {
            const selectedItems = selectedIds.map(id => S.itemMap.get(id)).filter(Boolean);
            const serverIds = selectedItems.filter(it => !it._is_local_phantom).map(it => it.id);
            const phantomIds = selectedItems.filter(it => it._is_local_phantom).map(it => it.id);

            if (serverIds.length > 0) {
                await apiCancelShare(serverIds);
            }

            if (phantomIds.length > 0) {
                const graveyard = JSON.parse(gmGet('pk_expired_shares', '[]'));
                const newGraveyard = graveyard.filter(x => !phantomIds.includes(x.id));
                gmSet('pk_expired_shares', JSON.stringify(newGraveyard));
            }

            S.clearSelection();

            await load(false, true);

            showToast(L.msg_cancel_share_done.replace('{n}', n));
        } catch (e) {
            showAlert(`${L.str_error}: ${e.message}`);
        } finally {
            setLoad(false);
        }
    };

    if (UI.btnCopyLinkOffline) {
        UI.btnCopyLinkOffline.onclick = () => {
            const ids = S.getSelectedIds();
            if (ids.length === 0) return;
            const tasks = ids.map(id => S.itemMap.get(id)).filter(t => t && (t.source_url || (t.params && t.params.url)));
            if (tasks.length === 0) return;
            const urls = tasks.map(t => t.source_url || t.params.url).join('\n');
            GM_setClipboard(urls);
            showToast(L.msg_copy_success);
        };
    }

    if (UI.btnRetryTask) {
        UI.btnRetryTask.onclick = async () => {
            const ids = S.getSelectedIds();
            if (ids.length === 0) return;

            const targets = ids.map(id => S.itemMap.get(id))
                               .filter(t => t && t.phase === 'PHASE_TYPE_ERROR' && (t.source_url || (t.params && t.params.url)));

            if (targets.length === 0) {
                showToast(L.err_no_failed_task, "error");
                return;
            }

            setLoad(true);
            updateLoadTxt(L.str_processing);

            let successCount = 0;

            try {
                for (const task of targets) {
                    const url = task.source_url || task.params.url;
                    try {
                        await apiAddOfflineTask(url);
                        await apiCancelTask([task.id]);
                        successCount++;
                        if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;
                    } catch (e) {
                        console.error(`[Retry] Failed for ${task.name}:`, e);
                        if (e.message && e.message.includes(L.err_task_exists)) {
                            await apiCancelTask([task.id]).catch(()=>{});
                            successCount++;
                        }
                    }
                    await sleep(150);
                }

                await load(false, true);

                showToast(L.msg_retry_submitted.replace('{n}', successCount));

            } catch (e) {
                showAlert(`${L.str_error}: ${e.message}`);
            } finally {
                setLoad(false);
                S.clearSelection();
                updateStat();
            }
        };
    }

    UI.btnMigrate.onclick = async () => {
        const selectedIds = S.getSelectedIds();
        const selectedCount = selectedIds.length;
        if (selectedCount === 0) return;
        const L = getStrings();

        const hasConflict = selectedIds.some(id => {
            const item = S.itemMap.get(id);
            if (item && item.kind === 'drive#folder') return isPathBusy(item.id);
            return S.movingIds.has(id);
        });
        if (hasConflict) {
            showAlert(L.msg_op_blocked_moving);
            return;
        }

        if (selectedCount > 100) {
            showToast(L.err_migrate_limit, 'error');
            return;
        }

        const estimatedSize = JSON.stringify(selectedIds).length;
        if (estimatedSize > 3.8 * 1024 * 1024) {
            const mb = (estimatedSize / 1024 / 1024).toFixed(2);
            showAlert(L.err_migrate_too_many.replace('{s}', mb));
            return;
        }

        if (!await showConfirm(L.msg_migrate_confirm.replace('{n}', selectedCount))) return;

        setLoad(true);
        updateLoadTxt(L.msg_migrate_packing);

        try {
            let sourceUid = "";
            for (let i = 0; i < localStorage.length; i++) {
                const k = localStorage.key(i);
                if (k && k.startsWith('credentials')) {
                    try {
                        const v = JSON.parse(localStorage.getItem(k));
                        if (v && v.sub) { sourceUid = v.sub; break; }
                    } catch {}
                }
            }
            if (!sourceUid) sourceUid = "UNKNOWN_UID_" + Date.now();

            const randPass = Math.random().toString(36).slice(-5) + Math.random().toString(36).slice(-5).toUpperCase();

            const payload = {
                file_ids: selectedIds,
                share_to: 'encryptedlink',
                expiration_days: 1,
                pass_code_option: 'REQUIRED',
                custom_pass_code: randPass,
                limit_count: 1
            };

            const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/share`, {
                method: 'POST',
                headers: getHeaders(),
                body: JSON.stringify(payload)
            });

            if (!res.ok) {
                const err = await res.json().catch(() => ({}));
                if (res.status === 403 || res.status === 400) {
                    throw new Error(L.err_migrate_ban);
                }
                throw new Error(err.error_description || `API Error ${res.status}`);
            }

            const data = await res.json();

            const stub = {
                source_uid: sourceUid,
                share_id: data.share_id,
                pass_code: randPass,
                file_count: selectedCount,
                file_ids: selectedIds,
                timestamp: Date.now()
            };

            gmSet('pk_migration_stub', JSON.stringify(stub));

            try {
                const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                store[data.share_id] = 1;
                gmSet('pk_share_limits', JSON.stringify(store));
            } catch(e) {}

            setLoad(false);
            await showAlert(L.msg_migrate_ready, L.btn_migrate);

            const keysToRemove = [];
            for (let i = 0; i < localStorage.length; i++) {
                const k = localStorage.key(i);
                if (k && (k.startsWith('credentials') || k.startsWith('captcha') || k === 'pk_captured_captcha')) {
                    keysToRemove.push(k);
                }
            }
            keysToRemove.forEach(k => localStorage.removeItem(k));
            if (typeof purgeAllCachesOnLogout === 'function') purgeAllCachesOnLogout();
            if (!location.href.includes('/login')) window.location.href = 'https://mypikpak.com/drive/login';

        } catch (e) {
            setLoad(false);
            showAlert(`${L.str_error}: ${e.message}`);
        }
    };

    UI.btnUnzip.onclick = async () => {
        ensureItemMap();
        const isArchive = (it) => {
            if (!it || it.kind === 'drive#folder') return false;
            const n = (it.name || '').toLowerCase();
            const m = (it.mime_type || '').toLowerCase();
            return m.includes('zip') || m.includes('rar') || m.includes('7z') ||
                   m.includes('compressed') || m.includes('archive') ||
                   n.endsWith('.zip') || n.endsWith('.rar') || n.endsWith('.7z') || n.endsWith('.tar') || n.endsWith('.gz');
        };
        const rawTargets = S.getSelectedIds().map(id => S.itemMap.get(id)).filter(i => isArchive(i));

        const existingFolderNames = new Set(
            S.items.filter(i => i.kind === 'drive#folder').map(i => i.name)
        );

        const targets = rawTargets.filter(item => {
            if (rawTargets.length === 1) return true;

            const isMarkedUnzipped = item.params && (item.params.global_file_kind === '1' || item.params.global_file_root);
            if (!isMarkedUnzipped) return true;
            let targetFolderName = item.name;
            const lastDot = targetFolderName.lastIndexOf('.');
            if (lastDot > 0) targetFolderName = targetFolderName.substring(0, lastDot);
            return !existingFolderNames.has(targetFolderName);
        });

        const skippedItems = rawTargets.filter(item => !targets.includes(item));
        const skippedCount = skippedItems.length;

        if (skippedCount > 0) {
            const delRes = await showDeleteConfirm(L.msg_unzip_skip_del_confirm.replace('{n}', skippedCount));
            if (delRes.confirm) {
                const idsToDelete = skippedItems.map(i => i.id);
                await executeBatchDelete(idsToDelete, { silent: true, forceRefresh: false, hardDelete: delRes.hardDelete });
                showToast(L.msg_del_items_done.replace('{n}', skippedCount));
            } else {
                showToast(L.msg_skip_unzipped.replace('{n}', skippedCount));
            }
        }

        if (targets.length === 1) {
            handleOpenArchive(targets[0]);
        } else if (targets.length > 1) {
            handleUnzip(targets);
        }
    };

    ctx.querySelector('#ctx-share').onclick = async () => {
        ctx.style.display = 'none';
        const ids = S.getSelectedIds();
        if (ids.length === 0) return;

        if (ids.length > 100) {
            showToast(L.err_share_limit, 'error');
            return;
        }

        const item = S.itemMap.get(ids[0]);
        if (!item) return;

        const m = showModal(`
            <div class="pk-share-modal">
                <div style="display: flex; align-items: center; padding: 16px 50px 0 24px; min-height: 30px;">
                    <h3 style="margin: 0; font-size: 18px; font-weight: 600; border: none; padding: 0; line-height: 1.2;">${L.share_title}</h3>
                </div>

                <div class="pk-s-sec" style="margin-top: 20px;">
                    <div class="pk-s-lbl">${L.share_mode}</div>
                    <div class="pk-s-tabs" id="sh_mode_tabs">
                        <div class="pk-s-tab act" data-val="public">${L.share_public}</div>
                        <div class="pk-s-tab" data-val="encrypted">${L.share_encrypted}</div>
                    </div>
                </div>

                <div class="pk-s-sec">
                    <div class="pk-s-lbl">${L.share_expiry}</div>
                    <div class="pk-s-opts">
                        <label class="pk-s-opt"><input type="radio" name="sh_exp" value="7"> 7${L.share_days}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_exp" value="14"> 14${L.share_days}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_exp" value="30"> 30${L.share_days}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_exp" value="-1" checked> ${L.share_perm}</label>

                        <div class="pk-s-opt" id="sh_exp_custom_btn" style="position:relative; margin-left:auto; border:1px solid var(--pk-bd); border-radius:4px; padding:2px 8px; transition:border-color 0.2s;">
                            <input type="radio" name="sh_exp" value="custom" style="display:none;">
                            <span id="sh_custom_txt">${L.share_custom}</span>
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-left:4px; transform:translateY(1px);"><polyline points="6 9 12 15 18 9"></polyline></svg>
                        </div>
                    </div>
                </div>

                <div class="pk-s-sec" id="sh_pass_sec" style="opacity:0.3; pointer-events:none;">
                    <div class="pk-s-lbl">${L.share_pass}</div>
                    <div class="pk-s-opts">
                        <label class="pk-s-opt"><input type="radio" name="sh_pass_type" value="rand" checked> ${L.share_rand}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_pass_type" value="custom"> ${L.share_custom}</label>
                        <input type="text" id="sh_pass_val" class="pk-s-input" placeholder="${L.ph_pass_range}" minlength="4" maxlength="10" disabled>
                    </div>
                </div>

                <div class="pk-s-sec">
                    <div class="pk-s-lbl">${L.share_count}</div>
                    <div class="pk-s-opts">
                        <label class="pk-s-opt"><input type="radio" name="sh_cnt" value="1"> 1${L.share_times}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_cnt" value="5"> 5${L.share_times}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_cnt" value="20"> 20${L.share_times}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_cnt" value="-1" checked> ${L.share_unlimit}</label>
                        <label class="pk-s-opt"><input type="radio" name="sh_cnt" value="custom"> ${L.share_custom}</label>
                        <input type="text" id="sh_cnt_val" class="pk-s-input" placeholder="${L.share_unlimit}"
                               style="width:60px; height:28px; text-align:center;" disabled autocomplete="off">
                    </div>
                </div>

                <div class="pk-modal-act" style="margin-top:10px; padding: 0 24px 24px 24px; display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
                    <button class="pk-btn" id="sh_cancel" style="justify-content: center; height: 40px; font-weight: 600; width: 100%; border-radius: 8px; font-size: 15px; background:transparent;">${L.btn_cancel}</button>
                    <button class="pk-btn pri" id="sh_go" style="justify-content: center; height: 40px; font-weight: 600; width: 100%; border-radius: 8px; font-size: 15px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); background:var(--pk-pri); color:#fff; border:none;">${L.btn_share_start}</button>
                </div>
            </div>
        `);

        const tabs = m.querySelectorAll('.pk-s-tab');
        const passSec = m.querySelector('#sh_pass_sec');
        const passVal = m.querySelector('#sh_pass_val');
        const passRadios = m.querySelectorAll('input[name="sh_pass_type"]');
        const btnGo = m.querySelector('#sh_go');

        const validate = () => {
            const mode = m.querySelector('.pk-s-tab.act').dataset.val;
            const passType = m.querySelector('input[name="sh_pass_type"]:checked').value;
            const pass = passVal.value.trim();

            let isValid = true;
            if (mode === 'encrypted' && passType === 'custom') {
                const reg = /^[a-zA-Z0-9]{4,10}$/;
                isValid = reg.test(pass);
            }

            btnGo.disabled = !isValid;
            btnGo.style.opacity = isValid ? '1' : '0.4';
            btnGo.style.cursor = isValid ? 'pointer' : 'not-allowed';
        };

        tabs.forEach(t => t.onclick = () => {
            tabs.forEach(x => x.classList.remove('act'));
            t.classList.add('act');
            const isEnc = t.dataset.val === 'encrypted';
            passSec.style.opacity = isEnc ? '1' : '0.3';
            passSec.style.pointerEvents = isEnc ? 'auto' : 'none';
            validate();
        });

        passRadios.forEach(r => r.onchange = () => {
            passVal.disabled = r.value !== 'custom';
            if (!passVal.disabled) passVal.focus();
            validate();
        });

        const cntRadios = m.querySelectorAll('input[name="sh_cnt"]');
        const cntVal = m.querySelector('#sh_cnt_val');

        cntRadios.forEach(r => r.onchange = () => {
            const isCustom = r.value === 'custom';
            cntVal.disabled = !isCustom;
            if (isCustom) {
                setTimeout(() => cntVal.focus(), 10);
            } else {
                cntVal.value = '';
            }
            validate();
        });

        cntVal.onfocus = () => { if(!cntVal.disabled) cntBox.style.borderColor = 'var(--pk-pri)'; };
        cntVal.onblur = () => { if(cntVal.value === '') cntBox.style.borderColor = 'var(--pk-bd)'; };
        cntVal.oninput = () => {
            cntVal.value = cntVal.value.replace(/[^\d]/g, '');
            validate();
        };

        const modalContainer = m.querySelector('.pk-modal');
        if (modalContainer) {
            modalContainer.style.padding = '0';
            modalContainer.style.width = 'fit-content';
        }

        const btnCustomExp = m.querySelector('#sh_exp_custom_btn');
        const txtCustom = m.querySelector('#sh_custom_txt');
        let customDays = null;

        m.querySelectorAll('input[name="sh_exp"]').forEach(r => {
            r.addEventListener('change', (e) => {
                if (e.target.value !== 'custom') {
                    btnCustomExp.style.borderColor = 'var(--pk-bd)';
                    btnCustomExp.style.color = 'var(--pk-fg)';
                    txtCustom.textContent = L.share_custom;
                    customDays = null;
                }
            });
        });

        btnCustomExp.onclick = (e) => {
            e.stopPropagation();
            e.preventDefault();
            const existing = document.querySelector('.pk-cal-pop');
            if (existing) { existing.remove(); return; }

            renderCalendar(btnCustomExp, (res) => {
                const presetRadio = m.querySelector(`input[name="sh_exp"][value="${res.days}"]`);

                if (presetRadio && res.ts === null) {
                    presetRadio.checked = true;
                    presetRadio.dispatchEvent(new Event('change'));
                } else {
                    customDays = res.days;
                    const radio = btnCustomExp.querySelector('input');
                    radio.checked = true;

                    btnCustomExp.style.borderColor = 'var(--pk-pri)';
                    btnCustomExp.style.color = 'var(--pk-pri)';

                    if (res.ts) {
                        const d = new Date(res.ts);
                        const dateStr = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
                        txtCustom.textContent = dateStr;
                    } else if (res.days === -1) {
                        txtCustom.textContent = L.share_perm;
                    } else {
                        txtCustom.textContent = `${res.days} ${L.share_days}`;
                    }
                }
            });
        };

        passVal.oninput = validate;

        m.querySelector('#sh_cancel').onclick = () => m.remove();
        btnGo.onclick = async () => {
            if (btnGo.disabled) return;

            const mode = m.querySelector('.pk-s-tab.act').dataset.val;
            const cntRadio = m.querySelector('input[name="sh_cnt"]:checked');
            let cnt = parseInt(cntRadio.value);
            if (cntRadio.value === 'custom') {
                const customInput = m.querySelector('#sh_cnt_val').value.trim();
                cnt = (customInput === "") ? -1 : (parseInt(customInput) || 1);
            }

            let exp = -1;
            const expRadio = m.querySelector('input[name="sh_exp"]:checked');
            if (expRadio.value === 'custom') {
                if (customDays === null) {
                    exp = 7;
                } else {
                    exp = customDays;
                }
            } else {
                exp = parseInt(expRadio.value);
            }

            const isCustomPass = m.querySelector('input[name="sh_pass_type"]:checked').value === 'custom';
            const pass = isCustomPass ? passVal.value.trim() : "";

            if (mode === 'encrypted' && isCustomPass) {
                if (pass.length < 4 || pass.length > 10) {
                    showAlert(L.err_share_pass);
                    return;
                }
            }

            m.remove();
            setLoad(true);
            updateLoadTxt(L.msg_creating_share);

            try {
                const payload = {
                    file_ids: ids,
                    share_to: mode === 'encrypted' ? 'encryptedlink' : 'publiclink',
                    expiration_days: parseInt(exp),
                    pass_code_option: mode === 'encrypted' ? 'REQUIRED' : 'NOT_REQUIRED'
                };

                if (expRadio.value === 'custom' && customDays !== null) {
                    payload.expiration_days = customDays;
                }

                if (cnt > 0) payload.limit_count = cnt;

                const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/share`, {
                    method: 'POST', headers: getHeaders(), body: JSON.stringify(payload)
                });
                if (!res.ok) throw new Error(`Create API Error ${res.status}`);

                let data = await res.json();
                let finalPassCode = data.pass_code;

                if (cnt > 0) {
                    const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                    store[data.share_id] = cnt;
                    gmSet('pk_share_limits', JSON.stringify(store));
                }

                if (mode === 'encrypted' && isCustomPass && pass) {
                    updateLoadTxt(L.str_saving);
                    const patchPayload = {
                        share_id: data.share_id,
                        pass_code_option: 'REQUIRED',
                        custom_pass_code: pass
                    };

                    const patchRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/share`, {
                        method: 'PATCH',
                        headers: getHeaders(),
                        body: JSON.stringify(patchPayload)
                    });

                    if (patchRes.ok) {
                        finalPassCode = pass;
                    } else {
                        console.warn("[Share] Custom password patch failed, using fallback.");
                    }
                }

                const fullText = data.share_url + (finalPassCode ? ` ${L.lbl_share_code}: ${finalPassCode}` : '');

                const resM = showModal(`
                    <div style="width: 400px; max-width: 90vw; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 10px 5px 0 5px;">
                        <div style="width: 64px; height: 64px; border-radius: 50%; background: rgba(26, 94, 255, 0.1); color: var(--pk-pri); display: flex; align-items: center; justify-content: center; margin-bottom: 20px;">
                            <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
                        </div>

                        <div style="font-size: 18px; font-weight: 700; color: var(--pk-fg); margin-bottom: 24px;">${L.title_share_result}</div>

                        <div style="width: 100%; display: flex; flex-direction: column; gap: 14px; margin-bottom: 30px;">
                            <input type="text" class="pk-share-res-val" value="${data.share_url}" readonly
                                   style="width: 100%; height: 44px; padding: 0 16px; border: 1.5px solid var(--pk-bd); border-radius: 10px; background: var(--pk-hl); color: var(--pk-pri); font-size: 14px; font-weight: 600; text-align: center; box-sizing: border-box; outline: none; transition: border-color 0.2s;">

                            ${finalPassCode ? `
                            <div style="display: flex; align-items: center; justify-content: center; gap: 10px;">
                                <span style="font-size: 13px; color: #888;">${L.lbl_share_code}:</span>
                                <span style="font-size: 18px; font-weight: 800; font-family: 'JetBrains Mono', monospace; color: var(--pk-fg); letter-spacing: 1.5px;">${finalPassCode}</span>
                            </div>` : ''}
                        </div>

                        <button class="pk-btn pri" id="res_copy"
                                style="width: 100%; height: 46px; border-radius: 12px; background: var(--pk-pri); color: #fff; font-weight: 700; font-size: 15px; border: none; box-shadow: 0 4px 14px rgba(26, 94, 255, 0.3); justify-content: center;">
                            ${L.btn_copy_share}
                        </button>
                    </div>
                `);

                const modalBox = resM.querySelector('.pk-modal');
                if (modalBox) {
                    modalBox.style.width = 'auto';
                    modalBox.style.minWidth = '460px';
                    modalBox.style.overflow = 'visible';
                    modalBox.style.padding = '30px';
                }

                resM.querySelector('#res_copy').onclick = () => {
                    GM_setClipboard(fullText);
                    const b = resM.querySelector('#res_copy');
                    b.textContent = L.msg_copy_success;
                    setTimeout(() => resM.remove(), 1000);
                };
            } catch (e) {
                showAlert(e.message);
            } finally {
                setLoad(false);
            }
        };
    };

    ctx.querySelector('#ctx-copy-name').onclick = () => {
        ctx.style.display = 'none';
        const names = [];
        const ids = S.getSelectedIds();
        ids.forEach(id => {
            const item = S.itemMap.get(id);
            if (item && item.name) {
                const name = item.name;
                if (item.kind !== 'drive#folder' && name.includes('.') && name.lastIndexOf('.') > 0) {
                    names.push(name.substring(0, name.lastIndexOf('.')));
                } else {
                    names.push(name);
                }
            }
        });
        if (names.length > 0) {
            GM_setClipboard(names.join('\n'));
            showToast(L.msg_copy_success);
        }
    };

    ctx.querySelector('#ctx-down').onclick = () => {
        ctx.style.display = 'none';
        UI.win.querySelector('#pk-down').click();
    };

    ctx.querySelector('#ctx-add-bl').onclick = (e) => {
        ctx.style.display = 'none';

        const action = e.target.getAttribute('data-action');

        processBlacklistAction(action);
    };
    ctx.querySelector('#ctx-copy').onclick = () => { ctx.style.display = 'none'; UI.btnCopy.click(); };
    ctx.querySelector('#ctx-cut').onclick = () => { ctx.style.display = 'none'; UI.btnCut.click(); };
    ctx.querySelector('#ctx-prune').onclick = () => { ctx.style.display = 'none'; UI.btnPrune.click(); };
    ctx.querySelector('#ctx-rename').onclick = () => {
        ctx.style.display = 'none';
        if (S.getSelectedCount() > 1) {
            UI.btnBulkRename.click();
        } else {
            UI.btnRename.click();
        }
    };
    const ctxDel = ctx.querySelector('#ctx-del');
    ctxDel.onclick = () => { ctx.style.display = 'none'; UI.btnDel.click(); };
    if (S.historyMode) {
        const ctxDelTxt = ctxDel.childNodes[1];
        if (ctxDelTxt) ctxDelTxt.textContent = " " + L.btn_clear_history;
    }

    const ctxShCancel = ctx.querySelector('#ctx-sh-cancel');
    if (ctxShCancel) {
        ctxShCancel.onclick = () => {
            ctx.style.display = 'none';
            if (UI.btnCancelShare) UI.btnCancelShare.click();
        };
    }

    const ctxShDetail = ctx.querySelector('#ctx-sh-detail');
    if (ctxShDetail) {
        ctxShDetail.onclick = () => {
            ctx.style.display = 'none';
            const id = S.getSelectedIds()[0];
            const item = S.itemMap.get(id);
            if (item) showShareDetail(item);
        };
    }

    const ctxShCopy = ctx.querySelector('#ctx-sh-copy');
    if (ctxShCopy) {
        ctxShCopy.onclick = () => {
            ctx.style.display = 'none';
            const id = S.getSelectedIds()[0];
            const item = S.itemMap.get(id);
            if (!item) return;

            const url = item.share_url || "";
            const pwd = item.pass_code || "";
            const title = item.name || item.title || "";

            let text = url + '\n';
            if (pwd) text += `${L.share_copy_pwd}: ${pwd}\n`;
            text += `${title}\n${L.share_copy_suffix}`;

            GM_setClipboard(text);
            showToast(L.msg_copy_success);
        };
    }

    const ctxRestore = ctx.querySelector('#ctx-restore');
    if (ctxRestore) {
        ctxRestore.onclick = () => {
            ctx.style.display = 'none';
            UI.btnRestore.click();
        };
    }

    const ctxDelForever = ctx.querySelector('#ctx-del-forever');
    if (ctxDelForever) {
        ctxDelForever.onclick = () => {
            ctx.style.display = 'none';
            UI.btnDelForever.click();
        };
    }

    updateStat();

    const restoreUIState = () => {
        const navs =[UI.btnNavHome, UI.btnNavTrash, UI.btnNavShare, UI.btnNavStarred, UI.btnNavRecent, UI.btnNavHistory, UI.btnNavOffline, UI.btnNavUpload];
        navs.forEach(n => { if(n) n.classList.remove('act'); });

        const stdBtns =[UI.btnNewFolder, UI.btnDel, UI.btnCopy, UI.btnCut, UI.btnPaste, UI.btnRename, UI.btnBulkRename, UI.btnPrune, UI.btnUnzip, UI.btnMigrate, UI.btnBlacklistManager];
        const shareBtns =[UI.btnCancelShare];
        const upBtns =[UI.btnUpPause, UI.btnUpStart, UI.btnUpDel, UI.btnUpClearAll];
        const upSep = el.querySelector('#pk-up-sep');
        const downBtns = [UI.btnAria2, UI.btnDown, UI.btnExt, UI.btnImgSearch];

        upBtns.forEach(b => { if(b) b.style.display = 'none'; });
        if(upSep) upSep.style.display = 'none';
        if (UI.btnRefresh) UI.btnRefresh.style.display = 'inline-flex';

        if (S.trashMode) {
            UI.win.classList.add('pk-mode-trash');
            if(UI.btnNavTrash) UI.btnNavTrash.classList.add('act');
            if(UI.actionBar) UI.actionBar.style.display = 'none';
            if(UI.trashBar) UI.trashBar.style.display = 'flex';
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'none';
            if (S.path[0]) S.path[0].name = L.trash_title;
        }
        else if (S.shareMode) {
            if(UI.btnNavShare) UI.btnNavShare.classList.add('act');
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'none';
            if (S.path[0]) S.path[0].name = L.btn_nav_share;

            stdBtns.forEach(b => { if(b && b !== UI.btnBlacklistManager) b.style.display = 'none'; });
            if (UI.btnBlacklistManager) UI.btnBlacklistManager.style.display = 'inline-flex';
            shareBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
        }
        else if (S.offlineMode) {
            if(UI.btnNavOffline) UI.btnNavOffline.classList.add('act');
            if (S.path[0]) S.path[0].name = L.title_offline;
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';[UI.btnNewFolder, UI.btnCopy, UI.btnCut, UI.btnPaste, UI.btnRename, UI.btnBulkRename, UI.btnPrune, UI.btnUnzip, UI.btnMigrate].forEach(b => { if(b) b.style.display = 'none'; });
            [UI.btnDel, UI.btnRefresh, UI.btnBlacklistManager].forEach(b => { if(b) b.style.display = 'inline-flex'; });
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            [UI.btnAria2, UI.btnDown].forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.btnExt) UI.btnExt.style.display = 'inline-flex';
            if(UI.btnImgSearch) UI.btnImgSearch.style.display = 'inline-flex';
        }
        else if (S.uploadMode) {
            if(UI.btnNavUpload) UI.btnNavUpload.classList.add('act');
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';
            if (S.path[0]) S.path[0].name = L.btn_nav_upload;
            [UI.btnAria2, UI.btnDown].forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.btnExt) UI.btnExt.style.display = 'inline-flex';
            if(UI.btnImgSearch) UI.btnImgSearch.style.display = 'inline-flex';

            stdBtns.forEach(b => { if(b) b.style.display = 'none'; });
            if (UI.btnRefresh) UI.btnRefresh.style.display = 'none';
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            upBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            if(upSep) upSep.style.display = 'block';
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'none';
        }
        else if (S.historyMode) {
            if(UI.btnNavHistory) UI.btnNavHistory.classList.add('act');
            if (S.path[0]) S.path[0].name = L.btn_nav_history;
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';

            stdBtns.forEach(b => { if(b && b !== UI.btnBlacklistManager && b !== UI.btnMigrate) b.style.display = 'none'; });
            if (UI.btnBlacklistManager) UI.btnBlacklistManager.style.display = 'inline-flex';
            if (UI.btnMigrate) UI.btnMigrate.style.display = 'inline-flex';
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'none';
            downBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
        }
        else if (S.recentMode || S.starredMode) {
            if (S.recentMode && UI.btnNavRecent) UI.btnNavRecent.classList.add('act');
            if (S.starredMode && UI.btnNavStarred) UI.btnNavStarred.classList.add('act');
            if (S.recentMode && S.path[0]) S.path[0].name = L.btn_nav_recent;
            if (S.starredMode && S.path[0]) S.path[0].name = L.btn_nav_starred;
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';

            stdBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'inline-flex';
            downBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
        }
        else {
            if(UI.btnNavHome) UI.btnNavHome.classList.add('act');
            if (S.path.length > 0 && S.path[0].id === '') S.path[0].name = L.btn_nav_home;
            if(UI.bottomGrp) UI.bottomGrp.style.display = 'flex';

            stdBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
            shareBtns.forEach(b => { if(b) b.style.display = 'none'; });
            if(UI.uploadWrap) UI.uploadWrap.style.display = 'inline-flex';
            downBtns.forEach(b => { if(b) b.style.display = 'inline-flex'; });
        }

        if(UI.topBar) UI.topBar.style.display = 'flex';
        if(UI.crumb) { UI.crumb.style.opacity = '1'; UI.crumb.style.display = 'flex'; }

        refresh();
    };

    restoreUIState();

    load();

    window.pkForceManagerReloadAfterAuth = (() => {
        let timer = null;
        let reloadSeq = 0;
        let authReloading = false;
        let pendingRelayout = false;

        const flushDeferredRelayout = () => {
            if (!pendingRelayout) return;
            pendingRelayout = false;
            if (!el || el.style.display === 'none') return;

            requestAnimationFrame(() => {
                if (!el || el.style.display === 'none') return;
                if (typeof syncLayoutMetrics === 'function') syncLayoutMetrics();

                if (isGridView() && typeof scheduleGridRelayout === 'function') {
                    scheduleGridRelayout(true);
                } else if (typeof renderList === 'function') {
                    renderList();
                } else if (typeof renderVisible === 'function') {
                    if (UI.in) UI.in.style.height = `${S.display.length * CONF.rowHeight}px`;
                    renderVisible();
                }
            });
        };

        const finishAuthReload = (seq) => {
            if (seq !== reloadSeq) return;
            authReloading = false;
            requestAnimationFrame(flushDeferredRelayout);
        };

        window.pkIsAuthManagerReloading = () => authReloading;
        window.pkDeferAuthManagerRelayout = () => { pendingRelayout = true; };

        return (reason = 'auth-recovered') => {
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                timer = null;
                try {
                    const ov = document.querySelector('.pk-ov');
                    if (!ov || ov.style.display === 'none') return;
                    if (!S || !Array.isArray(S.path) || S.path.length === 0) return;

                    const curNode = S.path[S.path.length - 1] || { id: '' };
                    const folderId = curNode.id || 'root';
                    const cacheKey = S.getRealCacheKey ? S.getRealCacheKey(folderId) : folderId;

                    console.log(`[Auth Sync] Forcing manager reload after auth recovery: ${reason}, folder=${folderId}`);

                    if (typeof globalNeedsSync !== 'undefined') globalNeedsSync = true;

                    if (typeof globalCache !== 'undefined') {
                        globalCache.delete(cacheKey);
                        if (folderId === 'root') {
                            globalCache.delete('');
                            globalCache.delete('root');
                        }
                    }

                    if (S.cache) {
                        S.cache.delete(cacheKey);
                        if (folderId === 'root') {
                            S.cache.delete('');
                            S.cache.delete('root');
                        }
                    }

                    if (typeof globalDirtyFolders !== 'undefined') {
                        globalDirtyFolders.add(folderId === 'root' ? '' : folderId);
                        if (folderId === 'root') globalDirtyFolders.add('root');
                    }

                    if (typeof scannedFolderIds !== 'undefined') {
                        scannedFolderIds.delete(folderId === 'root' ? '' : folderId);
                    }

                    const seq = ++reloadSeq;
                    authReloading = true;
                    pendingRelayout = false;

                    let reloadTask = Promise.resolve();

                    if (typeof load === 'function') {
                        reloadTask = Promise.resolve(load(false, true)).catch(e => console.warn('[Auth Sync] Forced reload failed:', e));
                    }
                    else if (typeof window.pkSmartRefreshTrigger === 'function') {
                        window.pkSmartRefreshTrigger(true);
                    }
                    else if (typeof refresh === 'function') {
                        refresh();
                    }

                    if (typeof runBackgroundCrawler === 'function' && typeof isBackgroundRunning !== 'undefined' && !isBackgroundRunning) {
                        runBackgroundCrawler();
                    }

                    reloadTask.finally(() => finishAuthReload(seq));
                } catch (e) {
                    authReloading = false;
                    requestAnimationFrame(flushDeferredRelayout);
                    console.warn('[Auth Sync] Force reload error:', e);
                }
            }, 260);
        };
    })();

    const AUTO_REFRESH_INTERVAL = 30000;

    const startSmartRefresh = () => {
        if (UI.win.dataset.autoRefresh) return null;
        UI.win.dataset.autoRefresh = "true";

        let silentAbortController = null;
        let retryTimer = null;
        let lastUserInteractionTime = 0;

        const updateInteractionTime = () => { lastUserInteractionTime = Date.now(); };
        el.addEventListener('mousedown', updateInteractionTime, true);
        el.addEventListener('keydown', updateInteractionTime, true);

        const diffWorkerBlob = new Blob([`
            self.onmessage = function(e) {
                const { oldList, newList } = e.data;
                if (oldList.length !== newList.length) { self.postMessage(true); return; }
                for (let i = 0; i < newList.length; i++) {
                    const a = oldList[i];
                    const b = newList[i];
                    if (a.id !== b.id || a.modified_time !== b.modified_time || a.size !== b.size || a.hash !== b.hash || a.starred !== b.starred) {
                        self.postMessage(true); return;
                    }
                }
                self.postMessage(false);
            };
        `], { type: 'application/javascript' });
        const diffWorker = new Worker(URL.createObjectURL(diffWorkerBlob));

        const runWatchdogAudit = async () => {
            if (document.hidden) return;
            try {
                const list = await apiShareList();
                const toAutoCancel = list.filter(it => it.kind === 'pikpak#share' && it.limit_count > 0 && it.save_count >= it.limit_count && it.share_status === 'OK');
                if (toAutoCancel.length > 0) {
                    const cancelIds = toAutoCancel.map(x => x.id);
                    const expiredGraveyard = JSON.parse(gmGet('pk_expired_shares', '[]'));
                    toAutoCancel.forEach(it => {
                        it.share_status = 'EXPIRED'; it._is_local_phantom = true;
                        expiredGraveyard.push(it);
                    });
                    if (expiredGraveyard.length > 50) expiredGraveyard.splice(0, expiredGraveyard.length - 50);
                    gmSet('pk_expired_shares', JSON.stringify(expiredGraveyard));
                    await apiCancelShare(cancelIds).catch(() => {});
                    const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                    cancelIds.forEach(id => delete store[id]);
                    gmSet('pk_share_limits', JSON.stringify(store));
                    console.log(`[Watchdog] Autonomous Audit: Auto-canceled ${cancelIds.length} shares.`);
                    if (S.shareMode && window.pkSmartRefreshTrigger) window.pkSmartRefreshTrigger(true);
                }
            } catch (e) { console.warn("[Watchdog] Audit failed", e); }
        };

        const checkAndRefresh = async (isRetry = false, bypassLock = false) => {
            if (document.hidden) return;

            const isTyping = ['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName);

            const hasUIState = S.getSelectedCount() > 0 || (UI.ctx && UI.ctx.style.display === 'block');
            const isInteracting = !bypassLock && (Date.now() - lastUserInteractionTime < 1500);

            const isBusy = S._isEmptyingTrash || S.loading || S.scanning || S.dupMode || S.isFlattened ||
                           hasUIState || isTyping || isInteracting;

            if (isBusy) {
                if (retryTimer) clearTimeout(retryTimer);
                retryTimer = setTimeout(() => checkAndRefresh(true, bypassLock), 2500);
                return;
            }

            if (S.historyMode || S.uploadMode) return;

            const cur = S.path[S.path.length - 1];
            const isStarredRoot = S.starredMode && S.path.length === 1;
            const isRecentRoot = S.recentMode && S.path.length === 1;

            const cacheKey = S.shareMode ? 'share_root' : (isStarredRoot ? 'starred_root' : (isRecentRoot ? 'recent_root' : (cur.id || 'root')));

            if (cur.id === 'virtual_search_root' || cur.id === 'analyze_root' || cur.id === 'upload_root') return;

            if (silentAbortController) silentAbortController.abort();
            silentAbortController = new AbortController();
            const signal = silentAbortController.signal;

            try {
                const runWatchdogAudit = async (targetList) => {
                    const toAutoCancel = targetList.filter(it => it.kind === 'pikpak#share' && it.limit_count > 0 && it.save_count >= it.limit_count && it.share_status === 'OK');
                    if (toAutoCancel.length > 0) {
                        const cancelIds = toAutoCancel.map(x => x.id);
                        const expiredGraveyard = JSON.parse(gmGet('pk_expired_shares', '[]'));
                        toAutoCancel.forEach(it => {
                            it.share_status = 'EXPIRED';
                            it._is_local_phantom = true;
                            expiredGraveyard.push(it);
                        });
                        if (expiredGraveyard.length > 50) expiredGraveyard.splice(0, expiredGraveyard.length - 50);
                        gmSet('pk_expired_shares', JSON.stringify(expiredGraveyard));
                        await apiCancelShare(cancelIds).catch(() => {});
                        const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                        cancelIds.forEach(id => delete store[id]);
                        gmSet('pk_share_limits', JSON.stringify(store));
                        console.log(`[Watchdog] Background Audit: Auto-canceled ${cancelIds.length} shares.`);
                        return true;
                    }
                    return false;
                };

                let allFetchedItems = [];

                if (!S.shareMode) {
                    apiShareList().then(list => runWatchdogAudit(list));
                }

                if (S.shareMode) {
                    allFetchedItems = await apiShareList();
                    await runWatchdogAudit(allFetchedItems);
                }
                else if (S.offlineMode) {
                    const rawTasks =[];
                    await apiTaskList(1000, (batch) => {
                        if (batch && batch.length) rawTasks.push(...batch);
                    });

                    allFetchedItems = rawTasks.map(t => {
                        const ref = t.reference_resource || {};
                        return {
                            id: t.id,
                            kind: 'drive#task',
                            name: ref.name || t.name || t.file_name || 'Untitled Task',
                            size: t.file_size,
                            phase: t.phase,
                            progress: parseInt(t.progress || 0),
                            message: t.message,
                            icon_link: t.icon_link,
                            thumbnail_link: ref.thumbnail_link ? ref.thumbnail_link : t.icon_link,
                            created_time: t.created_time,
                            modified_time: t.updated_time || ref.modified_time || '',
                            file_id: t.file_id || '',
                            source_url: (t.params && t.params.url) ? t.params.url : '',
                            params: Object.assign({}, t.params || {}, ref.params || {}),
                            mime_type: ref.mime_type || '',
                            starred: !!(ref.starred || (ref.tags && ref.tags.some(tg => tg.name === 'STAR')))
                        };
                    });
                }
                else if (S.historyMode) {
                    let keys = [];
                    if (typeof GM_listValues !== 'undefined') {
                        keys = GM_listValues();
                    } else {
                        keys = Object.keys(localStorage);
                    }

                    const historyItems = [];
                    const historyMap = new Map();

                    keys.forEach(k => {
                        if (k.startsWith('pk_progress_')) {
                            const id = k.replace('pk_progress_', '');
                            const raw = gmGet(k);
                            let data = { t: 0, d: 0, ts: 0 };

                            if (typeof raw === 'number') {
                                data.t = raw;
                                data.ts = 0;
                            } else if (typeof raw === 'object' && raw !== null) {
                                data = raw;
                            }

                            if (data.t > 1 || data.ts > 0) {
                                historyItems.push({ id, ...data });
                                historyMap.set(id, data);
                            }
                        }
                    });

                    historyItems.sort((a, b) => b.ts - a.ts);

                    const targetIds = historyItems.slice(0, 1000).map(x => x.id);
                    const CONCURRENCY = 6;

                    const fetchDetail = async (id) => {
                        try {
                            const netPriority = bypassLock ? 'high' : 'low';
                            const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/files/${id}?thumbnail_size=SIZE_MEDIUM`, { headers: getHeaders(), signal: signal, priority: netPriority });
                            if (!res.ok) return null;
                            const f = await res.json();
                            if (f && !f.trashed) return f;
                            return null;
                        } catch (e) {
                            return null;
                        }
                    };

                    for (let i = 0; i < targetIds.length; i += CONCURRENCY) {
                        if (document.hidden || signal.aborted || S.loading || S.scanning || S.dupMode || S.isFlattened || S.getSelectedCount() > 0) return;

                        const chunk = targetIds.slice(i, i + CONCURRENCY);
                        const results = await Promise.all(chunk.map(id => fetchDetail(id)));

                        results.forEach(rawF => {
                            if (!rawF) return;
                            const f = minifyFile(rawF);
                            const local = historyMap.get(f.id) || {};
                            f._history_progress = local.t || 0;
                            const cloudDur = f.params?.duration || 0;
                            f._history_duration = cloudDur || local.d || 0;
                            f._history_ts = local.ts || 0;
                            allFetchedItems.push(f);
                        });

                        if (allFetchedItems.length > 0 && i > 0 && i % 24 === 0) await sleep(10);
                    }

                    allFetchedItems.sort((a, b) => (b._history_ts || 0) - (a._history_ts || 0));
                }
                else if (S.recentMode) {
                    let nextToken = null;
                    const limit = 500;
                    do {
                        if (document.hidden || signal.aborted || S.loading || S.scanning || S.dupMode || S.isFlattened || S.getSelectedCount() > 0) return;
                        const filters = encodeURIComponent('{"phase":{"in":"PHASE_TYPE_COMPLETE"}}');
                        const url = `https://api-drive.mypikpak.com/drive/v1/tasks?limit=${limit}&filters=${filters}&thumbnail_size=SIZE_MEDIUM&with_reference_resource=true&_t=${Date.now()}${nextToken ? `&page_token=${nextToken}` : ''}`;

                        const netPriority = bypassLock ? 'high' : 'low';
                        const res = await fetch(url, { headers: getHeaders(), signal: signal, priority: netPriority });
                        if (!res.ok) {
                            if (res.status === 401 || res.status === 403) {
                                console.warn("[Recent] SWR fetch auth rejected.");
                                const didLogout = await confirmedLogout('recent-swr-fetch-auth', 5000, 5200);
                                if (didLogout) return;
                                return;
                            }
                            throw new Error("Recent SWR fetch error");
                        }

                        const json = await res.json();
                        const validTasks = (json.tasks ||[]).filter(t =>
                            t.phase === 'PHASE_TYPE_COMPLETE' &&
                            (t.type === 'offline' || t.type === 'upload') &&
                            t.file_id !== ""
                        );

                        const mapped = validTasks.map(t => {
                            const ref = t.reference_resource || {};
                            const mime = ref.mime_type || '';
                            const isFolder = (ref.kind === 'drive#folder') || (mime === 'application/x-directory') || (t.icon_link && t.icon_link.includes('folder'));
                            return {
                                id: t.file_id || t.id,
                                kind: isFolder ? 'drive#folder' : 'drive#file',
                                name: ref.name || t.file_name || t.name,
                                size: t.file_size,
                                thumbnail_link: ref.thumbnail_link || t.icon_link || '',
                                icon_link: t.icon_link || '',
                                web_content_link: t.file_id ? null : null,
                                created_time: t.created_time,
                                modified_time: t.updated_time || ref.modified_time || t.created_time,
                                mime_type: mime,
                                parent_id: '',
                                starred: !!(ref.starred || (ref.tags && ref.tags.some(tg => tg.name === 'STAR'))),
                                trashed: false,
                                params: Object.assign({}, t.params || {}, ref.params || {}),
                                _sourceTaskId: t.id
                            };
                        });

                        allFetchedItems.push(...mapped);
                        nextToken = json.next_page_token;
                        if (allFetchedItems.length >= 2000) break;
                    } while (nextToken);

                    const seen = new Set();
                    allFetchedItems = allFetchedItems.filter(f => {
                        if (seen.has(f.id)) return false;
                        seen.add(f.id);
                        return true;
                    });
                }
                else {
                    let nextToken = null;
                    const limit = 500;
                    const now = Date.now();

                    do {
                        if (document.hidden || signal.aborted || S.loading || S.scanning || S.dupMode || S.isFlattened || S.getSelectedCount() > 0) return;

                        const currentIsStarredRoot = S.starredMode && S.path.length === 1;
                        const targetParentId = (S.trashMode || currentIsStarredRoot) ? '*' : (cur.id || '');

                        const filterObj = { "trashed": { "eq": S.trashMode } };

                        if (currentIsStarredRoot) {
                            filterObj.trashed = { "eq": false };
                            filterObj.system_tag = { "in": "STAR" };
                        } else if (!S.trashMode) {
                            filterObj.phase = { "eq": "PHASE_TYPE_COMPLETE" };
                        }

                        const filters = `&filters=${encodeURIComponent(JSON.stringify(filterObj))}`;
                        const url = `https://api-drive.mypikpak.com/drive/v1/files?thumbnail_size=SIZE_MEDIUM&limit=${limit}${filters}&parent_id=${targetParentId}&_t=${now}${nextToken ? `&page_token=${nextToken}` : ''}`;

                        const netPriority = bypassLock ? 'high' : 'low';
                        const res = await fetch(url, { headers: getHeaders(), signal: signal, priority: netPriority });
                        if (!res.ok) {
                            if (res.status === 401 || res.status === 403) {
                                console.warn("[Recent] Silent fetch auth rejected.");
                                const didLogout = await confirmedLogout('recent-silent-fetch-auth', 5000, 5200);
                                if (didLogout) return;
                                return;
                            }
                            throw new Error("Silent fetch error");
                        }

                        const data = await res.json();
                        if (data.files) {
                            allFetchedItems.push(...data.files.map(f => minifyFile(f, true)));
                        }
                        nextToken = data.next_page_token;
                    } while (nextToken);
                }

                if (S.analyzeMode && S.analyzeMap) {
                    allFetchedItems.forEach(item => {
                        if (item.kind === 'drive#folder' && S.analyzeMap.has(item.id)) {
                            item.size = S.analyzeMap.get(item.id).size.toString();
                        }
                    });
                }

                if (document.hidden || signal.aborted || S.loading || S.scanning || S.dupMode || S.isFlattened || S.getSelectedCount() > 0) return;

                const nowCur = S.path[S.path.length - 1];
                const nowStarredRoot = S.starredMode && S.path.length === 1;
                let currentExpectedKey = 'root';
                if (S.shareMode) currentExpectedKey = 'share_root';
                else if (S.offlineMode) currentExpectedKey = 'offline_root';
                else if (nowStarredRoot) currentExpectedKey = 'starred_root';
                else currentExpectedKey = nowCur.id || 'root';

                if (currentExpectedKey === cacheKey) {
                    diffWorker.onmessage = (e) => {
                        const hasChanges = e.data;
                        if (!hasChanges) {
                            console.log("[SmartRefresh] Cache verified. No changes.");
                            return;
                        }

                        if ((!bypassLock && Date.now() - lastUserInteractionTime < 1500) || S.getSelectedCount() > 0) {
                            if (retryTimer) clearTimeout(retryTimer);
                            retryTimer = setTimeout(() => checkAndRefresh(true, bypassLock), 2000);
                            return;
                        }

                        if (allFetchedItems.length > 0) {
                            const sample = allFetchedItems[0];
                            if (S.shareMode) {
                            } else if (S.trashMode && !sample.trashed) {
                                console.warn("[SmartRefresh] Dirty data blocked: Home data trying to enter Trash view.");
                                return;
                            } else if (!S.trashMode && sample.trashed) {
                                console.warn("[SmartRefresh] Dirty data blocked: Trash data trying to enter Home view.");
                                return;
                            }
                        }

                        S.cache.set(cacheKey, allFetchedItems);
                        if (typeof globalCache !== 'undefined') globalCache.set(cacheKey, allFetchedItems);

                        const newItemMap = new Map();
                        const newStarredSet = new Set();
                        for (let i = 0; i < allFetchedItems.length; i++) {
                            const item = allFetchedItems[i];
                            newItemMap.set(item.id, item);
                            if (item.starred || (item.tags && item.tags.some(t => t.name === 'STAR'))) {
                                newStarredSet.add(item.id);
                            }
                        }

                        requestAnimationFrame(() => {
                            if (S.getSelectedCount() > 0 || S.loading || S.scanning) return;

                            S.items = allFetchedItems;
                            S.itemMap = newItemMap;
                            S.starredSet = newStarredSet;
                            const scrollTop = UI.vp.scrollTop;
                            refresh();
                            if (UI.vp) UI.vp.scrollTop = scrollTop;
                            console.log(`[SmartRefresh] SWR Sync Complete. Updated ${allFetchedItems.length} items.`);
                        });
                    };

                    const simplify = (list) => list.map(x => {
                        let diffHash = x.hash;
                        if (x.kind === 'drive#task') {
                            diffHash = `${x.phase}_${x.progress}_${x.params?.global_file_kind || ''}_${x.hash || ''}`;
                        } else if (x.params?.global_file_kind) {
                            diffHash = `${x.params.global_file_kind}_${x.hash || ''}`;
                        }
                        return {
                            id: x.id, modified_time: x.modified_time, size: x.size, hash: diffHash,
                            starred: !!(x.starred || (x.tags && x.tags.some(t => t.name === 'STAR')))
                        };
                    });

                    if (S.shareMode || cacheKey === 'share_root') {
                        const toAutoCancel = allFetchedItems.filter(it => it.kind === 'pikpak#share' && it.limit_count > 0 && it.save_count >= it.limit_count && it.share_status === 'OK');
                        if (toAutoCancel.length > 0) {
                            const cancelIds = toAutoCancel.map(x => x.id);
                            const expiredGraveyard = JSON.parse(gmGet('pk_expired_shares', '[]'));
                            toAutoCancel.forEach(it => {
                                it.share_status = 'EXPIRED';
                                it._is_local_phantom = true;
                                expiredGraveyard.push(it);
                            });
                            if (expiredGraveyard.length > 50) expiredGraveyard.splice(0, expiredGraveyard.length - 50);
                            gmSet('pk_expired_shares', JSON.stringify(expiredGraveyard));

                            await apiCancelShare(cancelIds).catch(() => {});
                            console.log(`[Watchdog] Auto-canceled and archived ${cancelIds.length} shares.`);

                            const store = JSON.parse(gmGet('pk_share_limits', '{}'));
                            cancelIds.forEach(id => delete store[id]);
                            gmSet('pk_share_limits', JSON.stringify(store));

                            allFetchedItems = allFetchedItems.filter(it => !cancelIds.includes(it.id));
                        }
                    }

                    diffWorker.postMessage({ oldList: simplify(S.items), newList: simplify(allFetchedItems) });
                }
            } catch (e) { }
        };

        window.pkSmartRefreshTrigger = (isForce = false) => {
            const session = globalCache.get('offline_session');
            if (S.offlineMode && (!session || !session.completed) && !isForce) {
                console.log(`[SmartRefresh] Pagination in progress (${S.items.length} items loaded). SWR standby...`);
                return;
            }
            if (S.recentMode) {
                const cached = globalCache.get('recent_root');
                if (cached && !Array.isArray(cached) && cached.nextToken && !isForce) {
                    console.log(`[SmartRefresh] Pagination in progress (${S.items.length} items loaded). SWR standby...`);
                    return;
                }
            }
            checkAndRefresh(false, isForce);
        };

        const onVisibilityChange = () => {
            if (retryTimer) clearTimeout(retryTimer);
            checkAndRefresh(false, false);
            runWatchdogAudit();
        };

        const uiSyncTimer = setInterval(() => checkAndRefresh(false, true), 60000);

        const watchdogTimer = setInterval(runWatchdogAudit, 60000);

        document.addEventListener('visibilitychange', onVisibilityChange);

        return {
            handler: onVisibilityChange,
            abort: () => {
                if(silentAbortController) silentAbortController.abort();
                if(retryTimer) clearTimeout(retryTimer);
                if(uiSyncTimer) clearInterval(uiSyncTimer);
                if(watchdogTimer) clearInterval(watchdogTimer);
                document.removeEventListener('visibilitychange', onVisibilityChange);
                el.removeEventListener('mousedown', updateInteractionTime, true);
                el.removeEventListener('keydown', updateInteractionTime, true);
                if (diffWorker) diffWorker.terminate();
                delete window.pkSmartRefreshTrigger;
            }
        };
    };

    const visibilityListener = startSmartRefresh();

    function handleClose() {
        let safePath = [...S.path];
        if (safePath.some(n => n.id === 'virtual_search_root' || n.id === 'analyze_root')) {
            safePath = S.preSearchPath || [{ id: '', name: L.btn_nav_home }];
        }
        globalSavedState = {
            path: safePath,
            trashMode: S.trashMode,
            isMaximized: UI.win.classList.contains('pk-maximized')
        };
        if (S.offlineMode && S.items.length > 0) {
            const cacheKey = 'offline_root';
            const cacheData = { items: [...S.items], nextToken: null };
            if (typeof globalCache !== 'undefined') globalCache.set(cacheKey, cacheData);
        }
        pkState = null;

        delete window.pkUpdateCrawlerUI;

        if (S && S.broadcast) S.broadcast.close();

        if (visibilityListener && visibilityListener.abort) {
            visibilityListener.abort();
        }

        if (typeof destroyTooltip === 'function') destroyTooltip();

        if (pkWinHideObserver) pkWinHideObserver.disconnect();
        el.remove();
        document.removeEventListener('keydown', keyHandler);
        document.removeEventListener('mouseup', mouseHandler);

        if (window._pkMouseSideNavHandler) {
            window.removeEventListener('mousedown', window._pkMouseSideNavHandler, true);
            window.removeEventListener('mouseup', window._pkMouseSideNavHandler, true);
            window.removeEventListener('auxclick', window._pkMouseSideNavHandler, true);
            window.removeEventListener('click', window._pkMouseSideNavHandler, true);
        }

        document.body.style.overflow = '';
        document.documentElement.style.overflow = '';
    }

    UI.btnClose.addEventListener('click', () => {
        document.body.classList.remove('pk-body-max');
        el.style.display = 'none';
    });

    updateCrawlerUI();

    S.updateBlCache();

    if (S.items && S.items.length > 0) {
        S.itemMap.clear();
        S.items.forEach(i => S.itemMap.set(i.id, i));
    }

    async function syncGlobalStarredStatus() {
        let nextToken = null;
        try {
            const latestStarredIds = new Set();

            do {
                const filter = encodeURIComponent('{"starred":{"eq":true},"trashed":{"eq":false}}');
                const url = `https://api-drive.mypikpak.com/drive/v1/files?filters=${filter}&limit=1000${nextToken ? `&page_token=${nextToken}` : ''}`;

                const res = await fetch(url, { headers: getHeaders() });
                if (!res.ok) break;

                const data = await res.json();
                if (data.files) {
                    data.files.forEach(f => latestStarredIds.add(f.id));
                }
                nextToken = data.next_page_token;
            } while (nextToken);

            S.pendingMap.forEach((targetStatus, id) => {
                if (targetStatus) {
                    latestStarredIds.add(id);
                } else {
                    latestStarredIds.delete(id);
                }
            });

            if (latestStarredIds.size > 0 || S.items.length > 0) {
                S.starredSet = latestStarredIds;
                let updatedCount = 0;
                if (typeof renderVisible === 'function') renderVisible();
            }
        } catch (e) {
            console.warn(L.err_star_sync_fail + ":", e);
        }
    }

    const refreshQuotaText = () => {
        const txt = el.querySelector('#pk-quota-txt');
        if (!txt || !S.quota) return;
        const isMaxNow = UI.win.classList.contains('pk-maximized');
        txt.textContent = isMaxNow ? `${S.quota.usedStr} / ${S.quota.limitStr}` : `${S.quota.pct}%`;
    };

    const updateQuotaUI = async () => {
        try {
            const res = await fetch(`https://api-drive.mypikpak.com/drive/v1/about?_t=${Date.now()}`, { headers: getHeaders() });
            if (!res.ok) {
                if (res.status === 401 || res.status === 403) {
                    console.warn("[Quota] Auth rejected.");
                    const didLogout = await confirmedLogout('quota-auth-rejected', 5000, 5200);
                    if (didLogout) return;
                }
                return;
            }
            const data = await res.json();
            const q = data.quota;
            if (!q) return;

            const used = parseInt(q.usage);
            const limit = parseInt(q.limit);
            const pct = Math.min(100, (used / limit) * 100).toFixed(1);

            const fQ = (v, d) => {
                let n = v, i = 0, units = ['B','KB','MB','GB','TB'];
                while(n >= 1024 && i < 4) { n /= 1024; i++; }
                return n.toFixed(d) + ' ' + units[i];
            };

            S.quota = {
                usedStr: fQ(used, 2),
                limitStr: fQ(limit, 0),
                pct: pct,
                usedRaw: used,
                limitRaw: limit
            };

            const bar = el.querySelector('#pk-quota-bar');
            const panel = el.querySelector('#pk-quota-panel');

            if (bar) bar.style.width = pct + '%';
            refreshQuotaText();

            if (panel) panel.setAttribute('data-pk-tip', `${L.lbl_storage}: ${pct}% (${S.quota.usedStr} / ${S.quota.limitStr})`);

            if (bar) {
                if (parseFloat(pct) > 90) bar.style.background = '#d93025';
                else if (parseFloat(pct) > 70) bar.style.background = '#faad14';
                else bar.style.background = 'var(--pk-pri)';
            }
        } catch (e) { console.warn("[Quota] Sync failed"); }
    };

    updateQuotaUI();
    const quotaTimer = setInterval(updateQuotaUI, 300000);

    const originalClose = UI.btnClose.onclick;
    UI.btnClose.onclick = (e) => {
        clearInterval(quotaTimer);
        if(originalClose) originalClose.call(UI.btnClose, e);
    };

    await load(false, true);

    if (globalSavedState && globalSavedState.scrollTop !== undefined) {
        setTimeout(() => {
            if (typeof UI !== 'undefined' && UI.vp) {
                UI.vp.scrollTop = globalSavedState.scrollTop;
                delete globalSavedState.scrollTop;
            }
        }, 50);
    }

    syncGlobalStarredStatus();

    if (gmGet('pk_turbo_mode', false)) {
        setTimeout(() => {
            if (location.href.includes('/login') || location.pathname.includes('login')) return;
            if (typeof showToast === 'function') {
                showToast(getStrings().msg_turbo_activated, 'success', 5000);
            }
        }, 800);
    }

    setTimeout(async () => {
        const stubStr = gmGet('pk_migration_stub', '');
        if (!stubStr) return;

        try {
            const stub = JSON.parse(stubStr);

            if (Date.now() - stub.timestamp > 86400000) {
                console.log("[Migration] Stub expired, clearing silently.");
                gmSet('pk_migration_stub', '');
                return;
            }

            const isAuthReady = await waitForAuth(5000);
            if (!isAuthReady) {
                console.warn("[Migration] Auth not ready, aborting detection.");
                return;
            }

            const aboutRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/about?_t=${Date.now()}`, { headers: getHeaders() });
            if (!aboutRes.ok) throw new Error(`About API Error ${aboutRes.status}`);
            const aboutData = await aboutRes.json();
            const currentUid = aboutData.sub;

            if (currentUid === stub.source_uid) {
                gmSet('pk_migration_stub', '');
                showToast(L.msg_migrate_same_account, 'warning');
                return;
            }

            if (await showConfirm(L.msg_migrate_detect.replace('{n}', stub.file_count), L.btn_migrate)) {
                setLoad(true);
                updateLoadTxt(L.msg_migrate_saving);

                const infoRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/share?share_id=${stub.share_id}&pass_code=${stub.pass_code}`, {
                    method: 'GET',
                    headers: getHeaders()
                });

                if (!infoRes.ok) throw new Error(`Share Info Fetch Error ${infoRes.status}`);
                const infoData = await infoRes.json();
                const passCodeToken = infoData.pass_code_token || "";

                const savePayload = {
                    share_id: stub.share_id,
                    pass_code_token: passCodeToken,
                    params: {
                        trace_file_ids: stub.file_ids.join(',')
                    }
                };

                const saveRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/share/restore`, {
                    method: 'POST',
                    headers: getHeaders(),
                    body: JSON.stringify(savePayload)
                });

                if (!saveRes.ok) {
                    const errData = await saveRes.json().catch(() => ({}));
                    if (errData.error === 'file_restore_own' || errData.error_code === 9) {
                        gmSet('pk_migration_stub', '');
                        setLoad(false);
                        showToast(L.msg_migrate_same_account, 'warning');
                        return;
                    }
                    if (errData.error === 'file_space_not_enough' || errData.error_code === 8) {
                        setLoad(false);
                        const keep = await showConfirm(L.msg_migrate_quota_err.replace('{d}', errData.error_description), L.title_migrate_fail);
                        if (!keep) gmSet('pk_migration_stub', '');
                        return;
                    }
                    throw new Error(errData.error_description || `Save API Error ${saveRes.status}`);
                }
                const saveData = await saveRes.json();

                if (saveData.task_id) {
                    let isDone = false;
                    let pollCount = 0;
                    while(!isDone && pollCount < 300) {
                        await sleep(2000);
                        pollCount++;
                        const tRes = await fetch(`https://api-drive.mypikpak.com/drive/v1/tasks/${saveData.task_id}`, { headers: getHeaders() });
                        if (!tRes.ok) continue;
                        const tData = await tRes.json();
                        if (tData.phase === 'PHASE_TYPE_COMPLETE') isDone = true;
                        else if (tData.phase === 'PHASE_TYPE_ERROR') throw new Error("Transfer task failed on server.");
                    }
                    if (!isDone) console.warn("[Migration] Task polling timed out, but might still be running.");
                }

                gmSet('pk_migration_stub', '');
                setLoad(false);
                await showAlert(L.msg_migrate_success, "🎉 " + L.title_alert);
                UI.btnRefresh.click();

            } else {
                gmSet('pk_migration_stub', '');
            }
        } catch(e) {
            setLoad(false);
            const keep = await showConfirm(L.msg_migrate_err_keep.replace('{e}', e.message), L.title_alert);
            if (!keep) gmSet('pk_migration_stub', '');
        }
    }, 1500);
}

let backgroundQueue = [];
let isBackgroundRunning = false;
let scannedFolderIds = new Set();
let globalPreloadPromise = null;
let globalCache = new Map();
let globalLineageMap = new Map();
let globalParentIndex = new Map();
let globalTombstoneCache = new Map();
let globalDirtyFolders = new Set();
let globalNeedsSync = false;
let isGlobalIndexReady = false;
let hasShownGlobalWarnSession = false;
let serverClockOffset = 0;
let hasSyncedTime = false;
let globalSavedState = null;

const syncTime = (headers) => {
    if (!headers) return;
    const serverDate = headers.get('Date') || headers.get('date');
    if (serverDate) {
        const remoteTime = new Date(serverDate).getTime();
        const localTime = Date.now();
        serverClockOffset = remoteTime - localTime;
        hasSyncedTime = true;
    }
};

const getServerNow = () => Date.now() + serverClockOffset;
let isGUISensitive = false;
let pkState = null;

const indexParents = (parentId, parentName, files) => {
    if (!files || !Array.isArray(files)) return;
    const pId = parentId || 'root';
    const pName = parentName || 'Root';

    for (const f of files) {
        if (f.kind === 'drive#folder') {
            globalParentIndex.set(f.id, { id: pId, name: pName });
        }
    }
};

const DurationProber = (() => {
    let queue = [];
    let isRunning = false;
    let probeVideo = null;
    let loopTimer = null;
    let runToken = 0;

    const startNext = async () => {
        if (!isRunning || queue.length === 0) { isRunning = false; return; }

        const currentToken = runToken;

        if (document.hidden) {
            console.log(`[Prober] Running in background... Queue: ${queue.length}`);
        }

        const isUserWatching = !!document.getElementById('pk-player-ov');
        const isSystemScanning = typeof pkState !== 'undefined' && pkState && pkState.scanning;

        if (isUserWatching || isSystemScanning) {
            loopTimer = setTimeout(startNext, 2000);
            return;
        }

        const item = queue.shift();
        if (!probeVideo) {
            probeVideo = document.createElement('video');
            probeVideo.muted = true;
            probeVideo.style.display = 'none';
        }

        console.log(`[Prober] Probing duration for: ${item.name}`);

        let watchdog = null;
        const currentVideo = probeVideo;

        const cleanup = () => {
            if (watchdog) clearTimeout(watchdog);
            if (currentVideo) {
                currentVideo.removeEventListener('loadedmetadata', onLoaded);
                currentVideo.removeEventListener('error', onError);
                currentVideo.src = "";
                currentVideo.load();
            }
            if (isRunning && currentToken === runToken) {
                loopTimer = setTimeout(startNext, 1500);
            }
        };

        const saveAndNotify = (targetItem, dur) => {
            gmSet('pk_duration_' + targetItem.id, dur);
            if (typeof pkState !== 'undefined' && pkState) {
                [pkState.items, pkState.display].forEach(list => {
                    const found = list.find(i => i.id === targetItem.id);
                    if (found && found.params) found.params.duration = dur;
                });
                const row = document.querySelector(`.pk-row[data-id="${targetItem.id}"]`);
                if (row) {
                    const cols = row.children;
                    const durCol = cols.length > 1 ? cols[cols.length - 2] : null;

                    if (durCol) {
                        durCol.style.color = 'var(--pk-pri)';
                        durCol.textContent = fmtDur(dur);
                        setTimeout(() => { if(durCol) durCol.style.color = ''; }, 2000);
                    }
                }
            }
        };

        const onLoaded = () => {
            if (currentToken === runToken) {
                const dur = Math.round(currentVideo.duration);
                if (dur > 0) {
                    console.log(`[Prober] Success: ${item.name} -> ${dur}s`);
                    saveAndNotify(item, dur);
                }
            }
            cleanup();
        };

        const onError = () => { cleanup(); };

        currentVideo.addEventListener('loadedmetadata', onLoaded);
        currentVideo.addEventListener('error', onError);

        try {
            const res = await apiGet(item.id);

            if (currentToken !== runToken) { cleanup(); return; }
            if (!res || !res.web_content_link) { cleanup(); return; }

            const url = res.web_content_link;
            const nameLower = (item.name || '').toLowerCase();
            const urlLower = url.toLowerCase();

            const isM3u8 = nameLower.endsWith('.m3u8') || urlLower.includes('.m3u8');
            const isWmv = nameLower.endsWith('.wmv') || urlLower.includes('.wmv') || nameLower.endsWith('.asf') || urlLower.includes('.asf');
            const isAvi = nameLower.endsWith('.avi') || urlLower.includes('.avi') || nameLower.endsWith('.divx') || urlLower.includes('.divx');
            const isFlv = nameLower.endsWith('.flv') || urlLower.includes('.flv');
            const isMkv = nameLower.endsWith('.mkv') || urlLower.includes('.mkv');
            const isRmvb = nameLower.endsWith('.rmvb') || urlLower.includes('.rmvb') || nameLower.endsWith('.rm') || urlLower.includes('.rm');

            if (isM3u8) {
                const fetchOptions = window.AbortSignal ? { signal: AbortSignal.timeout(15000) } : {};
                const text = await fetch(url, fetchOptions).then(r => r.text());

                if (currentToken === runToken) {
                    const matches = text.matchAll(/#EXTINF:([\d.]+)/g);
                    let total = 0;
                    for (const m of matches) total += parseFloat(m[1]);
                    if (total > 0) saveAndNotify(item, Math.round(total));
                }
                cleanup();
            }
            else if (isWmv || isAvi || isFlv || isMkv || isRmvb) {
                const rangeEnd = isMkv ? 65535 : 8191;
                const fetchOptions = {
                    headers: { 'Range': `bytes=0-${rangeEnd}` },
                    ...(window.AbortSignal ? { signal: AbortSignal.timeout(15000) } : {})
                };

                try {
                    const response = await fetch(url, fetchOptions);
                    if (currentToken === runToken && (response.ok || response.status === 206)) {
                        const buffer = await response.arrayBuffer();
                        const view = new DataView(buffer);
                        const bytes = new Uint8Array(buffer);
                        let seconds = 0;
                        let formatName = '';

                        if (isWmv) {
                            formatName = nameLower.endsWith('.asf') ? 'ASF' : 'WMV';
                            const guid = [0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65];
                            let foundIdx = -1;
                            for (let i = 0; i < bytes.length - 88; i++) {
                                let match = true;
                                for (let j = 0; j < 16; j++) { if (bytes[i + j] !== guid[j]) { match = false; break; } }
                                if (match) { foundIdx = i; break; }
                            }
                            if (foundIdx !== -1) {
                                const playDur = view.getBigUint64(foundIdx + 64, true);
                                const preroll = view.getBigUint64(foundIdx + 80, true);
                                seconds = Number(playDur - preroll) / 10000000;
                            }
                        } else if (isAvi) {
                            formatName = nameLower.endsWith('.divx') ? 'DIVX' : 'AVI';
                            const avih = [0x61, 0x76, 0x69, 0x68];
                            let foundIdx = -1;
                            for (let i = 0; i < bytes.length - 28; i++) {
                                if (bytes[i] === avih[0] && bytes[i+1] === avih[1] && bytes[i+2] === avih[2] && bytes[i+3] === avih[3]) { foundIdx = i; break; }
                            }
                            if (foundIdx !== -1) {
                                const microSecPerFrame = view.getUint32(foundIdx + 8, true);
                                const totalFrames = view.getUint32(foundIdx + 24, true);
                                seconds = (microSecPerFrame * totalFrames) / 1000000;
                            }
                        } else if (isFlv) {
                            formatName = 'FLV';
                            const durKey = [0x00, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00];
                            let foundIdx = -1;
                            for (let i = 0; i < bytes.length - 19; i++) {
                                let match = true;
                                for (let j = 0; j < 11; j++) { if (bytes[i + j] !== durKey[j]) { match = false; break; } }
                                if (match) { foundIdx = i; break; }
                            }
                            if (foundIdx !== -1) seconds = view.getFloat64(foundIdx + 11, false);
                        } else if (isRmvb) {
                            formatName = 'RM/RMVB';
                            const prop = [0x50, 0x52, 0x4F, 0x50];
                            let foundIdx = -1;
                            for (let i = 0; i < bytes.length - 36; i++) {
                                if (bytes[i] === prop[0] && bytes[i+1] === prop[1] && bytes[i+2] === prop[2] && bytes[i+3] === prop[3]) { foundIdx = i; break; }
                            }
                            if (foundIdx !== -1) {
                                const ms = view.getUint32(foundIdx + 32, false);
                                seconds = ms / 1000;
                            }
                        } else if (isMkv) {
                            formatName = 'MKV';
                            let timecodeScale = 1000000;
                            let durationVal = 0;

                            for (let i = 0; i < bytes.length - 10; i++) {
                                if (bytes[i] === 0x2A && bytes[i+1] === 0xD7 && bytes[i+2] === 0xB1) {
                                    let len = bytes[i+3] & 0x7F;
                                    if (len === 3) timecodeScale = (bytes[i+4]<<16) | (bytes[i+5]<<8) | bytes[i+6];
                                    if (len === 4) timecodeScale = (bytes[i+4]<<24) | (bytes[i+5]<<16) | (bytes[i+6]<<8) | bytes[i+7];
                                    break;
                                }
                            }
                            for (let i = 0; i < bytes.length - 10; i++) {
                                if (bytes[i] === 0x44 && bytes[i+1] === 0x89) {
                                    let lenByte = bytes[i+2];
                                    if (lenByte === 0x84) {
                                        durationVal = view.getFloat32(i+3, false);
                                        break;
                                    } else if (lenByte === 0x88) {
                                        durationVal = view.getFloat64(i+3, false);
                                        break;
                                    }
                                }
                            }
                            if (durationVal > 0) {
                                seconds = (durationVal * timecodeScale) / 1000000000;
                            }
                        }

                        if (seconds > 0) {
                            console.log(`[Prober] Success (${formatName} Binary Parsing): ${item.name} -> ${Math.round(seconds)}s`);
                            saveAndNotify(item, Math.round(seconds));
                        } else {
                            console.warn(`[Prober] Binary parsed but got 0 duration for ${item.name}`);
                        }
                    }
                } catch (e) {
                    console.warn(`[Prober] Failed to parse binary header for ${item.name}`);
                }
                cleanup();
            }
            else {
                if (document.hidden) {
                    console.log(`[Prober] Video tag throttled by browser, pausing prober: ${item.name}`);
                    queue.unshift(item);
                    isRunning = false;
                    cleanup();
                    return;
                }

                watchdog = setTimeout(() => {
                    console.warn(`[Prober] Watchdog timeout fetching metadata for: ${item.name}`);
                    cleanup();
                }, 15000);

                currentVideo.src = url;
                currentVideo.load();
            }
        } catch(e) {
            cleanup();
        }
    };

    return {
        add: (item, isBackground = false) => {
            if (queue.some(i => i.id === item.id)) return;
            if (isBackground) {
                queue.push(item);
            } else {
                queue.unshift(item);
            }
            if (!isRunning) { isRunning = true; startNext(); }
        },
        checkAndRun: () => {
            if (!isRunning && queue.length > 0) {
                isRunning = true;
                startNext();
            }
        },
        reset: () => {
            console.log(`[Prober] Resetting queue (Dropped ${queue.length} tasks).`);
            runToken++;
            queue = [];
            isRunning = false;
            if (loopTimer) clearTimeout(loopTimer);
            if (probeVideo) {
                probeVideo.removeAttribute('src');
                probeVideo.load();
                probeVideo = null;
            }
        }
    };
})();

const minifyFile = (f, isBackground = false) => {
    if (f._minified) return f;
    const { id, kind, name, parent_id, size, mime_type, thumbnail_link, icon_link, web_content_link, hash, gcid, md5_checksum } = f;
    const trashed = !!f.trashed;
    const tags = f.tags ? [...f.tags] : [];
    const lineage = f._lineage ? [...f._lineage] : undefined;
    const isStarred = !!(f.starred || f.star || f.is_star || (tags.some(t => t.name === 'STAR')));
    let duration = 0;
    const parse = (v) => { if (!v) return 0; const n = parseInt(v, 10); return isNaN(n) ? 0 : n; };
    if (f.video_media_metadata?.duration) duration = parse(f.video_media_metadata.duration);
    if (!duration && f.audio_media_metadata?.duration) duration = parse(f.audio_media_metadata.duration);
    if (!duration && f.medias && Array.isArray(f.medias)) {
        for (const m of f.medias) {
            const d = m.duration ? parse(m.duration) : (m.video?.duration ? parse(m.video.duration) : 0);
            if (d > 0) { duration = d; break; }
        }
    }
    if (!duration && f.params?.duration) duration = parse(f.params.duration);
    let final_modified_time = f.modified_time;
    if (!duration && id) duration = gmGet('pk_duration_' + id, 0);
    if (kind === 'drive#folder' && id) {
        const localFMod = gmGet('pk_fmod_' + id);
        if (localFMod) final_modified_time = localFMod;
    }
    if (!duration && kind === 'drive#file') {
        const ext = (name || '').split('.').pop().toLowerCase();
        const mime = (mime_type || '').toLowerCase();
        if (['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'ts'].includes(ext) || mime.startsWith('video/')) {
            const probeItem = { id, name, kind, mime_type, parent_id, size };
            setTimeout(() => DurationProber.add(probeItem, isBackground), 3000);
        }
    }
    return {
        id,
        kind,
        name,
        parent_id,
        size,
        file_count: (function() {
            if (f.file_count !== undefined) return f.file_count;
            if (f.usage && f.usage.file_count !== undefined) return f.usage.file_count;
            if (f.params && f.params.file_count !== undefined) return f.params.file_count;
            if (f.audit && f.audit.file_count !== undefined) return f.audit.file_count;
            return undefined;
        })(),
        modified_time: final_modified_time,
        thumbnail_link,
        icon_link,
        mime_type,
        trashed,
        web_content_link,
        tags,
        starred: isStarred,
        params: {
            duration,
            width: f.video_media_metadata?.width || f.params?.width,
            height: f.video_media_metadata?.height || f.params?.height,
            global_file_kind: f.params?.global_file_kind,
            global_file_root: f.params?.global_file_root
        },
        hash: hash || md5_checksum || gcid,
        _lineage: lineage,
        _minified: true
    };
};

async function runBackgroundCrawler() {
    if (isBackgroundRunning) return;
    isBackgroundRunning = true;

    if (window.pkUpdateCrawlerUI) window.pkUpdateCrawlerUI();
    const homeBtn = document.querySelector('#pk-nav-home');
    if (homeBtn) homeBtn.classList.add('pk-status-dot');

    const userSetLimit = parseInt(localStorage.getItem('pk_user_limit') || "50");
    const BACKGROUND_MAX_CONCURRENCY = Math.min(userSetLimit, 32);
    let currentConcurrencyLimit = 5;

    const MIN_CONCURRENCY = 2;
    let activeRequests = 0;
    let pendingRetries = 0;

    const fetchFolderContents = async (folder) => {
        activeRequests++;
        try {
            let files;
            if (globalCache.has(folder.id)) {
                files = globalCache.get(folder.id);
            } else {
                files = await apiList(folder.id, 1000, null, null, false, true);
                if (!isGUISensitive) {
                    globalCache.set(folder.id, files);
                }
            }

            if (files && Array.isArray(files)) {
                for (let i = 0; i < files.length; i++) {
                    const f = files[i];
                    if (f.kind === 'drive#folder') {
                        if (!scannedFolderIds.has(f.id)) {
                            backgroundQueue.push({ id: f.id, name: f.name, retryCount: 0 });
                            scannedFolderIds.add(f.id);
                        }
                    }
                }
            }

            if (currentConcurrencyLimit < BACKGROUND_MAX_CONCURRENCY) {
                currentConcurrencyLimit += 0.2;
            }

        } catch (err) {
            currentConcurrencyLimit = MIN_CONCURRENCY;
            folder.retryCount = (folder.retryCount || 0) + 1;
            const backoffTime = Math.min(folder.retryCount * 5000, 30000);
            pendingRetries++;
            try {
                await sleep(backoffTime);
                if (!isGUISensitive) backgroundQueue.unshift(folder);
            } finally {
                pendingRetries--;
            }
        } finally {
            activeRequests--;
        }
    };

    while (backgroundQueue.length > 0 || activeRequests > 0 || pendingRetries > 0 || (typeof globalDirtyFolders !== 'undefined' && globalDirtyFolders.size > 0)) {
        const isUserBusy = pkState && (pkState.scanning || pkState.loading || document.getElementById('pk-player-ov'));

        if (isUserBusy) {
            if (homeBtn) homeBtn.classList.remove('pk-status-dot');

            await sleep(2000);
            continue;
        }

        if (homeBtn) homeBtn.classList.add('pk-status-dot');

        if (backgroundQueue.length > 0 && activeRequests < Math.floor(currentConcurrencyLimit)) {
            const folder = backgroundQueue.pop();

            fetchFolderContents(folder);
            await sleep(50);
        }
        else if (activeRequests > 0 || pendingRetries > 0) {
            await sleep(500);
        }
        else if (typeof globalDirtyFolders !== 'undefined' && globalDirtyFolders.size > 0) {
            const dirtyId = Array.from(globalDirtyFolders)[0];
            globalDirtyFolders.delete(dirtyId);

            if (typeof globalCache !== 'undefined') {
                for (const k of globalCache.keys()) {
                    if (k && k.startsWith('__analyze_nodeMap_')) {
                        globalCache.delete(k);
                    }
                }
            }

            const normalizedId = dirtyId === 'root' ? '' : dirtyId;
            backgroundQueue.unshift({ id: normalizedId, name: "Dirty_Reval", retryCount: 0 });
            continue;
        }
        else {
            let discovered = 0;
            if (typeof globalCache !== 'undefined') {
                for (const [parentFid, files] of globalCache) {
                    if (!files) continue;
                    for (let i = 0; i < files.length; i++) {
                        const f = files[i];
                        if (f.kind === 'drive#folder' && !scannedFolderIds.has(f.id)) {
                            backgroundQueue.push({ id: f.id, name: f.name, retryCount: 0 });
                            scannedFolderIds.add(f.id);
                            discovered++;
                        }
                    }
                    if (discovered > 0) break;
                }
            }
            if (discovered === 0) break;
        }
    }
    isBackgroundRunning = false;
    if (window.pkUpdateCrawlerUI) window.pkUpdateCrawlerUI();
    if (homeBtn) homeBtn.classList.remove('pk-status-dot');
}

async function preLoadRootFiles(onProgress) {
    if (globalPreloadPromise) return globalPreloadPromise;

    console.log("Initiating background pre-load...");
    globalPreloadPromise = new Promise(async (resolve) => {
        try {

            const isAuthReady = await waitForAuth(2500);
            if (!isAuthReady) {
                console.warn("Background Crawler: Auth token wait timeout. Halting preload.");
                resolve(false);
                return;
            }

            if (typeof window.pkCleanupGhostFiles === 'function') window.pkCleanupGhostFiles();

            const rootFiles = await apiList('', 1000, onProgress);
            globalCache.set('root', rootFiles);
            console.log("Background pre-load (Root) successful.");

            const rootFolders = rootFiles.filter(f => f.kind === 'drive#folder');
            rootFolders.forEach(f => {
                if (!scannedFolderIds.has(f.id)) {
                    backgroundQueue.push({ id: f.id, name: f.name });
                    scannedFolderIds.add(f.id);
                }
            });

            runBackgroundCrawler();

        } catch (e) {
            console.error("Background pre-load failed:", e);
        } finally {
            resolve(globalCache.has('root'));
        }
    });

    return globalPreloadPromise;
}

async function tryInject() {
    if (location.href.includes('/login') || location.pathname.includes('login')) return;

    console.log("🚀 PikPak Script: Attempting inject.");
    if (document.getElementById('pk-launch')) {
        console.log("🚀 PikPak Script: Already injected.");
        return;
    }

    if (!document.body) {
        console.log("🚀 PikPak Script: Body not ready, retrying...");
        setTimeout(tryInject, 500);
        return;
    }

    inject();

    window.pkScheduleResumeTasks = (reason = 'visibility') => {
        if (window.__pkResumeTaskTimer) clearTimeout(window.__pkResumeTaskTimer);
        const waitMs = (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) ? 900 : 120;
        window.__pkResumeTaskTimer = setTimeout(() => {
            if (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) {
                console.log(`🚀 [Resume Sync] Auth recovery active, deferring resume tasks (${reason}).`);
                window.pkScheduleResumeTasks(`auth-wait:${reason}`);
                return;
            }
            if (location.href.includes('/login') || location.pathname.includes('login')) return;
            if (typeof DurationProber !== 'undefined') DurationProber.checkAndRun();
            if (typeof isBackgroundRunning !== 'undefined' && !isBackgroundRunning) runBackgroundCrawler();
            if (typeof pkState !== 'undefined' && pkState && pkState.uploadMode) {
                if (typeof refresh === 'function') refresh();
            }
        }, waitMs);
    };

    const isTurbo = typeof GM_getValue !== 'undefined' ? GM_getValue('pk_turbo_mode', false) : false;
    if (isTurbo) {
        console.log("🚀[Turbo Mode] Fast-track rendering.");
        const startTurbo = async () => {
            if (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) {
                console.log("🚀[Turbo Mode] Auth recovery active, delaying fast-track render.");
                setTimeout(startTurbo, 900);
                return;
            }
            const preload = preLoadRootFiles();
            if (!document.querySelector('.pk-ov')) {
                await ensureI18nReadyBeforeOpen();
                await openManager(globalCache, preload);
            }
        };
        setTimeout(startTurbo, 100);
    } else {
        const startPreload = () => {
            if (typeof window.pkIsAuthRecoveryActive === 'function' && window.pkIsAuthRecoveryActive()) {
                console.log("🚀 Preload deferred during auth recovery.");
                setTimeout(startPreload, 900);
                return;
            }
            preLoadRootFiles();
        };
        setTimeout(startPreload, 1500);
    }

    if (!window.__pkResumeHooksBound) {
        window.__pkResumeHooksBound = true;

        document.addEventListener('visibilitychange', () => {
            if (!document.hidden && typeof window.pkScheduleResumeTasks === 'function') {
                window.pkScheduleResumeTasks('visibility');
            }
        });

        window.addEventListener('focus', () => {
            if (!document.hidden && typeof window.pkScheduleResumeTasks === 'function') {
                window.pkScheduleResumeTasks('focus');
            }
        });

        window.addEventListener('pageshow', () => {
            if (!document.hidden && typeof window.pkScheduleResumeTasks === 'function') {
                window.pkScheduleResumeTasks('pageshow');
            }
        });
    }

    console.log("🚀 PikPak Script: INJECT SUCCESS! Background pre-load started.");
}

function inject() {
    if (document.getElementById('pk-launch')) return;
    const b = document.createElement('button'); b.id = 'pk-launch';

    const isTurbo = typeof GM_getValue !== 'undefined' ? GM_getValue('pk_turbo_mode', false) : false;
    const displayStyle = isTurbo ? 'none!important' : 'flex!important';

    b.style.cssText = `position:fixed;bottom:20px;right:20px;width:50px;height:50px;border-radius:50%;background:#1a5eff;border:none;cursor:pointer;z-index:2147483647;box-shadow:0 4px 12px rgba(0,0,0,0.3);padding:0;overflow:hidden;transition:transform 0.1s;display:${displayStyle};align-items:center!important;justify-content:center!important;`;

    b.innerHTML = `<svg width="60%" height="60%" viewBox="0 0 238 200" version="1.1" xmlns="http://www.w3.org/2000/svg">
        <path d="M0 0 C1.82724609 0.01353516 1.82724609 0.01353516 3.69140625 0.02734375 C4.59761719 0.03894531 5.50382812 0.05054688 6.4375 0.0625 C5.95097979 7.11704304 4.33696858 12.90149479 1.6875 19.4375 C1.35234375 20.32566406 1.0171875 21.21382812 0.671875 22.12890625 C0.3315625 22.98097656 -0.00875 23.83304688 -0.359375 24.7109375 C-0.66198242 25.47583496 -0.96458984 26.24073242 -1.27636719 27.02880859 C-3.01571023 29.77913653 -4.60880008 30.70366989 -7.5625 32.0625 C-10.93383789 32.72265625 -10.93383789 32.72265625 -14.78515625 33.125 C-15.47874237 33.20142731 -16.17232849 33.27785461 -16.88693237 33.3565979 C-18.36660067 33.51855298 -19.84685768 33.67520381 -21.3276062 33.82696533 C-25.19232303 34.22318595 -29.05286739 34.65697538 -32.9140625 35.0859375 C-33.67180466 35.16903168 -34.42954681 35.25212585 -35.21025085 35.33773804 C-40.99791882 35.97875931 -46.74864414 36.77615252 -52.5 37.6875 C-61.81496788 39.10080547 -71.19269316 40.07620454 -80.5625 41.0625 C-19.8425 41.0625 40.8775 41.0625 103.4375 41.0625 C91.8875 39.7425 80.3375 38.4225 68.4375 37.0625 C63.8175 36.4025 59.1975 35.7425 54.4375 35.0625 C49.17221542 34.42736314 43.90722683 33.79696512 38.63671875 33.20703125 C37.62996094 33.08714844 36.62320313 32.96726563 35.5859375 32.84375 C34.69052246 32.74126953 33.79510742 32.63878906 32.87255859 32.53320312 C30.35601376 32.0467485 28.59527547 31.44037784 26.4375 30.0625 C23.38532266 24.97553776 21.3341425 19.45473677 19.1875 13.9375 C18.91695801 13.25671387 18.64641602 12.57592773 18.36767578 11.87451172 C16.82394482 7.78804812 16.13851057 4.42502757 16.4375 0.0625 C33.20320897 -0.76054389 50.04132 2.04640823 66.578125 4.53515625 C70.96365446 5.13439358 75.35589707 5.627565 79.75488281 6.11669922 C97.85972043 8.13836316 97.85972043 8.13836316 106.6875 9.4375 C107.39487305 9.52700928 108.10224609 9.61651855 108.83105469 9.70874023 C113.96714941 10.51808328 116.87598017 12.31623275 120.4375 16.0625 C121.69830294 18.53927732 122.67025259 20.7202309 123.5625 23.3125 C124.02136126 24.56846882 124.48232815 25.8236702 124.9453125 27.078125 C125.27250149 28.00288179 125.27250149 28.00288179 125.60630035 28.94632053 C126.38750394 31.05750635 126.38750394 31.05750635 127.44002533 32.93062496 C131.07482517 39.83448151 131.00351579 46.31795394 130.95507812 53.99243164 C130.96050802 55.37978344 130.96763552 56.76712947 130.97631836 58.15446472 C130.99445028 61.89829685 130.98752708 65.6416848 130.97480202 69.38552403 C130.96462344 73.31622656 130.97408092 77.24689291 130.98034668 81.17759705 C130.98760817 87.77544941 130.97807403 94.37312221 130.95898438 100.97094727 C130.93720936 108.58452515 130.94427739 116.19767461 130.96629 123.81124216 C130.98447611 130.36524706 130.98698696 136.91912344 130.97653532 143.47314543 C130.97031913 147.38014362 130.96941296 151.2869408 130.98268127 155.19392586 C130.99428653 158.8672447 130.9861299 162.54001414 130.96310425 166.213274 C130.95534421 168.19404482 130.96713242 170.17486244 130.97961426 172.15560913 C130.90049754 180.52230774 129.95755225 186.09535704 124.25390625 192.5234375 C123.51011719 193.15507812 122.76632813 193.78671875 122 194.4375 C121.25878906 195.08460938 120.51757812 195.73171875 119.75390625 196.3984375 C114.7661098 199.98157627 110.22842399 200.35421576 104.22135925 200.32992554 C103.39785408 200.33445665 102.5743489 200.33898776 101.72588903 200.34365618 C98.968488 200.35630894 96.21128426 200.35467924 93.45385742 200.35302734 C91.475975 200.35901206 89.49809491 200.36581748 87.5202179 200.37338257 C82.14823484 200.39105594 76.77631549 200.39573853 71.40430617 200.39701414 C66.91878502 200.39891354 62.4332787 200.40627158 57.94776326 200.41335833 C47.36384951 200.42964512 36.77996977 200.43452703 26.19604492 200.43310547 C15.28118177 200.43190408 4.36651636 200.45300486 -6.54829675 200.4845928 C-15.92170288 200.51075235 -25.29504442 200.52147289 -34.66848677 200.52019465 C-40.26569836 200.51968491 -45.86273424 200.52537507 -51.45990944 200.54655075 C-56.725388 200.56592749 -61.99052314 200.5660613 -67.25601387 200.55151749 C-69.1861191 200.54942757 -71.11624579 200.55414114 -73.04631424 200.5662384 C-75.68641426 200.58171127 -78.32533312 200.57236959 -80.96540833 200.55697632 C-81.72466655 200.56726344 -82.48392478 200.57755057 -83.26619083 200.58814943 C-90.327556 200.49750269 -96.39704041 197.82485418 -101.375 192.75 C-102.18904297 191.95142578 -102.18904297 191.95142578 -103.01953125 191.13671875 C-108.29053612 184.05088689 -108.01804154 177.09915158 -108.0300293 168.55004883 C-108.04229625 167.18245883 -108.05575106 165.81487905 -108.07029724 164.4473114 C-108.10523797 160.74401042 -108.12059214 157.04088761 -108.13013434 153.33744264 C-108.13673436 151.01403475 -108.14708893 148.69067299 -108.15863991 146.36728477 C-108.19836069 138.23287671 -108.22038571 130.09860956 -108.22827148 121.96411133 C-108.23610728 114.43116961 -108.28516577 106.89925647 -108.35333699 99.36664182 C-108.41007964 92.86514961 -108.43519788 86.36399446 -108.43721896 79.86225718 C-108.43904166 75.9947118 -108.45309089 72.1282487 -108.50003624 68.26096535 C-108.72797687 48.29049317 -107.52961567 30.83210742 -95.5625 14.0625 C-92.23797604 10.732487 -88.44904231 10.20048941 -83.953125 9.5 C-83.20613342 9.37633057 -82.45914185 9.25266113 -81.68951416 9.12524414 C-74.04584045 7.901492 -66.3645662 7.06662299 -58.66394043 6.29776001 C-54.62860447 5.8940274 -50.59547976 5.46951727 -46.5625 5.04296875 C-45.77776306 4.96008102 -44.99302612 4.8771933 -44.18450928 4.79179382 C-36.33754684 3.9513441 -28.53467892 2.87051571 -20.734375 1.67578125 C-13.79617508 0.63078847 -7.03103815 -0.06826251 0 0 Z M-47 131 L-15 106 L-47 81 L-47 91 L-27 106 L-47 121 Z M45.4375 89.0625 C43.16309531 93.61130937 44.11732026 99.81887268 44.0625 104.8125 C44.02511719 106.08867188 43.98773438 107.36484375 43.94921875 108.6796875 C43.6563417 116.25277258 43.6563417 116.25277258 46.7109375 122.91015625 C50.0632924 125.55649945 51.41007501 125.90713502 55.50390625 125.58984375 C58.83921214 124.68021487 60.4149221 122.75927054 62.4375 120.0625 C64.03299443 115.26404894 63.62174204 110.1852134 63.625 105.1875 C63.64336914 103.71603516 63.64336914 103.71603516 63.66210938 102.21484375 C63.77173933 93.57358621 63.77173933 93.57358621 59.75 86.1875 C54.01325068 83.39664894 49.78182352 84.71817648 45.4375 89.0625 Z M-18.5625 155.0625 C-20.89546251 157.88967213 -20.89546251 157.88967213 -20.3125 161.125 C-19.8031756 164.161959 -19.8031756 164.161959 -17.5625 166.0625 C-15.5023267 166.81656896 -13.41368556 167.49416461 -11.3125 168.125 C-10.19359375 168.46660156 -9.0746875 168.80820313 -7.921875 169.16015625 C-1.62436639 170.85169635 4.26860909 171.24487637 10.75 171.25 C11.9555957 171.26836914 11.9555957 171.26836914 13.18554688 171.28710938 C21.14907742 171.30632948 28.31945463 169.57146397 35.875 167.125 C36.88433594 166.80660156 37.89367187 166.48820313 38.93359375 166.16015625 C41.73511224 165.200361 41.73511224 165.200361 43.4375 162.0625 C43.1133631 158.74009676 42.82973697 157.45473697 40.4375 155.0625 C35.63637087 154.61062902 31.50016124 155.74460874 26.9375 157.0625 C14.69655136 160.31686985 0.092469 160.8899845 -11.5625 155.0625 C-15.0625 154.72916667 -15.0625 154.72916667 -18.5625 155.0625 Z " fill="#FDFDFD" transform="translate(107.5625,-0.0625)"/>
    </svg>`;

    const savedLeft = gmGet('pk_pos_left', null);
    const savedTop = gmGet('pk_pos_top', null);

    if (savedLeft !== null && savedTop !== null) {
        b.style.bottom = 'auto';
        b.style.right = 'auto';
        b.style.left = savedLeft;
        b.style.top = savedTop;
    } else {
        b.style.bottom = 'auto';
        b.style.right = 'auto';
        b.style.left = '10px';
        b.style.top = '430px';
    }

    let isDragging = false;
    let dragStartX, dragStartY;

    const constrainBall = () => {
        const rect = b.getBoundingClientRect();
        let newLeft = rect.left;
        let newTop = rect.top;
        const maxL = window.innerWidth - rect.width;
        const maxT = window.innerHeight - rect.height;

        if (newLeft > maxL) newLeft = maxL;
        if (newTop > maxT) newTop = maxT;
        if (newLeft < 0) newLeft = 0;
        if (newTop < 0) newTop = 0;

        b.style.left = newLeft + 'px';
        b.style.top = newTop + 'px';
    };

    window.addEventListener('resize', constrainBall);

    const blockNativeDrag = (e) => { e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = 'copy'; };
    b.addEventListener('dragenter', blockNativeDrag);
    b.addEventListener('dragover', blockNativeDrag);
    b.addEventListener('drop', (e) => {
        blockNativeDrag(e);
        if (!document.querySelector('.pk-ov') || document.querySelector('.pk-ov').style.display === 'none') {
            const clickEvt = new MouseEvent('mousedown', { clientX: e.clientX, clientY: e.clientY });
            b.dispatchEvent(clickEvt);
            const upEvt = new MouseEvent('mouseup', { clientX: e.clientX, clientY: e.clientY });
            document.dispatchEvent(upEvt);
        }
    });

    b.onmousedown = (e) => {
        isDragging = false;
        dragStartX = e.clientX;
        dragStartY = e.clientY;
        const rect = b.getBoundingClientRect();
        b.style.bottom = 'auto'; b.style.right = 'auto';
        b.style.left = rect.left + 'px'; b.style.top = rect.top + 'px';
        b.style.transition = 'none';
        const offsetX = e.clientX - rect.left;
        const offsetY = e.clientY - rect.top;

        const onMove = (em) => {
            if (!isDragging && (Math.abs(em.clientX - dragStartX) > 3 || Math.abs(em.clientY - dragStartY) > 3)) {
                isDragging = true;
            }
            if (isDragging) {
                b.style.left = (em.clientX - offsetX) + 'px';
                b.style.top = (em.clientY - offsetY) + 'px';
            }
        };

        const onUp = async () => {
            document.removeEventListener('mousemove', onMove);
            document.removeEventListener('mouseup', onUp);
            b.style.transition = 'transform 0.1s';

            if (!isDragging) {
                if (window.innerWidth < 720 || window.innerHeight < 340) {
                    return;
                }

                let currentHeaders = getHeaders();
                if (!currentHeaders.Authorization || currentHeaders.Authorization.length < 10) {
                    console.warn("PikPak Master: No auth token on button click. Entering recovery recheck.");
                    if (typeof window.pkEnterAuthRecoveryWindow === 'function') window.pkEnterAuthRecoveryWindow('button-click-missing-token', 4000);
                    let isAuthReady = await waitForAuth(4500);
                    if (!isAuthReady) {
                        await sleep(800);
                        isAuthReady = await waitForAuth(4200);
                    }
                    if (!isAuthReady) {
                        console.warn("PikPak Master: Button click auth wait timeout.");
                        const didLogout = await confirmedLogout('button-click-missing-token-final', 4000, 4500);
                        if (didLogout) return;
                        return;
                    }
                    currentHeaders = getHeaders();
                }

                if (typeof window.pkMarkAuthRecovered === 'function') window.pkMarkAuthRecovered();

                const existingWin = document.querySelector('.pk-ov');
                if (existingWin) {
                    if (existingWin.style.display === 'none') {
                        const needGridReopenRelayout = !!existingWin.querySelector('.pk-win.pk-grid-view');

                        if (existingWin.querySelector('.pk-win.pk-maximized')) {
                            document.body.classList.add('pk-body-max');
                        }

                        existingWin.style.display = 'flex';
                        existingWin.focus();

                        if (needGridReopenRelayout) {
                            requestAnimationFrame(() => {
                                requestAnimationFrame(() => {
                                    window.dispatchEvent(new Event('resize'));
                                });
                            });
                        }
                    } else {
                        existingWin.style.display = 'none';
                    }
                } else {
                    await ensureI18nReadyBeforeOpen();
                    openManager(globalCache, globalPreloadPromise);
                }
            } else {
                constrainBall();
                gmSet('pk_pos_left', b.style.left);
                gmSet('pk_pos_top', b.style.top);
            }
        };
        document.addEventListener('mousemove', onMove);
        document.addEventListener('mouseup', onUp);
    };

    document.body.appendChild(b);

    setTimeout(constrainBall, 0);

    console.log("🚀 Button Created!");
}

const startObserver = () => {
    if (!document.body) return;
    const obs = new MutationObserver(() => {
        if (!document.getElementById('pk-launch')) {
            tryInject();
        }
    });
    obs.observe(document.body, { childList: true, subtree: true });
};

if (document.readyState === 'loading') {
    window.addEventListener('DOMContentLoaded', () => {
        tryInject();
        startObserver();
    });
} else {
    tryInject();
    startObserver();
}
})()
;