Google Search Custom Sidebar

Customizable Google Search sidebar: quick filters (lang, time, filetype, country, date), site search, Verbatim & Personalization tools.

当前为 2025-05-23 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Google Search Custom Sidebar
// @name:zh-TW   Google 搜尋自訂側邊欄
// @name:ja      Google検索カスタムサイドバー
// @namespace    https://greasyfork.org/en/users/1467948-stonedkhajiit
// @version      0.1.2
// @description  Customizable Google Search sidebar: quick filters (lang, time, filetype, country, date), site search, Verbatim & Personalization tools.
// @description:zh-TW Google 搜尋自訂側邊欄:快速篩選(語言、時間、檔案類型、國家、日期)、站內搜尋、一字不差與個人化工具。
// @description:ja Google検索カスタムサイドバー:高速フィルター(言語,期間,ファイル形式,国,日付)、サイト検索、完全一致検索とパーソナライズツール。
// @match        https://www.google.com/search*
// @include      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?q=[^&]+(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?(?:tbm=(?:isch|shop|bks|flm|fin|lcl)|udm=(?:2|28))(?:&.+)?$/
// @icon         https://www.google.com/favicon.ico
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_deleteValue
// @run-at       document-idle
// @author       StonedKhajiit
// @license      MIT
// @require      https://update.greasyfork.org/scripts/535624/1594215/Google%20Search%20Custom%20Sidebar%20-%20i18n.js
// @require      https://update.greasyfork.org/scripts/535625/1592847/Google%20Search%20Custom%20Sidebar%20-%20Styles.js
// ==/UserScript==

(function() {
    'use strict';

    // --- Constants and Configuration ---
    const SCRIPT_INTERNAL_NAME = 'GoogleSearchCustomSidebar';
    const SCRIPT_VERSION = '0.1.2';
    const LOG_PREFIX = `[${SCRIPT_INTERNAL_NAME} v${SCRIPT_VERSION}]`;

    const DEFAULT_SECTION_ORDER = [
        'sidebar-section-language', 'sidebar-section-time', 'sidebar-section-filetype',
        'sidebar-section-occurrence',
        'sidebar-section-country', 'sidebar-section-date-range', 'sidebar-section-site-search', 'sidebar-section-tools'
    ];

    const defaultSettings = {
        sidebarPosition: { left: 0, top: 80 },
        sectionStates: {},
        theme: 'system',
        hoverMode: false,
        idleOpacity: 0.8,
        sidebarWidth: 135,
        fontSize: 12.5,
        headerIconSize: 16,
        verticalSpacingMultiplier: 0.5,
        interfaceLanguage: 'auto',
        visibleSections: {
            'sidebar-section-language': true, 'sidebar-section-time': true, 'sidebar-section-filetype': true,
            'sidebar-section-occurrence': true,
            'sidebar-section-country': true, 'sidebar-section-date-range': true,
            'sidebar-section-site-search': true, 'sidebar-section-tools': true
        },
        sectionDisplayMode: 'remember',
        accordionMode: false,
        resetButtonLocation: 'topBlock',
        verbatimButtonLocation: 'header',
        advancedSearchLinkLocation: 'header',
        personalizationButtonLocation: 'tools',
        googleScholarShortcutLocation: 'tools', // New setting for Scholar shortcut location
        googleTrendsShortcutLocation: 'tools',  // New setting for Trends shortcut location
        countryDisplayMode: 'iconAndText',
        customLanguages: [],
        customTimeRanges: [],
        customFiletypes: [
            { text: "📄Documents", value: "pdf OR docx OR doc OR odt OR rtf OR txt" },
            { text: "💹Spreadsheets", value: "xlsx OR xls OR ods OR csv" },
            { text: "📊Presentations", value: "pptx OR ppt OR odp OR key" },
        ],
        customCountries: [],
        displayLanguages: [],
        displayCountries: [],
        favoriteSites: [
            // == SINGLE SITES: GENERAL KNOWLEDGE & REFERENCE ==
            { text: 'Wikipedia (EN)', url: 'en.wikipedia.org' },
            { text: 'Wiktionary', url: 'wiktionary.org' },
            { text: 'Internet Archive', url: 'archive.org' },

            // == SINGLE SITES: DEVELOPER & TECH ==
            { text: 'GitHub', url: 'github.com' },
            { text: 'GitLab', url: 'gitlab.com' },
            { text: 'Stack Overflow', url: 'stackoverflow.com' },
            { text: 'Hacker News', url: 'news.ycombinator.com' },
            { text: 'Greasy Fork', url: 'greasyfork.org' },

            // == SINGLE SITES: SOCIAL, FORUMS & COMMUNITIES ==
            { text: 'Reddit', url: 'reddit.com' },
            { text: 'X', url: 'x.com' },
            { text: 'Mastodon', url: 'mastodon.social' },
            { text: 'Bluesky', url: 'bsky.app' },
            { text: 'Lemmy', url: 'lemmy.world' },

            // == SINGLE SITES: ENTERTAINMENT, ARTS & HOBBIES ==
            { text: 'IMDb', url: 'imdb.com' },
            { text: 'TMDb', url: 'themoviedb.org' },
            { text: 'Letterboxd', url: 'letterboxd.com' },
            { text: 'Metacritic', url: 'metacritic.com' },
            { text: 'OpenCritic', url: 'opencritic.com' },
            { text: 'Steam', url: 'store.steampowered.com' },
            { text: 'Bandcamp', url: 'bandcamp.com' },
            { text: 'Last.fm', url: 'last.fm' },

            // == COMBINED SITE GROUPS ==
            {
                text: '💬Social',
                url: 'x.com OR facebook.com OR instagram.com OR threads.net OR bluesky.social OR mastodon.social OR reddit.com OR tumblr.com OR linkedin.com OR lemmy.world'
            },
            {
                text: '📦Repositories',
                url: 'github.com OR gitlab.com OR bitbucket.org OR codeberg.org OR sourceforge.net'
            },
            {
                text: '🎓Academics',
                url: 'scholar.google.com OR arxiv.org OR researchgate.net OR jstor.org OR academia.edu OR pubmed.ncbi.nlm.nih.gov OR semanticscholar.org OR core.ac.uk'
            },
            {
                text: '📰News',
                url: 'bbc.com/news OR reuters.com OR apnews.com OR nytimes.com OR theguardian.com OR cnn.com OR wsj.com'
            },
            {
                text: '🎨Creative',
                url: 'behance.net OR dribbble.com OR artstation.com OR deviantart.com'
            }
        ],
        enableSiteSearchCheckboxMode: true,
        enableFiletypeCheckboxMode: true,
        sidebarCollapsed: false,
        draggableHandleEnabled: true,
        enabledPredefinedOptions: {
            language: ['lang_en'],
            country: ['countryUS'],
            time: ['d', 'w', 'm', 'y', 'h'],
            filetype: ['pdf', 'docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls', 'txt']
        },
        sidebarSectionOrder: [...DEFAULT_SECTION_ORDER],
        hideGoogleLogoWhenExpanded: false,
    };

    let sidebar = null, systemThemeMediaQuery = null;
    const MIN_SIDEBAR_TOP_POSITION = 5;
    let debouncedSaveSettings;
    let globalMessageTimeout = null;

    const IDS = {
        SIDEBAR: 'customizable-search-sidebar', SETTINGS_OVERLAY: 'settings-overlay', SETTINGS_WINDOW: 'settings-window',
        COLLAPSE_BUTTON: 'sidebar-collapse-button', SETTINGS_BUTTON: 'open-settings-button',
        TOOL_RESET_BUTTON: 'tool-reset-button', TOOL_VERBATIM: 'tool-verbatim', TOOL_PERSONALIZE: 'tool-personalize-search',
        TOOL_GOOGLE_SCHOLAR: 'tool-google-scholar',
        TOOL_GOOGLE_TRENDS: 'tool-google-trends',
        APPLY_SELECTED_SITES_BUTTON: 'apply-selected-sites-button',
        APPLY_SELECTED_FILETYPES_BUTTON: 'apply-selected-filetypes-button',
        FIXED_TOP_BUTTONS: 'sidebar-fixed-top-buttons',
        SETTINGS_MESSAGE_BAR: 'gscs-settings-message-bar',
        SETTING_WIDTH: 'setting-sidebar-width', SETTING_FONT_SIZE: 'setting-font-size', SETTING_HEADER_ICON_SIZE: 'setting-header-icon-size',
        SETTING_VERTICAL_SPACING: 'setting-vertical-spacing', SETTING_INTERFACE_LANGUAGE: 'setting-interface-language',
        SETTING_SECTION_MODE: 'setting-section-display-mode', SETTING_ACCORDION: 'setting-accordion-mode',
        SETTING_DRAGGABLE: 'setting-draggable-handle', SETTING_RESET_LOCATION: 'setting-reset-button-location',
        SETTING_VERBATIM_LOCATION: 'setting-verbatim-button-location', SETTING_ADV_SEARCH_LOCATION: 'setting-adv-search-link-location',
        SETTING_PERSONALIZE_LOCATION: 'setting-personalize-button-location',
        SETTING_SCHOLAR_LOCATION: 'setting-scholar-shortcut-location', // New ID for settings UI
        SETTING_TRENDS_LOCATION: 'setting-trends-shortcut-location',   // New ID for settings UI
        SETTING_SITE_SEARCH_CHECKBOX_MODE: 'setting-site-search-checkbox-mode',
        SETTING_FILETYPE_SEARCH_CHECKBOX_MODE: 'setting-filetype-search-checkbox-mode',
        SETTING_COUNTRY_DISPLAY_MODE: 'setting-country-display-mode', SETTING_THEME: 'setting-theme',
        SETTING_HOVER: 'setting-hover-mode', SETTING_OPACITY: 'setting-idle-opacity',
        SETTING_HIDE_GOOGLE_LOGO: 'setting-hide-google-logo',
        TAB_PANE_GENERAL: 'tab-pane-general', TAB_PANE_APPEARANCE: 'tab-pane-appearance', TAB_PANE_FEATURES: 'tab-pane-features', TAB_PANE_CUSTOM: 'tab-pane-custom',
        SITES_LIST: 'custom-sites-list', LANG_LIST: 'custom-languages-list', TIME_LIST: 'custom-time-ranges-list',
        FT_LIST: 'custom-filetypes-list', COUNTRIES_LIST: 'custom-countries-list',
        NEW_SITE_NAME: 'new-site-name', NEW_SITE_URL: 'new-site-url', ADD_SITE_BTN: 'add-site-button',
        NEW_LANG_TEXT: 'new-lang-text', NEW_LANG_VALUE: 'new-lang-value', ADD_LANG_BTN: 'add-lang-button',
        NEW_TIME_TEXT: 'new-timerange-text', NEW_TIME_VALUE: 'new-timerange-value', ADD_TIME_BTN: 'add-timerange-button',
        NEW_FT_TEXT: 'new-ft-text', NEW_FT_VALUE: 'new-ft-value', ADD_FT_BTN: 'add-ft-button',
        NEW_COUNTRY_TEXT: 'new-country-text', NEW_COUNTRY_VALUE: 'new-country-value', ADD_COUNTRY_BTN: 'add-country-button',
        DATE_MIN: 'date-min', DATE_MAX: 'date-max', DATE_RANGE_ERROR_MSG: 'date-range-error-msg',
        SIDEBAR_SECTION_ORDER_LIST: 'sidebar-section-order-list',
        NOTIFICATION_CONTAINER: 'gscs-notification-container',
        MODAL_ADD_NEW_OPTION_BTN: 'gscs-modal-add-new-option-btn',
        MODAL_PREDEFINED_CHOOSER_CONTAINER: 'gscs-modal-predefined-chooser-container',
        MODAL_PREDEFINED_CHOOSER_LIST: 'gscs-modal-predefined-chooser-list',
        MODAL_PREDEFINED_CHOOSER_ADD_BTN: 'gscs-modal-predefined-chooser-add-btn',
        MODAL_PREDEFINED_CHOOSER_CANCEL_BTN: 'gscs-modal-predefined-chooser-cancel-btn',
        CLEAR_SITE_SEARCH_OPTION: 'clear-site-search-option',
        CLEAR_FILETYPE_SEARCH_OPTION: 'clear-filetype-search-option'
    };
    const CSS = {
        SIDEBAR_COLLAPSED: 'sidebar-collapsed', SIDEBAR_HEADER: 'sidebar-header', SIDEBAR_CONTENT_WRAPPER: 'sidebar-content-wrapper',
        DRAG_HANDLE: 'sidebar-drag-handle', SETTINGS_BUTTON: 'sidebar-settings-button', HEADER_BUTTON: 'sidebar-header-button',
        SIDEBAR_SECTION: 'sidebar-section', FIXED_TOP_BUTTON_ITEM: 'fixed-top-button-item', SECTION_TITLE: 'section-title',
        SECTION_CONTENT: 'section-content', COLLAPSED: 'collapsed', FILTER_OPTION: 'filter-option', SELECTED: 'selected',
        SITE_SEARCH_ITEM_CHECKBOX: 'site-search-item-checkbox',
        FILETYPE_SEARCH_ITEM_CHECKBOX: 'filetype-search-item-checkbox',
        APPLY_SITES_BUTTON: 'apply-sites-button',
        APPLY_FILETYPES_BUTTON: 'apply-filetypes-button',
        DATE_INPUT_LABEL: 'date-input-label', DATE_INPUT: 'date-input', TOOL_BUTTON: 'tool-button', ACTIVE: 'active',
        CUSTOM_LIST: 'custom-list', ITEM_CONTROLS: 'item-controls', EDIT_CUSTOM_ITEM: 'edit-custom-item',
        DELETE_CUSTOM_ITEM: 'delete-custom-item', CUSTOM_LIST_INPUT_GROUP: 'custom-list-input-group',
        ADD_CUSTOM_BUTTON: 'add-custom-button', SETTINGS_HEADER: 'settings-header', SETTINGS_CLOSE_BTN: 'settings-close-button',
        SETTINGS_TABS: 'settings-tabs', TAB_BUTTON: 'tab-button', SETTINGS_TAB_CONTENT: 'settings-tab-content',
        TAB_PANE: 'tab-pane', SETTING_ITEM: 'setting-item', INLINE_LABEL: 'inline', SETTINGS_FOOTER: 'settings-footer',
        SAVE_BUTTON: 'save-button', CANCEL_BUTTON: 'cancel-button', RESET_BUTTON: 'reset-button',
        LIGHT_THEME: 'light-theme', DARK_THEME: 'dark-theme',
        SIMPLE_ITEM: 'simple', RANGE_VALUE: 'range-value', RANGE_HINT: 'setting-range-hint', SECTION_ORDER_LIST: 'section-order-list',
        INPUT_ERROR_MESSAGE: 'input-error-message', ERROR_VISIBLE: 'error-visible', INPUT_HAS_ERROR: 'input-has-error',
        DATE_RANGE_ERROR_MSG: 'date-range-error-message',
        MESSAGE_BAR: 'gscs-message-bar', MSG_INFO: 'gscs-msg-info', MSG_SUCCESS: 'gscs-msg-success',
        MSG_WARNING: 'gscs-msg-warning', MSG_ERROR: 'gscs-msg-error', MANAGE_CUSTOM_BUTTON: 'manage-custom-button',
        NOTIFICATION: 'gscs-notification',
        NTF_INFO: 'gscs-ntf-info', NTF_SUCCESS: 'gscs-ntf-success',
        NTF_WARNING: 'gscs-ntf-warning', NTF_ERROR: 'gscs-ntf-error',
        DRAGGING_ITEM: 'gscs-dragging-item',
        DRAG_OVER_HIGHLIGHT: 'gscs-drag-over-highlight',
        DRAG_ICON: 'gscs-drag-icon',
        REMOVE_FROM_LIST_BTN: 'gscs-remove-from-list-btn',
        MODAL_ADD_NEW_OPTION_BTN_CLASS: 'gscs-modal-add-new-option-button-class',
        MODAL_PREDEFINED_CHOOSER_CLASS: 'gscs-modal-predefined-chooser',
        MODAL_PREDEFINED_CHOOSER_ITEM: 'gscs-modal-predefined-chooser-item',
        SETTING_VALUE_HINT: 'setting-value-hint'
    };
    const DATA_ATTR = {
        FILTER_TYPE: 'filterType', FILTER_VALUE: 'filterValue', SITE_URL: 'siteUrl', SECTION_ID: 'sectionId',
        FILETYPE_VALUE: 'filetypeValue',
        LIST_ID: 'listId', INDEX: 'index', LISTENER_ATTACHED: 'listenerAttached', TAB: 'tab', MANAGE_TYPE: 'managetype',
        ITEM_TYPE: 'itemType', ITEM_ID: 'itemId'
    };
    const STORAGE_KEY = 'googleSearchCustomSidebarSettings_v1';
    const SVG_ICONS = {
        chevronLeft: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`,
        chevronRight: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`,
        settings: `<svg xmlns="http://www.w3.org/2000/svg" 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>`,
        reset: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
        verbatim: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-4.3875 -3.2375) scale(1.15)"><path d="M6 17.5c0 1.5 1.5 2.5 3 2.5h1.5c1.5 0 3-1 3-2.5V9c0-1.5-1.5-2.5-3-2.5H9C7.5 6.5 6 7.5 6 9v8.5z"/><path d="M15 17.5c0 1.5 1.5 2.5 3 2.5h1.5c1.5 0 3-1 3-2.5V9c0-1.5-1.5-2.5-3-2.5H18c-1.5 0-3 1-3 2.5v8.5z"/></g></svg>`,
        magnifyingGlass: `<svg xmlns="http://www.w3.org/2000/svg" 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>`,
        close: `<svg xmlns="http://www.w3.org/2000/svg" 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><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
        edit: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
        delete: `<svg xmlns="http://www.w3.org/2000/svg" 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"></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><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>`,
        add: `<svg xmlns="http://www.w3.org/2000/svg" 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><line x1="5" y1="12" x2="19" y2="12"></line></svg>`,
        update: `<svg xmlns="http://www.w3.org/2000/svg" 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>`,
        personalization: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`,
        dragGrip: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor"><circle cx="9" cy="6" r="1.5"/><circle cx="15" cy="6" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="18" r="1.5"/><circle cx="15" cy="18" r="1.5"/></svg>`,
        removeFromList: `<svg xmlns="http://www.w3.org/2000/svg" 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><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
        googleScholar: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em"><path d="M12 3L1 9l11 6 9-4.91V17h2V9L12 3zm0 11.24L3.62 9 12 5.11 20.38 9 12 14.24zM5 13.18V17.5a1.5 1.5 0 001.5 1.5h11A1.5 1.5 0 0019 17.5v-4.32l-7 3.82-7-3.82z"/></svg>`,
        googleTrends: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="1em" height="1em"><path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6h-6z"/></svg>`
    };
    const PREDEFINED_OPTIONS = {
        language: [ { textKey: 'predefined_lang_en', value: 'lang_en' }, { textKey: 'predefined_lang_ja', value: 'lang_ja' }, { textKey: 'predefined_lang_ko', value: 'lang_ko' }, { textKey: 'predefined_lang_fr', value: 'lang_fr' }, { textKey: 'predefined_lang_de', value: 'lang_de' }, { textKey: 'predefined_lang_es', value: 'lang_es' }, { textKey: 'predefined_lang_it', value: 'lang_it' }, { textKey: 'predefined_lang_pt', value: 'lang_pt' }, { textKey: 'predefined_lang_ru', value: 'lang_ru' }, { textKey: 'predefined_lang_ar', value: 'lang_ar' }, { textKey: 'predefined_lang_hi', value: 'lang_hi' }, { textKey: 'predefined_lang_nl', value: 'lang_nl' }, { textKey: 'predefined_lang_tr', value: 'lang_tr' }, { textKey: 'predefined_lang_vi', value: 'lang_vi' }, { textKey: 'predefined_lang_th', value: 'lang_th' }, { textKey: 'predefined_lang_id', value: 'lang_id' }, { textKey: 'predefined_lang_zh_tw', value: 'lang_zh-TW' }, { textKey: 'predefined_lang_zh_cn', value: 'lang_zh-CN' }, { textKey: 'predefined_lang_zh_all', value: 'lang_zh-TW|lang_zh-CN' }, ],
        country: [ { textKey: 'predefined_country_us', value: 'countryUS' }, { textKey: 'predefined_country_gb', value: 'countryGB' }, { textKey: 'predefined_country_ca', value: 'countryCA' }, { textKey: 'predefined_country_au', value: 'countryAU' }, { textKey: 'predefined_country_de', value: 'countryDE' }, { textKey: 'predefined_country_fr', value: 'countryFR' }, { textKey: 'predefined_country_jp', value: 'countryJP' }, { textKey: 'predefined_country_kr', value: 'countryKR' }, { textKey: 'predefined_country_cn', value: 'countryCN' }, { textKey: 'predefined_country_in', value: 'countryIN' }, { textKey: 'predefined_country_br', value: 'countryBR' }, { textKey: 'predefined_country_mx', value: 'countryMX' }, { textKey: 'predefined_country_es', value: 'countryES' }, { textKey: 'predefined_country_it', value: 'countryIT' }, { textKey: 'predefined_country_ru', value: 'countryRU' }, { textKey: 'predefined_country_nl', value: 'countryNL' }, { textKey: 'predefined_country_sg', value: 'countrySG' }, { textKey: 'predefined_country_hk', value: 'countryHK' }, { textKey: 'predefined_country_tw', value: 'countryTW' }, { textKey: 'predefined_country_my', value: 'countryMY' }, { textKey: 'predefined_country_vn', value: 'countryVN' }, { textKey: 'predefined_country_ph', value: 'countryPH' }, { textKey: 'predefined_country_th', value: 'countryTH' }, { textKey: 'predefined_country_za', value: 'countryZA' }, { textKey: 'predefined_country_tr', value: 'countryTR' }, ],
        time: [ { textKey: 'predefined_time_h', value: 'h' }, { textKey: 'predefined_time_h2', value: 'h2' }, { textKey: 'predefined_time_h6', value: 'h6' }, { textKey: 'predefined_time_h12', value: 'h12' }, { textKey: 'predefined_time_d', value: 'd' }, { textKey: 'predefined_time_d2', value: 'd2' }, { textKey: 'predefined_time_d3', value: 'd3' }, { textKey: 'predefined_time_w', value: 'w' }, { textKey: 'predefined_time_m', value: 'm' }, { textKey: 'predefined_time_y', value: 'y' }, ],
        filetype: [ { textKey: 'predefined_filetype_pdf', value: 'pdf' }, { textKey: 'predefined_filetype_docx', value: 'docx' }, { textKey: 'predefined_filetype_doc', value: 'doc' }, { textKey: 'predefined_filetype_xlsx', value: 'xlsx' }, { textKey: 'predefined_filetype_xls', value: 'xls' }, { textKey: 'predefined_filetype_pptx', value: 'pptx' }, { textKey: 'predefined_filetype_ppt', value: 'ppt' }, { textKey: 'predefined_filetype_txt', value: 'txt' }, { textKey: 'predefined_filetype_rtf', value: 'rtf' }, { textKey: 'predefined_filetype_html', value: 'html' }, { textKey: 'predefined_filetype_htm', value: 'htm' }, { textKey: 'predefined_filetype_xml', value: 'xml' }, { textKey: 'predefined_filetype_jpg', value: 'jpg' }, { textKey: 'predefined_filetype_png', value: 'png' }, { textKey: 'predefined_filetype_gif', value: 'gif' }, { textKey: 'predefined_filetype_svg', value: 'svg' }, { textKey: 'predefined_filetype_bmp', value: 'bmp' }, { textKey: 'predefined_filetype_js', value: 'js' }, { textKey: 'predefined_filetype_css', value: 'css' }, { textKey: 'predefined_filetype_py', value: 'py' }, { textKey: 'predefined_filetype_java', value: 'java' }, { textKey: 'predefined_filetype_cpp', value: 'cpp' }, { textKey: 'predefined_filetype_cs', value: 'cs' }, { textKey: 'predefined_filetype_kml', value: 'kml'}, { textKey: 'predefined_filetype_kmz', value: 'kmz'}, ]
    };
    const ALL_SECTION_DEFINITIONS = [
        { id: 'sidebar-section-language', type: 'filter', titleKey: 'section_language', scriptDefined: [{textKey:'filter_any_language',v:''}], param: 'lr', predefinedOptionsKey: 'language', customItemsKey: 'customLanguages', displayItemsKey: 'displayLanguages' },
        { id: 'sidebar-section-time', type: 'filter', titleKey: 'section_time', scriptDefined: [{textKey:'filter_any_time',v:''}], param: 'qdr', predefinedOptionsKey: 'time', customItemsKey: 'customTimeRanges' },
        { id: 'sidebar-section-filetype', type: 'filetype', titleKey: 'section_filetype', scriptDefined: [{ textKey: 'filter_any_format', v: '' }], param: 'as_filetype', predefinedOptionsKey: 'filetype', customItemsKey: 'customFiletypes' },
        {
            id: 'sidebar-section-occurrence',
            type: 'filter',
            titleKey: 'section_occurrence',
            scriptDefined: [
                { textKey: 'filter_occurrence_any', v: 'any' },
                { textKey: 'filter_occurrence_title', v: 'title' },
                { textKey: 'filter_occurrence_text', v: 'text' },
                { textKey: 'filter_occurrence_url', v: 'url' },
            ],
            param: 'as_occt'
        },
        { id: 'sidebar-section-country', type: 'filter', titleKey: 'section_country', scriptDefined: [{textKey:'filter_any_country',v:''}], param: 'cr', predefinedOptionsKey: 'country', customItemsKey: 'customCountries', displayItemsKey: 'displayCountries' },
        { id: 'sidebar-section-date-range', type: 'date', titleKey: 'section_date_range' },
        { id: 'sidebar-section-site-search', type: 'site', titleKey: 'section_site_search', scriptDefined: [{ textKey: 'filter_any_site', v:''}] },
        { id: 'sidebar-section-tools', type: 'tools', titleKey: 'section_tools' }
    ];

    const LocalizationService = (function() {
        const builtInTranslations = {
            'en': {
                scriptName: 'Google Search Custom Sidebar', settingsTitle: 'Google Search Custom Sidebar Settings', manageOptionsTitle: 'Manage Options', manageSitesTitle: 'Manage Favorite Sites', manageLanguagesTitle: 'Manage Language Options', manageCountriesTitle: 'Manage Country/Region Options', manageTimeRangesTitle: 'Manage Time Ranges', manageFileTypesTitle: 'Manage File Types', section_language: 'Language', section_time: 'Time', section_filetype: 'File Type', section_country: 'Country/Region', section_date_range: 'Date Range', section_site_search: 'Site Search', section_tools: 'Tools',
                section_occurrence: 'Keyword Location',
                filter_any_language: 'Any Language', filter_any_time: 'Any Time', filter_any_format: 'Any Format', filter_any_country: 'Any Country/Region', filter_any_site: 'Any Site',
                filter_occurrence_any: 'Anywhere in the page', filter_occurrence_title: 'In the title of the page', filter_occurrence_text: 'In the text of the page', filter_occurrence_url: 'In the URL of the page',
                filter_clear_site_search: 'Clear Site Search', filter_clear_tooltip_suffix: '(Clear)', predefined_lang_zh_tw: 'Traditional Chinese', predefined_lang_zh_cn: 'Simplified Chinese', predefined_lang_zh_all: 'All Chinese', predefined_lang_en: 'English', predefined_lang_ja: 'Japanese', predefined_lang_ko: 'Korean', predefined_lang_fr: 'French', predefined_lang_de: 'German', predefined_lang_es: 'Spanish', predefined_lang_it: 'Italian', predefined_lang_pt: 'Portuguese', predefined_lang_ru: 'Russian', predefined_lang_ar: 'Arabic', predefined_lang_hi: 'Hindi', predefined_lang_nl: 'Dutch', predefined_lang_tr: 'Turkish', predefined_lang_vi: 'Vietnamese', predefined_lang_th: 'Thai', predefined_lang_id: 'Indonesian', predefined_country_tw: '🇹🇼 Taiwan', predefined_country_jp: '🇯🇵 Japan', predefined_country_kr: '🇰🇷 South Korea', predefined_country_cn: '🇨🇳 China', predefined_country_hk: '🇭🇰 Hong Kong', predefined_country_sg: '🇸🇬 Singapore', predefined_country_my: '🇲🇾 Malaysia', predefined_country_vn: '🇻🇳 Vietnam', predefined_country_ph: '🇵🇭 Philippines', predefined_country_th: '🇹🇭 Thailand', predefined_country_us: '🇺🇸 United States', predefined_country_ca: '🇨🇦 Canada', predefined_country_br: '🇧🇷 Brazil', predefined_country_mx: '🇲🇽 Mexico', predefined_country_gb: '🇬🇧 United Kingdom', predefined_country_de: '🇩🇪 Germany', predefined_country_fr: '🇫🇷 France', predefined_country_it: '🇮🇹 Italy', predefined_country_es: '🇪🇸 Spain', predefined_country_ru: '🇷🇺 Russia', predefined_country_nl: '🇳🇱 Netherlands', predefined_country_au: '🇦🇺 Australia', predefined_country_in: '🇮🇳 India', predefined_country_za: '🇿🇦 South Africa', predefined_country_tr: '🇹🇷 Turkey', predefined_time_h: 'Past hour', predefined_time_h2: 'Past 2 hours', predefined_time_h6: 'Past 6 hours', predefined_time_h12: 'Past 12 hours', predefined_time_d: 'Past 24 hours', predefined_time_d2: 'Past 2 days', predefined_time_d3: 'Past 3 days', predefined_time_w: 'Past week', predefined_time_m: 'Past month', predefined_time_y: 'Past year', predefined_filetype_pdf: 'PDF', predefined_filetype_docx: 'Word (docx)', predefined_filetype_doc: 'Word (doc)', predefined_filetype_xlsx: 'Excel (xlsx)', predefined_filetype_xls: 'Excel (xls)', predefined_filetype_pptx: 'PowerPoint (pptx)', predefined_filetype_ppt: 'PowerPoint (ppt)', predefined_filetype_txt: 'Plain Text', predefined_filetype_rtf: 'Rich Text Format', predefined_filetype_html: 'Web Page (html)', predefined_filetype_htm: 'Web Page (htm)', predefined_filetype_xml: 'XML', predefined_filetype_jpg: 'JPEG Image', predefined_filetype_png: 'PNG Image', predefined_filetype_gif: 'GIF Image', predefined_filetype_svg: 'SVG Image', predefined_filetype_bmp: 'BMP Image', predefined_filetype_js: 'JavaScript', predefined_filetype_css: 'CSS', predefined_filetype_py: 'Python', predefined_filetype_java: 'Java', predefined_filetype_cpp: 'C++', predefined_filetype_cs: 'C#', predefined_filetype_kml: 'Google Earth (kml)', predefined_filetype_kmz: 'Google Earth (kmz)',
                tool_reset_filters: 'Reset Filters', tool_verbatim_search: 'Verbatim Search', tool_advanced_search: 'Advanced Search', tool_apply_date: 'Apply Dates',
                tool_personalization_toggle: 'Personalization', tool_apply_selected_sites: 'Apply Selected',
                tool_apply_selected_filetypes: 'Apply Selected',
                tool_google_scholar: 'Scholar',
                tooltip_google_scholar_search: 'Search keywords on Google Scholar',
                service_name_google_scholar: 'Google Scholar',
                tool_google_trends: 'Trends',
                tooltip_google_trends_search: 'Explore keywords on Google Trends',
                service_name_google_trends: 'Google Trends',
                link_advanced_search_title: 'Open Google Advanced Search page', tooltip_site_search: 'Search within {siteUrl}', tooltip_clear_site_search: 'Remove site: restriction', tooltip_toggle_personalization_on: 'Click to turn Personalization ON (Results tailored to you)', tooltip_toggle_personalization_off: 'Click to turn Personalization OFF (More generic results)', settings_tab_general: 'General', settings_tab_appearance: 'Appearance', settings_tab_features: 'Features', settings_tab_custom: 'Custom', settings_close_button_title: 'Close', settings_interface_language: 'Interface Language:', settings_language_auto: 'Auto (Browser Default)', settings_section_mode: 'Section Collapse Mode:', settings_section_mode_remember: 'Remember State', settings_section_mode_expand: 'Expand All', settings_section_mode_collapse: 'Collapse All',
                settings_accordion_mode: 'Accordion Mode (only when "Remember State" is active)',
                settings_accordion_mode_hint_desc: 'When enabled, expanding one section will automatically collapse other open sections.',
                settings_enable_drag: 'Enable Dragging', settings_reset_button_location: 'Reset Button Location:', settings_verbatim_button_location: 'Verbatim Button Location:', settings_adv_search_location: '"Advanced Search" Link Location:', settings_personalize_button_location: 'Personalization Button Location:',
                settings_scholar_location: 'Google Scholar Shortcut Location:', // New setting text
                settings_trends_location: 'Google Trends Shortcut Location:',   // New setting text
                settings_enable_site_search_checkbox_mode: 'Enable Checkbox Mode for Site Search',
                settings_enable_site_search_checkbox_mode_hint: 'Allows selecting multiple favorite sites for a combined (OR) search.',
                settings_enable_filetype_search_checkbox_mode: 'Enable Checkbox Mode for Filetype Search',
                settings_enable_filetype_search_checkbox_mode_hint: 'Allows selecting multiple filetypes for a combined (OR) search.',
                settings_location_tools: 'Tools Section', settings_location_top: 'Top Block', settings_location_header: 'Sidebar Header', settings_location_hide: 'Hide', settings_sidebar_width: 'Sidebar Width (px)', settings_width_range_hint: '(Range: 90-270, Step: 5)', settings_font_size: 'Base Font Size (px)', settings_font_size_range_hint: '(Range: 8-24, Step: 0.5)', settings_header_icon_size: 'Header Icon Size (px)', settings_header_icon_size_range_hint: '(Range: 8-32, Step: 0.5)', settings_vertical_spacing: 'Vertical Spacing', settings_vertical_spacing_range_hint: '(Multiplier Range: 0.05-1.5, Step: 0.05)', settings_theme: 'Theme:', settings_theme_system: 'Follow System', settings_theme_light: 'Light', settings_theme_dark: 'Dark', settings_theme_minimal_light: 'Minimal (Light)', settings_theme_minimal_dark: 'Minimal (Dark)', settings_hover_mode: 'Hover Mode', settings_idle_opacity: 'Idle Opacity:', settings_opacity_range_hint: '(Range: 0.1-1.0, Step: 0.05)', settings_country_display: 'Country/Region Display:', settings_country_display_icontext: 'Icon & Text', settings_country_display_text: 'Text Only', settings_country_display_icon: 'Icon Only', settings_visible_sections: 'Visible Sections:', settings_section_order: 'Adjust Sidebar Section Order (Drag & Drop):',
                settings_section_order_hint: '(Drag items to reorder. Only affects checked sections)',
                settings_no_orderable_sections: 'No visible sections to order.',
                settings_move_up_title: 'Move Up',
                settings_move_down_title: 'Move Down',
                settings_hide_google_logo: 'Hide Google Logo when sidebar is expanded',
                settings_hide_google_logo_hint: 'Useful if the sidebar is placed in the top-left corner with a minimal theme.',
                settings_custom_intro: 'Manage filter options for each section:',
                settings_manage_sites_button: 'Manage Favorite Sites...', settings_manage_languages_button: 'Manage Language Options...', settings_manage_countries_button: 'Manage Country/Region Options...', settings_manage_time_ranges_button: 'Manage Time Ranges...', settings_manage_file_types_button: 'Manage File Types...', settings_save_button: 'Save Settings', settings_cancel_button: 'Cancel', settings_reset_all_button: 'Reset All',
                modal_label_enable_predefined: 'Enable Predefined {type}:',
                modal_label_my_custom: 'My Custom {type}:',
                modal_label_display_options_for: 'Display Options for {type} (Drag to Sort):',
                modal_button_add_new_option: 'Add New Option...',
                modal_button_add_predefined_option: 'Add Predefined...',
                modal_button_add_custom_option: 'Add Custom...',
                modal_placeholder_name: 'Name', modal_placeholder_domain: 'Domain (e.g., site.com OR example.net)', modal_placeholder_text: 'Text', modal_placeholder_value: 'Value (e.g., pdf OR docx)',
                modal_hint_domain: 'Format: domain (e.g., `wikipedia.org`) or TLD/SLD (`.edu`). Use `OR` (case-insensitive, space separated) for multiple.',
                modal_hint_language: 'Format: starts with `lang_`, e.g., `lang_ja`, `lang_zh-TW`. Use `|` for multiple.', modal_hint_country: 'Format: `country` + 2-letter uppercase code, e.g., `countryDE`', modal_hint_time: 'Format: `h`, `d`, `w`, `m`, `y`, optionally followed by numbers, e.g., `h1`, `d7`, `w`',
                modal_hint_filetype: 'Format: extension (e.g., `pdf`). Use `OR` (case-insensitive, space separated) for multiple (e.g., `docx OR xls`).',
                modal_tooltip_domain: 'Enter domain(s) or TLD/SLD(s). Use OR for multiple, e.g., site.com OR example.org',
                modal_tooltip_language: 'Format: lang_xx or lang_xx-XX, separate multiple with |', modal_tooltip_country: 'Format: countryXX (XX = uppercase country code)', modal_tooltip_time: 'Format: h, d, w, m, y, optionally followed by numbers',
                modal_tooltip_filetype: 'File extension(s). Use OR for multiple, e.g., pdf OR docx',
                modal_button_add_title: 'Add', modal_button_update_title: 'Update Item', modal_button_cancel_edit_title: 'Cancel Edit', modal_button_edit_title: 'Edit', modal_button_delete_title: 'Delete', modal_button_remove_from_list_title: 'Remove from list', modal_button_complete: 'Done', value_empty: '(empty)', date_range_from: 'From:', date_range_to: 'To:', sidebar_collapse_title: 'Collapse', sidebar_expand_title: 'Expand', sidebar_drag_title: 'Drag', sidebar_settings_title: 'Settings',
                alert_invalid_start_date: 'Invalid start date', alert_invalid_end_date: 'Invalid end date', alert_end_before_start: 'End date cannot be earlier than start date', alert_start_in_future: 'Start date cannot be in the future', alert_end_in_future: 'End date cannot be in the future', alert_select_date: 'Please select a date', alert_error_applying_date: 'Error applying date range', alert_error_applying_filter: 'Error applying filter {type}={value}', alert_error_applying_site_search: 'Error applying site search for {site}', alert_error_clearing_site_search: 'Error clearing site search', alert_error_resetting_filters: 'Error resetting filters', alert_error_toggling_verbatim: 'Error toggling Verbatim search', alert_error_toggling_personalization: 'Error toggling Personalization search', alert_enter_display_name: 'Please enter the display name for {type}.', alert_enter_value: 'Please enter the corresponding value for {type}.', alert_invalid_value_format: 'The value format for {type} is incorrect. {hint}', alert_duplicate_name: 'Custom item display name "{name}" already exists. Please use a different name.', alert_update_failed_invalid_index: 'Update failed: Invalid item index.', alert_edit_failed_missing_fields: 'Cannot edit: Input or button fields not found.',
                alert_no_more_predefined_to_add: 'No more predefined {type} options available to add.',
                alert_no_keywords_for_shortcut: 'No keywords found in current search to use for {service_name}.',
                alert_error_opening_link: 'Error opening link for {service_name}.',
                alert_generic_error: 'An unexpected error occurred. Please check the console or try again. Context: {context}',
                confirm_delete_item: 'Are you sure you want to delete the custom item "{name}"?', confirm_remove_item_from_list: 'Are you sure you want to remove "{name}" from this display list?', confirm_reset_settings: 'Are you sure you want to reset all settings to their default values?', alert_settings_reset_success: 'Settings have been reset to default. You can continue editing or click "Save Settings" to confirm.', confirm_reset_all_menu: 'Are you sure you want to reset all settings to their default values?\nThis cannot be undone and requires a page refresh to take effect.', alert_reset_all_menu_success: 'All settings have been reset to defaults.\nPlease refresh the page to apply the changes.', alert_reset_all_menu_fail: 'Failed to reset settings via menu command! Please check the console.', alert_init_fail: '{scriptName} initialization failed. Some features may not work. Please check the console for technical details.\nTechnical Error: {error}', menu_open_settings: '⚙️ Open Settings', menu_reset_all_settings: '🚨 Reset All Settings',
            },
        };
        let effectiveTranslations = JSON.parse(JSON.stringify(builtInTranslations));
        let _currentLocale = 'en';

        function _mergeExternalTranslations() {
            if (typeof window.GSCS_Namespace !== 'undefined' && typeof window.GSCS_Namespace.i18nPack === 'object' && typeof window.GSCS_Namespace.i18nPack.translations === 'object') {
                const externalTranslations = window.GSCS_Namespace.i18nPack.translations;
                for (const langCode in externalTranslations) {
                    if (Object.prototype.hasOwnProperty.call(externalTranslations, langCode)) {
                        if (!effectiveTranslations[langCode]) {
                            effectiveTranslations[langCode] = {};
                        }
                        for (const key in externalTranslations[langCode]) {
                            if (Object.prototype.hasOwnProperty.call(externalTranslations[langCode], key)) {
                                effectiveTranslations[langCode][key] = externalTranslations[langCode][key];
                            }
                        }
                    }
                }
                 // After merging, ensure 'en' from builtInTranslations acts as a fallback for all known languages
                const englishDefaults = builtInTranslations.en;
                for (const langCode in effectiveTranslations) {
                    if (langCode !== 'en' && Object.prototype.hasOwnProperty.call(effectiveTranslations, langCode)) {
                        for (const key in englishDefaults) {
                            if (Object.prototype.hasOwnProperty.call(englishDefaults, key) && typeof effectiveTranslations[langCode][key] === 'undefined') {
                                effectiveTranslations[langCode][key] = englishDefaults[key];
                            }
                        }
                    }
                }

            } else {
                console.warn(`${LOG_PREFIX} [i18n] External i18n pack (window.GSCS_Namespace.i18nPack) not found or invalid. Using built-in translations only.`);
            }
            // Ensure all keys from builtInTranslations.en exist in 'en' to prevent errors
            // if i18n.js is older or missing keys.
            const ensureKeys = (lang, defaults) => {
                if (!effectiveTranslations[lang]) effectiveTranslations[lang] = {};
                for (const key in defaults) {
                    if (!effectiveTranslations[lang][key]) {
                        effectiveTranslations[lang][key] = defaults[key]; // Fallback to built-in English if key is missing in target lang
                    }
                }
            };
            ensureKeys('en', builtInTranslations.en); // Ensure English is complete based on built-in
        }

        function _detectBrowserLocale() {
            let locale = 'en'; // Default
            try {
                if (navigator.languages && navigator.languages.length) {
                    locale = navigator.languages[0];
                } else if (navigator.language) {
                    locale = navigator.language;
                }
            } catch (e) {
                console.warn(`${LOG_PREFIX} [i18n] Error accessing navigator.language(s):`, e);
            }

            // Try to match full locale (e.g., "zh-TW")
            if (effectiveTranslations[locale]) return locale;

            // Try to match generic part (e.g., "zh" from "zh-TW")
            if (locale.includes('-')) {
                const parts = locale.split('-');
                if (parts.length > 0 && effectiveTranslations[parts[0]]) return parts[0];
                // Try "language-Script" (e.g., "zh-Hant") if applicable, though less common for userscripts
                if (parts.length > 2 && effectiveTranslations[`${parts[0]}-${parts[1]}`]) return `${parts[0]}-${parts[1]}`;
            }
            return 'en'; // Fallback to English
        }

        function _updateActiveLocale(settingsToUse) {
            let newLocale = 'en'; // Default
            const langSettingSource = (settingsToUse && Object.keys(settingsToUse).length > 0 && typeof settingsToUse.interfaceLanguage === 'string')
                ? settingsToUse
                : defaultSettings; // Fallback to defaultSettings if settingsToUse is empty/invalid

            const userSelectedLang = langSettingSource.interfaceLanguage;

            if (userSelectedLang && userSelectedLang !== 'auto') {
                if (effectiveTranslations[userSelectedLang]) {
                    newLocale = userSelectedLang;
                } else if (userSelectedLang.includes('-')) {
                    const genericLang = userSelectedLang.split('-')[0];
                    if (effectiveTranslations[genericLang]) {
                        newLocale = genericLang;
                    } else {
                        newLocale = _detectBrowserLocale(); // Fallback to browser if specific parts aren't found
                    }
                } else {
                    newLocale = _detectBrowserLocale(); // Fallback if selected lang doesn't exist
                }
            } else { // 'auto' or undefined
                newLocale = _detectBrowserLocale();
            }

            if (_currentLocale !== newLocale) {
                _currentLocale = newLocale;
                // console.log(`${LOG_PREFIX} [i18n] Locale updated to: ${_currentLocale}`);
            }
             // Warn if the chosen language isn't exactly what was set (e.g. "fr-CA" setting becomes "fr" due to availability)
            if (userSelectedLang && userSelectedLang !== 'auto' && _currentLocale !== userSelectedLang && !userSelectedLang.startsWith(_currentLocale.split('-')[0])) {
                console.warn(`${LOG_PREFIX} [i18n] User selected language "${userSelectedLang}" was not fully available or matched. Using best match: "${_currentLocale}".`);
            }
        }

        _mergeExternalTranslations(); // Merge external translations once at service creation

        function getString(key, replacements = {}) {
            let str = `[ERR: ${key} @ ${_currentLocale}]`; // Default error string
            let found = false;

            // 1. Try current locale
            if (effectiveTranslations[_currentLocale] && typeof effectiveTranslations[_currentLocale][key] !== 'undefined') {
                str = effectiveTranslations[_currentLocale][key];
                found = true;
            }
            // 2. If current locale has a generic part (e.g., "zh" from "zh-TW"), try that
            else if (_currentLocale.includes('-')) {
                const genericLang = _currentLocale.split('-')[0];
                if (effectiveTranslations[genericLang] && typeof effectiveTranslations[genericLang][key] !== 'undefined') {
                    str = effectiveTranslations[genericLang][key];
                    found = true;
                }
            }

            // 3. If not found and current locale is not English, fallback to English
            if (!found && _currentLocale !== 'en') {
                if (effectiveTranslations['en'] && typeof effectiveTranslations['en'][key] !== 'undefined') {
                    str = effectiveTranslations['en'][key];
                    found = true;
                    // console.warn(`${LOG_PREFIX} [i18n] Missing translation for key: "${key}" in locale: "${_currentLocale}". Fell back to "en".`);
                }
            }

            // 4. If still not found (even in English), it's a critical miss
            if (!found) {
                if (!(effectiveTranslations['en'] && typeof effectiveTranslations['en'][key] !== 'undefined')) {
                    console.error(`${LOG_PREFIX} [i18n] CRITICAL: Missing translation for key: "${key}" in BOTH locale: "${_currentLocale}" AND default locale "en".`);
                } else {
                    // This case should ideally not be hit if English is complete in builtInTranslations
                    str = effectiveTranslations['en'][key]; // Should have been caught by step 3 if _currentLocale wasn't 'en'
                    found = true;
                }
                if(!found) str = `[ERR_NF: ${key}]`; // Final error if truly not found anywhere
            }

            // Replace placeholders
            if (typeof str === 'string') {
                for (const placeholder in replacements) {
                    if (Object.prototype.hasOwnProperty.call(replacements, placeholder)) {
                        str = str.replace(new RegExp(`\\{${placeholder}\\}`, 'g'), replacements[placeholder]);
                    }
                }
            } else {
                 console.error(`${LOG_PREFIX} [i18n] CRITICAL: Translation for key "${key}" is not a string:`, str);
                 return `[INVALID_TYPE_FOR_KEY: ${key}]`;
            }
            return str;
        }

        return {
            getString: getString,
            getCurrentLocale: function() { return _currentLocale; },
            getTranslationsForLocale: function(locale = 'en') { return effectiveTranslations[locale] || effectiveTranslations['en']; },
            initializeBaseLocale: function() { _updateActiveLocale(defaultSettings); }, // Initialize with defaults
            updateActiveLocale: function(activeSettings) { _updateActiveLocale(activeSettings); },
            getAvailableLocales: function() {
                const locales = new Set(['auto', 'en']); // 'auto' and 'en' are always options
                Object.keys(effectiveTranslations).forEach(lang => {
                    // Only add if it's a valid language pack (not just an empty object)
                    if (Object.keys(effectiveTranslations[lang]).length > 0) {
                        locales.add(lang);
                    }
                });
                return Array.from(locales).sort((a, b) => {
                    if (a === 'auto') return -1;
                    if (b === 'auto') return 1;
                    if (a === 'en' && b !== 'auto') return -1;
                    if (b === 'en' && a !== 'auto') return 1;

                    let nameA = a, nameB = b;
                    try { nameA = new Intl.DisplayNames([a],{type:'language'}).of(a); } catch(e){}
                    try { nameB = new Intl.DisplayNames([b],{type:'language'}).of(b); } catch(e){}
                    return nameA.localeCompare(nameB);
                });
            }
        };
    })();
    const _ = LocalizationService.getString; // Shortcut

    const Utils = {
        debounce: function(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const context = this;
                const later = () => {
                    timeout = null;
                    func.apply(context, args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        },
        mergeDeep: function(target, source) {
            if (!source) return target; // If source is undefined or null, return target as is.
            target = target || {}; // Ensure target is an object if it's initially null/undefined.

            for (const key in source) {
                if (Object.prototype.hasOwnProperty.call(source, key)) {
                    const targetValue = target[key];
                    const sourceValue = source[key];

                    if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
                        // Recurse for nested objects
                        target[key] = Utils.mergeDeep(targetValue, sourceValue);
                    } else if (typeof sourceValue !== 'undefined') {
                        // Assign if sourceValue is a primitive, array, or explicitly undefined
                        target[key] = sourceValue;
                    }
                    // If sourceValue is undefined, target[key] remains unchanged (implicit else)
                }
            }
            return target;
        },
        clamp: function(num, min, max) {
            return Math.min(Math.max(num, min), max);
        },
        parseIconAndText: function(fullText) {
            // Regex to match one or more non-letter, non-number characters at the beginning, followed by optional whitespace
            // \P{L} matches any character that is not a letter.
            // \P{N} matches any character that is not a number.
            // Using [^\p{L}\p{N}\s] might be too restrictive if icons can be complex symbols.
            // This regex tries to capture common emoji/symbol patterns for icons.
            const match = fullText.match(/^(\P{L}\P{N}\s*)+/u);
            let icon = '';
            let text = fullText;

            if (match && match[0].trim() !== '') {
                icon = match[0].trim(); // Trim to remove trailing spaces from the icon part
                text = fullText.substring(icon.length).trim(); // Trim to remove leading spaces from the text part
            }
            return { icon, text };
        },
        getCurrentURL: function() {
            try {
                return new URL(window.location.href);
            } catch (e) {
                console.error(`${LOG_PREFIX} Error creating URL object:`, e);
                return null;
            }
        },
        parseCombinedValue: function(valueString) {
            if (typeof valueString !== 'string' || !valueString.trim()) {
                return [];
            }
            // Split by " OR " (case-insensitive, with spaces around OR)
            return valueString.split(/\s+OR\s+/i).map(s => s.trim()).filter(s => s.length > 0);
        }
    };
    const NotificationManager = (function() {
        let container = null;

        function init() {
            if (document.getElementById(IDS.NOTIFICATION_CONTAINER)) {
                container = document.getElementById(IDS.NOTIFICATION_CONTAINER);
                return;
            }
            container = document.createElement('div');
            container.id = IDS.NOTIFICATION_CONTAINER;
            if (document.body) {
                document.body.appendChild(container);
            } else {
                // This case should be rare as script runs at document-idle
                console.error(LOG_PREFIX + " NotificationManager.init(): document.body is not available!");
                container = null; // Ensure container is null if append fails
            }
        }

        function show(messageKey, messageArgs = {}, type = 'info', duration = 3000) {
            if (!container) {
                // Fallback to alert if container isn't initialized
                const alertMsg = (typeof _ === 'function' && _(messageKey, messageArgs) && !(_(messageKey, messageArgs).startsWith('[ERR:')))
                    ? _(messageKey, messageArgs)
                    : `${messageKey} (args: ${JSON.stringify(messageArgs)})`; // Basic fallback if _ is not ready
                alert(alertMsg);
                return null;
            }

            const notificationElement = document.createElement('div');
            notificationElement.classList.add(CSS.NOTIFICATION);

            const typeClass = CSS[`NTF_${type.toUpperCase()}`] || CSS.NTF_INFO; // Fallback to info type
            notificationElement.classList.add(typeClass);

            notificationElement.textContent = _(messageKey, messageArgs);

            if (duration <= 0) { // Persistent notification, add a close button
                const closeButton = document.createElement('span');
                closeButton.innerHTML = '×'; // Simple 'x'
                closeButton.style.cursor = 'pointer';
                closeButton.style.marginLeft = '10px';
                closeButton.style.float = 'right'; // Position to the right
                closeButton.onclick = () => notificationElement.remove();
                notificationElement.appendChild(closeButton);
            }

            container.appendChild(notificationElement);

            if (duration > 0) {
                setTimeout(() => {
                    notificationElement.style.opacity = '0'; // Start fade out
                    setTimeout(() => notificationElement.remove(), 500); // Remove after fade out
                }, duration);
            }
            return notificationElement; // Return the element for potential further manipulation
        }

        return { init: init, show: show };
    })();

    // --- UI Element Creation and Management ---
    // (createGenericListItem, populateListInModal, getListMapping etc. are complex and related to ModalManager now)
    // Simplified here as they are mostly within ModalManager's scope or specific to section building

    function createGenericListItem(index, item, listId, mapping) {
        // ... (implementation as provided, assuming it's largely correct for modal context)
        const listItem = document.createElement('li');
        listItem.dataset[DATA_ATTR.INDEX] = index;
        listItem.dataset[DATA_ATTR.LIST_ID] = listId;
        listItem.dataset[DATA_ATTR.ITEM_ID] = item.id || item.value || item.url; // Unique ID for the item itself
        listItem.draggable = true; // All modal list items are draggable

        const dragIconSpan = document.createElement('span');
        dragIconSpan.classList.add(CSS.DRAG_ICON);
        dragIconSpan.innerHTML = SVG_ICONS.dragGrip;
        listItem.appendChild(dragIconSpan);

        const textSpan = document.createElement('span');
        let displayText = item.text;
        let paramName = ''; // To show "param=value"

        if (item.type === 'predefined' && item.originalKey) {
            displayText = _(item.originalKey);
            if (listId === IDS.COUNTRIES_LIST) { // Special handling for country icon+text
                const parsed = Utils.parseIconAndText(displayText);
                displayText = `${parsed.icon} ${parsed.text}`.trim();
            }
        }

        // Determine param name for display
        if (mapping) { // Mapping comes from getListMapping
            if (listId === IDS.LANG_LIST) paramName = ALL_SECTION_DEFINITIONS.find(s=>s.id === 'sidebar-section-language').param;
            else if (listId === IDS.COUNTRIES_LIST) paramName = ALL_SECTION_DEFINITIONS.find(s=>s.id === 'sidebar-section-country').param;
            else if (listId === IDS.SITES_LIST) paramName = 'site'; // Site search uses `site:` in query
            else if (listId === IDS.TIME_LIST) paramName = ALL_SECTION_DEFINITIONS.find(s=>s.id === 'sidebar-section-time').param;
            else if (listId === IDS.FT_LIST) {
                const ftSection = ALL_SECTION_DEFINITIONS.find(s => s.id === 'sidebar-section-filetype');
                if (ftSection) paramName = ftSection.param;
            }
        }

        const valueForDisplay = item.value || item.url || _('value_empty');
        textSpan.textContent = `${displayText} (${paramName}=${valueForDisplay})`;
        textSpan.title = textSpan.textContent; // Tooltip for full text
        listItem.appendChild(textSpan);

        const controlsSpan = document.createElement('span');
        controlsSpan.classList.add(CSS.ITEM_CONTROLS);

        // Determine if item is "custom" or "predefined" for button display
        if (item.type === 'custom' || listId === IDS.SITES_LIST || listId === IDS.TIME_LIST || listId === IDS.FT_LIST) {
            // Sites, Time, Filetype lists are always treated as "custom" in terms of editability
            controlsSpan.innerHTML =
                `<button class="${CSS.EDIT_CUSTOM_ITEM}" title="${_('modal_button_edit_title')}">${SVG_ICONS.edit}</button> ` +
                `<button class="${CSS.DELETE_CUSTOM_ITEM}" title="${_('modal_button_delete_title')}">${SVG_ICONS.delete}</button>`;
            listItem.dataset[DATA_ATTR.ITEM_TYPE] = 'custom';
        } else if (item.type === 'predefined') {
            // Languages, Countries in mixed mode can have predefined items that can be removed (not deleted from source)
            controlsSpan.innerHTML =
                `<button class="${CSS.REMOVE_FROM_LIST_BTN}" title="${_('modal_button_remove_from_list_title')}">${SVG_ICONS.removeFromList}</button>`;
            listItem.dataset[DATA_ATTR.ITEM_TYPE] = 'predefined';
        }
        listItem.appendChild(controlsSpan);
        return listItem;
    }

    function populateListInModal(listId, items, contextElement = document) {
        const listElement = contextElement.querySelector(`#${listId}`);
        if (!listElement) {
            console.warn(`${LOG_PREFIX} List element not found: #${listId} in context`, contextElement);
            return;
        }
        listElement.innerHTML = ''; // Clear existing items
        const fragment = document.createDocumentFragment();
        const mapping = getListMapping(listId); // Get mapping for param name display

        if (!Array.isArray(items)) items = []; // Ensure items is an array

        items.forEach((item, index) => {
            fragment.appendChild(createGenericListItem(index, item, listId, mapping));
        });
        listElement.appendChild(fragment);
    }

    function getListMapping(listId) {
        // Centralized configuration for custom lists in modals
        const listMappings = {
            [IDS.SITES_LIST]:     { itemsArrayKey: 'favoriteSites',           customItemsMasterKey: null,                 valueKey: 'url',   populateFn: populateListInModal, textInput: `#${IDS.NEW_SITE_NAME}`,    valueInput: `#${IDS.NEW_SITE_URL}`,    addButton: `#${IDS.ADD_SITE_BTN}`,    nameKey: 'section_site_search', isSortableMixed: false, predefinedSourceKey: null },
            [IDS.LANG_LIST]:      { itemsArrayKey: 'displayLanguages',        customItemsMasterKey: 'customLanguages',    valueKey: 'value', populateFn: populateListInModal, textInput: `#${IDS.NEW_LANG_TEXT}`,    valueInput: `#${IDS.NEW_LANG_VALUE}`,    addButton: `#${IDS.ADD_LANG_BTN}`,    nameKey: 'section_language',    isSortableMixed: true,  predefinedSourceKey: 'language' },
            [IDS.COUNTRIES_LIST]: { itemsArrayKey: 'displayCountries',      customItemsMasterKey: 'customCountries',  valueKey: 'value', populateFn: populateListInModal, textInput: `#${IDS.NEW_COUNTRY_TEXT}`, valueInput: `#${IDS.NEW_COUNTRY_VALUE}`, addButton: `#${IDS.ADD_COUNTRY_BTN}`, nameKey: 'section_country',   isSortableMixed: true,  predefinedSourceKey: 'country' },
            [IDS.TIME_LIST]:      { itemsArrayKey: 'customTimeRanges',        customItemsMasterKey: null,                 valueKey: 'value', populateFn: populateListInModal, textInput: `#${IDS.NEW_TIME_TEXT}`,    valueInput: `#${IDS.NEW_TIME_VALUE}`,    addButton: `#${IDS.ADD_TIME_BTN}`,    nameKey: 'section_time',        isSortableMixed: false, predefinedSourceKey: 'time' }, // predefinedSourceKey for enabling checkbox list
            [IDS.FT_LIST]:        { itemsArrayKey: 'customFiletypes',         customItemsMasterKey: null,                 valueKey: 'value', populateFn: populateListInModal, textInput: `#${IDS.NEW_FT_TEXT}`,      valueInput: `#${IDS.NEW_FT_VALUE}`,      addButton: `#${IDS.ADD_FT_BTN}`,      nameKey: 'section_filetype',    isSortableMixed: false, predefinedSourceKey: 'filetype' },// predefinedSourceKey for enabling checkbox list
        };
        return listMappings[listId] || null;
    }

    function validateCustomInput(inputElement) {
        if (!inputElement) return false; // Should not happen if called correctly
        const value = inputElement.value.trim();
        const id = inputElement.id;
        let isValid = false;
        let isEmpty = value === '';

        // Basic validation: name/text fields cannot be empty
        if (id === IDS.NEW_SITE_NAME || id === IDS.NEW_LANG_TEXT || id === IDS.NEW_TIME_TEXT || id === IDS.NEW_FT_TEXT || id === IDS.NEW_COUNTRY_TEXT) {
            isValid = !isEmpty;
        } else if (id === IDS.NEW_SITE_URL) {
            // Domain format: single valid domain or TLD/SLD like .edu, .gov.uk
            // Or multiple domains separated by " OR "
            const singleDomainRegex = /^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,})$|(?:^\.(?:[a-zA-Z0-9-]{1,63}\.)*[a-zA-Z]{2,63}$)/;
            const parts = Utils.parseCombinedValue(value); // Handles " OR " separation
            if (isEmpty) isValid = true; // Empty is fine, might not be required if text is set (for display only items)
            else if (parts.length > 0) isValid = parts.every(part => singleDomainRegex.test(part));
            else isValid = false; // e.g. "OR" alone, or just spaces
        } else if (id === IDS.NEW_LANG_VALUE) {
            // Language code format: lang_xx or lang_xx-XX, multiple with |
            isValid = isEmpty || /^lang_[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,4})?(?:\|lang_[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,4})?)*$/.test(value);
        } else if (id === IDS.NEW_TIME_VALUE) {
            // Time value format: h, d, w, m, y, optionally followed by numbers
            isValid = isEmpty || /^[hdwmy]\d*$/.test(value);
        } else if (id === IDS.NEW_FT_VALUE) {
            // Filetype format: extension, multiple with " OR "
            const singleFiletypeRegex = /^[a-zA-Z0-9]+$/;
            const parts = Utils.parseCombinedValue(value);
            if (isEmpty) isValid = true;
            else if (parts.length > 0) isValid = parts.every(part => singleFiletypeRegex.test(part));
            else isValid = false;
        } else if (id === IDS.NEW_COUNTRY_VALUE) {
            // Country code format: countryXX (XX = uppercase country code)
            isValid = isEmpty || /^country[A-Z]{2}$/.test(value);
        }

        // Visual feedback
        inputElement.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); // Clear previous states
        _clearInputError(inputElement); // Clear any existing error message for this input

        if (!isEmpty) { // Only add validation classes if not empty
            inputElement.classList.add(isValid ? 'input-valid' : 'input-invalid');
            if (!isValid) inputElement.classList.add(CSS.INPUT_HAS_ERROR); // Red border for error
        }

        return isValid || isEmpty; // Return true if format is valid OR if it's empty (emptiness check is separate)
    }

    function _getInputErrorElement(inputElement) {
        if (!inputElement || !inputElement.id) return null;
        // Try to find the specific error message span for this input
        let errorEl = inputElement.nextElementSibling;
        if (errorEl && errorEl.classList.contains(CSS.INPUT_ERROR_MESSAGE) && errorEl.id === `${inputElement.id}-error-msg`) {
            return errorEl;
        }
        // Fallback: search within parent div if structured that way
        const parentDiv = inputElement.parentElement;
        if (parentDiv) {
            return parentDiv.querySelector(`#${inputElement.id}-error-msg`);
        }
        return null;
    }
    function _showInputError(inputElement, messageKey, messageArgs = {}) {
        if (!inputElement) return;
        const errorElement = _getInputErrorElement(inputElement);
        if (errorElement) {
            errorElement.textContent = _(messageKey, messageArgs);
            errorElement.classList.add(CSS.ERROR_VISIBLE);
        }
        inputElement.classList.add(CSS.INPUT_HAS_ERROR);
        inputElement.classList.remove('input-valid'); // Remove valid class if error
    }
    function _clearInputError(inputElement) {
        if (!inputElement) return;
        const errorElement = _getInputErrorElement(inputElement);
        if (errorElement) {
            errorElement.textContent = '';
            errorElement.classList.remove(CSS.ERROR_VISIBLE);
        }
        inputElement.classList.remove(CSS.INPUT_HAS_ERROR, 'input-invalid');
    }
    function _clearAllInputErrorsInGroup(inputGroupElement) {
        if (!inputGroupElement) return;
        inputGroupElement.querySelectorAll(`input[type="text"]`).forEach(input => {
            _clearInputError(input);
            input.classList.remove('input-valid', 'input-invalid'); // Also clear validation classes
        });
    }
    function _showGlobalMessage(messageKey, messageArgs = {}, type = 'info', duration = 3000, targetElementId = IDS.SETTINGS_MESSAGE_BAR) {
        const messageBar = document.getElementById(targetElementId);
        if (!messageBar) {
            // If specific target (like modal message bar) not found, try general notification or alert
            if (targetElementId !== IDS.SETTINGS_MESSAGE_BAR && NotificationManager && typeof NotificationManager.show === 'function') {
                NotificationManager.show(messageKey, messageArgs, type, duration > 0 ? duration : 5000); // Longer for notifications if persistent
            } else {
                 const alertMsg = (typeof _ === 'function' && _(messageKey, messageArgs) && !(_(messageKey, messageArgs).startsWith('[ERR:')))
                    ? _(messageKey, messageArgs)
                    : `${messageKey} (args: ${JSON.stringify(messageArgs)})`;
                alert(alertMsg);
            }
            return;
        }

        if (globalMessageTimeout && targetElementId === IDS.SETTINGS_MESSAGE_BAR) { // Clear previous timeout for main settings bar
            clearTimeout(globalMessageTimeout);
            globalMessageTimeout = null;
        }

        messageBar.textContent = _(messageKey, messageArgs);
        messageBar.className = `${CSS.MESSAGE_BAR}`; // Reset classes
        messageBar.classList.add(CSS[`MSG_${type.toUpperCase()}`] || CSS.MSG_INFO); // Add type-specific class
        messageBar.style.display = 'block';

        if (duration > 0 && targetElementId === IDS.SETTINGS_MESSAGE_BAR) {
            globalMessageTimeout = setTimeout(() => {
                messageBar.style.display = 'none';
                messageBar.textContent = '';
                messageBar.className = CSS.MESSAGE_BAR; // Reset classes
            }, duration);
        }
    }
    // Validates and prepares custom item data from input fields
    function _validateAndPrepareCustomItemData(textInput, valueInput, itemTypeName, listId) {
        if (!textInput || !valueInput) {
            _showGlobalMessage('alert_edit_failed_missing_fields', {}, 'error', 0); // Persistent error
            return { isValid: false };
        }
        _clearInputError(textInput);
        _clearInputError(valueInput);

        const text = textInput.value.trim();
        const value = valueInput.value.trim();
        let hint = '';

        if (text === '') {
            _showInputError(textInput, 'alert_enter_display_name', { type: itemTypeName });
            textInput.focus();
            return { isValid: false, errorField: textInput };
        } else {
             // If text is not empty, ensure no lingering error style
            textInput.classList.remove(CSS.INPUT_HAS_ERROR);
            // validateCustomInput(textInput); // No need to re-validate text if non-empty for basic types
        }


        if (value === '') {
            _showInputError(valueInput, 'alert_enter_value', { type: itemTypeName });
            valueInput.focus();
            return { isValid: false, errorField: valueInput };
        } else {
            const isValueFormatValid = validateCustomInput(valueInput); // This also handles visual feedback
            if (!isValueFormatValid) { // validateCustomInput already showed error if !isValid and not empty
                 if (valueInput.classList.contains('input-invalid')) { // Error was shown by validateCustomInput
                     valueInput.focus();
                     return { isValid: false, errorField: valueInput };
                 }
                 // If not marked invalid by validateCustomInput but still failed (e.g. more complex logic not in regex)
                if (listId === IDS.COUNTRIES_LIST) hint = _('modal_tooltip_country');
                else if (listId === IDS.LANG_LIST) hint = _('modal_tooltip_language');
                else if (listId === IDS.TIME_LIST) hint = _('modal_tooltip_time');
                else if (listId === IDS.FT_LIST) hint = _('modal_tooltip_filetype');
                else if (listId === IDS.SITES_LIST) hint = _('modal_tooltip_domain');

                _showInputError(valueInput, 'alert_invalid_value_format', { type: itemTypeName, hint: hint });
                valueInput.focus();
                return { isValid: false, errorField: valueInput };
            }
        }
        return { isValid: true, text: text, value: value };
    }
    function _isDuplicateCustomItem(text, itemsToCheck, listId, editingIndex, editingItemInfoRef) {
        const lowerText = text.toLowerCase();
        return itemsToCheck.some((item, idx) => {
            // Only check custom-type items for name duplication, or all items in SITES/TIME/FT lists
            const itemIsCustom = item.type === 'custom' ||
                                 listId === IDS.SITES_LIST ||
                                 listId === IDS.TIME_LIST ||
                                 listId === IDS.FT_LIST;
            if (!itemIsCustom) return false; // Don't check predefined display names for duplication

            // If editing, allow saving if the name hasn't changed from its original
            if (editingItemInfoRef && editingItemInfoRef.listId === listId && editingIndex === idx) {
                if (editingItemInfoRef.originalText?.toLowerCase() === lowerText) {
                    return false; // Not a duplicate if it's the same item and name hasn't changed
                }
            }
            return item.text.toLowerCase() === lowerText;
        });
    }
    function applyThemeToElement(element, themeSetting) {
        if (!element) return;
        // Remove all potential theme classes first
        element.classList.remove(
            CSS.LIGHT_THEME, CSS.DARK_THEME,
            'minimal-theme', 'minimal-light', 'minimal-dark' // From styles.js
        );

        let effectiveTheme = themeSetting;
        // Settings window/modals should not use minimal theme directly, but fall back to light/dark
        const isSettingsOrModal = element.id === IDS.SETTINGS_WINDOW ||
                                  element.id === IDS.SETTINGS_OVERLAY ||
                                  element.classList.contains('settings-modal-content') || // Modal content itself
                                  element.classList.contains('settings-modal-overlay');   // Modal overlay

        if (isSettingsOrModal) {
            if (themeSetting === 'minimal-light') effectiveTheme = 'light';
            else if (themeSetting === 'minimal-dark') effectiveTheme = 'dark';
        }

        switch (effectiveTheme) {
            case 'dark':
                element.classList.add(CSS.DARK_THEME);
                break;
            case 'minimal-light':
                element.classList.add('minimal-theme', 'minimal-light');
                break;
            case 'minimal-dark':
                element.classList.add('minimal-theme', 'minimal-dark');
                break;
            case 'system':
                const systemIsDark = systemThemeMediaQuery && systemThemeMediaQuery.matches;
                element.classList.add(systemIsDark ? CSS.DARK_THEME : CSS.LIGHT_THEME);
                break;
            case 'light':
            default:
                element.classList.add(CSS.LIGHT_THEME);
                break;
        }
    }

    const PredefinedOptionChooser = (function() {
        let _chooserContainer = null;
        let _currentListId = null;
        let _currentPredefinedSourceKey = null;
        let _currentDisplayItemsArrayRef = null; // Reference to the array like settings.displayLanguages
        let _currentModalContentContext = null; // The modal body where this chooser is shown
        let _onAddCallback = null;

        function _buildChooserHTML(listId, predefinedSourceKey, displayItemsArrayRef) {
            const allPredefinedSystemOptions = PREDEFINED_OPTIONS[predefinedSourceKey] || [];

            // Get values of predefined items already in the display list
            const currentDisplayedValues = new Set(
                displayItemsArrayRef.filter(item => item.type === 'predefined').map(item => item.value)
            );

            const availablePredefinedToAdd = allPredefinedSystemOptions.filter(
                opt => !currentDisplayedValues.has(opt.value)
            );

            if (availablePredefinedToAdd.length === 0) {
                const itemTypeName = getListMapping(listId)?.nameKey ? _(getListMapping(listId).nameKey) : predefinedSourceKey;
                _showGlobalMessage('alert_no_more_predefined_to_add', { type: itemTypeName }, 'info', 3000, IDS.SETTINGS_MESSAGE_BAR); // Show in main settings message bar
                return null; // No HTML if nothing to add
            }

            let listHTML = `<ul id="${IDS.MODAL_PREDEFINED_CHOOSER_LIST}">`;
            availablePredefinedToAdd.forEach(opt => {
                let displayText = _(opt.textKey);
                 if (listId === IDS.COUNTRIES_LIST) { // Special handling for country display
                    const parsed = Utils.parseIconAndText(displayText);
                    displayText = `${parsed.icon} ${parsed.text}`.trim();
                }
                const sanitizedValueForId = opt.value.replace(/[^a-zA-Z0-9-_]/g, ''); // Make value safe for ID
                listHTML += `<li class="${CSS.MODAL_PREDEFINED_CHOOSER_ITEM}"><input type="checkbox" value="${opt.value}" id="chooser-${sanitizedValueForId}"><label for="chooser-${sanitizedValueForId}">${displayText}</label></li>`;
            });
            listHTML += `</ul>`;

            const buttonsHTML = `
                <div class="chooser-buttons" style="text-align: right; margin-top: 10px;">
                    <button id="${IDS.MODAL_PREDEFINED_CHOOSER_ADD_BTN}" class="${CSS.TOOL_BUTTON}" style="margin-right: 5px;">${_('modal_button_add_title')}</button>
                    <button id="${IDS.MODAL_PREDEFINED_CHOOSER_CANCEL_BTN}" class="${CSS.TOOL_BUTTON}">${_('settings_cancel_button')}</button>
                </div>`; // Use TOOL_BUTTON class for consistency

            return listHTML + buttonsHTML;
        }

        function _handleAdd() {
            if (!_chooserContainer) return;
            const selectedValues = [];
            _chooserContainer.querySelectorAll(`#${IDS.MODAL_PREDEFINED_CHOOSER_LIST} input[type="checkbox"]:checked`).forEach(cb => {
                selectedValues.push(cb.value);
            });

            if (selectedValues.length > 0 && typeof _onAddCallback === 'function') {
                _onAddCallback(selectedValues, _currentPredefinedSourceKey, _currentDisplayItemsArrayRef, _currentListId, _currentModalContentContext);
            }
            hide(); // Close the chooser after adding
        }

        function show(manageType, listId, predefinedSourceKey, displayItemsArrayRef, contextElement, onAddCb) {
            hide(); // Ensure any previous chooser is closed

            _currentListId = listId;
            _currentPredefinedSourceKey = predefinedSourceKey;
            _currentDisplayItemsArrayRef = displayItemsArrayRef;
            _currentModalContentContext = contextElement; // The modal's body
            _onAddCallback = onAddCb;

            const chooserHTML = _buildChooserHTML(listId, predefinedSourceKey, displayItemsArrayRef);
            if (!chooserHTML) return; // Nothing to show

            _chooserContainer = document.createElement('div');
            _chooserContainer.id = IDS.MODAL_PREDEFINED_CHOOSER_CONTAINER;
            _chooserContainer.classList.add(CSS.MODAL_PREDEFINED_CHOOSER_CLASS); // Apply specific styling
            _chooserContainer.innerHTML = chooserHTML;

            // Insert the chooser UI into the modal
            // Typically after the "Add New Option..." button or before the main custom list
            const addNewBtn = contextElement.querySelector(`#${IDS.MODAL_ADD_NEW_OPTION_BTN}`);
            if (addNewBtn && addNewBtn.parentNode) {
                addNewBtn.insertAdjacentElement('afterend', _chooserContainer);
            } else {
                // Fallback: insert before the main list if the "Add New" button isn't there (e.g., if it was hidden)
                const mainListElement = contextElement.querySelector(`#${listId}`);
                mainListElement?.insertAdjacentElement('beforebegin', _chooserContainer);
            }
            _chooserContainer.style.display = 'block'; // Make it visible

            // Add event listeners for the chooser's buttons
            _chooserContainer.querySelector(`#${IDS.MODAL_PREDEFINED_CHOOSER_ADD_BTN}`).addEventListener('click', _handleAdd);
            _chooserContainer.querySelector(`#${IDS.MODAL_PREDEFINED_CHOOSER_CANCEL_BTN}`).addEventListener('click', hide);
        }

        function hide() {
            if (_chooserContainer) {
                _chooserContainer.remove();
                _chooserContainer = null;
            }
            // Clear state
            _currentListId = null;
            _currentPredefinedSourceKey = null;
            _currentDisplayItemsArrayRef = null;
            _currentModalContentContext = null;
            _onAddCallback = null;
        }

        return {
            show: show,
            hide: hide,
            isOpen: function() { return !!_chooserContainer; }
        };
    })();


    const ModalManager = (function() {
        let _currentModal = null;
        let _currentModalContent = null; // Reference to the modal's content div for event binding
        let _editingItemInfo = null; // { listId, index, originalValue, originalText, addButton, cancelButton, arrayKey }
        let _draggedListItem = null; // For drag-and-drop within modal lists

        // Configuration for different types of custom options modals
        const modalConfigsData = {
            'site':     { modalTitleKey: 'manageSitesTitle',     listId: IDS.SITES_LIST,     itemsArrayKey: 'favoriteSites',           customItemsMasterKey: null,                 textPKey: 'modal_placeholder_name',   valPKey: 'modal_placeholder_domain', hintKey: 'modal_hint_domain',   fmtKey: 'modal_tooltip_domain',   isSortableMixed: false, predefinedSourceKey: null,       hasPredefinedToggles: false, manageType: 'site' },
            'language': { modalTitleKey: 'manageLanguagesTitle', listId: IDS.LANG_LIST,      itemsArrayKey: 'displayLanguages',        customItemsMasterKey: 'customLanguages',    textPKey: 'modal_placeholder_text',   valPKey: 'modal_placeholder_value',  hintKey: 'modal_hint_language', fmtKey: 'modal_tooltip_language', predefinedSourceKey: 'language', isSortableMixed: true,         hasPredefinedToggles: false, manageType: 'language' }, // displayLanguages is sortable, can mix predefined/custom
            'country':  { modalTitleKey: 'manageCountriesTitle', listId: IDS.COUNTRIES_LIST, itemsArrayKey: 'displayCountries',      customItemsMasterKey: 'customCountries',  textPKey: 'modal_placeholder_text',   valPKey: 'modal_placeholder_value',  hintKey: 'modal_hint_country',  fmtKey: 'modal_tooltip_country',  predefinedSourceKey: 'country',  isSortableMixed: true,         hasPredefinedToggles: false, manageType: 'country'  }, // displayCountries is sortable
            'time':     { modalTitleKey: 'manageTimeRangesTitle',listId: IDS.TIME_LIST,      itemsArrayKey: 'customTimeRanges',        customItemsMasterKey: null,                 textPKey: 'modal_placeholder_text',   valPKey: 'modal_placeholder_value',  hintKey: 'modal_hint_time',     fmtKey: 'modal_tooltip_time',     predefinedSourceKey: 'time',     isSortableMixed: false,        hasPredefinedToggles: true, manageType: 'time' }, // customTimeRanges only, but has predefined toggles
            'filetype': { modalTitleKey: 'manageFileTypesTitle', listId: IDS.FT_LIST,        itemsArrayKey: 'customFiletypes',         customItemsMasterKey: null,                 textPKey: 'modal_placeholder_text',   valPKey: 'modal_placeholder_value',  hintKey: 'modal_hint_filetype', fmtKey: 'modal_tooltip_filetype', predefinedSourceKey: 'filetype', isSortableMixed: false,        hasPredefinedToggles: true, manageType: 'filetype' }, // customFiletypes only, but has predefined toggles
        };


        function _createPredefinedOptionsSectionHTML(currentOptionType, typeNameKey, predefinedOptionsSource, enabledPredefinedValues) {
            const label = _(typeNameKey); // e.g., "Time", "File Type"
            const optionsHTML = (predefinedOptionsSource[currentOptionType] || []).map(option => {
                const checkboxId = `predefined-${currentOptionType}-${option.value.replace(/[^a-zA-Z0-9-_]/g, '')}`; // Sanitize value for ID
                const translatedOptionText = _(option.textKey);
                const isChecked = enabledPredefinedValues.has(option.value);
                return `<li><input type="checkbox" id="${checkboxId}" value="${option.value}" data-option-type="${currentOptionType}" ${isChecked ? 'checked' : ''}><label for="${checkboxId}">${translatedOptionText}</label></li>`;
            }).join('');

            return `<label style="font-weight: bold;">${_('modal_label_enable_predefined', { type: label })}</label><ul class="predefined-options-list" data-option-type="${currentOptionType}">${optionsHTML}</ul>`;
        }


        function _createModalListAndInputHTML(currentListId, textPlaceholderKey, valuePlaceholderKey, hintKey, formatTooltipKey, itemTypeName, isSortableMixed = false) {
            const mapping = getListMapping(currentListId); // To get input IDs
            const typeNameToDisplay = itemTypeName || (mapping ? _(mapping.nameKey) : 'Items'); // Fallback if itemTypeName isn't directly passed

            let headerHTML = '';
            let addNewOptionButtonHTML = '';

            if (isSortableMixed) {
                // For Language/Country, where predefined and custom are mixed and sorted
                headerHTML = `<label style="font-weight: bold; margin-top: 0.5em; display: block;">${_('modal_label_display_options_for', {type: typeNameToDisplay})}</label>`;
                // Button to open the PredefinedOptionChooser or focus custom input
                addNewOptionButtonHTML = `<div style="margin-bottom: 0.5em;"><button id="${IDS.MODAL_ADD_NEW_OPTION_BTN}" class="${CSS.MODAL_ADD_NEW_OPTION_BTN_CLASS} ${CSS.TOOL_BUTTON}">${_('modal_button_add_new_option')}</button></div>`;
            } else {
                 // For Sites, Time, Filetype (custom items only in the sortable list part)
                 headerHTML = `<label style="font-weight: bold; margin-top: 0.5em; display: block;">${_('modal_label_my_custom', { type: typeNameToDisplay })}</label>`;
            }

            // Get input IDs from mapping
            const textInputId = mapping ? mapping.textInput.substring(1) : `new-custom-${currentListId}-text`; // remove #
            const valueInputId = mapping ? mapping.valueInput.substring(1) : `new-custom-${currentListId}-value`;
            const addButtonId = mapping ? mapping.addButton.substring(1) : `add-custom-${currentListId}-button`;

            const inputGroupHTML = `
                <div class="${CSS.CUSTOM_LIST_INPUT_GROUP}">
                    <div><input type="text" id="${textInputId}" placeholder="${_(textPlaceholderKey)}"><span id="${textInputId}-error-msg" class="${CSS.INPUT_ERROR_MESSAGE}"></span></div>
                    <div><input type="text" id="${valueInputId}" placeholder="${_(valuePlaceholderKey)}" title="${_(formatTooltipKey)}"><span id="${valueInputId}-error-msg" class="${CSS.INPUT_ERROR_MESSAGE}"></span></div>
                    <button id="${addButtonId}" class="${CSS.ADD_CUSTOM_BUTTON} custom-list-action-button" data-list-id="${currentListId}" title="${_('modal_button_add_title')}">${SVG_ICONS.add}</button>
                    <button class="cancel-edit-button" style="display: none;" title="${_('modal_button_cancel_edit_title')}">${SVG_ICONS.close}</button>
                </div>`;
            const hintHTML = `<span class="setting-value-hint">${_(hintKey)}</span>`;

            return `${addNewOptionButtonHTML}${headerHTML}<ul id="${currentListId}" class="${CSS.CUSTOM_LIST}"></ul>${inputGroupHTML}${hintHTML}`;
        }


        // Resets the input fields and button states when an edit is cancelled or completed
        function _resetEditStateInternal(contextElement = _currentModalContent) {
            if (_editingItemInfo) {
                const mapping = getListMapping(_editingItemInfo.listId);
                if (_editingItemInfo.addButton) {
                    _editingItemInfo.addButton.innerHTML = SVG_ICONS.add;
                    _editingItemInfo.addButton.title = _('modal_button_add_title');
                    _editingItemInfo.addButton.classList.remove('update-mode'); // Visual cue for update mode
                }
                if (_editingItemInfo.cancelButton) {
                    _editingItemInfo.cancelButton.style.display = 'none';
                }

                // Clear input fields if mapping and context are available
                if (mapping && contextElement) {
                    const textInput = contextElement.querySelector(mapping.textInput);
                    const valueInput = contextElement.querySelector(mapping.valueInput);
                    const inputGroup = textInput?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`);
                    if(inputGroup) _clearAllInputErrorsInGroup(inputGroup);

                    if (textInput) { textInput.value = ''; textInput.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); _clearInputError(textInput); }
                    if (valueInput) { valueInput.value = ''; valueInput.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); _clearInputError(valueInput); }
                }
            }
            _editingItemInfo = null;
        }

        function _prepareEditItemActionInternal(item, index, listId, mapping, contextElement) {
            const textInput = contextElement.querySelector(mapping.textInput);
            const valueInput = contextElement.querySelector(mapping.valueInput);
            const addButton = contextElement.querySelector(mapping.addButton); // Should be the "Add/Update" button
            const cancelButton = addButton?.parentElement?.querySelector('.cancel-edit-button');

            if (textInput && valueInput && addButton && cancelButton) {
                // If already editing another item, reset that state first
                if (_editingItemInfo && (_editingItemInfo.listId !== listId || _editingItemInfo.index !== index)) {
                    _resetEditStateInternal(contextElement);
                }

                textInput.value = item.text;
                valueInput.value = item[mapping.valueKey] || item.value; // Use specific valueKey if defined (e.g., 'url' for sites)

                _editingItemInfo = {
                    listId,
                    index,
                    originalValue: item[mapping.valueKey] || item.value, // Store original for comparison on save
                    originalText: item.text,
                    addButton,
                    cancelButton,
                    arrayKey: mapping.itemsArrayKey || mapping.displayArrayKey // Which array in settings this list maps to
                };

                addButton.innerHTML = SVG_ICONS.update; // Change icon to "update"
                addButton.title = _('modal_button_update_title');
                addButton.classList.add('update-mode');
                cancelButton.style.display = 'inline-block'; // Show cancel button

                // Validate initially loaded values (might be invalid if manually edited in storage)
                validateCustomInput(valueInput);
                validateCustomInput(textInput); // Although text is usually just non-empty
                textInput.focus();
            } else {
                // This indicates a problem with the modal's HTML structure or selectors
                const errorSourceInput = textInput || valueInput || addButton?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`)?.querySelector('input[type="text"]');
                if (errorSourceInput) _showInputError(errorSourceInput, 'alert_edit_failed_missing_fields');
                else _showGlobalMessage('alert_edit_failed_missing_fields', {}, 'error', 0, _currentModalContent?.querySelector(`#${IDS.SETTINGS_MESSAGE_BAR}`) ? IDS.SETTINGS_MESSAGE_BAR : null);

            }
        }

        function _handleCustomListActionsInternal(event, contextElement, itemsArrayRef) {
            const button = event.target.closest(`button.${CSS.EDIT_CUSTOM_ITEM}, button.${CSS.DELETE_CUSTOM_ITEM}, button.${CSS.REMOVE_FROM_LIST_BTN}`);
            if (!button) return;

            const listItem = button.closest(`li[data-${DATA_ATTR.INDEX}][data-list-id]`);
            if (!listItem) return;

            const index = parseInt(listItem.dataset[DATA_ATTR.INDEX], 10);
            const listId = listItem.getAttribute('data-list-id'); // e.g., IDS.SITES_LIST

            if (isNaN(index) || !listId || index < 0 || index >= itemsArrayRef.length) return;

            const mapping = getListMapping(listId);
            if (!mapping) return;

            const item = itemsArrayRef[index];
            if (!item) return; // Should not happen

            const itemIsTrulyCustom = item.type === 'custom' ||
                                     (!item.type && (listId === IDS.SITES_LIST || listId === IDS.TIME_LIST || listId === IDS.FT_LIST));


            if (button.classList.contains(CSS.DELETE_CUSTOM_ITEM) && itemIsTrulyCustom) {
                if (confirm(_('confirm_delete_item', { name: item.text }))) {
                    // If deleting the item currently being edited, reset edit state
                    if (_editingItemInfo && _editingItemInfo.listId === listId && _editingItemInfo.index === index) {
                        _resetEditStateInternal(contextElement);
                    }
                    itemsArrayRef.splice(index, 1);
                    mapping.populateFn(listId, itemsArrayRef, contextElement); // Re-render list
                }
            } else if (button.classList.contains(CSS.REMOVE_FROM_LIST_BTN) && item.type === 'predefined') {
                // For sortable mixed lists (Lang, Country), predefined items can be removed from display
                if (confirm(_('confirm_remove_item_from_list', { name: (item.originalKey ? _(item.originalKey) : item.text) }))) {
                    itemsArrayRef.splice(index, 1);
                    mapping.populateFn(listId, itemsArrayRef, contextElement);
                }
            } else if (button.classList.contains(CSS.EDIT_CUSTOM_ITEM) && itemIsTrulyCustom) {
                _prepareEditItemActionInternal(item, index, listId, mapping, contextElement);
            }
        }

        function _handleCustomItemSubmitInternal(listId, contextElement, itemsArrayRef) {
            const mapping = getListMapping(listId);
            if (!mapping) return;

            const itemTypeName = _(mapping.nameKey); // For error messages
            const textInput = contextElement.querySelector(mapping.textInput);
            const valueInput = contextElement.querySelector(mapping.valueInput);

            // Clear previous errors for this group before re-validating
            const inputGroup = textInput?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`);
            if (inputGroup) _clearAllInputErrorsInGroup(inputGroup);


            const validationResult = _validateAndPrepareCustomItemData(textInput, valueInput, itemTypeName, listId);
            if (!validationResult.isValid) {
                if (validationResult.errorField) validationResult.errorField.focus(); // Focus the first problematic field
                return;
            }

            const { text, value } = validationResult;
            const editingIdx = (_editingItemInfo && _editingItemInfo.listId === listId) ? _editingItemInfo.index : -1;

            // Check for duplicate display names (only for custom items or all in non-mixed lists)
            let isDuplicate;
            if (mapping.isSortableMixed) { // Lang, Country - check against other custom items in the display list
                isDuplicate = _isDuplicateCustomItem(text, itemsArrayRef, listId, editingIdx, _editingItemInfo);
            } else { // Sites, Time, Filetypes - all items are effectively "custom" in editability
                isDuplicate = itemsArrayRef.some((item, idx) => {
                    if (editingIdx === idx && (_editingItemInfo?.originalText?.toLowerCase() === text.toLowerCase())) return false; // Allow saving if name unchanged
                    return item.text.toLowerCase() === text.toLowerCase();
                });
            }

            if (isDuplicate) {
                if (textInput) _showInputError(textInput, 'alert_duplicate_name', { name: text });
                textInput?.focus();
                return;
            }

            // Prepare new item data
            let newItemData;
            if (listId === IDS.SITES_LIST) {
                newItemData = { text: text, url: value }; // Sites use 'url'
            } else if (mapping.isSortableMixed) { // Lang, Country
                newItemData = { id: value, text: text, value: value, type: 'custom' }; // Ensure type is 'custom'
            } else { // Time, Filetype
                newItemData = { text: text, value: value };
            }

            // Determine if this item being edited is actually of a type that *can* be edited
            // (e.g., a predefined item in a mixed list should not be "updated" this way)
            const itemBeingEdited = (editingIdx > -1) ? itemsArrayRef[editingIdx] : null;
            const itemBeingEditedIsCustom = itemBeingEdited &&
                                           (itemBeingEdited.type === 'custom' || // Explicitly custom
                                            listId === IDS.SITES_LIST ||         // Or one of these list types
                                            listId === IDS.TIME_LIST ||
                                            listId === IDS.FT_LIST);

            if (editingIdx > -1 && itemBeingEditedIsCustom) { // Update existing custom item
                // Merge new data into existing, preserving other properties if any
                itemsArrayRef[editingIdx] = {...itemsArrayRef[editingIdx], ...newItemData };
                _resetEditStateInternal(contextElement); // Reset inputs and button
            } else { // Add as new custom item
                itemsArrayRef.push(newItemData);
            }

            mapping.populateFn(listId, itemsArrayRef, contextElement); // Re-render the list

            // Clear inputs if not in edit mode (or if edit was just completed for a different list)
            if (!_editingItemInfo || _editingItemInfo.listId !== listId) {
                if (textInput) { textInput.value = ''; _clearInputError(textInput); textInput.focus(); }
                if (valueInput) { valueInput.value = ''; _clearInputError(valueInput); }
            }
        }


        // --- Drag and Drop for Modal Lists ---
        function _getDragAfterModalListItem(container, y) {
            const draggableElements = [...container.querySelectorAll(`li[draggable="true"]:not(.${CSS.DRAGGING_ITEM})`)];
            return draggableElements.reduce((closest, child) => {
                const box = child.getBoundingClientRect();
                const offset = y - box.top - box.height / 2;
                if (offset < 0 && offset > closest.offset) {
                    return { offset: offset, element: child };
                } else {
                    return closest;
                }
            }, { offset: Number.NEGATIVE_INFINITY }).element;
        }

        function _handleModalListDragStart(event) {
            if (!event.target.matches('li[draggable="true"]')) return;
            _draggedListItem = event.target;
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('text/plain', _draggedListItem.dataset.index); // Store original index
            _draggedListItem.classList.add(CSS.DRAGGING_ITEM);

            // Disable pointer events on other items to prevent interference during dragover
            const list = _draggedListItem.closest('ul');
            if (list) { list.querySelectorAll('li:not(.gscs-dragging-item)').forEach(li => li.style.pointerEvents = 'none'); }
        }

        function _handleModalListDragOver(event) {
            event.preventDefault(); // Necessary to allow drop
            const listElement = event.currentTarget; // The UL element
            // Clear previous highlights
            listElement.querySelectorAll(`li.${CSS.DRAG_OVER_HIGHLIGHT}`).forEach(li => li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT));

            const targetItem = event.target.closest('li[draggable="true"]');
            if (targetItem && targetItem !== _draggedListItem) {
                targetItem.classList.add(CSS.DRAG_OVER_HIGHLIGHT); // Highlight item being dragged over
            } else {
                // If not directly over an item, find where it would be inserted
                const afterElement = _getDragAfterModalListItem(listElement, event.clientY);
                if (afterElement) {
                    afterElement.classList.add(CSS.DRAG_OVER_HIGHLIGHT); // Highlight element it would go before
                }
            }
        }
        function _handleModalListDragLeave(event) {
            const listElement = event.currentTarget;
            // Only remove highlight if leaving the list container itself, not just moving between items
            if (event.relatedTarget && listElement.contains(event.relatedTarget)) return;
            listElement.querySelectorAll(`li.${CSS.DRAG_OVER_HIGHLIGHT}`).forEach(li => li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT));
        }


        function _handleModalListDrop(event, listId, itemsArrayRef) {
            event.preventDefault();
            if (!_draggedListItem) return;

            const draggedIndexOriginal = parseInt(event.dataTransfer.getData('text/plain'), 10);
            if (isNaN(draggedIndexOriginal) || draggedIndexOriginal < 0 || draggedIndexOriginal >= itemsArrayRef.length) {
                _handleModalListDragEnd(event.currentTarget); // Pass the list element
                return;
            }

            const listElement = event.currentTarget; // The UL
            const mapping = getListMapping(listId);
            if (!mapping) { _handleModalListDragEnd(listElement); return; }


            const draggedItemData = itemsArrayRef[draggedIndexOriginal]; // Get the actual data object
            if (!draggedItemData) { _handleModalListDragEnd(listElement); return; }


            // Create a new array without the dragged item to calculate new position correctly
            const itemsWithoutDragged = itemsArrayRef.filter((item, index) => index !== draggedIndexOriginal);

            const afterElement = _getDragAfterModalListItem(listElement, event.clientY);
            let newIndexInSplicedArray;

            if (afterElement) {
                const originalIndexOfAfterElement = parseInt(afterElement.dataset.index, 10);
                // Find the index of 'afterElement' in the 'itemsWithoutDragged' array
                let countSkipped = 0; newIndexInSplicedArray = -1;
                for(let i=0; i < itemsArrayRef.length; i++) {
                    if (i === draggedIndexOriginal) continue; // Skip the one we are dragging
                    if (i === originalIndexOfAfterElement) {
                        newIndexInSplicedArray = countSkipped;
                        break;
                    }
                    countSkipped++;
                }
                 // If afterElement was the last visible item and dragged item was before it
                 if (newIndexInSplicedArray === -1 && originalIndexOfAfterElement === itemsArrayRef.length -1 && draggedIndexOriginal < originalIndexOfAfterElement) {
                    newIndexInSplicedArray = itemsWithoutDragged.length;
                } else if (newIndexInSplicedArray === -1) { // Fallback: if not found (e.g. dragging to end)
                    newIndexInSplicedArray = itemsWithoutDragged.length;
                }

            } else {
                // Dropped at the end of the list
                newIndexInSplicedArray = itemsWithoutDragged.length;
            }

            // Insert the dragged item data into the new position in the temporary array
            itemsWithoutDragged.splice(newIndexInSplicedArray, 0, draggedItemData);

            // Update the original itemsArrayRef with the new order
            itemsArrayRef.length = 0; // Clear original array
            itemsWithoutDragged.forEach(item => itemsArrayRef.push(item)); // Push items in new order

            _handleModalListDragEnd(listElement); // Cleanup drag styles
            mapping.populateFn(listId, itemsArrayRef, _currentModalContent); // Re-render the list

            // Update dataset.index on all li elements after re-rendering
            const newLiElements = listElement.querySelectorAll('li');
            newLiElements.forEach((li, idx) => {
                li.dataset.index = idx;
            });
        }


        function _handleModalListDragEnd(listElement) { // listElement is the UL
            if (_draggedListItem) {
                _draggedListItem.classList.remove(CSS.DRAGGING_ITEM);
            }
            _draggedListItem = null;
            (listElement || _currentModalContent)?.querySelectorAll(`li.${CSS.DRAG_OVER_HIGHLIGHT}`).forEach(li => li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT));
             // Re-enable pointer events on list items
             _currentModalContent?.querySelectorAll('ul.custom-list li[draggable="true"]').forEach(li => li.style.pointerEvents = '');
        }


        function _addPredefinedItemsToModalList(selectedValues, predefinedSourceKey, displayItemsArrayRef, listIdToUpdate, modalContentContext) {
            const mapping = getListMapping(listIdToUpdate);
            if (!mapping) return;

            selectedValues.forEach(value => {
                const predefinedOpt = PREDEFINED_OPTIONS[predefinedSourceKey]?.find(p => p.value === value);
                if (predefinedOpt && !displayItemsArrayRef.some(item => item.value === value && item.type === 'predefined')) {
                    displayItemsArrayRef.push({
                        id: predefinedOpt.value, // Use value as ID for predefined
                        text: _(predefinedOpt.textKey), // Store original text key for re-translation if lang changes
                        value: predefinedOpt.value,
                        type: 'predefined',
                        originalKey: predefinedOpt.textKey // Store the key
                    });
                }
            });
            // No sort here, rely on user to sort if needed, or initial sort if it's a mixed list.
            // Or, sort them by their translated text if that's desired for newly added ones.
            // For now, just append.
            mapping.populateFn(listIdToUpdate, displayItemsArrayRef, modalContentContext);
        }

        // Binds events to the content within the modal
        function _bindModalContentEventsInternal(modalContent, itemsArrayRef, listIdForDragDrop = null) {
            if (!modalContent) return;

            // Prevent re-binding if already bound for this specific list setup
            if (modalContent.dataset.modalEventsBound === 'true' && listIdForDragDrop === modalContent.dataset.boundListId) return;


            // General click listener for the modal body (delegated)
            modalContent.addEventListener('click', (event) => {
                const target = event.target;
                // Determine which list this action pertains to (can be complex if multiple lists in one modal, but not current design)
                let listIdForAction = listIdForDragDrop || target.closest('[data-list-id]')?.dataset.listId || target.closest(`.${CSS.ADD_CUSTOM_BUTTON}`)?.dataset.listId;

                const addNewOptionButton = target.closest(`#${IDS.MODAL_ADD_NEW_OPTION_BTN}`);
                if (addNewOptionButton && listIdForAction) {
                    // This button is for mixed lists (Lang/Country) to add predefined or custom items
                    const mapping = getListMapping(listIdForAction);
                    const configForModal = modalConfigsData[Object.keys(modalConfigsData).find(key => modalConfigsData[key].listId === listIdForAction)];

                    if (mapping && configForModal && configForModal.predefinedSourceKey && configForModal.isSortableMixed) {
                         PredefinedOptionChooser.show(
                            configForModal.manageType,
                            listIdForAction,
                            configForModal.predefinedSourceKey,
                            itemsArrayRef, // Pass the live array
                            modalContent,
                            _addPredefinedItemsToModalList
                        );
                    } else if (mapping) { // If not a mixed list with predefined chooser, just focus input
                         const textInput = modalContent.querySelector(mapping.textInput);
                         textInput?.focus();
                    }
                    return; // Handled
                }


                const addButton = target.closest(`.${CSS.ADD_CUSTOM_BUTTON}.custom-list-action-button`);
                const itemControlButton = target.closest(`button.${CSS.EDIT_CUSTOM_ITEM}, button.${CSS.DELETE_CUSTOM_ITEM}, button.${CSS.REMOVE_FROM_LIST_BTN}`);
                const cancelEditButton = target.closest('.cancel-edit-button');

                if (itemControlButton && listIdForAction) { _handleCustomListActionsInternal(event, modalContent, itemsArrayRef); return; }
                if (addButton && listIdForAction) { _handleCustomItemSubmitInternal(listIdForAction, modalContent, itemsArrayRef); return; }
                if (cancelEditButton) { _resetEditStateInternal(modalContent); return; }
            });
            modalContent.dataset.modalEventsBound = 'true';
            modalContent.dataset.boundListId = listIdForDragDrop; // Store which list drag events are bound to


            // Input validation listener
            modalContent.addEventListener('input', (event) => {
                const target = event.target;
                // Validate relevant input fields on input
                if (target.matches(`#${IDS.NEW_SITE_NAME}, #${IDS.NEW_SITE_URL}, #${IDS.NEW_LANG_TEXT}, #${IDS.NEW_LANG_VALUE}, #${IDS.NEW_TIME_TEXT}, #${IDS.NEW_TIME_VALUE}, #${IDS.NEW_FT_TEXT}, #${IDS.NEW_FT_VALUE}, #${IDS.NEW_COUNTRY_TEXT}, #${IDS.NEW_COUNTRY_VALUE}`)) {
                    _clearInputError(target); // Clear previous error first
                    validateCustomInput(target);
                }
            });

            // Bind drag-and-drop listeners if a specific listId is provided for it
            if (listIdForDragDrop) {
                const draggableListElement = modalContent.querySelector(`#${listIdForDragDrop}`);
                if (draggableListElement) {
                    if (draggableListElement.dataset.dragEventsBound !== 'true') { // Prevent multiple bindings
                        draggableListElement.dataset.dragEventsBound = 'true';
                        draggableListElement.addEventListener('dragstart', _handleModalListDragStart);
                        draggableListElement.addEventListener('dragover', _handleModalListDragOver);
                        draggableListElement.addEventListener('dragleave', _handleModalListDragLeave);
                        draggableListElement.addEventListener('drop', (event) => _handleModalListDrop(event, listIdForDragDrop, itemsArrayRef));
                        draggableListElement.addEventListener('dragend', (event) => _handleModalListDragEnd(draggableListElement));
                    }
                }
            }
        }

        return {
            show: function(titleKey, contentHTML, onCompleteCallback, currentTheme) {
                this.hide(); // Hide any existing modal

                _currentModal = document.createElement('div');
                _currentModal.className = 'settings-modal-overlay';
                applyThemeToElement(_currentModal, currentTheme); // Apply theme to overlay

                _currentModalContent = document.createElement('div');
                _currentModalContent.className = 'settings-modal-content';
                applyThemeToElement(_currentModalContent, currentTheme); // Apply theme to content

                const header = document.createElement('div');
                header.className = 'settings-modal-header';
                header.innerHTML = `<h4>${_(titleKey)}</h4><button class="settings-modal-close-btn" title="${_('settings_close_button_title')}">${SVG_ICONS.close}</button>`;

                const body = document.createElement('div');
                body.className = 'settings-modal-body';
                body.innerHTML = contentHTML;

                const footer = document.createElement('div');
                footer.className = 'settings-modal-footer';
                footer.innerHTML = `<button class="modal-complete-btn">${_('modal_button_complete')}</button>`;

                _currentModalContent.appendChild(header);
                _currentModalContent.appendChild(body);
                _currentModalContent.appendChild(footer);
                _currentModal.appendChild(_currentModalContent);

                // Event listeners for closing
                let closeModalHandlerInstance = null; // To store the event handler for removal
                let completeModalHandlerInstance = null;
                const self = this; // For context in handlers

                closeModalHandlerInstance = (event) => {
                    // Close if clicking overlay directly or the close button
                    if (event.target === _currentModal || event.target.closest('.settings-modal-close-btn')) {
                        self.hide(true); // true for cancel
                        // Clean up listeners to prevent memory leaks
                        _currentModal?.removeEventListener('click', closeModalHandlerInstance, true); // Use capture for overlay click
                        header.querySelector('.settings-modal-close-btn')?.removeEventListener('click', closeModalHandlerInstance);
                        footer.querySelector('.modal-complete-btn')?.removeEventListener('click', completeModalHandlerInstance);
                    }
                };
                completeModalHandlerInstance = () => {
                    if (onCompleteCallback && typeof onCompleteCallback === 'function') {
                        onCompleteCallback(_currentModalContent); // Pass content for data extraction
                    }
                     // Clean up listeners
                    _currentModal?.removeEventListener('click', closeModalHandlerInstance, true);
                    header.querySelector('.settings-modal-close-btn')?.removeEventListener('click', closeModalHandlerInstance);
                    footer.querySelector('.modal-complete-btn')?.removeEventListener('click', completeModalHandlerInstance);
                    self.hide(false); // false for complete
                };

                _currentModal.addEventListener('click', closeModalHandlerInstance, true); // Overlay click (capture phase)
                header.querySelector('.settings-modal-close-btn').addEventListener('click', closeModalHandlerInstance);
                footer.querySelector('.modal-complete-btn').addEventListener('click', completeModalHandlerInstance);

                // Prevent clicks inside the modal content from closing it via overlay click
                _currentModalContent.addEventListener('click', (event) => event.stopPropagation());

                document.body.appendChild(_currentModal);
                return _currentModalContent; // Return the content div for further population/binding
            },
            hide: function(isCancel = false) {
                PredefinedOptionChooser.hide(); // Ensure chooser is also hidden
                if (_currentModal) {
                    // Clear any specific states tied to the modal content before removing
                    const inputGroup = _currentModalContent?.querySelector(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`);
                    if (inputGroup) _clearAllInputErrorsInGroup(inputGroup);
                    _resetEditStateInternal(); // Clear editing state

                    _currentModal.remove(); // Remove from DOM
                }
                _currentModal = null;
                _currentModalContent = null;
                _handleModalListDragEnd(); // Clean up general drag state if any
            },
            openManageCustomOptions: function(manageType, currentSettingsRef, PREDEFINED_OPTIONS_REF, onModalCompleteCallback) {
                const config = modalConfigsData[manageType];
                if (!config) { console.error("Error: Could not get config for manageType:", manageType); return; }

                const mapping = getListMapping(config.listId); // For input IDs etc.
                if (!mapping) { console.error("Error: Could not get mapping for listId:", config.listId); return; }

                // Create a deep copy of the items to be managed in the modal, so direct modifications
                // don't affect the main settings until "Done" is clicked.
                const tempItems = JSON.parse(JSON.stringify(currentSettingsRef[config.itemsArrayKey] || []));
                let contentHTML = '';
                const itemTypeNameForDisplay = _(mapping.nameKey); // e.g., "Languages", "Favorite Sites"

                if (config.isSortableMixed) { // For Language, Country
                    contentHTML += _createModalListAndInputHTML(config.listId, config.textPKey, config.valPKey, config.hintKey, config.fmtKey, itemTypeNameForDisplay, true);
                } else if (config.hasPredefinedToggles && config.predefinedSourceKey && PREDEFINED_OPTIONS_REF[config.predefinedSourceKey]) {
                    // For Time, Filetype - which have a list of predefined checkboxes AND a custom list
                    const enabledValues = new Set(currentSettingsRef.enabledPredefinedOptions[config.predefinedSourceKey] || []);
                    contentHTML += _createPredefinedOptionsSectionHTML(config.predefinedSourceKey, mapping.nameKey, PREDEFINED_OPTIONS_REF, enabledValues);
                    contentHTML += '<hr style="margin: 1em 0;">'; // Separator
                    contentHTML += _createModalListAndInputHTML(config.listId, config.textPKey, config.valPKey, config.hintKey, config.fmtKey, itemTypeNameForDisplay, false);
                } else { // For Sites (no predefined toggles, only custom list)
                    contentHTML += _createModalListAndInputHTML(config.listId, config.textPKey, config.valPKey, config.hintKey, config.fmtKey, itemTypeNameForDisplay, false);
                }

                const modalContentElement = this.show(
                    config.modalTitleKey,
                    contentHTML,
                    (modalContent) => { // onCompleteCallback
                        let newEnabledPredefs = null;
                        if (config.hasPredefinedToggles && config.predefinedSourceKey) {
                            newEnabledPredefs = [];
                            modalContent.querySelectorAll(`.predefined-options-list input[data-option-type="${config.predefinedSourceKey}"]:checked`).forEach(cb => newEnabledPredefs.push(cb.value));
                        }
                        onModalCompleteCallback(tempItems, newEnabledPredefs, config.itemsArrayKey, config.predefinedSourceKey, config.customItemsMasterKey, config.isSortableMixed, manageType);
                    },
                    currentSettingsRef.theme // Pass current theme for modal styling
                );

                // Populate the list within the modal and bind events
                if (modalContentElement) {
                    if (mapping && mapping.populateFn) {
                        mapping.populateFn(config.listId, tempItems, modalContentElement);
                    }
                    _bindModalContentEventsInternal(modalContentElement, tempItems, config.listId); // Pass tempItems for direct modification
                }
            },
            resetEditStateGlobally: function() { _resetEditStateInternal(_currentModalContent || document); },
            isModalOpen: function() { return !!_currentModal; }
        };
    })();

    const SettingsUIPaneGenerator = (function() {
        function createGeneralPaneHTML() {
            let langOpts = LocalizationService.getAvailableLocales().map(lc => {
                let dn;
                if (lc === 'auto') dn = _('settings_language_auto');
                else { try { dn = new Intl.DisplayNames([lc],{type:'language'}).of(lc); dn = dn.charAt(0).toUpperCase() + dn.slice(1); } catch(e){ dn = lc; } dn = `${dn} (${lc})`; }
                return `<option value="${lc}">${dn}</option>`;
            }).join('');
            const accordionHintHTML = `<div class="${CSS.SETTING_VALUE_HINT}" style="margin-top:0.3em; margin-left:1.7em; font-weight:normal;">${_('settings_accordion_mode_hint_desc')}</div>`;
            const locationOptionsHTML = `
                <option value="tools">${_('settings_location_tools')}</option>
                <option value="topBlock">${_('settings_location_top')}</option>
                <option value="header">${_('settings_location_header')}</option>
                <option value="none">${_('settings_location_hide')}</option>`;

            return `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_INTERFACE_LANGUAGE}">${_('settings_interface_language')}</label><select id="${IDS.SETTING_INTERFACE_LANGUAGE}">${langOpts}</select></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_SECTION_MODE}">${_('settings_section_mode')}</label><select id="${IDS.SETTING_SECTION_MODE}"><option value="remember">${_('settings_section_mode_remember')}</option><option value="expandAll">${_('settings_section_mode_expand')}</option><option value="collapseAll">${_('settings_section_mode_collapse')}</option></select><div style="margin-top:0.6em;"><input type="checkbox" id="${IDS.SETTING_ACCORDION}"><label for="${IDS.SETTING_ACCORDION}" class="${CSS.INLINE_LABEL}">${_('settings_accordion_mode')}</label>${accordionHintHTML}</div></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><input type="checkbox" id="${IDS.SETTING_DRAGGABLE}"><label for="${IDS.SETTING_DRAGGABLE}" class="${CSS.INLINE_LABEL}">${_('settings_enable_drag')}</label></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_RESET_LOCATION}">${_('settings_reset_button_location')}</label><select id="${IDS.SETTING_RESET_LOCATION}">${locationOptionsHTML}</select></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_VERBATIM_LOCATION}">${_('settings_verbatim_button_location')}</label><select id="${IDS.SETTING_VERBATIM_LOCATION}">${locationOptionsHTML}</select></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_ADV_SEARCH_LOCATION}">${_('settings_adv_search_location')}</label><select id="${IDS.SETTING_ADV_SEARCH_LOCATION}">${locationOptionsHTML}</select></div>`+
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_PERSONALIZE_LOCATION}">${_('settings_personalize_button_location')}</label><select id="${IDS.SETTING_PERSONALIZE_LOCATION}">${locationOptionsHTML}</select></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_SCHOLAR_LOCATION}">${_('settings_scholar_location')}</label><select id="${IDS.SETTING_SCHOLAR_LOCATION}">${locationOptionsHTML}</select></div>` + // New setting for Scholar
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_TRENDS_LOCATION}">${_('settings_trends_location')}</label><select id="${IDS.SETTING_TRENDS_LOCATION}">${locationOptionsHTML}</select></div>`; // New setting for Trends
        }
        function createAppearancePaneHTML() {
            // New HTML for hideGoogleLogo setting
            const hideLogoSettingHTML =
                `<div class="${CSS.SETTING_ITEM}">` +
                `<input type="checkbox" id="${IDS.SETTING_HIDE_GOOGLE_LOGO}"><label for="${IDS.SETTING_HIDE_GOOGLE_LOGO}" class="${CSS.INLINE_LABEL}">${_('settings_hide_google_logo')}</label>` +
                `<div class="${CSS.SETTING_VALUE_HINT}" style="margin-top:0.3em; margin-left:1.7em; font-weight:normal;">${_('settings_hide_google_logo_hint')}</div>` +
                `</div>`;

            return `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_WIDTH}">${_('settings_sidebar_width')}</label><span class="${CSS.RANGE_HINT}">${_('settings_width_range_hint')}</span><input type="range" id="${IDS.SETTING_WIDTH}" min="90" max="270" step="5"><span class="${CSS.RANGE_VALUE}"></span></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_FONT_SIZE}">${_('settings_font_size')}</label><span class="${CSS.RANGE_HINT}">${_('settings_font_size_range_hint')}</span><input type="range" id="${IDS.SETTING_FONT_SIZE}" min="8" max="24" step="0.5"><span class="${CSS.RANGE_VALUE}"></span></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_HEADER_ICON_SIZE}">${_('settings_header_icon_size')}</label><span class="${CSS.RANGE_HINT}">${_('settings_header_icon_size_range_hint')}</span><input type="range" id="${IDS.SETTING_HEADER_ICON_SIZE}" min="8" max="32" step="0.5"><span class="${CSS.RANGE_VALUE}"></span></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_VERTICAL_SPACING}">${_('settings_vertical_spacing')}</label><span class="${CSS.RANGE_HINT}">${_('settings_vertical_spacing_range_hint')}</span><input type="range" id="${IDS.SETTING_VERTICAL_SPACING}" min="0.05" max="1.5" step="0.05"><span class="${CSS.RANGE_VALUE}"></span></div>` +
                   `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_THEME}">${_('settings_theme')}</label><select id="${IDS.SETTING_THEME}"><option value="system">${_('settings_theme_system')}</option><option value="light">${_('settings_theme_light')}</option><option value="dark">${_('settings_theme_dark')}</option><option value="minimal-light">${_('settings_theme_minimal_light')}</option><option value="minimal-dark">${_('settings_theme_minimal_dark')}</option></select></div><div class="${CSS.SETTING_ITEM}"><input type="checkbox" id="${IDS.SETTING_HOVER}"><label for="${IDS.SETTING_HOVER}" class="${CSS.INLINE_LABEL}">${_('settings_hover_mode')}</label><div style="margin-top:0.8em;padding-left:1.5em;"><label for="${IDS.SETTING_OPACITY}" style="display:block;margin-bottom:0.4em;font-weight:normal;">${_('settings_idle_opacity')}</label><span class="${CSS.RANGE_HINT}" style="width:auto;display:inline-block;margin-right:1em;">${_('settings_opacity_range_hint')}</span><input type="range" id="${IDS.SETTING_OPACITY}" min="0.1" max="1.0" step="0.05" style="width:calc(100% - 18em);vertical-align:middle;display:inline-block;"><span class="${CSS.RANGE_VALUE}" style="display:inline-block;min-width:3em;text-align:right;vertical-align:middle;"></span></div></div><div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_COUNTRY_DISPLAY_MODE}">${_('settings_country_display')}</label><select id="${IDS.SETTING_COUNTRY_DISPLAY_MODE}"><option value="iconAndText">${_('settings_country_display_icontext')}</option><option value="textOnly">${_('settings_country_display_text')}</option><option value="iconOnly">${_('settings_country_display_icon')}</option></select></div>` +
                   hideLogoSettingHTML;
        }
        function createFeaturesPaneHTML() {
            const visItemsHTML = ALL_SECTION_DEFINITIONS.map(def=>{ const dn=_(def.titleKey)||def.id; return `<div class="${CSS.SETTING_ITEM} ${CSS.SIMPLE_ITEM}"><input type="checkbox" id="setting-visible-${def.id}" data-${DATA_ATTR.SECTION_ID}="${def.id}"><label for="setting-visible-${def.id}" class="${CSS.INLINE_LABEL}">${dn}</label></div>`; }).join('');
            const siteSearchCheckboxModeHTML =
                `<div class="${CSS.SETTING_ITEM}">` +
                `<input type="checkbox" id="${IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE}"><label for="${IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE}" class="${CSS.INLINE_LABEL}">${_('settings_enable_site_search_checkbox_mode')}</label>` +
                `<div class="${CSS.SETTING_VALUE_HINT}" style="margin-top:0.3em; margin-left:1.7em; font-weight:normal;">${_('settings_enable_site_search_checkbox_mode_hint')}</div>` +
                `</div>`;
            const filetypeSearchCheckboxModeHTML =
                `<div class="${CSS.SETTING_ITEM}">` +
                `<input type="checkbox" id="${IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE}"><label for="${IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE}" class="${CSS.INLINE_LABEL}">${_('settings_enable_filetype_search_checkbox_mode')}</label>` +
                `<div class="${CSS.SETTING_VALUE_HINT}" style="margin-top:0.3em; margin-left:1.7em; font-weight:normal;">${_('settings_enable_filetype_search_checkbox_mode_hint')}</div>` +
                `</div>`;
            return `<p>${_('settings_visible_sections')}</p>${visItemsHTML}` +
                   `${siteSearchCheckboxModeHTML}${filetypeSearchCheckboxModeHTML}<hr style="margin:1.2em 0;">` +
                   `<p style="font-weight:bold;margin-bottom:0.5em;">${_('settings_section_order')}</p><p class="${CSS.SETTING_VALUE_HINT}" style="font-size:0.9em;margin-top:-0.3em;margin-bottom:0.7em;">${_('settings_section_order_hint')}</p><ul id="${IDS.SIDEBAR_SECTION_ORDER_LIST}" class="${CSS.SECTION_ORDER_LIST}"></ul>`;
        }
        function createCustomPaneHTML() {
            return `<div class="${CSS.SETTING_ITEM}"><p>${_('settings_custom_intro')}</p><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="site">${_('settings_manage_sites_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="language">${_('settings_manage_languages_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="country">${_('settings_manage_countries_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="time">${_('settings_manage_time_ranges_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="filetype">${_('settings_manage_file_types_button')}</button></div>`;
        }
        return { createGeneralPaneHTML, createAppearancePaneHTML, createFeaturesPaneHTML, createCustomPaneHTML };
    })();

    const SectionOrderDragHandler = (function() {
        let _draggedItem = null; let _listElement = null; let _settingsRef = null; let _onOrderUpdateCallback = null;
        function getDragAfterElement(container, y) { const draggableElements = [...container.querySelectorAll(`li[draggable="true"]:not(.${CSS.DRAGGING_ITEM})`)]; return draggableElements.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) { return { offset: offset, element: child }; } else { return closest; } }, { offset: Number.NEGATIVE_INFINITY }).element; }
        function handleDragStart(event) { _draggedItem = event.target; event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', _draggedItem.dataset.sectionId); _draggedItem.classList.add(CSS.DRAGGING_ITEM); if (_listElement) { _listElement.querySelectorAll('li:not(.gscs-dragging-item)').forEach(li => li.style.pointerEvents = 'none'); } }
        function handleDragOver(event) { event.preventDefault(); if (!_listElement) return; _listElement.querySelectorAll(`li.${CSS.DRAG_OVER_HIGHLIGHT}`).forEach(li => { li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT); }); const targetItem = event.target.closest('li[draggable="true"]'); if (targetItem && targetItem !== _draggedItem) { targetItem.classList.add(CSS.DRAG_OVER_HIGHLIGHT); } else if (!targetItem && _listElement.contains(event.target)) { const afterElement = getDragAfterElement(_listElement, event.clientY); if (afterElement) { afterElement.classList.add(CSS.DRAG_OVER_HIGHLIGHT); } } }
        function handleDragLeave(event) { const relatedTarget = event.relatedTarget; if (_listElement && (!relatedTarget || !_listElement.contains(relatedTarget))) { _listElement.querySelectorAll(`li.${CSS.DRAG_OVER_HIGHLIGHT}`).forEach(li => { li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT); }); } }
        function handleDrop(event) {
            event.preventDefault(); if (!_draggedItem || !_listElement || !_settingsRef || !_onOrderUpdateCallback) return;
            const draggedSectionId = event.dataTransfer.getData('text/plain');
            let currentVisibleOrder = _settingsRef.sidebarSectionOrder.filter(id => _settingsRef.visibleSections[id]);
            const oldIndexInVisible = currentVisibleOrder.indexOf(draggedSectionId);
            if (oldIndexInVisible > -1) { currentVisibleOrder.splice(oldIndexInVisible, 1); } else { handleDragEnd(); return; } // Should not happen if drag started correctly
            const afterElement = getDragAfterElement(_listElement, event.clientY);
            if (afterElement) { const targetId = afterElement.dataset.sectionId; const newIndexInVisible = currentVisibleOrder.indexOf(targetId); if (newIndexInVisible > -1) { currentVisibleOrder.splice(newIndexInVisible, 0, draggedSectionId); } else { currentVisibleOrder.push(draggedSectionId); } // Fallback if targetId not in currentVisibleOrder (should be rare)
            } else { currentVisibleOrder.push(draggedSectionId); } // Dropped at the end
            const hiddenSectionOrder = _settingsRef.sidebarSectionOrder.filter(id => !_settingsRef.visibleSections[id]); // Keep hidden sections at the end
            _settingsRef.sidebarSectionOrder = [...currentVisibleOrder, ...hiddenSectionOrder];
            handleDragEnd(); _onOrderUpdateCallback();
        }
        function handleDragEnd() { if (_draggedItem) { _draggedItem.classList.remove(CSS.DRAGGING_ITEM); } _draggedItem = null; if (_listElement) { _listElement.querySelectorAll('li').forEach(li => { li.classList.remove(CSS.DRAG_OVER_HIGHLIGHT); li.style.pointerEvents = ''; }); } }
        function initialize(listEl, currentSettings, orderUpdateCallback) { _listElement = listEl; _settingsRef = currentSettings; _onOrderUpdateCallback = orderUpdateCallback; if (_listElement && _listElement.dataset.sectionOrderDragBound !== 'true') { _listElement.addEventListener('dragstart', handleDragStart); _listElement.addEventListener('dragover', handleDragOver); _listElement.addEventListener('dragleave', handleDragLeave); _listElement.addEventListener('drop', handleDrop); _listElement.addEventListener('dragend', handleDragEnd); _listElement.dataset.sectionOrderDragBound = 'true'; } }
        function destroy() { if (_listElement && _listElement.dataset.sectionOrderDragBound === 'true') { _listElement.removeEventListener('dragstart', handleDragStart); _listElement.removeEventListener('dragover', handleDragOver); _listElement.removeEventListener('dragleave', handleDragLeave); _listElement.removeEventListener('drop', handleDrop); _listElement.removeEventListener('dragend', handleDragEnd); delete _listElement.dataset.sectionOrderDragBound; } _listElement = null; _settingsRef = null; _onOrderUpdateCallback = null; }
        return { initialize, destroy };
    })();

    // SettingsManager - Manages loading, saving, applying settings, and settings UI
    const SettingsManager = (function() {
        let _settingsWindow = null; let _settingsOverlay = null; let _currentSettings = {};
        let _settingsBackup = {}; let _defaultSettingsRef = null; let _isInitialized = false;
        let _applySettingsToSidebar_cb = ()=>{}; let _buildSidebarUI_cb = ()=>{};
        let _applySectionCollapseStates_cb = ()=>{}; let _initMenuCommands_cb = ()=>{};
        let _renderSectionOrderList_ext_cb = ()=>{}; // External callback for rendering order list

        // Internal helper to populate a slider and its value display
        function _populateSliderSetting_internal(win,id,value,formatFn=(val)=>val){const i=win.querySelector(`#${id}`);if(i){i.value=value;let vs=i.parentNode.querySelector(`.${CSS.RANGE_VALUE}`);if(vs&&vs.classList.contains(CSS.RANGE_VALUE)){vs.textContent=formatFn(value);}}}
        // Internal helper to populate general settings tab
        function _populateGeneralSettings_internal(win,s){ const lS=win.querySelector(`#${IDS.SETTING_INTERFACE_LANGUAGE}`);if(lS)lS.value=s.interfaceLanguage;const sMS=win.querySelector(`#${IDS.SETTING_SECTION_MODE}`),acC=win.querySelector(`#${IDS.SETTING_ACCORDION}`);if(sMS&&acC){sMS.value=s.sectionDisplayMode;const iRM=s.sectionDisplayMode==='remember';acC.disabled=!iRM;acC.checked=iRM?s.accordionMode:false; const accordionHint = acC.parentElement.querySelector(`.${CSS.SETTING_VALUE_HINT}`); if(accordionHint) accordionHint.style.color = iRM ? '' : 'grey';} const dC=win.querySelector(`#${IDS.SETTING_DRAGGABLE}`);if(dC)dC.checked=s.draggableHandleEnabled;const rLS=win.querySelector(`#${IDS.SETTING_RESET_LOCATION}`);if(rLS)rLS.value=s.resetButtonLocation;const vLS=win.querySelector(`#${IDS.SETTING_VERBATIM_LOCATION}`);if(vLS)vLS.value=s.verbatimButtonLocation;const aSLS=win.querySelector(`#${IDS.SETTING_ADV_SEARCH_LOCATION}`);if(aSLS)aSLS.value=s.advancedSearchLinkLocation; const pznLS = win.querySelector(`#${IDS.SETTING_PERSONALIZE_LOCATION}`); if (pznLS) pznLS.value = s.personalizationButtonLocation; const schLS = win.querySelector(`#${IDS.SETTING_SCHOLAR_LOCATION}`); if (schLS) schLS.value = s.googleScholarShortcutLocation; const trnLS = win.querySelector(`#${IDS.SETTING_TRENDS_LOCATION}`); if (trnLS) trnLS.value = s.googleTrendsShortcutLocation;}
        // Internal helper to populate appearance settings tab
        function _populateAppearanceSettings_internal(win,s){_populateSliderSetting_internal(win,IDS.SETTING_WIDTH,s.sidebarWidth);_populateSliderSetting_internal(win,IDS.SETTING_FONT_SIZE,s.fontSize,v=>parseFloat(v).toFixed(1));_populateSliderSetting_internal(win,IDS.SETTING_HEADER_ICON_SIZE,s.headerIconSize,v=>parseFloat(v).toFixed(1));_populateSliderSetting_internal(win,IDS.SETTING_VERTICAL_SPACING,s.verticalSpacingMultiplier,v=>`x ${parseFloat(v).toFixed(2)}`);_populateSliderSetting_internal(win,IDS.SETTING_OPACITY,s.idleOpacity,v=>parseFloat(v).toFixed(2));const tS=win.querySelector(`#${IDS.SETTING_THEME}`);if(tS)tS.value=s.theme;const cDS=win.querySelector(`#${IDS.SETTING_COUNTRY_DISPLAY_MODE}`);if(cDS)cDS.value=s.countryDisplayMode;const hC=win.querySelector(`#${IDS.SETTING_HOVER}`),oI=win.querySelector(`#${IDS.SETTING_OPACITY}`);if(hC&&oI){hC.checked=s.hoverMode;const iHE=s.hoverMode;oI.disabled=!iHE;const oC=oI.closest('div');if(oC){oC.style.opacity=iHE?'1':'0.6';oC.style.pointerEvents=iHE?'auto':'none';}} const hideLogoCb = win.querySelector(`#${IDS.SETTING_HIDE_GOOGLE_LOGO}`); if (hideLogoCb) hideLogoCb.checked = s.hideGoogleLogoWhenExpanded; }
        // Internal helper to populate features settings tab
        function _populateFeatureSettings_internal(win,s,renderFn){win.querySelectorAll(`#${IDS.TAB_PANE_FEATURES} input[type="checkbox"][data-${DATA_ATTR.SECTION_ID}]`)?.forEach(cb=>{const sId=cb.getAttribute(`data-${DATA_ATTR.SECTION_ID}`);if(sId&&s.visibleSections.hasOwnProperty(sId)){cb.checked=s.visibleSections[sId];}else if(sId&&_defaultSettingsRef.visibleSections.hasOwnProperty(sId)){cb.checked=_defaultSettingsRef.visibleSections[sId]??false;}}); const siteSearchCheckboxModeEl = win.querySelector(`#${IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE}`); if(siteSearchCheckboxModeEl) siteSearchCheckboxModeEl.checked = s.enableSiteSearchCheckboxMode; const filetypeSearchCheckboxModeEl = win.querySelector(`#${IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE}`); if(filetypeSearchCheckboxModeEl) filetypeSearchCheckboxModeEl.checked = s.enableFiletypeCheckboxMode; renderFn(s);}
        // Internal helper to set the active settings tab
        function _initializeActiveSettingsTab_internal(){if(!_settingsWindow)return;const tC=_settingsWindow.querySelector(`.${CSS.SETTINGS_TABS}`),cC=_settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT}`);if(!tC||!cC)return;const aTB=tC.querySelector(`.${CSS.TAB_BUTTON}.${CSS.ACTIVE}`);const tT=(aTB&&aTB.dataset[DATA_ATTR.TAB])?aTB.dataset[DATA_ATTR.TAB]:'general';tC.querySelectorAll(`.${CSS.TAB_BUTTON}`).forEach(b=>b.classList.toggle(CSS.ACTIVE,b.dataset[DATA_ATTR.TAB]===tT));cC.querySelectorAll(`.${CSS.TAB_PANE}`).forEach(p=>p.classList.toggle(CSS.ACTIVE,p.dataset[DATA_ATTR.TAB]===tT));}
        // Load settings from GM_storage
        function _loadFromStorage(){try{const s=GM_getValue(STORAGE_KEY,'{}');return JSON.parse(s||'{}');}catch(e){console.error(`${LOG_PREFIX} Error loading/parsing settings:`,e);return{};}}
        // Migrate settings from older versions if necessary (specific to displayLanguages/Countries)
        function _migrateToDisplayArraysIfNecessary(settings) {
            const displayTypes = [
                { displayKey: 'displayLanguages', predefinedKey: 'language', customKey: 'customLanguages', defaultEnabled: defaultSettings.enabledPredefinedOptions.language },
                { displayKey: 'displayCountries', predefinedKey: 'country', customKey: 'customCountries', defaultEnabled: defaultSettings.enabledPredefinedOptions.country }
            ];
            let migrationPerformed = false;
            displayTypes.forEach(typeInfo => {
                // Check if migration is needed: displayKey array is missing/empty AND there are old-style predefined/custom options
                if ((!settings[typeInfo.displayKey] || settings[typeInfo.displayKey].length === 0) &&
                    (
                        (settings.enabledPredefinedOptions && settings.enabledPredefinedOptions[typeInfo.predefinedKey]?.length > 0) ||
                        (settings[typeInfo.customKey] && settings[typeInfo.customKey]?.length > 0)
                    )
                ) {
                    console.log(`${LOG_PREFIX} Migrating settings for ${typeInfo.displayKey}`);
                    migrationPerformed = true;
                    const newDisplayArray = [];
                    const addedValues = new Set(); // To avoid duplicates if a value exists in both predefined and custom

                    // 1. Add enabled predefined options first
                    const enabledPredefined = settings.enabledPredefinedOptions?.[typeInfo.predefinedKey] || typeInfo.defaultEnabled || [];
                    enabledPredefined.forEach(val => {
                        const predefinedOpt = PREDEFINED_OPTIONS[typeInfo.predefinedKey]?.find(p => p.value === val);
                        if (predefinedOpt && !addedValues.has(predefinedOpt.value)) {
                            newDisplayArray.push({
                                id: predefinedOpt.value, // Use value as a unique ID for predefined
                                text: _(predefinedOpt.textKey), // Store original text key
                                value: predefinedOpt.value,
                                type: 'predefined',
                                originalKey: predefinedOpt.textKey
                            });
                            addedValues.add(predefinedOpt.value);
                        }
                    });
                    // Sort predefined items by their translated text (current locale)
                    newDisplayArray.sort((a,b) => {
                        const textA = a.originalKey ? _(a.originalKey) : a.text;
                        const textB = b.originalKey ? _(b.originalKey) : b.text;
                        return textA.localeCompare(textB, LocalizationService.getCurrentLocale(), {sensitivity: 'base'})
                    });


                    // 2. Add custom options
                    const customItems = settings[typeInfo.customKey] || [];
                    customItems.forEach(customOpt => {
                        if (customOpt.value && !addedValues.has(customOpt.value)) { // Ensure custom value wasn't already added as predefined
                            newDisplayArray.push({
                                id: customOpt.value, // Use value as ID for custom, assume unique among customs
                                text: customOpt.text,
                                value: customOpt.value,
                                type: 'custom'
                                // No originalKey for custom items
                            });
                            addedValues.add(customOpt.value);
                        }
                    });
                    settings[typeInfo.displayKey] = newDisplayArray;
                    // Clear old enabledPredefinedOptions for this type as they are now in displayKey
                    if (settings.enabledPredefinedOptions) {
                        settings.enabledPredefinedOptions[typeInfo.predefinedKey] = [];
                    }
                } else if (!settings[typeInfo.displayKey]) { // Ensure array exists if no migration needed
                    settings[typeInfo.displayKey] = JSON.parse(JSON.stringify(defaultSettings[typeInfo.displayKey] || []));
                }
            });
            if (migrationPerformed) console.log(`${LOG_PREFIX} Migration to display arrays complete.`);
        }
        // Validate and merge saved settings with defaults
        function _validateAndMergeSettings(saved){
            let newSettings = JSON.parse(JSON.stringify(_defaultSettingsRef)); // Start with a deep copy of defaults
            newSettings = Utils.mergeDeep(newSettings, saved); // Merge saved settings into the new copy

            _validateAndMergeCoreSettings_internal(newSettings, saved, _defaultSettingsRef);
            _validateAndMergeAppearanceSettings_internal(newSettings, saved, _defaultSettingsRef);
            _validateAndMergeFeatureSettings_internal(newSettings, saved, _defaultSettingsRef);
            _validateAndMergeCustomLists_internal(newSettings, saved, _defaultSettingsRef);

            _migrateToDisplayArraysIfNecessary(newSettings); // IMPORTANT: Migrate before validating display arrays

            // Validate displayLanguages and displayCountries (after potential migration)
            ['displayLanguages', 'displayCountries'].forEach(displayKey => {
                if (!Array.isArray(newSettings[displayKey])) {
                    newSettings[displayKey] = JSON.parse(JSON.stringify(_defaultSettingsRef[displayKey])) || [];
                }
                // Filter out invalid items
                newSettings[displayKey] = newSettings[displayKey].filter(item =>
                    item && typeof item.id === 'string' &&
                    (item.type === 'predefined' ? (typeof item.text === 'string' && typeof item.originalKey === 'string') : typeof item.text === 'string') && // Text is always string, originalKey for predefined
                    typeof item.value === 'string' &&
                    (item.type === 'predefined' || item.type === 'custom') // Valid type
                );
            });


            _validateAndMergePredefinedOptions_internal(newSettings, saved, _defaultSettingsRef); // Validate other predefined options
            _finalizeSectionOrder_internal(newSettings, saved, _defaultSettingsRef); // Finalize section order

            return newSettings;
        }
        // Sub-validation functions
        function _validateAndMergeCoreSettings_internal(target,source,defaults){if(typeof target.sidebarPosition!=='object'||target.sidebarPosition===null||Array.isArray(target.sidebarPosition)){target.sidebarPosition=JSON.parse(JSON.stringify(defaults.sidebarPosition));}target.sidebarPosition.left=parseInt(target.sidebarPosition.left,10)||defaults.sidebarPosition.left;target.sidebarPosition.top=parseInt(target.sidebarPosition.top,10)||defaults.sidebarPosition.top;if(typeof target.sectionStates!=='object'||target.sectionStates===null||Array.isArray(target.sectionStates)){target.sectionStates={};}target.sidebarCollapsed=!!target.sidebarCollapsed;target.draggableHandleEnabled=typeof target.draggableHandleEnabled==='boolean'?target.draggableHandleEnabled:defaults.draggableHandleEnabled;target.interfaceLanguage=typeof source.interfaceLanguage==='string'?source.interfaceLanguage:defaults.interfaceLanguage;}
        function _validateAndMergeAppearanceSettings_internal(target,source,defaults){target.sidebarWidth=Utils.clamp(parseInt(target.sidebarWidth,10)||defaults.sidebarWidth,90,270);target.fontSize=Utils.clamp(parseFloat(target.fontSize)||defaults.fontSize,8,24);target.headerIconSize=Utils.clamp(parseFloat(target.headerIconSize)||defaults.headerIconSize,8,32);target.verticalSpacingMultiplier=Utils.clamp(parseFloat(target.verticalSpacingMultiplier)||defaults.verticalSpacingMultiplier,0.05,1.5);target.idleOpacity=Utils.clamp(parseFloat(target.idleOpacity)||defaults.idleOpacity,0.1,1.0);target.hoverMode=!!target.hoverMode;const validThemes=['system','light','dark','minimal-light','minimal-dark'];if(target.theme==='minimal')target.theme='minimal-light';else if(!validThemes.includes(target.theme))target.theme=defaults.theme; target.hideGoogleLogoWhenExpanded = typeof source.hideGoogleLogoWhenExpanded === 'boolean' ? source.hideGoogleLogoWhenExpanded : defaults.hideGoogleLogoWhenExpanded;}
        function _validateAndMergeFeatureSettings_internal(target,source,defaults){if(typeof target.visibleSections!=='object'||target.visibleSections===null||Array.isArray(target.visibleSections)){target.visibleSections=JSON.parse(JSON.stringify(defaults.visibleSections));}const validSectionIDs=new Set(ALL_SECTION_DEFINITIONS.map(def=>def.id));Object.keys(defaults.visibleSections).forEach(id=>{if(!validSectionIDs.has(id)){console.warn(`${LOG_PREFIX} Invalid section ID in defaultSettings.visibleSections: ${id}`);}else if(typeof target.visibleSections[id]!=='boolean'){target.visibleSections[id]=defaults.visibleSections[id]??true;}});const validSectionModes=['remember','expandAll','collapseAll'];if(!validSectionModes.includes(target.sectionDisplayMode))target.sectionDisplayMode=defaults.sectionDisplayMode;target.accordionMode=!!target.accordionMode; target.enableSiteSearchCheckboxMode = typeof target.enableSiteSearchCheckboxMode === 'boolean' ? target.enableSiteSearchCheckboxMode : defaults.enableSiteSearchCheckboxMode; target.enableFiletypeCheckboxMode = typeof target.enableFiletypeCheckboxMode === 'boolean' ? target.enableFiletypeCheckboxMode : defaults.enableFiletypeCheckboxMode; const validButtonLocations=['header','topBlock','tools','none'];if(!validButtonLocations.includes(target.resetButtonLocation))target.resetButtonLocation=defaults.resetButtonLocation;if(!validButtonLocations.includes(target.verbatimButtonLocation))target.verbatimButtonLocation=defaults.verbatimButtonLocation;if(!validButtonLocations.includes(target.advancedSearchLinkLocation))target.advancedSearchLinkLocation=defaults.advancedSearchLinkLocation; if (!validButtonLocations.includes(target.personalizationButtonLocation)) { target.personalizationButtonLocation = defaults.personalizationButtonLocation; } if (!validButtonLocations.includes(target.googleScholarShortcutLocation)) { target.googleScholarShortcutLocation = defaults.googleScholarShortcutLocation; } if (!validButtonLocations.includes(target.googleTrendsShortcutLocation)) { target.googleTrendsShortcutLocation = defaults.googleTrendsShortcutLocation; } const validCountryDisplayModes=['iconAndText','textOnly','iconOnly'];if(!validCountryDisplayModes.includes(target.countryDisplayMode))target.countryDisplayMode=defaults.countryDisplayMode;}
        function _validateAndMergeCustomLists_internal(target,source,defaults){const listKeys=['favoriteSites','customLanguages','customTimeRanges','customFiletypes','customCountries'];listKeys.forEach(key=>{target[key]=Array.isArray(target[key])?target[key].filter(item=>item&&typeof item.text==='string'&&typeof item[key==='favoriteSites'?'url':'value']==='string'&&item.text.trim()!==''&&item[key==='favoriteSites'?'url':'value'].trim()!==''):JSON.parse(JSON.stringify(defaults[key]));});}
        function _validateAndMergePredefinedOptions_internal(target,source,defaults){
            // This function now primarily handles 'time' and 'filetype' enabledPredefinedOptions,
            // as 'language' and 'country' are managed via their displayArrays.
            target.enabledPredefinedOptions = target.enabledPredefinedOptions || {};
            ['time', 'filetype'].forEach(type => {
                if (!target.enabledPredefinedOptions[type] || !Array.isArray(target.enabledPredefinedOptions[type])) {
                    target.enabledPredefinedOptions[type] = JSON.parse(JSON.stringify(defaults.enabledPredefinedOptions[type] || []));
                }
                const savedTypeOptions = source.enabledPredefinedOptions?.[type];
                if (PREDEFINED_OPTIONS[type] && Array.isArray(savedTypeOptions)) {
                    const validValues = new Set(PREDEFINED_OPTIONS[type].map(opt => opt.value));
                    target.enabledPredefinedOptions[type] = savedTypeOptions.filter(val => typeof val === 'string' && validValues.has(val));
                } else if (!PREDEFINED_OPTIONS[type]) { // If this type has no predefined options in the script
                    target.enabledPredefinedOptions[type] = [];
                }
                // If savedTypeOptions is undefined or not an array, it keeps the default or already merged value.
            });
            // Ensure language and country are empty in enabledPredefinedOptions as they use display arrays
            if (target.displayLanguages && target.enabledPredefinedOptions) target.enabledPredefinedOptions.language = [];
            if (target.displayCountries && target.enabledPredefinedOptions) target.enabledPredefinedOptions.country = [];
        }
        function _finalizeSectionOrder_internal(target,source,defaults){const finalOrder=[];const currentVisibleOrderSet=new Set();const validSectionIDs=new Set(ALL_SECTION_DEFINITIONS.map(def=>def.id));const orderSource=(Array.isArray(source.sidebarSectionOrder)&&source.sidebarSectionOrder.length>0)?source.sidebarSectionOrder:defaults.sidebarSectionOrder;orderSource.forEach(id=>{if(typeof id==='string'&&validSectionIDs.has(id)&&target.visibleSections[id]===true&&!currentVisibleOrderSet.has(id)){finalOrder.push(id);currentVisibleOrderSet.add(id);}});defaults.sidebarSectionOrder.forEach(id=>{if(typeof id==='string'&&validSectionIDs.has(id)&&target.visibleSections[id]===true&&!currentVisibleOrderSet.has(id)){finalOrder.push(id);}});target.sidebarSectionOrder=finalOrder;}
        // Event handlers for live updates in settings UI (put in an object for organization)
        const _sEH_internal = {
            // Sliders
            [IDS.SETTING_WIDTH]:(t,vS)=>_hSLI(t,'sidebarWidth',vS,90,270,5),
            [IDS.SETTING_FONT_SIZE]:(t,vS)=>_hSLI(t,'fontSize',vS,8,24,0.5,v=>parseFloat(v).toFixed(1)),
            [IDS.SETTING_HEADER_ICON_SIZE]:(t,vS)=>_hSLI(t,'headerIconSize',vS,8,32,0.5,v=>parseFloat(v).toFixed(1)),
            [IDS.SETTING_VERTICAL_SPACING]:(t,vS)=>_hSLI(t,'verticalSpacingMultiplier',vS,0.05,1.5,0.05,v=>`x ${parseFloat(v).toFixed(2)}`),
            [IDS.SETTING_OPACITY]:(t,vS)=>_hSLI(t,'idleOpacity',vS,0.1,1.0,0.05,v=>parseFloat(v).toFixed(2)),
            // Selects & Checkboxes
            [IDS.SETTING_INTERFACE_LANGUAGE]:(t)=>{const nL=t.value;if(_currentSettings.interfaceLanguage!==nL){_currentSettings.interfaceLanguage=nL;LocalizationService.updateActiveLocale(_currentSettings);_initMenuCommands_cb();publicApi.populateWindow();_buildSidebarUI_cb();}}, // Re-populate and re-build for language change
            [IDS.SETTING_THEME]:(t)=>{_currentSettings.theme=t.value;_applySettingsToSidebar_cb(_currentSettings);}, // Apply theme change
            [IDS.SETTING_HOVER]:(t)=>{_currentSettings.hoverMode=t.checked;const oI=_settingsWindow.querySelector(`#${IDS.SETTING_OPACITY}`);if(oI){const iHE=_currentSettings.hoverMode;oI.disabled=!iHE;const oC=oI.closest('div');if(oC){oC.style.opacity=iHE?'1':'0.6';oC.style.pointerEvents=iHE?'auto':'none';}}_applySettingsToSidebar_cb(_currentSettings);},
            [IDS.SETTING_DRAGGABLE]:(t)=>{_currentSettings.draggableHandleEnabled=t.checked;_applySettingsToSidebar_cb(_currentSettings);DragManager.setDraggable(t.checked, sidebar, sidebar?.querySelector(`.${CSS.DRAG_HANDLE}`), _currentSettings, debouncedSaveSettings);},
            [IDS.SETTING_ACCORDION]:(t)=>{const sMS=_settingsWindow.querySelector(`#${IDS.SETTING_SECTION_MODE}`);if(sMS?.value==='remember')_currentSettings.accordionMode=t.checked;else{t.checked=false;_currentSettings.accordionMode=false;}_applySettingsToSidebar_cb(_currentSettings);_applySectionCollapseStates_cb();},
            [IDS.SETTING_SECTION_MODE]:(t)=>{_currentSettings.sectionDisplayMode=t.value;const aC=_settingsWindow.querySelector(`#${IDS.SETTING_ACCORDION}`);if(aC){const iRM=t.value==='remember';aC.disabled=!iRM; if(aC.parentElement.querySelector(`.${CSS.SETTING_VALUE_HINT}`)) aC.parentElement.querySelector(`.${CSS.SETTING_VALUE_HINT}`).style.color = iRM ? '' : 'grey'; if(!iRM){aC.checked=false;_currentSettings.accordionMode=false;}else{/* Restore previous accordion state if switching back to remember */ aC.checked=_settingsBackup?.accordionMode??_currentSettings.accordionMode??_defaultSettingsRef.accordionMode;_currentSettings.accordionMode=aC.checked;}}_applySettingsToSidebar_cb(_currentSettings);_applySectionCollapseStates_cb();},
            [IDS.SETTING_RESET_LOCATION]:(t)=>{_currentSettings.resetButtonLocation=t.value;_buildSidebarUI_cb();},
            [IDS.SETTING_VERBATIM_LOCATION]:(t)=>{_currentSettings.verbatimButtonLocation=t.value;_buildSidebarUI_cb();},
            [IDS.SETTING_ADV_SEARCH_LOCATION]:(t)=>{_currentSettings.advancedSearchLinkLocation=t.value;_buildSidebarUI_cb();},
            [IDS.SETTING_PERSONALIZE_LOCATION]: (target) => { _currentSettings.personalizationButtonLocation = target.value; _buildSidebarUI_cb(); },
            [IDS.SETTING_SCHOLAR_LOCATION]: (target) => { _currentSettings.googleScholarShortcutLocation = target.value; _buildSidebarUI_cb(); }, // New
            [IDS.SETTING_TRENDS_LOCATION]: (target) => { _currentSettings.googleTrendsShortcutLocation = target.value; _buildSidebarUI_cb(); },   // New
            [IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE]: (target) => { _currentSettings.enableSiteSearchCheckboxMode = target.checked; _buildSidebarUI_cb(); },
            [IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE]: (target) => { _currentSettings.enableFiletypeCheckboxMode = target.checked; _buildSidebarUI_cb(); },
            [IDS.SETTING_COUNTRY_DISPLAY_MODE]:(t)=>{_currentSettings.countryDisplayMode=t.value;_buildSidebarUI_cb();},
            [IDS.SETTING_HIDE_GOOGLE_LOGO]:(t)=>{_currentSettings.hideGoogleLogoWhenExpanded=t.checked;_applySettingsToSidebar_cb(_currentSettings);},
        };
        // Helper for handling slider input event
        function _hSLI(t,sK,vS,min,max,step,fFn=v=>v){const v=Utils.clamp((step===1||step===5)?parseInt(t.value,10):parseFloat(t.value),min,max);if(isNaN(v))_currentSettings[sK]=_defaultSettingsRef[sK];else _currentSettings[sK]=v;if(vS)vS.textContent=fFn(_currentSettings[sK]);_applySettingsToSidebar_cb(_currentSettings);}
        // Unified live update handler
        function _lUH_internal(e){const t=e.target;if(!t)return;const sI=t.id;const vS=(t.type==='range')?t.parentNode.querySelector(`.${CSS.RANGE_VALUE}`):null;if(_sEH_internal[sI]){if(t.type==='range')_sEH_internal[sI](t,vS);else _sEH_internal[sI](t);}}

        const publicApi = {
            initialize: function(defaultSettingsObj, applyCb, buildCb, collapseCb, menuCb, renderOrderCb) { if(_isInitialized) return; _defaultSettingsRef = defaultSettingsObj; _applySettingsToSidebar_cb = applyCb; _buildSidebarUI_cb = buildCb; _applySectionCollapseStates_cb = collapseCb; _initMenuCommands_cb = menuCb; _renderSectionOrderList_ext_cb = renderOrderCb; this.load(); this.buildSkeleton(); _isInitialized = true; },
            load: function(){ const s=_loadFromStorage(); _currentSettings=_validateAndMergeSettings(s); LocalizationService.updateActiveLocale(_currentSettings);},
            save: function(logContext='SaveBtn'){
                try {
                    // Consolidate custom items from displayLanguages/Countries back to customLanguages/Countries master lists
                    ['displayLanguages', 'displayCountries'].forEach(displayKey => {
                        const mapping = getListMapping(displayKey === 'displayLanguages' ? IDS.LANG_LIST : IDS.COUNTRIES_LIST);
                        if (mapping && mapping.customItemsMasterKey && _currentSettings[displayKey] && Array.isArray(_currentSettings[mapping.customItemsMasterKey])) {
                            const displayItems = _currentSettings[displayKey];
                            const currentDisplayCustomItems = displayItems.filter(item => item.type === 'custom');
                            const currentDisplayCustomItemValues = new Set(currentDisplayCustomItems.map(item => item.value));

                            // Filter master list: keep only those custom items that are still in the display list
                            const newMasterList = (_currentSettings[mapping.customItemsMasterKey] || [])
                                .filter(masterItem => currentDisplayCustomItemValues.has(masterItem.value))
                                .map(oldMasterItem => { // Update text from display item if it changed
                                    const correspondingDisplayItem = currentDisplayCustomItems.find(d => d.value === oldMasterItem.value);
                                    return correspondingDisplayItem ? { text: correspondingDisplayItem.text, value: oldMasterItem.value } : oldMasterItem;
                                });

                            // Add new custom items from display list to master if not already there
                            currentDisplayCustomItems.forEach(dispItem => {
                                if (!newMasterList.find(mi => mi.value === dispItem.value)) {
                                    newMasterList.push({ text: dispItem.text, value: dispItem.value });
                                }
                            });
                            _currentSettings[mapping.customItemsMasterKey] = newMasterList;
                        }
                    });

                    GM_setValue(STORAGE_KEY, JSON.stringify(_currentSettings));
                    console.log(`${LOG_PREFIX} Settings saved by SM${logContext ? ` (${logContext})` : ''}.`);
                    _settingsBackup = JSON.parse(JSON.stringify(_currentSettings)); // Update backup after successful save
                } catch (e) {
                    console.error(`${LOG_PREFIX} SM save error:`, e);
                    NotificationManager.show('alert_generic_error', { context: 'saving settings' }, 'error', 5000);
                }
            },
            reset: function(){ if(confirm(_('confirm_reset_settings'))){ _currentSettings = JSON.parse(JSON.stringify(_defaultSettingsRef)); _migrateToDisplayArraysIfNecessary(_currentSettings); /* Ensure section order is reset if empty */ if(!_currentSettings.sidebarSectionOrder||_currentSettings.sidebarSectionOrder.length===0){ _currentSettings.sidebarSectionOrder = [..._defaultSettingsRef.sidebarSectionOrder]; } LocalizationService.updateActiveLocale(_currentSettings); this.populateWindow(); _applySettingsToSidebar_cb(_currentSettings); _buildSidebarUI_cb(); _initMenuCommands_cb(); _showGlobalMessage('alert_settings_reset_success',{},'success',4000);}},
            resetAllFromMenu: function(){ if(confirm(_('confirm_reset_all_menu'))){ try{ GM_setValue(STORAGE_KEY,JSON.stringify(_defaultSettingsRef)); alert(_('alert_reset_all_menu_success')); }catch(e){ _showGlobalMessage('alert_reset_all_menu_fail',{},'error',0); /* Use alert as NotificationManager might not be ready */ }}},
            getCurrentSettings: function(){ return _currentSettings;},
            buildSkeleton: function(){ if(_settingsWindow)return; _settingsOverlay=document.createElement('div');_settingsOverlay.id=IDS.SETTINGS_OVERLAY;_settingsWindow=document.createElement('div');_settingsWindow.id=IDS.SETTINGS_WINDOW;const h=document.createElement('div');h.classList.add(CSS.SETTINGS_HEADER);h.innerHTML=`<h3>${_('settingsTitle')}</h3><button class="${CSS.SETTINGS_CLOSE_BTN}" title="${_('settings_close_button_title')}">${SVG_ICONS.close}</button>`;const mB=document.createElement('div');mB.id=IDS.SETTINGS_MESSAGE_BAR;mB.classList.add(CSS.MESSAGE_BAR);mB.style.display='none';const ts=document.createElement('div');ts.classList.add(CSS.SETTINGS_TABS);ts.innerHTML=`<button class="${CSS.TAB_BUTTON} ${CSS.ACTIVE}" data-${DATA_ATTR.TAB}="general">${_('settings_tab_general')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="appearance">${_('settings_tab_appearance')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="features">${_('settings_tab_features')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="custom">${_('settings_tab_custom')}</button>`;const c=document.createElement('div');c.classList.add(CSS.SETTINGS_TAB_CONTENT);c.innerHTML=`<div class="${CSS.TAB_PANE} ${CSS.ACTIVE}" data-${DATA_ATTR.TAB}="general" id="${IDS.TAB_PANE_GENERAL}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="appearance" id="${IDS.TAB_PANE_APPEARANCE}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="features" id="${IDS.TAB_PANE_FEATURES}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="custom" id="${IDS.TAB_PANE_CUSTOM}"></div>`;const f=document.createElement('div');f.classList.add(CSS.SETTINGS_FOOTER);f.innerHTML=`<button class="${CSS.RESET_BUTTON}">${_('settings_reset_all_button')}</button><button class="${CSS.CANCEL_BUTTON}">${_('settings_cancel_button')}</button><button class="${CSS.SAVE_BUTTON}">${_('settings_save_button')}</button>`;_settingsWindow.appendChild(h);_settingsWindow.appendChild(mB);_settingsWindow.appendChild(ts);_settingsWindow.appendChild(c);_settingsWindow.appendChild(f);_settingsOverlay.appendChild(_settingsWindow);document.body.appendChild(_settingsOverlay);this.bindEvents();},
            populateWindow: function(){
                if(!_settingsWindow)return;
                try {
                    // Update translatable texts first
                    _settingsWindow.querySelector(`.${CSS.SETTINGS_HEADER} h3`).textContent=_( 'settingsTitle');
                    _settingsWindow.querySelector(`.${CSS.SETTINGS_CLOSE_BTN}`).title=_( 'settings_close_button_title');
                    _settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="general"]`).textContent=_( 'settings_tab_general');
                    _settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="appearance"]`).textContent=_( 'settings_tab_appearance');
                    _settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="features"]`).textContent=_( 'settings_tab_features');
                    _settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="custom"]`).textContent=_( 'settings_tab_custom');
                    _settingsWindow.querySelector(`.${CSS.RESET_BUTTON}`).textContent=_( 'settings_reset_all_button');
                    _settingsWindow.querySelector(`.${CSS.CANCEL_BUTTON}`).textContent=_( 'settings_cancel_button');
                    _settingsWindow.querySelector(`.${CSS.SAVE_BUTTON}`).textContent=_( 'settings_save_button');

                    // Re-generate tab content HTML to reflect any language changes in labels
                    const paneGeneral = _settingsWindow.querySelector(`#${IDS.TAB_PANE_GENERAL}`); if(paneGeneral) paneGeneral.innerHTML = SettingsUIPaneGenerator.createGeneralPaneHTML();
                    const paneAppearance = _settingsWindow.querySelector(`#${IDS.TAB_PANE_APPEARANCE}`); if(paneAppearance) paneAppearance.innerHTML = SettingsUIPaneGenerator.createAppearancePaneHTML();
                    const paneFeatures = _settingsWindow.querySelector(`#${IDS.TAB_PANE_FEATURES}`); if(paneFeatures) paneFeatures.innerHTML = SettingsUIPaneGenerator.createFeaturesPaneHTML();
                    const paneCustom = _settingsWindow.querySelector(`#${IDS.TAB_PANE_CUSTOM}`); if(paneCustom) paneCustom.innerHTML = SettingsUIPaneGenerator.createCustomPaneHTML();

                    // Populate values into the newly generated/updated UI
                    _populateGeneralSettings_internal(_settingsWindow, _currentSettings);
                    _populateAppearanceSettings_internal(_settingsWindow, _currentSettings);
                    _populateFeatureSettings_internal(_settingsWindow, _currentSettings, _renderSectionOrderList_ext_cb); // Pass callback
                    ModalManager.resetEditStateGlobally(); // Reset modal edit state if any was active
                    _initializeActiveSettingsTab_internal(); // Ensure correct tab is active
                    this.bindLiveUpdateEvents(); // Re-bind for dynamic elements if panes were rebuilt
                    this.bindFeaturesTabEvents(); // Specifically for features tab dynamic elements
                }catch(e){ _showGlobalMessage('alert_init_fail',{scriptName:SCRIPT_INTERNAL_NAME,error:"Settings UI pop err"},'error',0); }
            },
            show: function(){ if(!_settingsOverlay||!_settingsWindow)return;_settingsBackup = JSON.parse(JSON.stringify(_currentSettings));LocalizationService.updateActiveLocale(_currentSettings);this.populateWindow();applyThemeToElement(_settingsWindow, _currentSettings.theme);applyThemeToElement(_settingsOverlay, _currentSettings.theme);_settingsOverlay.style.display = 'flex';},
            hide: function(isCancel = false){ if(!_settingsOverlay)return;ModalManager.resetEditStateGlobally();if(ModalManager.isModalOpen()) ModalManager.hide(true);_settingsOverlay.style.display = 'none';const messageBar = document.getElementById(IDS.SETTINGS_MESSAGE_BAR);if(messageBar) messageBar.style.display = 'none';if(isCancel && _settingsBackup && Object.keys(_settingsBackup).length > 0){ _currentSettings = JSON.parse(JSON.stringify(_settingsBackup));LocalizationService.updateActiveLocale(_currentSettings);this.populateWindow();_applySettingsToSidebar_cb(_currentSettings);_buildSidebarUI_cb();_initMenuCommands_cb();} else if(isCancel) { console.warn(`${LOG_PREFIX} SM: Cancelled, no backup to restore or backup was identical.`); }},
            bindEvents: function(){
                if(!_settingsWindow || _settingsWindow.dataset.eventsBound === 'true') return; // Prevent multiple bindings
                _settingsWindow.querySelector(`.${CSS.SETTINGS_CLOSE_BTN}`)?.addEventListener('click', () => this.hide(true));
                _settingsWindow.querySelector(`.${CSS.CANCEL_BUTTON}`)?.addEventListener('click', () => this.hide(true));
                _settingsWindow.querySelector(`.${CSS.SAVE_BUTTON}`)?.addEventListener('click', () => { this.save(); LocalizationService.updateActiveLocale(_currentSettings); _initMenuCommands_cb(); _buildSidebarUI_cb(); this.hide(false); });
                _settingsWindow.querySelector(`.${CSS.RESET_BUTTON}`)?.addEventListener('click', () => this.reset());
                const tabsContainer = _settingsWindow.querySelector(`.${CSS.SETTINGS_TABS}`);
                if(tabsContainer){ tabsContainer.addEventListener('click', e => { const targetButton = e.target.closest(`.${CSS.TAB_BUTTON}`); if(targetButton && !targetButton.classList.contains(CSS.ACTIVE)){ ModalManager.resetEditStateGlobally(); const tabToActivate = targetButton.dataset[DATA_ATTR.TAB]; if(!tabToActivate) return; tabsContainer.querySelectorAll(`.${CSS.TAB_BUTTON}`).forEach(b => b.classList.remove(CSS.ACTIVE)); targetButton.classList.add(CSS.ACTIVE); _settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT}`)?.querySelectorAll(`.${CSS.TAB_PANE}`)?.forEach(p => p.classList.remove(CSS.ACTIVE)); _settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT} .${CSS.TAB_PANE}[data-${DATA_ATTR.TAB}="${tabToActivate}"]`)?.classList.add(CSS.ACTIVE); } }); }
                _settingsWindow.dataset.eventsBound = 'true';
                // Bind "Manage..." buttons in Custom tab
                const customTabPane = _settingsWindow.querySelector(`#${IDS.TAB_PANE_CUSTOM}`);
                if(customTabPane){ customTabPane.addEventListener('click', (e) => { const manageButton = e.target.closest(`button.${CSS.MANAGE_CUSTOM_BUTTON}`); if(manageButton){ const manageType = manageButton.dataset[DATA_ATTR.MANAGE_TYPE]; if(manageType){ ModalManager.openManageCustomOptions( manageType, _currentSettings, PREDEFINED_OPTIONS, (updatedItemsArray, newEnabledPredefs, itemsArrayKey, predefinedOptKey, customItemsMasterKey, isSortableMixed, manageTypeFromCallback) => { if (itemsArrayKey) { _currentSettings[itemsArrayKey] = updatedItemsArray; } if (predefinedOptKey && newEnabledPredefs) { if (!_currentSettings.enabledPredefinedOptions) _currentSettings.enabledPredefinedOptions = {}; _currentSettings.enabledPredefinedOptions[predefinedOptKey] = newEnabledPredefs; } _buildSidebarUI_cb(); } ); } } }); }
            },
            bindLiveUpdateEvents: function(){ if(!_settingsWindow)return; _settingsWindow.querySelectorAll('input[type="range"]').forEach(el=>{ el.removeEventListener('input',_lUH_internal); el.addEventListener('input',_lUH_internal); }); _settingsWindow.querySelectorAll('select, input[type="checkbox"]:not([data-section-id])').forEach(el=>{ if(_sEH_internal[el.id]){ el.removeEventListener('change',_lUH_internal); el.addEventListener('change',_lUH_internal); } }); },
            bindFeaturesTabEvents: function() { // For elements specific to the features tab that might be rebuilt
                const featuresPane = _settingsWindow?.querySelector(`#${IDS.TAB_PANE_FEATURES}`); if (!featuresPane) return;
                // Section visibility checkboxes
                featuresPane.querySelectorAll(`input[type="checkbox"][data-${DATA_ATTR.SECTION_ID}]`).forEach(checkbox => { checkbox.removeEventListener('change', this._handleVisibleSectionChange); checkbox.addEventListener('change', this._handleVisibleSectionChange.bind(this)); });
                // Site search checkbox mode
                const siteSearchCheckboxModeEl = featuresPane.querySelector(`#${IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE}`);
                if (siteSearchCheckboxModeEl && _sEH_internal[IDS.SETTING_SITE_SEARCH_CHECKBOX_MODE]) {
                    siteSearchCheckboxModeEl.removeEventListener('change', _lUH_internal);
                    siteSearchCheckboxModeEl.addEventListener('change', _lUH_internal);
                }
                // Filetype search checkbox mode
                const filetypeSearchCheckboxModeEl = featuresPane.querySelector(`#${IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE}`);
                if (filetypeSearchCheckboxModeEl && _sEH_internal[IDS.SETTING_FILETYPE_SEARCH_CHECKBOX_MODE]) {
                    filetypeSearchCheckboxModeEl.removeEventListener('change', _lUH_internal);
                    filetypeSearchCheckboxModeEl.addEventListener('change', _lUH_internal);
                }

                // Section order list drag handler
                const orderListElement = featuresPane.querySelector(`#${IDS.SIDEBAR_SECTION_ORDER_LIST}`);
                if (orderListElement) { SectionOrderDragHandler.initialize(orderListElement, _currentSettings, () => { _renderSectionOrderList_ext_cb(_currentSettings); _buildSidebarUI_cb(); }); }
            },
            _handleVisibleSectionChange: function(e){ const target = e.target; const sectionId = target.getAttribute(`data-${DATA_ATTR.SECTION_ID}`); if (sectionId && _currentSettings.visibleSections.hasOwnProperty(sectionId)) { _currentSettings.visibleSections[sectionId] = target.checked; _finalizeSectionOrder_internal(_currentSettings, _currentSettings, _defaultSettingsRef); _renderSectionOrderList_ext_cb(_currentSettings); _buildSidebarUI_cb(); } },
        };
        return publicApi;
    })();
    // --- DragManager for sidebar positioning ---
    const DragManager = (function() {
        let _isDragging = false; let _dragStartX, _dragStartY, _sidebarStartX, _sidebarStartY;
        let _sidebarElement, _handleElement; let _settingsManagerRef, _saveCallbackRef;

        function _getEventCoordinates(e) { return (e.touches && e.touches.length > 0) ? { x: e.touches[0].clientX, y: e.touches[0].clientY } : { x: e.clientX, y: e.clientY }; }
        function _startDrag(e) {
            const currentSettings = _settingsManagerRef.getCurrentSettings();
            if (!currentSettings.draggableHandleEnabled || currentSettings.sidebarCollapsed || (e.type === 'mousedown' && e.button !== 0)) { // Only main mouse button
                return;
            }
            e.preventDefault(); // Prevent text selection, etc.
            _isDragging = true;
            const coords = _getEventCoordinates(e);
            _dragStartX = coords.x;
            _dragStartY = coords.y;
            _sidebarStartX = _sidebarElement.offsetLeft;
            _sidebarStartY = _sidebarElement.offsetTop;
            _sidebarElement.style.cursor = 'grabbing';
            _sidebarElement.style.userSelect = 'none'; // Prevent text selection during drag
            document.body.style.cursor = 'grabbing'; // Change cursor for the whole body
        }
        function _drag(e) {
            if (!_isDragging) return;
            e.preventDefault();
            const coords = _getEventCoordinates(e);
            const dx = coords.x - _dragStartX;
            const dy = coords.y - _dragStartY;
            let newLeft = _sidebarStartX + dx;
            let newTop = _sidebarStartY + dy;

            // Constrain within viewport
            const maxLeft = window.innerWidth - (_sidebarElement?.offsetWidth ?? 0); // Use offsetWidth if available
            const maxTop = window.innerHeight - (_sidebarElement?.offsetHeight ?? 0);
            newLeft = Utils.clamp(newLeft, 0, maxLeft);
            newTop = Utils.clamp(newTop, MIN_SIDEBAR_TOP_POSITION, maxTop); // MIN_SIDEBAR_TOP_POSITION from global

            if (_sidebarElement) {
                _sidebarElement.style.left = `${newLeft}px`;
                _sidebarElement.style.top = `${newTop}px`;
            }
        }
        function _stopDrag() {
            if (_isDragging) {
                _isDragging = false;
                if (_sidebarElement) {
                    _sidebarElement.style.cursor = 'default'; // Reset cursor on sidebar
                    _sidebarElement.style.userSelect = ''; // Re-enable text selection
                }
                document.body.style.cursor = ''; // Reset body cursor

                const currentSettings = _settingsManagerRef.getCurrentSettings();
                if (!currentSettings.sidebarPosition) currentSettings.sidebarPosition = {};
                currentSettings.sidebarPosition.left = _sidebarElement.offsetLeft;
                currentSettings.sidebarPosition.top = _sidebarElement.offsetTop;

                if (typeof _saveCallbackRef === 'function') {
                    _saveCallbackRef('Drag Stop'); // Save settings
                }
            }
        }
        return {
            init: function(sidebarEl, handleEl, settingsMgr, saveCb) {
                _sidebarElement = sidebarEl;
                _handleElement = handleEl;
                _settingsManagerRef = settingsMgr;
                _saveCallbackRef = saveCb;

                if (_handleElement) {
                    _handleElement.addEventListener('mousedown', _startDrag);
                    _handleElement.addEventListener('touchstart', _startDrag, { passive: false }); // Use passive: false for preventDefault
                }
                document.addEventListener('mousemove', _drag);
                document.addEventListener('touchmove', _drag, { passive: false });
                document.addEventListener('mouseup', _stopDrag);
                document.addEventListener('touchend', _stopDrag);
                document.addEventListener('touchcancel', _stopDrag); // Handle cancellation (e.g. system interruption)
            },
            setDraggable: function(isEnabled, sidebarEl, handleEl) { // Update references if sidebar/handle changes
                _sidebarElement = sidebarEl;
                _handleElement = handleEl;
                if (_handleElement) {
                    _handleElement.style.display = isEnabled ? 'block' : 'none'; // Show/hide handle based on setting
                }
            }
        };
    })();

    // --- URLActionManager for applying filters via URL manipulation ---
    const URLActionManager = (function() {
        function _getURLObject() { try { return new URL(window.location.href); } catch (e) { console.error(`${LOG_PREFIX} Error creating URL object: `, e); return null; }}
        function _navigateTo(url) { const urlString = url.toString(); window.location.href = urlString; }
        function _setSearchParam(urlObj, paramName, value) { urlObj.searchParams.set(paramName, value); }
        function _deleteSearchParam(urlObj, paramName) { urlObj.searchParams.delete(paramName); }
        function _getTbsParts(urlObj) { const tbs = urlObj.searchParams.get('tbs'); return tbs ? tbs.split(',').filter(p => p.trim() !== '') : []; }
        function _setTbsParam(urlObj, tbsPartsArray) { const newTbsValue = tbsPartsArray.join(','); if (newTbsValue) { _setSearchParam(urlObj, 'tbs', newTbsValue); } else { _deleteSearchParam(urlObj, 'tbs'); }}

        return {
            triggerResetFilters: function() {
                try {
                    const u = _getURLObject(); if (!u) return;
                    const q = u.searchParams.get('q') || '';
                    const nP = new URLSearchParams(); // Start with fresh params

                    // Clean query: remove existing site: and filetype: operators (single or grouped)
                    let cQ = q.replace(/\s*\(\s*(?:(?:site|filetype):[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' '); // Remove (site:X OR site:Y)
                    cQ = cQ.replace(/\s*(?:site|filetype):[\w.:()-]+\s*/gi, ' '); // Remove site:X or filetype:Y
                    cQ = cQ.replace(/\s\s+/g, ' ').trim(); // Clean up multiple spaces

                    if (cQ) { nP.set('q', cQ); } // Set cleaned query if it's not empty
                    u.search = nP.toString(); // Replace search part of URL with cleaned query

                    // Delete other filter params
                    _deleteSearchParam(u, 'tbs'); _deleteSearchParam(u, 'lr'); _deleteSearchParam(u, 'cr');
                    _deleteSearchParam(u, 'as_filetype'); _deleteSearchParam(u, 'as_occt');
                    _navigateTo(u);
                } catch (e) {
                    NotificationManager.show('alert_error_resetting_filters', {}, 'error', 5000);
                }
            },
            triggerToggleVerbatim: function() { try { const u = _getURLObject(); if (!u) return; let tP = _getTbsParts(u); const vP = 'li:1'; const iCA = tP.includes(vP); tP = tP.filter(p => p !== vP); if (!iCA) { tP.push(vP); } _setTbsParam(u, tP); _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_toggling_verbatim', {}, 'error', 5000); }},
            isPersonalizationActive: function() { try { const currentUrl = _getURLObject(); if (!currentUrl) { return true; } return currentUrl.searchParams.get('pws') !== '0'; } catch (e) { console.warn(`${LOG_PREFIX} [URLActionManager.isPersonalizationActive] Error checking personalization status:`, e); return true; /* Assume active on error */ } },
            triggerTogglePersonalization: function() { try { const u = _getURLObject(); if (!u) { return; } const personalizationCurrentlyActive = URLActionManager.isPersonalizationActive(); if (personalizationCurrentlyActive) { _setSearchParam(u, 'pws', '0'); } else { _deleteSearchParam(u, 'pws'); } _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_toggling_personalization', {}, 'error', 5000); console.error(`${LOG_PREFIX} [URLActionManager.triggerTogglePersonalization] Error:`, e); } },
            applyFilter: function(type, value) { // Handles non-q-based filters and single 'as_filetype' which might contain OR
                try {
                    const u = _getURLObject(); if (!u) return;
                    let tbsParts = _getTbsParts(u); // Get current tbs parts
                    let processedTbsParts; // To store modified tbs parts

                    const isTimeFilter = type === 'qdr';
                    const isStandaloneParam = ['lr', 'cr', 'as_occt'].includes(type);

                    if (isTimeFilter) {
                        // Filter out existing time-related tbs parts
                        processedTbsParts = tbsParts.filter(p => !p.startsWith(`qdr:`) && !p.startsWith('cdr:') && !p.startsWith('cd_min:') && !p.startsWith('cd_max:'));
                        if (value !== '') processedTbsParts.push(`qdr:${value}`); // Add new qdr if value is not empty
                        _setTbsParam(u, processedTbsParts); // Set the modified tbs
                    } else if (isStandaloneParam) {
                        _deleteSearchParam(u, type); // Delete existing param first
                        if (value !== '' && !(type === 'as_occt' && value === 'any')) { // 'any' for as_occt means no param
                            _setSearchParam(u, type, value);
                        }
                        // For standalone params, tbs is generally not directly modified by them,
                        // but if other tbs parts exist, they should be preserved.
                        // _setTbsParam(u, tbsParts); // Keep original tbs unless specifically changed by time filter
                    } else if (type === 'as_filetype') {
                        const filetypesToApply = Utils.parseCombinedValue(value);
                        if (filetypesToApply.length > 1) {
                            this.applyCombinedFiletypeSearch(filetypesToApply); // Delegate to combined if OR is present
                            return; // applyCombinedFiletypeSearch handles navigation
                        }
                        // Single filetype or empty (clear)
                        let currentQuery = u.searchParams.get('q') || '';
                        // Clean existing filetype: and (filetype:X OR filetype:Y) from query
                        currentQuery = currentQuery.replace(/\s*\(\s*(?:filetype:[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' ');
                        currentQuery = currentQuery.replace(/\s*filetype:[\w.:()-]+\s*/gi, ' ');
                        currentQuery = currentQuery.replace(/\s\s+/g, ' ').trim();

                        if (value !== '') { // value here is a single type after parsing or if it was single initially
                            _setSearchParam(u, 'q', (currentQuery + ` filetype:${filetypesToApply[0]}`).trim());
                        } else {
                            if(currentQuery) _setSearchParam(u, 'q', currentQuery);
                            else _deleteSearchParam(u, 'q'); // If query becomes empty, remove it
                        }
                        _deleteSearchParam(u, 'as_filetype'); // Remove as_filetype if we are using q param
                        // For as_filetype handled via q, tbs is not modified here.
                        // _setTbsParam(u, tbsParts);
                    }
                    // Removed the redundant _setTbsParam(u, tP) from here.
                    // Time filter sets its own tbs. Standalone params don't touch tbs directly.
                    // Filetype (when handled via 'q') also doesn't touch tbs here.
                    _navigateTo(u);
                } catch (e) {
                    NotificationManager.show('alert_error_applying_filter', { type: type, value: value }, 'error', 5000);
                }
            },

            // applySiteSearch and applyCombinedFiletypeSearch expect an array of individual terms
            applySiteSearch: function(siteCriteria) { // siteCriteria can be a string "siteA OR siteB" or an array ["siteA", "siteB"]
                const sitesToSearch = Array.isArray(siteCriteria)
                    ? siteCriteria.flatMap(sc => Utils.parseCombinedValue(sc)) // If array, parse each element
                    : Utils.parseCombinedValue(siteCriteria); // If string, parse it
                const uniqueSites = [...new Set(sitesToSearch.map(s => s.toLowerCase()))];


                if (uniqueSites.length === 0) {
                    this.clearSiteSearch(); return;
                }
                try {
                    const u = _getURLObject(); if (!u) return;
                    let q = u.searchParams.get('q') || '';

                    // Remove existing site: and filetype: operators (single or grouped) to avoid conflicts
                    q = q.replace(/\s*\(\s*(?:(?:site|filetype):[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' ');
                    q = q.replace(/\s*(?:site|filetype):[\w.:()-]+\s*/gi, ' ');
                    q = q.replace(/\s\s+/g, ' ').trim();

                    let siteQueryPart = '';
                    if (uniqueSites.length === 1) {
                        siteQueryPart = `site:${uniqueSites[0]}`;
                    } else {
                        siteQueryPart = `(${uniqueSites.map(s => `site:${s}`).join(' OR ')})`;
                    }

                    const nQ = `${q} ${siteQueryPart}`.trim();
                    _setSearchParam(u, 'q', nQ);
                    // When applying a site search, other filters like tbs, lr, cr, as_filetype, as_occt might become irrelevant
                    // or lead to no results. Google often resets them. Emulate this by clearing them.
                    _deleteSearchParam(u, 'tbs'); _deleteSearchParam(u, 'lr'); _deleteSearchParam(u, 'cr');
                    _deleteSearchParam(u, 'as_filetype'); _deleteSearchParam(u, 'as_occt');
                    _navigateTo(u);
                } catch (e) {
                    const siteForError = uniqueSites.join(', ');
                    NotificationManager.show('alert_error_applying_site_search', { site: siteForError }, 'error', 5000);
                }
            },
            applyCombinedFiletypeSearch: function(filetypeCriteria) { // filetypeCriteria can be a string "typeA OR typeB" or an array ["typeA", "typeB"]
                const filetypesToSearch = Array.isArray(filetypeCriteria)
                    ? filetypeCriteria.flatMap(fc => Utils.parseCombinedValue(fc))
                    : Utils.parseCombinedValue(filetypeCriteria);
                const uniqueFiletypes = [...new Set(filetypesToSearch.map(f => f.toLowerCase()))];

                if (uniqueFiletypes.length === 0) {
                    this.clearFiletypeSearch(); // Call the specific clear function
                    return;
                }
                try {
                    const u = _getURLObject(); if (!u) return;
                    let q = u.searchParams.get('q') || '';

                    // Remove existing filetype: and site: operators (single or grouped)
                    q = q.replace(/\s*\(\s*(?:(?:filetype|site):[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' ');
                    q = q.replace(/\s*(?:filetype|site):[\w.:()-]+\s*/gi, ' ');
                    q = q.replace(/\s\s+/g, ' ').trim();

                    let filetypeQueryPart = '';
                    if (uniqueFiletypes.length === 1) {
                        filetypeQueryPart = `filetype:${uniqueFiletypes[0]}`;
                    } else {
                        filetypeQueryPart = `(${uniqueFiletypes.map(ft => `filetype:${ft}`).join(' OR ')})`;
                    }

                    const nQ = `${q} ${filetypeQueryPart}`.trim();
                    _setSearchParam(u, 'q', nQ);
                    _deleteSearchParam(u, 'as_filetype'); // Clear as_filetype if using 'q' for filetypes
                    _navigateTo(u);
                } catch (e) {
                    const ftForError = uniqueFiletypes.join(', ');
                    NotificationManager.show('alert_error_applying_filter', { type: 'filetype (combined)', value: ftForError }, 'error', 5000);
                }
            },
            clearSiteSearch: function() {
                try {
                    const u = _getURLObject(); if (!u) return;
                    const q = u.searchParams.get('q') || '';
                    // Remove only site: operators, leave filetype: if present
                    let nQ = q.replace(/\s*\(\s*(?:site:[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' '); // Remove (site:X OR site:Y)
                    nQ = nQ.replace(/\s*site:[\w.:()-]+\s*/gi, ' '); // Remove site:X
                    nQ = nQ.replace(/\s\s+/g, ' ').trim();
                    if (nQ) { _setSearchParam(u, 'q', nQ); } else { _deleteSearchParam(u, 'q'); }
                    _navigateTo(u);
                } catch (e) {
                    NotificationManager.show('alert_error_clearing_site_search', {}, 'error', 5000);
                }
            },
            clearFiletypeSearch: function() {
                try {
                    const u = _getURLObject(); if (!u) return;
                    let q = u.searchParams.get('q') || '';
                    // Remove only filetype: operators, leave site: if present
                    q = q.replace(/\s*\(\s*(?:filetype:[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' '); // Remove (filetype:X OR filetype:Y)
                    q = q.replace(/\s*filetype:[\w.:()-]+\s*/gi, ' '); // Remove filetype:X
                    q = q.replace(/\s\s+/g, ' ').trim();
                    if (q) { _setSearchParam(u, 'q', q); } else { _deleteSearchParam(u, 'q'); }
                    _deleteSearchParam(u, 'as_filetype'); // Also clear as_filetype param
                    _navigateTo(u);
                } catch (e) {
                    NotificationManager.show('alert_error_applying_filter', { type: 'filetype', value: '(clear)' }, 'error', 5000);
                }
            },
            isVerbatimActive: function() { try { const currentUrl = _getURLObject(); if (!currentUrl) return false; return /li:1/.test(currentUrl.searchParams.get('tbs') || ''); } catch (e) { console.warn(`${LOG_PREFIX} Error checking verbatim status:`, e); return false; }},
            applyDateRange: function(dateMinStr, dateMaxStr) { try { const url = _getURLObject(); if (!url) return; let dateTbsPart = 'cdr:1'; if (dateMinStr) { const [y, m, d] = dateMinStr.split('-'); dateTbsPart += `,cd_min:${m}/${d}/${y}`; } if (dateMaxStr) { const [y, m, d] = dateMaxStr.split('-'); dateTbsPart += `,cd_max:${m}/${d}/${y}`; } let tbsParts = _getTbsParts(url); let preservedTbsParts = tbsParts.filter(p => !p.startsWith('qdr:') && !p.startsWith('cdr:') && !p.startsWith('cd_min:') && !p.startsWith('cd_max:')); let newTbsParts = [...preservedTbsParts, dateTbsPart]; _setTbsParam(url, newTbsParts); _navigateTo(url); } catch (e) { NotificationManager.show('alert_error_applying_date', {}, 'error', 5000); }}
        };
    })();

    // --- Global Style Injection ---
    function addGlobalStyles() { if (typeof window.GSCS_Namespace !== 'undefined' && typeof window.GSCS_Namespace.stylesText === 'string') { const cleanedCSS = window.GSCS_Namespace.stylesText.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1').replace(/\n\s*\n/g, '\n'); GM_addStyle(cleanedCSS); } else { console.error(`${LOG_PREFIX} CRITICAL: CSS styles provider not found.`); if (typeof IDS !== 'undefined' && IDS.SIDEBAR) { GM_addStyle(`#${IDS.SIDEBAR} { border: 3px dashed red !important; padding: 15px !important; background: white !important; color: red !important; } #${IDS.SIDEBAR}::before { content: "Error: CSS Missing!"; }`);} } }
    // --- System Theme Listener ---
    function setupSystemThemeListener() { if (systemThemeMediaQuery && systemThemeMediaQuery._sidebarThemeListener) { try { systemThemeMediaQuery.removeEventListener('change', systemThemeMediaQuery._sidebarThemeListener); } catch (e) {} systemThemeMediaQuery._sidebarThemeListener = null; } if (window.matchMedia) { systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const listener = () => { const cs = SettingsManager.getCurrentSettings(); if (sidebar && cs.theme === 'system') { applyThemeToElement(sidebar, 'system'); } }; systemThemeMediaQuery.addEventListener('change', listener); systemThemeMediaQuery._sidebarThemeListener = listener; } }
    // --- Sidebar Skeleton and UI Building ---
    function buildSidebarSkeleton() { sidebar = document.createElement('div'); sidebar.id = IDS.SIDEBAR; const header = document.createElement('div'); header.classList.add(CSS.SIDEBAR_HEADER); const collapseBtn = document.createElement('button'); collapseBtn.id = IDS.COLLAPSE_BUTTON; collapseBtn.innerHTML = SVG_ICONS.chevronLeft; collapseBtn.title = _('sidebar_collapse_title'); const dragHandle = document.createElement('div'); dragHandle.classList.add(CSS.DRAG_HANDLE); dragHandle.title = _('sidebar_drag_title'); const settingsBtn = document.createElement('button'); settingsBtn.id = IDS.SETTINGS_BUTTON; settingsBtn.classList.add(CSS.SETTINGS_BUTTON); settingsBtn.innerHTML = SVG_ICONS.settings; settingsBtn.title = _('sidebar_settings_title'); header.appendChild(collapseBtn); header.appendChild(dragHandle); header.appendChild(settingsBtn); sidebar.appendChild(header); document.body.appendChild(sidebar); }
    function applySettings(settingsToApply) {
        if (!sidebar) return;
        const currentSettings = settingsToApply || SettingsManager.getCurrentSettings();
        let targetTop = currentSettings.sidebarPosition.top;
        targetTop = Math.max(MIN_SIDEBAR_TOP_POSITION, targetTop);
        sidebar.style.left = `${currentSettings.sidebarPosition.left}px`;
        sidebar.style.top = `${targetTop}px`;
        sidebar.style.setProperty('--sidebar-font-base-size', `${currentSettings.fontSize}px`);
        sidebar.style.setProperty('--sidebar-header-icon-base-size', `${currentSettings.headerIconSize}px`);
        sidebar.style.setProperty('--sidebar-spacing-multiplier', currentSettings.verticalSpacingMultiplier);
        if (!currentSettings.sidebarCollapsed) { sidebar.style.width = `${currentSettings.sidebarWidth}px`; }
        else { sidebar.style.width = '40px';}
        applyThemeToElement(sidebar, currentSettings.theme);
        if (sidebar._hoverListeners) { sidebar.removeEventListener('mouseenter', sidebar._hoverListeners.enter); sidebar.removeEventListener('mouseleave', sidebar._hoverListeners.leave); sidebar._hoverListeners = null; sidebar.style.opacity = '1';}
        if (currentSettings.hoverMode && !currentSettings.sidebarCollapsed) { const idleOpacityValue = currentSettings.idleOpacity; sidebar.style.opacity = idleOpacityValue.toString(); const enterL = () => { if (!currentSettings.sidebarCollapsed) sidebar.style.opacity = '1'; }; const leaveL = () => { if (!currentSettings.sidebarCollapsed) sidebar.style.opacity = idleOpacityValue.toString(); }; sidebar.addEventListener('mouseenter', enterL); sidebar.addEventListener('mouseleave', leaveL); sidebar._hoverListeners = { enter: enterL, leave: leaveL }; }
        else { sidebar.style.opacity = '1'; }
        applySidebarCollapseVisuals(currentSettings.sidebarCollapsed);

        // Apply Google Logo hiding logic
        const googleLogo = document.querySelector('#logo'); // Updated selector
        if (googleLogo) {
            if (currentSettings.hideGoogleLogoWhenExpanded && !currentSettings.sidebarCollapsed) {
                googleLogo.style.visibility = 'hidden';
            } else {
                googleLogo.style.visibility = 'visible';
            }
        }
    }
    function _parseTimeValueToMinutes(timeValue) { if (!timeValue || typeof timeValue !== 'string') return Infinity; const match = timeValue.match(/^([hdwmy])(\d*)$/i); if (!match) return Infinity; const unit = match[1].toLowerCase(); const number = parseInt(match[2] || '1', 10); if (isNaN(number)) return Infinity; switch (unit) { case 'h': return number * 60; case 'd': return number * 24 * 60; case 'w': return number * 7 * 24 * 60; case 'm': return number * 30 * 24 * 60; case 'y': return number * 365 * 24 * 60; default: return Infinity; } }

    function _prepareFilterOptions(sectionId, scriptDefinedOptions, currentSettings, predefinedOptionsSource) {
        const finalOptions = [];
        const tempAddedValues = new Set(); // To track values already added to finalOptions
        const sectionDef = ALL_SECTION_DEFINITIONS.find(s => s.id === sectionId);
        if (!sectionDef) return [];

        const isSortableMixedType = sectionDef.displayItemsKey && Array.isArray(currentSettings[sectionDef.displayItemsKey]);
        const isFiletypeCheckboxModeActive = sectionId === 'sidebar-section-filetype' && currentSettings.enableFiletypeCheckboxMode;
        const isSiteCheckboxModeActive = sectionId === 'sidebar-section-site-search' && currentSettings.enableSiteSearchCheckboxMode;

        // 1. Add "Any" type script-defined options (value === '') for sections where it's part of the sortable list
        // These are options like "Any Language", "Any Time", "Any Country/Region", "Anywhere in page" (for Occurrence)
        if (scriptDefinedOptions) {
            scriptDefinedOptions.forEach(opt => {
                if (opt && typeof opt.textKey === 'string' && typeof opt.v === 'string' && opt.v === '') {
                    // Only add "Any" to finalOptions if it's NOT filetype/site in checkbox mode (those have dedicated clear divs)
                    if (!((isFiletypeCheckboxModeActive && sectionId === 'sidebar-section-filetype') ||
                          (isSiteCheckboxModeActive && sectionId === 'sidebar-section-site-search'))) {
                        const translatedText = (sectionId === 'sidebar-section-site-search') ? _('filter_any_site') : _(opt.textKey);
                        finalOptions.push({ text: translatedText, value: opt.v, originalText: translatedText, isCustom: false, isAnyOption: true });
                        tempAddedValues.add(opt.v);
                    }
                }
            });
        }


        if (isSortableMixedType) { // For Language, Country (options come from displayLanguages/displayCountries)
            const displayItems = currentSettings[sectionDef.displayItemsKey] || [];
            displayItems.forEach(item => {
                if (!tempAddedValues.has(item.value)) { // Avoid re-adding if somehow value was empty and matched "Any"
                    let displayText = item.text;
                    if (item.type === 'predefined' && item.originalKey) {
                        displayText = _(item.originalKey); // Translate predefined item text
                        if (sectionId === 'sidebar-section-country') { // Special display for countries
                            const parsed = Utils.parseIconAndText(displayText);
                            displayText = `${parsed.icon} ${parsed.text}`.trim();
                        }
                    }
                    finalOptions.push({ text: displayText, value: item.value, originalText: displayText, isCustom: item.type === 'custom' });
                    tempAddedValues.add(item.value);
                }
            });
        } else { // For Time, Filetype (if not checkbox mode), Occurrence
                 // Site Search without checkbox mode is handled by its own population logic mostly.
            const predefinedKey = sectionDef.predefinedOptionsKey;
            const customKey = sectionDef.customItemsKey;
            const predefinedOptsFromSource = predefinedOptionsSource && predefinedKey ? (predefinedOptionsSource[predefinedKey] || []) : [];
            const customOptsFromSettings = customKey ? (currentSettings[customKey] || []) : [];
            let enabledPredefinedSystemVals;

            // For Filetype in checkbox mode, enabledPredefinedOptions are handled by populateFiletypeList directly
            if (isFiletypeCheckboxModeActive && sectionId === 'sidebar-section-filetype') {
                enabledPredefinedSystemVals = currentSettings.enabledPredefinedOptions[predefinedKey] || []; // Still need for general logic
            } else { // For Time, Occurrence, or Filetype (non-checkbox)
                enabledPredefinedSystemVals = predefinedKey ? (currentSettings.enabledPredefinedOptions[predefinedKey] || []) : [];
            }

            const itemsForThisSection = []; // Temporary array to gather options before sorting
            const enabledSet = new Set(enabledPredefinedSystemVals);

            // Add enabled predefined system options
            if (Array.isArray(predefinedOptsFromSource)) {
                predefinedOptsFromSource.forEach(opt => {
                    if (opt && typeof opt.textKey === 'string' && typeof opt.value === 'string' && enabledSet.has(opt.value) && !tempAddedValues.has(opt.value)) {
                        const translatedText = _(opt.textKey);
                        itemsForThisSection.push({ text: translatedText, value: opt.value, originalText: translatedText, isCustom: false });
                        // tempAddedValues.add(opt.value); // Add here if we are pushing directly to finalOptions
                    }
                });
            }

            // Add custom options for this section
            const validCustomOptions = Array.isArray(customOptsFromSettings) ? customOptsFromSettings.filter(cOpt => cOpt && typeof cOpt.text === 'string' && typeof cOpt.value === 'string') : [];
            validCustomOptions.forEach(opt => {
                // For custom items, 'value' can now contain OR. The 'isCustom' flag helps differentiate.
                if (!tempAddedValues.has(opt.value)){ // Check against tempAddedValues to avoid re-adding scriptDefined "Any"
                    itemsForThisSection.push({ text: opt.text, value: opt.value, originalText: opt.text, isCustom: true });
                    // tempAddedValues.add(opt.value);
                }
            });

            // Add collected items to finalOptions (ensuring no duplicates with initial "Any")
            itemsForThisSection.forEach(opt => {
                 if (!tempAddedValues.has(opt.value)){ // Double check for safety, though logic above should handle it
                    finalOptions.push(opt);
                    tempAddedValues.add(opt.value); // Add to set after pushing
                }
            });
        }

        // Add other script-defined options (those with non-empty values) if not already added
        if (scriptDefinedOptions) {
            scriptDefinedOptions.forEach(opt => {
                if (opt && typeof opt.textKey === 'string' && typeof opt.v === 'string' && opt.v !== '' && !tempAddedValues.has(opt.v)) {
                    const translatedText = _(opt.textKey);
                    finalOptions.push({ text: translatedText, value: opt.v, originalText: translatedText, isCustom: false });
                    tempAddedValues.add(opt.v);
                }
            });
        }

        // Sorting logic
        let anyOptionToSortSeparately = null;
        const anyOptionIdx = finalOptions.findIndex(opt => opt.isAnyOption === true);

        if (anyOptionIdx !== -1) {
            anyOptionToSortSeparately = finalOptions.splice(anyOptionIdx, 1)[0]; // Remove "Any" for separate sort handling
        }

        // Only sort if not a mixed type that relies on user-defined order from settings (Language, Country)
        if (!isSortableMixedType) {
            finalOptions.sort((a, b) => {
                const isTimeSection = (sectionId === 'sidebar-section-time');
                if (isTimeSection) { // Special sort for time values
                    const timeA = _parseTimeValueToMinutes(a.value);
                    const timeB = _parseTimeValueToMinutes(b.value);
                    if (timeA !== Infinity || timeB !== Infinity) { // Ensure at least one is a valid time
                        if (timeA !== timeB) return timeA - timeB;
                    }
                }
                // Default sort by text for other types or as fallback for time
                const sTA = a.originalText || a.text; // Use originalText for sorting if available (e.g., for predefined)
                const sTB = b.originalText || b.text;
                const sL = LocalizationService.getCurrentLocale() === 'en' ? undefined : LocalizationService.getCurrentLocale(); // Use current locale for sorting
                return sTA.localeCompare(sTB, sL, { numeric: true, sensitivity: 'base' });
            });
        }
        // Else, for isSortableMixedType, items from displayKey are already in user-defined order.

        if (anyOptionToSortSeparately) {
            finalOptions.unshift(anyOptionToSortSeparately); // Add "Any" option back to the beginning
        }
        return finalOptions;
    }

    function _createFilterOptionElement(optionData, filterParam, isCountrySection, countryDisplayMode) { const optionElement = document.createElement('div'); optionElement.classList.add(CSS.FILTER_OPTION); const displayText = optionData.text; if (isCountrySection) { const { icon, text: countryTextOnly } = Utils.parseIconAndText(displayText); switch (countryDisplayMode) { case 'textOnly': optionElement.textContent = countryTextOnly || displayText; break; case 'iconOnly': if (icon) { optionElement.innerHTML = `<span class="country-icon-container">${icon}</span>`; } else { optionElement.textContent = countryTextOnly || displayText; } break; case 'iconAndText': default: if (icon) { const textPart = countryTextOnly || displayText.substring(icon.length).trim(); optionElement.innerHTML = `<span class="country-icon-container">${icon}</span>${textPart}`; } else { optionElement.textContent = displayText; } break; } } else { optionElement.textContent = displayText; } optionElement.title = `${displayText} (${filterParam}=${optionData.value || _('filter_clear_tooltip_suffix')})`; optionElement.dataset[DATA_ATTR.FILTER_TYPE] = filterParam; optionElement.dataset[DATA_ATTR.FILTER_VALUE] = optionData.value; return optionElement; }
    function buildSidebarUI() { if (!sidebar) { console.error("Sidebar element not ready for buildSidebarUI"); return; } const currentSettings = SettingsManager.getCurrentSettings(); const header = sidebar.querySelector(`.${CSS.SIDEBAR_HEADER}`); if (!header) { console.error("Sidebar header not found in buildSidebarUI"); return; } sidebar.querySelectorAll(`#${IDS.FIXED_TOP_BUTTONS}, .${CSS.SIDEBAR_CONTENT_WRAPPER}`).forEach(el => el.remove()); header.querySelectorAll(`.${CSS.HEADER_BUTTON}:not(#${IDS.SETTINGS_BUTTON}):not(#${IDS.COLLAPSE_BUTTON}), a.${CSS.HEADER_BUTTON}`).forEach(el => el.remove()); const rBL = currentSettings.resetButtonLocation; const vBL = currentSettings.verbatimButtonLocation; const aSL = currentSettings.advancedSearchLinkLocation; const pznBL = currentSettings.personalizationButtonLocation; const schL = currentSettings.googleScholarShortcutLocation; const trnL = currentSettings.googleTrendsShortcutLocation; const settingsButtonRef = header.querySelector(`#${IDS.SETTINGS_BUTTON}`); _buildSidebarHeaderControls(header, settingsButtonRef, rBL, vBL, aSL, pznBL, schL, trnL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, _createScholarShortcutHTML, _createTrendsShortcutHTML, currentSettings); const fixedTopControlsContainer = _buildSidebarFixedTopControls(rBL, vBL, aSL, pznBL, schL, trnL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, _createScholarShortcutHTML, _createTrendsShortcutHTML, currentSettings); if (fixedTopControlsContainer) { header.after(fixedTopControlsContainer); } const contentWrapper = document.createElement('div'); contentWrapper.classList.add(CSS.SIDEBAR_CONTENT_WRAPPER); const sectionDefinitionsMap = new Map(ALL_SECTION_DEFINITIONS.map(def => [def.id, def])); const sectionsFragment = _buildSidebarSections(sectionDefinitionsMap, rBL, vBL, aSL, pznBL, schL, trnL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, _createScholarShortcutHTML, _createTrendsShortcutHTML, currentSettings, PREDEFINED_OPTIONS); contentWrapper.appendChild(sectionsFragment); sidebar.appendChild(contentWrapper); _initializeSidebarEventListenersAndStates(); }
    function _buildSidebarSections(sectionDefinitionMap, rBL, vBL, aSL, pznBL, schL, trnL, advSearchFn, personalizeBtnFn, scholarFn, trendsFn, currentSettings, PREDEFINED_OPTIONS_REF) { const contentFragment = document.createDocumentFragment(); currentSettings.sidebarSectionOrder.forEach(sectionId => { if (!currentSettings.visibleSections[sectionId]) return; const sectionData = sectionDefinitionMap.get(sectionId); if (!sectionData) { console.warn(`${LOG_PREFIX} No definition for section ID: ${sectionId}`); return; } let sectionElement = null; const sectionTitleKey = sectionData.titleKey; const sectionIdForDisplay = sectionData.id; switch (sectionData.type) { case 'filter': sectionElement = createFilterSection(sectionIdForDisplay, sectionTitleKey, sectionData.scriptDefined, sectionData.param, currentSettings, PREDEFINED_OPTIONS_REF, currentSettings.countryDisplayMode); break; case 'filetype': sectionElement = _createFiletypeSectionElement(sectionIdForDisplay, sectionTitleKey, sectionData.scriptDefined, sectionData.param, currentSettings, PREDEFINED_OPTIONS_REF); break; case 'date': sectionElement = _createDateSectionElement(sectionIdForDisplay, sectionTitleKey); break; case 'site': sectionElement = _createSiteSearchSectionElement(sectionIdForDisplay, sectionTitleKey, currentSettings.favoriteSites, currentSettings.enableSiteSearchCheckboxMode); break; case 'tools': sectionElement = _createToolsSectionElement( sectionIdForDisplay, sectionTitleKey, rBL, vBL, aSL, pznBL, schL, trnL, advSearchFn, personalizeBtnFn, scholarFn, trendsFn ); break; default: console.warn(`${LOG_PREFIX} Unknown section type: ${sectionData.type} for ID: ${sectionIdForDisplay}`); break; } if (sectionElement) contentFragment.appendChild(sectionElement); }); return contentFragment; }
    function createFilterSection(id, titleKey, scriptDefinedOptions, filterParam, currentSettings, predefinedOptionsSource, countryDisplayMode) { if (!sidebar) return null; const { section, sectionContent, sectionTitle } = _createSectionShell(id, titleKey); sectionTitle.textContent = _(titleKey); const fragment = document.createDocumentFragment(); const isCountrySection = (id === 'sidebar-section-country'); const combinedOptions = _prepareFilterOptions(id, scriptDefinedOptions, currentSettings, predefinedOptionsSource); combinedOptions.forEach(option => { fragment.appendChild(_createFilterOptionElement(option, filterParam, isCountrySection, countryDisplayMode)); }); sectionContent.innerHTML = ''; sectionContent.appendChild(fragment); if (!sectionContent.dataset.filterClickListenerAttached) { sectionContent.addEventListener('click', function(event) { const target = event.target.closest(`.${CSS.FILTER_OPTION}`); if (target && target.classList.contains(CSS.FILTER_OPTION)) { event.preventDefault(); const clickedFilterType = target.dataset[DATA_ATTR.FILTER_TYPE]; const clickedFilterValue = target.dataset[DATA_ATTR.FILTER_VALUE]; if (typeof clickedFilterType !== 'undefined' && typeof clickedFilterValue !== 'undefined') { this.querySelectorAll(`.${CSS.FILTER_OPTION}`).forEach(opt => opt.classList.remove(CSS.SELECTED)); target.classList.add(CSS.SELECTED); if (clickedFilterValue === '' || (clickedFilterType === 'as_occt' && clickedFilterValue === 'any') ) { const defaultVal = (clickedFilterType === 'as_occt') ? 'any' : ''; const anyOpt = this.querySelector(`.${CSS.FILTER_OPTION}[data-${DATA_ATTR.FILTER_VALUE}="${defaultVal}"]`); if (anyOpt) anyOpt.classList.add(CSS.SELECTED); } URLActionManager.applyFilter(clickedFilterType, clickedFilterValue); } } }); sectionContent.dataset.filterClickListenerAttached = 'true'; } return section; }

    function _createSiteSearchSectionElement(sectionId, titleKey, favoriteSites, checkboxModeEnabled) {
        const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey);
        sectionTitle.textContent = _(titleKey);
        populateSiteSearchList(sectionContent, favoriteSites, checkboxModeEnabled);
        return section;
    }

    function populateSiteSearchList(sectionContentElement, favoriteSitesArray, checkboxModeEnabled) {
        if (!sectionContentElement) { console.error("Site search section content element missing"); return; }
        sectionContentElement.innerHTML = ''; // Clear previous content

        const sites = Array.isArray(favoriteSitesArray) ? favoriteSitesArray : [];
        const listFragment = document.createDocumentFragment(); // For LIs

        // "Any Site" option (always a div, not part of UL)
        const clearOptDiv = document.createElement('div');
        clearOptDiv.classList.add(CSS.FILTER_OPTION);
        clearOptDiv.id = IDS.CLEAR_SITE_SEARCH_OPTION;
        clearOptDiv.title = _('tooltip_clear_site_search');
        clearOptDiv.textContent = _('filter_any_site');
        clearOptDiv.dataset[DATA_ATTR.FILTER_TYPE] = 'site_clear'; // Special type for click handler
        sectionContentElement.appendChild(clearOptDiv);

        const listElement = document.createElement('ul');
        listElement.classList.add(CSS.CUSTOM_LIST);
        if (checkboxModeEnabled) {
            listElement.classList.add('checkbox-mode-enabled');
        }

        sites.forEach((site, index) => {
            if (site?.text && site?.url) {
                const li = document.createElement('li');
                const siteValue = site.url; // Can be "site.com" or "site.com OR example.org"

                if (checkboxModeEnabled) {
                    const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.id = `site-cb-${index}-${Date.now()}`; // Ensure unique ID
                    checkbox.value = siteValue;
                    checkbox.classList.add(CSS.SITE_SEARCH_ITEM_CHECKBOX);
                    checkbox.dataset[DATA_ATTR.SITE_URL] = siteValue; // Store potentially combined URL
                    li.appendChild(checkbox);
                }

                const opt = document.createElement(checkboxModeEnabled ? 'label' : 'div');
                if (checkboxModeEnabled) {
                    opt.htmlFor = `site-cb-${index}-${Date.now()}`; // Ensure matches checkbox ID
                } else {
                    opt.classList.add(CSS.FILTER_OPTION);
                }
                opt.dataset[DATA_ATTR.SITE_URL] = siteValue; // Store potentially combined URL
                opt.title = _('tooltip_site_search', { siteUrl: siteValue.replace(/\s+OR\s+/gi, ', ') }); // Show combined in tooltip
                opt.textContent = site.text;
                li.appendChild(opt);
                listFragment.appendChild(li);
            }
        });
        listElement.appendChild(listFragment);
        sectionContentElement.appendChild(listElement);

        if (checkboxModeEnabled) {
            let applyButton = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_SITES_BUTTON}`);
            if (!applyButton) {
                applyButton = document.createElement('button');
                applyButton.id = IDS.APPLY_SELECTED_SITES_BUTTON;
                applyButton.classList.add(CSS.TOOL_BUTTON, CSS.APPLY_SITES_BUTTON);
                applyButton.textContent = _('tool_apply_selected_sites');
                sectionContentElement.appendChild(applyButton);
            }
            applyButton.disabled = true; // Initially disabled
            applyButton.style.display = 'none'; // Initially hidden
        }

        // Attach event listener if not already attached
        if (!sectionContentElement.dataset.siteSearchClickListenerAttached) {
            sectionContentElement.dataset.siteSearchClickListenerAttached = 'true';
            sectionContentElement.addEventListener('click', (event) => {
                const target = event.target;
                const currentSettings = SettingsManager.getCurrentSettings(); // Get current settings
                const isCheckboxMode = currentSettings.enableSiteSearchCheckboxMode;
                const clearSiteOpt = target.closest(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`);

                if (clearSiteOpt) {
                    URLActionManager.clearSiteSearch();
                    // Update UI: deselect all, select "Any Site"
                    sectionContentElement.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}, label.${CSS.SELECTED}`).forEach(o => o.classList.remove(CSS.SELECTED));
                    clearSiteOpt.classList.add(CSS.SELECTED);
                    if (isCheckboxMode) {
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => cb.checked = false);
                        _updateApplySitesButtonState(sectionContentElement);
                    }
                } else if (isCheckboxMode) {
                    const labelElement = target.closest('label');
                    if (labelElement && labelElement.dataset[DATA_ATTR.SITE_URL]) {
                        event.preventDefault(); // Prevent default label behavior which might toggle checkbox twice
                        const siteUrlOrCombined = labelElement.dataset[DATA_ATTR.SITE_URL];
                        // Single-select behavior when clicking a label in checkbox mode
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => {
                            const correspondingLabel = sectionContentElement.querySelector(`label[for="${cb.id}"]`);
                            cb.checked = (cb.value === siteUrlOrCombined); // Check only the clicked one
                            if(correspondingLabel) correspondingLabel.classList.toggle(CSS.SELECTED, cb.checked);
                        });
                        URLActionManager.applySiteSearch(siteUrlOrCombined);
                        _updateApplySitesButtonState(sectionContentElement); // Update button based on single selection
                        sectionContentElement.querySelector(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                    }
                } else { // Not checkbox mode, or click was not on a label in checkbox mode
                    const siteOptionDiv = target.closest(`div.${CSS.FILTER_OPTION}:not(#${IDS.CLEAR_SITE_SEARCH_OPTION})`);
                    if (siteOptionDiv && siteOptionDiv.dataset[DATA_ATTR.SITE_URL]) {
                        const siteUrlOrCombined = siteOptionDiv.dataset[DATA_ATTR.SITE_URL];
                        sectionContentElement.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}`).forEach(o => o.classList.remove(CSS.SELECTED));
                        URLActionManager.applySiteSearch(siteUrlOrCombined);
                        siteOptionDiv.classList.add(CSS.SELECTED);
                        sectionContentElement.querySelector(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                    }
                }
            });

            // Change listener for checkboxes in checkbox mode
            if (checkboxModeEnabled) {
                sectionContentElement.addEventListener('change', (event) => {
                    if (event.target.matches(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}`)) {
                        _updateApplySitesButtonState(sectionContentElement); // Update button visibility/state
                        const label = sectionContentElement.querySelector(`label[for="${event.target.id}"]`);
                        if (label) label.classList.toggle(CSS.SELECTED, event.target.checked);
                        if (event.target.checked) { // If any checkbox is checked, "Any Site" is no longer selected
                            sectionContentElement.querySelector(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                        }
                    }
                });
                // Listener for the "Apply Selected Sites" button
                const applyBtn = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_SITES_BUTTON}`);
                if(applyBtn && !applyBtn.dataset[DATA_ATTR.LISTENER_ATTACHED]){
                    applyBtn.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
                    applyBtn.addEventListener('click', () => {
                        const selectedValuesFromCheckboxes = [];
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}:checked`).forEach(cb => {
                            selectedValuesFromCheckboxes.push(cb.value); // cb.value contains the site URL or combined "OR" string
                        });
                        if (selectedValuesFromCheckboxes.length > 0) {
                            URLActionManager.applySiteSearch(selectedValuesFromCheckboxes);
                            sectionContentElement.querySelector(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                        }
                    });
                }
            }
        }
    }

    function _createFiletypeSectionElement(sectionId, titleKey, scriptDefinedOptions, filterParam, currentSettings, predefinedOptionsSource) {
        const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey);
        sectionTitle.textContent = _(titleKey);
        populateFiletypeList(sectionContent, scriptDefinedOptions, currentSettings, predefinedOptionsSource, filterParam);
        return section;
    }

    function populateFiletypeList(sectionContentElement, scriptDefinedOpts, currentSettings, predefinedOptsSource, filterParam) {
        if (!sectionContentElement) { console.error("Filetype section content element missing"); return; }
        sectionContentElement.innerHTML = ''; // Clear previous content

        const checkboxModeEnabled = currentSettings.enableFiletypeCheckboxMode;
        // Get all options (script-defined "Any", enabled predefined, custom)
        const combinedOptions = _prepareFilterOptions('sidebar-section-filetype', scriptDefinedOpts, currentSettings, predefinedOptsSource);
        const listFragment = document.createDocumentFragment(); // For LIs

        // "Any Format" option (always a div, not part of UL)
        const clearOptDiv = document.createElement('div');
        clearOptDiv.classList.add(CSS.FILTER_OPTION);
        clearOptDiv.id = IDS.CLEAR_FILETYPE_SEARCH_OPTION;
        clearOptDiv.title = _('filter_clear_tooltip_suffix'); // Generic clear tooltip
        clearOptDiv.textContent = _('filter_any_format');
        clearOptDiv.dataset[DATA_ATTR.FILTER_TYPE] = 'filetype_clear'; // Special type for click handler
        sectionContentElement.appendChild(clearOptDiv);

        const listElement = document.createElement('ul');
        listElement.classList.add(CSS.CUSTOM_LIST);
        if (checkboxModeEnabled) {
            listElement.classList.add('checkbox-mode-enabled');
        }

        combinedOptions.forEach((option, index) => {
            if (option.isAnyOption) return; // "Any Format" is handled by the div above

            const li = document.createElement('li');
            const filetypeValue = option.value; // Can be single "pdf" or combined "pdf OR docx"

            if (checkboxModeEnabled) {
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.id = `ft-cb-${index}-${Date.now()}`; // Unique ID
                checkbox.value = filetypeValue;
                checkbox.classList.add(CSS.FILETYPE_SEARCH_ITEM_CHECKBOX);
                checkbox.dataset[DATA_ATTR.FILETYPE_VALUE] = filetypeValue;
                li.appendChild(checkbox);

                const label = document.createElement('label');
                label.htmlFor = checkbox.id; // Match checkbox ID
                label.dataset[DATA_ATTR.FILETYPE_VALUE] = filetypeValue;
                label.title = `${option.text} (${filterParam}=${filetypeValue.replace(/\s+OR\s+/gi, ', ')})`;
                label.textContent = option.text;
                li.appendChild(label);
            } else { // Radio-button like behavior with divs
                const divOpt = document.createElement('div');
                divOpt.classList.add(CSS.FILTER_OPTION);
                divOpt.dataset[DATA_ATTR.FILTER_TYPE] = filterParam; // e.g., "as_filetype"
                divOpt.dataset[DATA_ATTR.FILTER_VALUE] = filetypeValue;
                divOpt.title = `${option.text} (${filterParam}=${filetypeValue.replace(/\s+OR\s+/gi, ', ')})`;
                divOpt.textContent = option.text;
                li.appendChild(divOpt);
            }
            listFragment.appendChild(li);
        });
        listElement.appendChild(listFragment);
        sectionContentElement.appendChild(listElement);

        if (checkboxModeEnabled) {
            let applyButton = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_FILETYPES_BUTTON}`);
            if(!applyButton) { // Create if doesn't exist
                applyButton = document.createElement('button');
                applyButton.id = IDS.APPLY_SELECTED_FILETYPES_BUTTON;
                applyButton.classList.add(CSS.TOOL_BUTTON, CSS.APPLY_FILETYPES_BUTTON);
                applyButton.textContent = _('tool_apply_selected_filetypes');
                sectionContentElement.appendChild(applyButton);
            }
            applyButton.disabled = true; // Initially disabled
            applyButton.style.display = 'none'; // Initially hidden
        }

        // Attach event listener if not already attached
        if (!sectionContentElement.dataset.filetypeClickListenerAttached) {
            sectionContentElement.dataset.filetypeClickListenerAttached = 'true';
            sectionContentElement.addEventListener('click', (event) => {
                const target = event.target;
                const isCheckboxMode = SettingsManager.getCurrentSettings().enableFiletypeCheckboxMode;
                const clearFiletypeOpt = target.closest(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`);

                if (clearFiletypeOpt) {
                    URLActionManager.clearFiletypeSearch();
                    sectionContentElement.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}, label.${CSS.SELECTED}`).forEach(o => o.classList.remove(CSS.SELECTED));
                    clearFiletypeOpt.classList.add(CSS.SELECTED);
                    if (isCheckboxMode) {
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => cb.checked = false);
                        _updateApplyFiletypesButtonState(sectionContentElement);
                    }
                } else if (isCheckboxMode) {
                    const labelElement = target.closest('label');
                    if (labelElement && labelElement.dataset[DATA_ATTR.FILETYPE_VALUE]) {
                        event.preventDefault();
                        const filetypeValueOrCombined = labelElement.dataset[DATA_ATTR.FILETYPE_VALUE];
                        // Single-select behavior when clicking a label in checkbox mode
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => {
                            const correspondingLabel = sectionContentElement.querySelector(`label[for="${cb.id}"]`);
                            cb.checked = (cb.value === filetypeValueOrCombined);
                             if(correspondingLabel) correspondingLabel.classList.toggle(CSS.SELECTED, cb.checked);
                        });
                        URLActionManager.applyCombinedFiletypeSearch(filetypeValueOrCombined);
                        _updateApplyFiletypesButtonState(sectionContentElement);
                        sectionContentElement.querySelector(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                    }
                } else { // Not checkbox mode
                    const optionDiv = target.closest(`div.${CSS.FILTER_OPTION}:not(#${IDS.CLEAR_FILETYPE_SEARCH_OPTION})`);
                    if (optionDiv && optionDiv.dataset[DATA_ATTR.FILTER_VALUE]) {
                        const clickedFilterType = optionDiv.dataset[DATA_ATTR.FILTER_TYPE]; // Should be "as_filetype"
                        const clickedFilterValueOrCombined = optionDiv.dataset[DATA_ATTR.FILTER_VALUE];
                        sectionContentElement.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}`).forEach(o => o.classList.remove(CSS.SELECTED));
                        optionDiv.classList.add(CSS.SELECTED);
                        URLActionManager.applyFilter(clickedFilterType, clickedFilterValueOrCombined); // applyFilter handles single or combined
                        sectionContentElement.querySelector(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                    }
                }
            });

            // Change listener for checkboxes in checkbox mode
            if (checkboxModeEnabled) {
                sectionContentElement.addEventListener('change', (event) => {
                    if (event.target.matches(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}`)) {
                        _updateApplyFiletypesButtonState(sectionContentElement);
                        const label = sectionContentElement.querySelector(`label[for="${event.target.id}"]`);
                        if (label) label.classList.toggle(CSS.SELECTED, event.target.checked);
                        if (event.target.checked) {
                            sectionContentElement.querySelector(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                        }
                    }
                });
                // Listener for the "Apply Selected" button
                const applyBtn = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_FILETYPES_BUTTON}`);
                if (applyBtn && !applyBtn.dataset[DATA_ATTR.LISTENER_ATTACHED]) {
                    applyBtn.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
                    applyBtn.addEventListener('click', () => {
                        const selectedValuesFromCheckboxes = [];
                        sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}:checked`).forEach(cb => {
                            selectedValuesFromCheckboxes.push(cb.value); // cb.value contains the filetype or combined "OR" string
                        });
                        if (selectedValuesFromCheckboxes.length > 0) {
                            URLActionManager.applyCombinedFiletypeSearch(selectedValuesFromCheckboxes);
                            sectionContentElement.querySelector(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`)?.classList.remove(CSS.SELECTED);
                        }
                    });
                }
            }
        }
    }

    function _updateApplySitesButtonState(sectionContentElement) {
        if (!sectionContentElement) return;
        const applyButton = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_SITES_BUTTON}`);
        if (!applyButton) return;
        const checkedCount = sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}:checked`).length;
        applyButton.disabled = checkedCount === 0;
        applyButton.style.display = checkedCount > 0 ? 'inline-flex' : 'none'; // Use inline-flex if that's how tool-buttons are displayed
    }

    function _updateApplyFiletypesButtonState(sectionContentElement) {
        if (!sectionContentElement) return;
        const applyButton = sectionContentElement.querySelector(`#${IDS.APPLY_SELECTED_FILETYPES_BUTTON}`);
        if (!applyButton) return;
        const checkedCount = sectionContentElement.querySelectorAll(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}:checked`).length;
        applyButton.disabled = checkedCount === 0;
        applyButton.style.display = checkedCount > 0 ? 'inline-flex' : 'none';
    }

    // Render the sortable list of sections in the settings UI
    function renderSectionOrderList(settingsRef) { const settingsWindowEl = document.getElementById(IDS.SETTINGS_WINDOW); const orderListElement = settingsWindowEl?.querySelector(`#${IDS.SIDEBAR_SECTION_ORDER_LIST}`); if (!orderListElement) return; orderListElement.innerHTML = ''; const currentSettings = settingsRef || SettingsManager.getCurrentSettings(); const visibleOrderedSections = currentSettings.sidebarSectionOrder.filter(id => currentSettings.visibleSections[id]); if (visibleOrderedSections.length === 0) { orderListElement.innerHTML = `<li><span style="font-style:italic;color:var(--settings-tab-color);">${_('settings_no_orderable_sections')}</span></li>`; return; } const fragment = document.createDocumentFragment(); visibleOrderedSections.forEach((sectionId) => { const definition = ALL_SECTION_DEFINITIONS.find(def => def.id === sectionId); const displayName = definition ? _(definition.titleKey) : sectionId; const listItem = document.createElement('li'); listItem.dataset.sectionId = sectionId; listItem.draggable = true; const dragIconSpan = document.createElement('span'); dragIconSpan.classList.add(CSS.DRAG_ICON); dragIconSpan.innerHTML = SVG_ICONS.dragGrip; listItem.appendChild(dragIconSpan); const nameSpan = document.createElement('span'); nameSpan.textContent = displayName; listItem.appendChild(nameSpan); fragment.appendChild(listItem); }); orderListElement.appendChild(fragment); }
    function _initMenuCommands() { if (typeof GM_registerMenuCommand === 'function') { const openSettingsText = _('menu_open_settings'); const resetAllText = _('menu_reset_all_settings'); if (typeof GM_unregisterMenuCommand === 'function') { try { GM_unregisterMenuCommand(openSettingsText); } catch (e) {} try { GM_unregisterMenuCommand(resetAllText);   } catch (e) {} } GM_registerMenuCommand(openSettingsText, SettingsManager.show.bind(SettingsManager)); GM_registerMenuCommand(resetAllText, SettingsManager.resetAllFromMenu.bind(SettingsManager)); } }
    function _createSectionShell(id, titleKey) { const section = document.createElement('div'); section.id = id; section.classList.add(CSS.SIDEBAR_SECTION); const sectionTitle = document.createElement('div'); sectionTitle.classList.add(CSS.SECTION_TITLE); sectionTitle.textContent = _(titleKey); section.appendChild(sectionTitle); const sectionContent = document.createElement('div'); sectionContent.classList.add(CSS.SECTION_CONTENT); section.appendChild(sectionContent); return { section, sectionContent, sectionTitle }; }
    function _createDateSectionElement(sectionId, titleKey) { const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey); sectionTitle.textContent = _(titleKey); const today = new Date(); const yyyy = today.getFullYear(); const mm = String(today.getMonth() + 1).padStart(2, '0'); const dd = String(today.getDate()).padStart(2, '0'); const todayString = `${yyyy}-${mm}-${dd}`; sectionContent.innerHTML = `<label class="${CSS.DATE_INPUT_LABEL}" for="${IDS.DATE_MIN}">${_('date_range_from')}</label>` + `<input type="date" class="${CSS.DATE_INPUT}" id="${IDS.DATE_MIN}" max="${todayString}">` + `<label class="${CSS.DATE_INPUT_LABEL}" for="${IDS.DATE_MAX}">${_('date_range_to')}</label>` + `<input type="date" class="${CSS.DATE_INPUT}" id="${IDS.DATE_MAX}" max="${todayString}">` + `<span id="${IDS.DATE_RANGE_ERROR_MSG}" class="${CSS.DATE_RANGE_ERROR_MSG} ${CSS.INPUT_ERROR_MESSAGE}"></span>` + `<button class="${CSS.TOOL_BUTTON} apply-date-range">${_('tool_apply_date')}</button>`; return section; }
    function _createStandardButton({ id = null, className, svgIcon, textContent = null, title, clickHandler, isActive = false }) { const button = document.createElement('button'); if (id) button.id = id; button.classList.add(className); if (isActive) button.classList.add(CSS.ACTIVE); button.title = title; let content = svgIcon || ''; if (textContent) { content = svgIcon ? `${svgIcon} ${textContent}` : textContent; } button.innerHTML = content.trim(); if (clickHandler) { if (!button.dataset[DATA_ATTR.LISTENER_ATTACHED]) { button.addEventListener('click', clickHandler); button.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; } } return button; }
    function _createPersonalizationButtonHTML(forLocation = 'tools') { const personalizationActive = URLActionManager.isPersonalizationActive(); const isIconOnlyLocation = (forLocation === 'header'); const svgIcon = SVG_ICONS.personalization || ''; const displayText = !isIconOnlyLocation ? _('tool_personalization_toggle') : ''; const titleKey = personalizationActive ? 'tooltip_toggle_personalization_off' : 'tooltip_toggle_personalization_on'; return _createStandardButton({ id: IDS.TOOL_PERSONALIZE, className: (forLocation === 'header') ? CSS.HEADER_BUTTON : CSS.TOOL_BUTTON, svgIcon: svgIcon, textContent: displayText, title: _(titleKey), clickHandler: () => URLActionManager.triggerTogglePersonalization(), isActive: personalizationActive }); }
    function _createAdvancedSearchElementHTML(isButtonLike = false) { const el = document.createElement('a'); let iconHTML = SVG_ICONS.magnifyingGlass || ''; if (isButtonLike) { el.classList.add(CSS.TOOL_BUTTON); el.innerHTML = `${iconHTML} ${_('tool_advanced_search')}`; } else { el.classList.add(CSS.HEADER_BUTTON); el.innerHTML = iconHTML; } const baseUrl = "https://www.google.com/advanced_search"; let finalUrl = baseUrl; try { const currentFullUrl = Utils.getCurrentURL(); if (currentFullUrl) { const currentQuery = currentFullUrl.searchParams.get('q'); if (currentQuery) { let queryWithoutSite = currentQuery.replace(/\s*\(\s*(?:site:[\w.:()-]+(?:\s+OR\s+|$))+[^)]*\)\s*/gi, ' '); queryWithoutSite = queryWithoutSite.replace(/\s*site:[\w.:()-]+\s*/gi, ' '); queryWithoutSite = queryWithoutSite.replace(/\s\s+/g, ' ').trim(); if (queryWithoutSite) { finalUrl = `${baseUrl}?as_q=${encodeURIComponent(queryWithoutSite)}`; } } } } catch (e) { console.warn(`${LOG_PREFIX} Error constructing advanced search URL with query:`, e); } el.href = finalUrl; el.target = "_blank"; el.rel = "noopener noreferrer"; el.title = _('link_advanced_search_title'); return el; }
    function _createScholarShortcutHTML(forLocation = 'tools') { const isIconOnlyLocation = (forLocation === 'header'); const svgIcon = SVG_ICONS.googleScholar || ''; const displayText = !isIconOnlyLocation ? _('tool_google_scholar') : ''; return _createStandardButton({ id: IDS.TOOL_GOOGLE_SCHOLAR, className: (forLocation === 'header') ? CSS.HEADER_BUTTON : CSS.TOOL_BUTTON, svgIcon: svgIcon, textContent: displayText, title: _('tooltip_google_scholar_search'), clickHandler: () => { try { const currentUrl = Utils.getCurrentURL(); if (currentUrl) { const query = currentUrl.searchParams.get('q'); if (query) { const scholarUrl = `https://scholar.google.com/scholar?q=${encodeURIComponent(query)}`; window.open(scholarUrl, '_blank'); } else { window.open('https://scholar.google.com/', '_blank'); NotificationManager.show('alert_no_keywords_for_shortcut', { service_name: _('service_name_google_scholar') }, 'info'); } } } catch (e) { console.error(`${LOG_PREFIX} Error opening Google Scholar:`, e); NotificationManager.show('alert_error_opening_link', { service_name: _('service_name_google_scholar') }, 'error'); } } }); }
    function _createTrendsShortcutHTML(forLocation = 'tools') { const isIconOnlyLocation = (forLocation === 'header'); const svgIcon = SVG_ICONS.googleTrends || ''; const displayText = !isIconOnlyLocation ? _('tool_google_trends') : ''; return _createStandardButton({ id: IDS.TOOL_GOOGLE_TRENDS, className: (forLocation === 'header') ? CSS.HEADER_BUTTON : CSS.TOOL_BUTTON, svgIcon: svgIcon, textContent: displayText, title: _('tooltip_google_trends_search'), clickHandler: () => { try { const currentUrl = Utils.getCurrentURL(); if (currentUrl) { const query = currentUrl.searchParams.get('q'); if (query) { const trendsUrl = `https://trends.google.com/trends/explore?q=${encodeURIComponent(query)}`; window.open(trendsUrl, '_blank'); } else { window.open('https://trends.google.com/trends/', '_blank'); NotificationManager.show('alert_no_keywords_for_shortcut', { service_name: _('service_name_google_trends') }, 'info'); } } } catch (e) { console.error(`${LOG_PREFIX} Error opening Google Trends:`, e); NotificationManager.show('alert_error_opening_link', { service_name: _('service_name_google_trends') }, 'error'); } } }); }
    function _buildSidebarHeaderControls(headerEl, settingsBtnRef, rBL, vBL, aSL, pznBL, schL, trnL, advSearchFn, personalizeBtnFn, scholarFn, trendsFn, settings) { const verbatimActive = URLActionManager.isVerbatimActive(); const buttonsInOrder = []; if (aSL === 'header' && advSearchFn && settings.advancedSearchLinkLocation !== 'none') { buttonsInOrder.push(advSearchFn(false)); } if (schL === 'header' && scholarFn && settings.googleScholarShortcutLocation !== 'none') { buttonsInOrder.push(scholarFn('header')); } if (trnL === 'header' && trendsFn && settings.googleTrendsShortcutLocation !== 'none') { buttonsInOrder.push(trendsFn('header')); } if (vBL === 'header' && settings.verbatimButtonLocation !== 'none') { buttonsInOrder.push(_createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.HEADER_BUTTON, svgIcon: SVG_ICONS.verbatim, title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive })); } if (pznBL === 'header' && personalizeBtnFn && settings.personalizationButtonLocation !== 'none') { buttonsInOrder.push(personalizeBtnFn('header')); } if (rBL === 'header' && settings.resetButtonLocation !== 'none') { buttonsInOrder.push(_createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.HEADER_BUTTON, svgIcon: SVG_ICONS.reset, title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters })); } buttonsInOrder.forEach(btn => { if (settingsBtnRef) { headerEl.insertBefore(btn, settingsBtnRef); } else { headerEl.appendChild(btn); } }); }
    function _buildSidebarFixedTopControls(rBL, vBL, aSL, pznBL, schL, trnL, advSearchFn, personalizeBtnFn, scholarFn, trendsFn, settings) { const fTBC = document.createElement('div'); fTBC.id = IDS.FIXED_TOP_BUTTONS; const fTF = document.createDocumentFragment(); const verbatimActive = URLActionManager.isVerbatimActive(); if (rBL === 'topBlock' && settings.resetButtonLocation !== 'none') { const btn = _createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.reset, textContent: _('tool_reset_filters'), title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters }); const bD = document.createElement('div'); bD.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bD.appendChild(btn); fTF.appendChild(bD); } if (pznBL === 'topBlock' && personalizeBtnFn && settings.personalizationButtonLocation !== 'none') { const btnPzn = personalizeBtnFn('topBlock'); const bDPzn = document.createElement('div'); bDPzn.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDPzn.appendChild(btnPzn); fTF.appendChild(bDPzn); } if (vBL === 'topBlock' && settings.verbatimButtonLocation !== 'none') { const btnVerbatim = _createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.verbatim, textContent: _('tool_verbatim_search'), title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive }); const bDVerbatim = document.createElement('div'); bDVerbatim.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDVerbatim.appendChild(btnVerbatim); fTF.appendChild(bDVerbatim); } if (aSL === 'topBlock' && advSearchFn && settings.advancedSearchLinkLocation !== 'none') { const linkEl = advSearchFn(true); const bDAdv = document.createElement('div'); bDAdv.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDAdv.appendChild(linkEl); fTF.appendChild(bDAdv); } if (schL === 'topBlock' && scholarFn && settings.googleScholarShortcutLocation !== 'none') { const btnSch = scholarFn('topBlock'); const bDSch = document.createElement('div'); bDSch.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDSch.appendChild(btnSch); fTF.appendChild(bDSch); } if (trnL === 'topBlock' && trendsFn && settings.googleTrendsShortcutLocation !== 'none') { const btnTrn = trendsFn('topBlock'); const bDTrn = document.createElement('div'); bDTrn.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDTrn.appendChild(btnTrn); fTF.appendChild(bDTrn); } if (fTF.childElementCount > 0) { fTBC.appendChild(fTF); return fTBC; } return null; }
    function _createToolsSectionElement(sectionId, titleKey, rBL, vBL, aSL, pznBL, schL, trnL, advSearchFn, personalizeBtnFn, scholarFn, trendsFn) { const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey); sectionTitle.textContent = _(titleKey); const frag = document.createDocumentFragment(); const verbatimActive = URLActionManager.isVerbatimActive(); const currentSettings = SettingsManager.getCurrentSettings(); if (rBL === 'tools' && currentSettings.resetButtonLocation !== 'none') { const btn = _createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.reset, textContent: _('tool_reset_filters'), title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters }); frag.appendChild(btn); } if (pznBL === 'tools' && personalizeBtnFn && currentSettings.personalizationButtonLocation !== 'none') { const btnPzn = personalizeBtnFn('tools'); frag.appendChild(btnPzn); } if (vBL === 'tools' && currentSettings.verbatimButtonLocation !== 'none') { const btnVerbatim = _createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.verbatim, textContent: _('tool_verbatim_search'), title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive }); frag.appendChild(btnVerbatim); } if (aSL === 'tools' && advSearchFn && currentSettings.advancedSearchLinkLocation !== 'none') { frag.appendChild(advSearchFn(true)); } if (schL === 'tools' && scholarFn && currentSettings.googleScholarShortcutLocation !== 'none') { frag.appendChild(scholarFn('tools')); } if (trnL === 'tools' && trendsFn && currentSettings.googleTrendsShortcutLocation !== 'none') { frag.appendChild(trendsFn('tools')); } if (frag.childElementCount > 0) { sectionContent.appendChild(frag); return section; } return null; }
    function _validateDateInputs(minInput, maxInput, errorMsgElement) { _clearElementMessage(errorMsgElement, CSS.ERROR_VISIBLE); minInput.classList.remove(CSS.INPUT_HAS_ERROR); maxInput.classList.remove(CSS.INPUT_HAS_ERROR); let isValid = true; const today = new Date(); today.setHours(0, 0, 0, 0); const startDateStr = minInput.value; const endDateStr = maxInput.value; let startDate = null; let endDate = null; if (startDateStr) { startDate = new Date(startDateStr); startDate.setHours(0,0,0,0); if (startDate > today) { _showElementMessage(errorMsgElement, 'alert_start_in_future', {}, CSS.ERROR_VISIBLE); minInput.classList.add(CSS.INPUT_HAS_ERROR); isValid = false; } } if (endDateStr) { endDate = new Date(endDateStr); endDate.setHours(0,0,0,0); if (endDate > today && !maxInput.getAttribute('max')) { /* Check only if max is not today (which it is by default) */ if (isValid) _showElementMessage(errorMsgElement, 'alert_end_in_future', {}, CSS.ERROR_VISIBLE); else errorMsgElement.textContent += " " + _('alert_end_in_future'); maxInput.classList.add(CSS.INPUT_HAS_ERROR); isValid = false; } } if (startDate && endDate && startDate > endDate) { if (isValid) _showElementMessage(errorMsgElement, 'alert_end_before_start', {}, CSS.ERROR_VISIBLE); else errorMsgElement.textContent += " " + _('alert_end_before_start'); minInput.classList.add(CSS.INPUT_HAS_ERROR); maxInput.classList.add(CSS.INPUT_HAS_ERROR); isValid = false; } return isValid; }
    function addDateRangeListener() { const dateRangeSection = sidebar?.querySelector('#sidebar-section-date-range'); if (!dateRangeSection) return; const applyButton = dateRangeSection.querySelector('.apply-date-range'); const errorMsgElement = dateRangeSection.querySelector(`#${IDS.DATE_RANGE_ERROR_MSG}`); const dateMinInput = dateRangeSection.querySelector(`#${IDS.DATE_MIN}`); const dateMaxInput = dateRangeSection.querySelector(`#${IDS.DATE_MAX}`); if (!applyButton || !errorMsgElement || !dateMinInput || !dateMaxInput) { console.warn(`${LOG_PREFIX} Date range elements not found for listener setup.`); return; } const handleDateValidation = () => { const isValid = _validateDateInputs(dateMinInput, dateMaxInput, errorMsgElement); applyButton.disabled = !isValid; }; if (!dateMinInput.dataset[DATA_ATTR.LISTENER_ATTACHED]) { dateMinInput.addEventListener('input', handleDateValidation); dateMinInput.addEventListener('change', handleDateValidation); dateMinInput.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; } if (!dateMaxInput.dataset[DATA_ATTR.LISTENER_ATTACHED]) { dateMaxInput.addEventListener('input', handleDateValidation); dateMaxInput.addEventListener('change', handleDateValidation); dateMaxInput.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; } if (!applyButton.dataset[DATA_ATTR.LISTENER_ATTACHED]) { applyButton.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; applyButton.addEventListener('click', () => { if (!_validateDateInputs(dateMinInput, dateMaxInput, errorMsgElement)) return; URLActionManager.applyDateRange(dateMinInput.value, dateMaxInput.value); }); } handleDateValidation(); /* Initial validation */ }
    function _initializeSidebarEventListenersAndStates() { addDateRangeListener(); addToolButtonListeners(); initializeSelectedFilters(); applySectionCollapseStates(); }
    function _clearElementMessage(element, visibleClass = CSS.ERROR_VISIBLE) { if(!element)return; element.textContent=''; element.classList.remove(visibleClass);}
    function _showElementMessage(element, messageKey, messageArgs = {}, visibleClass = CSS.ERROR_VISIBLE) { if(!element)return; element.textContent=_(messageKey,messageArgs); element.classList.add(visibleClass);}
    // --- Tool Button Listeners (for buttons not handled elsewhere like date apply) ---
    function addToolButtonListeners() { const queryAreas = [ sidebar?.querySelector(`.${CSS.SIDEBAR_HEADER}`), sidebar?.querySelector(`#${IDS.FIXED_TOP_BUTTONS}`), sidebar?.querySelector(`#sidebar-section-tools .${CSS.SECTION_CONTENT}`) ].filter(Boolean); queryAreas.forEach(area => { area.querySelectorAll(`#${IDS.TOOL_VERBATIM}:not([data-${DATA_ATTR.LISTENER_ATTACHED}])`).forEach(b => { b.addEventListener('click', URLActionManager.triggerToggleVerbatim); b.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; }); area.querySelectorAll(`#${IDS.TOOL_RESET_BUTTON}:not([data-${DATA_ATTR.LISTENER_ATTACHED}])`).forEach(b => { b.addEventListener('click', URLActionManager.triggerResetFilters); b.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; }); }); }
    // --- Sidebar Collapse/Expand and Section State Application ---
    function applySidebarCollapseVisuals(isCollapsed) { if(!sidebar)return; const collapseButton = sidebar.querySelector(`#${IDS.COLLAPSE_BUTTON}`); if(isCollapsed){ sidebar.classList.add(CSS.SIDEBAR_COLLAPSED); if(collapseButton){ collapseButton.innerHTML = SVG_ICONS.chevronRight; collapseButton.title = _('sidebar_expand_title');}} else{ sidebar.classList.remove(CSS.SIDEBAR_COLLAPSED); if(collapseButton){ collapseButton.innerHTML = SVG_ICONS.chevronLeft; collapseButton.title = _('sidebar_collapse_title');}} }
    function applySectionCollapseStates() { if(!sidebar)return; const currentSettings = SettingsManager.getCurrentSettings(); const sections = sidebar.querySelectorAll(`.${CSS.SIDEBAR_CONTENT_WRAPPER} .${CSS.SIDEBAR_SECTION}`); sections.forEach(section => { const content = section.querySelector(`.${CSS.SECTION_CONTENT}`); const title = section.querySelector(`.${CSS.SECTION_TITLE}`); const sectionId = section.id; if (content && title && sectionId) { let shouldBeCollapsed = false; if (currentSettings.sectionDisplayMode === 'collapseAll') { shouldBeCollapsed = true; } else if (currentSettings.sectionDisplayMode === 'expandAll') { shouldBeCollapsed = false; } else { /* remember state */ shouldBeCollapsed = currentSettings.sectionStates?.[sectionId] === true; } content.classList.toggle(CSS.COLLAPSED, shouldBeCollapsed); title.classList.toggle(CSS.COLLAPSED, shouldBeCollapsed); if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; currentSettings.sectionStates[sectionId] = shouldBeCollapsed; } } }); }

    // --- Initialize Selected Filters based on URL ---
    function initializeSelectedFilters() {
        if (!sidebar) return;
        try {
            const currentUrl = URLActionManager._getURLObject ? URLActionManager._getURLObject() : Utils.getCurrentURL(); // Use URLActionManager's internal helper if possible
            if (!currentUrl) return; // Should not happen if script is running on a valid page
            const params = currentUrl.searchParams;
            const currentTbs = params.get('tbs') || '';
            const currentQuery = params.get('q') || '';

            // Handle standalone params like lr, cr, as_occt
            ALL_SECTION_DEFINITIONS.forEach(sectionDef => {
                if (sectionDef.type === 'filter' && sectionDef.param && sectionDef.id !== 'sidebar-section-filetype' && sectionDef.id !== 'sidebar-section-site-search') { // Exclude filetype/site as they use 'q'
                     _initializeStandaloneFilterState(params, sectionDef.id, sectionDef.param);
                }
            });
            _initializeTimeFilterState(currentTbs);
            _initializeVerbatimState();
            _initializePersonalizationState();
            _initializeDateRangeInputs(currentTbs);
            _initializeSiteSearchState(currentQuery); // Pass only query for site search
            _initializeFiletypeSearchState(currentQuery, params.get('as_filetype')); // Pass query and as_filetype
        } catch (e) {
            console.error(`${LOG_PREFIX} Error initializing filter highlights:`, e);
        }
    }

    function _initializeStandaloneFilterState(params, sectionId, paramToGetFromURL) {
        const sectionElement = sidebar?.querySelector(`#${sectionId}`);
        if (!sectionElement) return;
        const urlValue = params.get(paramToGetFromURL);
        const options = sectionElement.querySelectorAll(`.${CSS.FILTER_OPTION}`);
        let anOptionWasSelectedBasedOnUrl = false;

        options.forEach(opt => {
            const optionValue = opt.dataset[DATA_ATTR.FILTER_VALUE];
            const isSelected = (urlValue !== null && urlValue === optionValue); // Strict check for value presence
            opt.classList.toggle(CSS.SELECTED, isSelected);
            if (isSelected) anOptionWasSelectedBasedOnUrl = true;
        });

        // If no option was selected based on URL (e.g., param not present or value doesn't match), select the default
        if (!anOptionWasSelectedBasedOnUrl) {
            // For as_occt, default is 'any'. For others (lr, cr), default is empty string value.
            const defaultOptionQuery = (paramToGetFromURL === 'as_occt')
                ? `.${CSS.FILTER_OPTION}[data-${DATA_ATTR.FILTER_VALUE}="any"]`
                : `.${CSS.FILTER_OPTION}[data-${DATA_ATTR.FILTER_VALUE}=""]`;
            const defaultOpt = sectionElement.querySelector(defaultOptionQuery);
            if (defaultOpt) {
                defaultOpt.classList.add(CSS.SELECTED);
            }
        }
    }

    function _initializeTimeFilterState(currentTbs){ const timeSection = sidebar?.querySelector('#sidebar-section-time'); if(!timeSection) return; const qdrMatch = currentTbs.match(/qdr:([^,]+)/); const activeQdrValue = qdrMatch ? qdrMatch[1] : null; const hasDateRange = /cdr:1/.test(currentTbs); const timeOptions = timeSection.querySelectorAll(`.${CSS.FILTER_OPTION}`); timeOptions.forEach(opt => { const optionValue = opt.dataset[DATA_ATTR.FILTER_VALUE]; let shouldBeSelected = false; if(hasDateRange){ shouldBeSelected = (optionValue === '');/* If custom date range, select "Any Time" */ } else if(activeQdrValue){ shouldBeSelected = (optionValue === activeQdrValue); } else { shouldBeSelected = (optionValue === ''); /* Default to "Any Time" */ } opt.classList.toggle(CSS.SELECTED, shouldBeSelected); }); }
    function _initializeVerbatimState(){ const isVerbatimActiveNow = URLActionManager.isVerbatimActive(); sidebar?.querySelectorAll(`#${IDS.TOOL_VERBATIM}`).forEach(b=>b.classList.toggle(CSS.ACTIVE, isVerbatimActiveNow)); }
    function _initializePersonalizationState() { const isActive = URLActionManager.isPersonalizationActive(); sidebar?.querySelectorAll(`#${IDS.TOOL_PERSONALIZE}`).forEach(button => { button.classList.toggle(CSS.ACTIVE, isActive); const titleKey = isActive ? 'tooltip_toggle_personalization_off' : 'tooltip_toggle_personalization_on'; button.title = _(titleKey); /* Update icon/text if needed based on state - already done by _createPersonalizationButtonHTML */ const svgIcon = SVG_ICONS.personalization || ''; const isIconOnly = button.classList.contains(CSS.HEADER_BUTTON) && !button.classList.contains(CSS.TOOL_BUTTON); const currentText = !isIconOnly ? _('tool_personalization_toggle') : ''; let newHTML = ''; if(svgIcon) newHTML += svgIcon; if(currentText) newHTML += (svgIcon && currentText ? ' ' : '') + currentText; button.innerHTML = newHTML.trim(); }); }
    function _initializeDateRangeInputs(currentTbs){ const dateSection = sidebar?.querySelector('#sidebar-section-date-range'); if (!dateSection) return; const dateMinInput = dateSection.querySelector(`#${IDS.DATE_MIN}`); const dateMaxInput = dateSection.querySelector(`#${IDS.DATE_MAX}`); const errorMsgElement = dateSection.querySelector(`#${IDS.DATE_RANGE_ERROR_MSG}`); const applyButton = dateSection.querySelector('.apply-date-range'); if (errorMsgElement) _clearElementMessage(errorMsgElement, CSS.ERROR_VISIBLE); if (/cdr:1/.test(currentTbs)) { const minMatch = currentTbs.match(/cd_min:(\d{1,2})\/(\d{1,2})\/(\d{4})/); const maxMatch = currentTbs.match(/cd_max:(\d{1,2})\/(\d{1,2})\/(\d{4})/); if (dateMinInput) dateMinInput.value = minMatch ? `${minMatch[3]}-${minMatch[1].padStart(2, '0')}-${minMatch[2].padStart(2, '0')}` : ''; if (dateMaxInput) dateMaxInput.value = maxMatch ? `${maxMatch[3]}-${maxMatch[1].padStart(2, '0')}-${maxMatch[2].padStart(2, '0')}` : ''; } else { if (dateMinInput) dateMinInput.value = ''; if (dateMaxInput) dateMaxInput.value = ''; } if (dateMinInput && dateMaxInput && errorMsgElement && applyButton) { const isValid = _validateDateInputs(dateMinInput, dateMaxInput, errorMsgElement); applyButton.disabled = !isValid; } }

    function _initializeSiteSearchState(currentQuery){
        const siteSearchSectionContent = sidebar?.querySelector('#sidebar-section-site-search .'+CSS.SECTION_CONTENT);
        if (!siteSearchSectionContent) return;

        const clearSiteOptDiv = siteSearchSectionContent.querySelector(`#${IDS.CLEAR_SITE_SEARCH_OPTION}`);
        const listElement = siteSearchSectionContent.querySelector('ul.' + CSS.CUSTOM_LIST);
        const currentSettings = SettingsManager.getCurrentSettings();
        const checkboxModeEnabled = currentSettings.enableSiteSearchCheckboxMode;

        // Reset all selections first
        siteSearchSectionContent.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}, label.${CSS.SELECTED}`).forEach(opt => opt.classList.remove(CSS.SELECTED));
        if (checkboxModeEnabled && listElement) {
            listElement.querySelectorAll(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => cb.checked = false);
        }

        // Try to match (site:A OR site:B) pattern
        const siteMatchOr = currentQuery.match(/\(\s*(site:[\w.:()-]+(?:\s+OR\s+site:[\w.:()-]+)*)\s*\)/i);
        let activeSiteUrlsFromQuery = [];

        if (siteMatchOr && siteMatchOr[1]) {
            const innerQuery = siteMatchOr[1];
            const individualSiteMatches = [...innerQuery.matchAll(/site:([\w.:()-]+)/gi)];
            activeSiteUrlsFromQuery = individualSiteMatches.map(match => match[1].toLowerCase());
        } else {
            // Try to match single site: pattern if no OR group found
            const siteMatchSimple = currentQuery.match(/(?<!\S)site:([\w.:()-]+)(?!\S)/i); // Ensure it's not part of another word
            if (siteMatchSimple && siteMatchSimple[1]) {
                activeSiteUrlsFromQuery.push(siteMatchSimple[1].toLowerCase());
            }
        }
        activeSiteUrlsFromQuery.sort(); // For consistent comparison

        if (activeSiteUrlsFromQuery.length > 0) {
            if(clearSiteOptDiv) clearSiteOptDiv.classList.remove(CSS.SELECTED);
            let customOptionFullyMatched = false;

            if (listElement) {
                const customSiteOptions = Array.from(listElement.querySelectorAll(checkboxModeEnabled ? 'label' : `div.${CSS.FILTER_OPTION}`));
                for (const optElement of customSiteOptions) {
                    const customSiteValue = optElement.dataset[DATA_ATTR.SITE_URL]; // This contains the "url" from favoriteSites
                    if (!customSiteValue) continue;

                    // Parse the site value from the option (it might also be an "OR" group)
                    const definedCustomSites = Utils.parseCombinedValue(customSiteValue).map(s => s.toLowerCase()).sort();

                    // Check if the sites from URL exactly match the sites defined in this custom option
                    if (definedCustomSites.length > 0 && definedCustomSites.length === activeSiteUrlsFromQuery.length && definedCustomSites.every((val, index) => val === activeSiteUrlsFromQuery[index])) {
                        if (checkboxModeEnabled) {
                            const checkbox = listElement.querySelector(`input[type="checkbox"][value="${customSiteValue}"]`);
                            if (checkbox) checkbox.checked = true;
                        }
                        optElement.classList.add(CSS.SELECTED);
                        customOptionFullyMatched = true;
                        break; // Found a full match for the custom OR group
                    }
                }
            }

            // If no custom OR option fully matched, but URL has sites, select individual checkboxes/options if applicable
            // This handles cases where URL might have site:foo.com OR site:bar.com but no single custom option matches this exact combo.
            if (!customOptionFullyMatched && listElement) {
                activeSiteUrlsFromQuery.forEach(url => {
                    if (checkboxModeEnabled) {
                        const checkbox = listElement.querySelector(`input[type="checkbox"].${CSS.SITE_SEARCH_ITEM_CHECKBOX}[value="${url}"]`);
                        if (checkbox) { checkbox.checked = true; const label = listElement.querySelector(`label[for="${checkbox.id}"]`); if(label) label.classList.add(CSS.SELECTED); }
                    } else {
                        // This case is less likely if a combined query was in URL, but handle for single site from URL
                        const option = listElement.querySelector(`.${CSS.FILTER_OPTION}[data-${DATA_ATTR.SITE_URL}="${url}"]`);
                        if (option) option.classList.add(CSS.SELECTED);
                    }
                });
            }
        } else {
            // No site search in URL, select "Any Site"
            if (clearSiteOptDiv) clearSiteOptDiv.classList.add(CSS.SELECTED);
        }

        if (checkboxModeEnabled) {
            _updateApplySitesButtonState(siteSearchSectionContent);
        }
    }

    function _initializeFiletypeSearchState(currentQuery, asFiletypeParam) {
        const filetypeSectionContent = sidebar?.querySelector('#sidebar-section-filetype .'+CSS.SECTION_CONTENT);
        if (!filetypeSectionContent) return;

        const clearFiletypeOptDiv = filetypeSectionContent.querySelector(`#${IDS.CLEAR_FILETYPE_SEARCH_OPTION}`);
        const listElement = filetypeSectionContent.querySelector('ul.' + CSS.CUSTOM_LIST);
        const currentSettings = SettingsManager.getCurrentSettings();
        const checkboxModeEnabled = currentSettings.enableFiletypeCheckboxMode;

        // Reset all selections first
        filetypeSectionContent.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}, label.${CSS.SELECTED}`).forEach(opt => opt.classList.remove(CSS.SELECTED));
        if (checkboxModeEnabled && listElement) {
            listElement.querySelectorAll(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}`).forEach(cb => cb.checked = false);
        }

        let activeFiletypesFromQuery = [];
        // Try to match (filetype:A OR filetype:B) pattern in 'q'
        const filetypeMatchOr = currentQuery.match(/\(\s*(filetype:[\w.:()-]+(?:\s+OR\s+filetype:[\w.:()-]+)*)\s*\)/i);

        if (filetypeMatchOr && filetypeMatchOr[1]) {
            const innerQuery = filetypeMatchOr[1];
            const individualFiletypeMatches = [...innerQuery.matchAll(/filetype:([\w.:()-]+)/gi)];
            activeFiletypesFromQuery = individualFiletypeMatches.map(match => match[1].toLowerCase());
        } else {
            // Try to match single filetype: pattern in 'q'
            const filetypeMatchSimpleSingle = currentQuery.match(/(?<!\S)filetype:([\w.:()-]+)(?!\S)/i);
            if (filetypeMatchSimpleSingle && filetypeMatchSimpleSingle[1]) {
                activeFiletypesFromQuery.push(filetypeMatchSimpleSingle[1].toLowerCase());
            } else if (asFiletypeParam && !currentQuery.includes('filetype:')) { // Only use as_filetype if q doesn't have filetype
                // as_filetype can also contain OR, but it's less common with direct Google UI.
                // We treat it as a single value or a direct "OR" string here.
                activeFiletypesFromQuery = Utils.parseCombinedValue(asFiletypeParam).map(ft => ft.toLowerCase());
            }
        }
        activeFiletypesFromQuery.sort(); // For consistent comparison

        if (activeFiletypesFromQuery.length > 0) {
            if(clearFiletypeOptDiv) clearFiletypeOptDiv.classList.remove(CSS.SELECTED);
            let customOptionFullyMatched = false;

            if (listElement) {
                const customFiletypeOptions = Array.from(listElement.querySelectorAll(checkboxModeEnabled ? 'label' : `div.${CSS.FILTER_OPTION}`));
                for (const optElement of customFiletypeOptions) {
                    const customFtValueAttr = checkboxModeEnabled ? optElement.dataset[DATA_ATTR.FILETYPE_VALUE] : optElement.dataset[DATA_ATTR.FILTER_VALUE];
                    if (!customFtValueAttr) continue;

                    const definedCustomFiletypes = Utils.parseCombinedValue(customFtValueAttr).map(s => s.toLowerCase()).sort();

                    // Exact match for combined types
                     if (definedCustomFiletypes.length > 0 && definedCustomFiletypes.length === activeFiletypesFromQuery.length && definedCustomFiletypes.every((val, index) => val === activeFiletypesFromQuery[index])) {
                        if (checkboxModeEnabled) {
                            const checkbox = listElement.querySelector(`input[type="checkbox"][value="${customFtValueAttr}"]`);
                            if (checkbox) checkbox.checked = true;
                        }
                        optElement.classList.add(CSS.SELECTED);
                        customOptionFullyMatched = true;
                        break;
                    }
                }
            }

            // If no custom option fully matched, select individual items
            if (!customOptionFullyMatched && listElement) {
                activeFiletypesFromQuery.forEach(ft => {
                    if (checkboxModeEnabled) {
                        const checkbox = listElement.querySelector(`input[type="checkbox"].${CSS.FILETYPE_SEARCH_ITEM_CHECKBOX}[value="${ft}"]`);
                        if (checkbox) { checkbox.checked = true; const label = listElement.querySelector(`label[for="${checkbox.id}"]`); if(label) label.classList.add(CSS.SELECTED); }
                    } else {
                        const option = listElement.querySelector(`.${CSS.FILTER_OPTION}[data-${DATA_ATTR.FILTER_VALUE}="${ft}"]`);
                        if (option) option.classList.add(CSS.SELECTED);
                    }
                });
            }
        } else {
             // No filetype search in URL, select "Any Format"
             if (clearFiletypeOptDiv) clearFiletypeOptDiv.classList.add(CSS.SELECTED);
        }
        if (checkboxModeEnabled) {
            _updateApplyFiletypesButtonState(filetypeSectionContent);
        }
    }

    // --- Event Binding for Sidebar ---
    function bindSidebarEvents() { if (!sidebar) return; const collapseButton = sidebar.querySelector(`#${IDS.COLLAPSE_BUTTON}`); const settingsButton = sidebar.querySelector(`#${IDS.SETTINGS_BUTTON}`); if (collapseButton) collapseButton.title = _('sidebar_collapse_title'); if (settingsButton) settingsButton.title = _('sidebar_settings_title'); sidebar.addEventListener('click', (e) => { const settingsBtnTarget = e.target.closest(`#${IDS.SETTINGS_BUTTON}`); if (settingsBtnTarget) { SettingsManager.show(); return; } const collapseBtnTarget = e.target.closest(`#${IDS.COLLAPSE_BUTTON}`); if (collapseBtnTarget) { toggleSidebarCollapse(); return; } const sectionTitleTarget = e.target.closest(`.${CSS.SIDEBAR_CONTENT_WRAPPER} .${CSS.SECTION_TITLE}`); if (sectionTitleTarget && !sidebar.classList.contains(CSS.SIDEBAR_COLLAPSED)) { handleSectionCollapse(e); return; } }); }
    // --- Sidebar and Section Collapse Logic ---
    function toggleSidebarCollapse() { const cs = SettingsManager.getCurrentSettings(); cs.sidebarCollapsed = !cs.sidebarCollapsed; applySettings(cs); SettingsManager.save('Sidebar Collapse');}
    function handleSectionCollapse(event) { const title = event.target.closest(`.${CSS.SECTION_TITLE}`); if (!title || sidebar?.classList.contains(CSS.SIDEBAR_COLLAPSED) || title.closest(`#${IDS.FIXED_TOP_BUTTONS}`)) return; const section = title.closest(`.${CSS.SIDEBAR_SECTION}`); if (!section) return; const content = section.querySelector(`.${CSS.SECTION_CONTENT}`); const sectionId = section.id; if (!content || !sectionId) return; const currentSettings = SettingsManager.getCurrentSettings(); const isCurrentlyCollapsed = content.classList.contains(CSS.COLLAPSED); const shouldBeCollapsedAfterClick = !isCurrentlyCollapsed; let overallStateChanged = false; if (currentSettings.accordionMode && !shouldBeCollapsedAfterClick) { const sectionsContainer = section.parentElement; if (_applyAccordionEffectToSections(sectionId, sectionsContainer, currentSettings)) overallStateChanged = true; } if (_toggleSectionVisualState(section, title, content, sectionId, shouldBeCollapsedAfterClick, currentSettings)) overallStateChanged = true; if (overallStateChanged && currentSettings.sectionDisplayMode === 'remember') { debouncedSaveSettings('Section Collapse/Accordion'); } }
    function _applyAccordionEffectToSections(clickedSectionId, allSectionsContainer, currentSettings) { let stateChangedForAccordion = false; allSectionsContainer?.querySelectorAll(`.${CSS.SIDEBAR_SECTION}`)?.forEach(otherSection => { if (otherSection.id !== clickedSectionId) { const otherContent = otherSection.querySelector(`.${CSS.SECTION_CONTENT}`); const otherTitle = otherSection.querySelector(`.${CSS.SECTION_TITLE}`); if (otherContent && !otherContent.classList.contains(CSS.COLLAPSED)) { otherContent.classList.add(CSS.COLLAPSED); otherTitle?.classList.add(CSS.COLLAPSED); if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; if (currentSettings.sectionStates[otherSection.id] !== true) { currentSettings.sectionStates[otherSection.id] = true; stateChangedForAccordion = true; } } } } }); return stateChangedForAccordion; }
    function _toggleSectionVisualState(sectionEl, titleEl, contentEl, sectionId, newCollapsedState, currentSettings) { let sectionStateActuallyChanged = false; const isCurrentlyCollapsed = contentEl.classList.contains(CSS.COLLAPSED); if (isCurrentlyCollapsed !== newCollapsedState) { contentEl.classList.toggle(CSS.COLLAPSED, newCollapsedState); titleEl.classList.toggle(CSS.COLLAPSED, newCollapsedState); sectionStateActuallyChanged = true; } if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; if (currentSettings.sectionStates[sectionId] !== newCollapsedState) { currentSettings.sectionStates[sectionId] = newCollapsedState; if (!sectionStateActuallyChanged) sectionStateActuallyChanged = true; /* Mark changed even if visual was already matching if state obj differs */ } } return sectionStateActuallyChanged; }

    // --- Initialization ---
    function initializeScript() {
        console.log(LOG_PREFIX + " Initializing script...");
        debouncedSaveSettings = Utils.debounce(() => SettingsManager.save('Debounced Save'), 800);
        try {
            addGlobalStyles(); NotificationManager.init(); LocalizationService.initializeBaseLocale();
            SettingsManager.initialize( defaultSettings, applySettings, buildSidebarUI, applySectionCollapseStates, _initMenuCommands, renderSectionOrderList );
            setupSystemThemeListener(); buildSidebarSkeleton();
            DragManager.init( sidebar, sidebar.querySelector(`.${CSS.DRAG_HANDLE}`), SettingsManager, debouncedSaveSettings );
            const initialSettings = SettingsManager.getCurrentSettings();
            DragManager.setDraggable(initialSettings.draggableHandleEnabled, sidebar, sidebar.querySelector(`.${CSS.DRAG_HANDLE}`));
            applySettings(initialSettings); buildSidebarUI(); bindSidebarEvents(); _initMenuCommands();
            console.log(`${LOG_PREFIX} Script initialization complete. Final effective locale: ${LocalizationService.getCurrentLocale()}`);
        } catch (error) {
            console.error(`${LOG_PREFIX} [initializeScript] CRITICAL ERROR DURING INITIALIZATION:`, error, error.stack);
            const scriptNameForAlert = (typeof _ === 'function' && _('scriptName') && !(_('scriptName').startsWith('[ERR:'))) ? _('scriptName') : SCRIPT_INTERNAL_NAME;
            // Try to use NotificationManager if available, otherwise alert
            if (typeof NotificationManager !== 'undefined' && NotificationManager.show) { NotificationManager.show('alert_init_fail', { scriptName: scriptNameForAlert, error: error.message }, 'error', 0); }
            else { _showGlobalMessage('alert_init_fail', { scriptName: scriptNameForAlert, error: error.message }, 'error', 0); } // Fallback
            // Attempt to clean up UI elements if init fails badly
            if(sidebar && sidebar.remove) sidebar.remove(); const settingsOverlayEl = document.getElementById(IDS.SETTINGS_OVERLAY); if(settingsOverlayEl) settingsOverlayEl.remove(); ModalManager.hide();
        }
    }

    // --- Script Entry Point (Dependency Loading Logic) ---
    if (document.getElementById(IDS.SIDEBAR)) { console.warn(`${LOG_PREFIX} Sidebar with ID "${IDS.SIDEBAR}" already exists. Skipping initialization.`); return; }
    const dependenciesReady = { styles: false, i18n: false }; let initializationAttempted = false; let timeoutFallback;
    function checkDependenciesAndInitialize() { if (initializationAttempted) return; if (dependenciesReady.styles && dependenciesReady.i18n) { console.log(`${LOG_PREFIX} All dependencies ready. Initializing script.`); clearTimeout(timeoutFallback); initializationAttempted = true; if (document.readyState === 'complete' || document.readyState === 'interactive' || document.readyState === 'loaded') { initializeScript(); } else { window.addEventListener('DOMContentLoaded', initializeScript, { once: true }); } } }
    document.addEventListener('gscsStylesLoaded', function stylesLoadedHandler() { console.log(`${LOG_PREFIX} Event "gscsStylesLoaded" received.`); dependenciesReady.styles = true; checkDependenciesAndInitialize(); }, { once: true });
    document.addEventListener('gscsi18nLoaded', function i18nLoadedHandler() { console.log(`${LOG_PREFIX} Event "gscsi18nLoaded" received.`); dependenciesReady.i18n = true; checkDependenciesAndInitialize(); }, { once: true });
    timeoutFallback = setTimeout(() => { if (initializationAttempted) return; console.log(`${LOG_PREFIX} Fallback: Checking dependencies after timeout.`); if (typeof window.GSCS_Namespace !== 'undefined') { if (typeof window.GSCS_Namespace.stylesText === 'string' && window.GSCS_Namespace.stylesText.trim() !== '' && !dependenciesReady.styles) { console.log(`${LOG_PREFIX} Fallback: Styles found via namespace.`); dependenciesReady.styles = true; } if (typeof window.GSCS_Namespace.i18nPack === 'object' && Object.keys(window.GSCS_Namespace.i18nPack.translations || {}).length > 0 && !dependenciesReady.i18n) { console.log(`${LOG_PREFIX} Fallback: i18n pack found via namespace.`); dependenciesReady.i18n = true; } } if (dependenciesReady.styles && dependenciesReady.i18n) { checkDependenciesAndInitialize(); } else { console.error(`${LOG_PREFIX} Fallback: Dependencies still not fully loaded after timeout. Styles: ${dependenciesReady.styles}, i18n: ${dependenciesReady.i18n}.`); if (!initializationAttempted) { console.warn(`${LOG_PREFIX} Attempting to initialize with potentially incomplete dependencies due to fallback timeout.`); if (!dependenciesReady.styles) { console.warn(`${LOG_PREFIX} Styles dependency forced true in fallback.`); dependenciesReady.styles = true; } if (!dependenciesReady.i18n) { console.warn(`${LOG_PREFIX} i18n dependency forced true in fallback.`); dependenciesReady.i18n = true; } checkDependenciesAndInitialize(); } } }, 2000);
    // Additional check for cases where scripts load very fast and events might be missed if this script's DOMContentLoaded is later
    if (document.readyState === 'complete' || document.readyState === 'interactive' || document.readyState === 'loaded') {
        if (typeof window.GSCS_Namespace !== 'undefined') {
            if (typeof window.GSCS_Namespace.stylesText === 'string' && window.GSCS_Namespace.stylesText.trim() !== '' && !dependenciesReady.styles) {
                // console.log(`${LOG_PREFIX} Post-load check: Styles found via namespace.`);
                dependenciesReady.styles = true;
            }
            if (typeof window.GSCS_Namespace.i18nPack === 'object' && Object.keys(window.GSCS_Namespace.i18nPack.translations || {}).length > 0 && !dependenciesReady.i18n) {
                // console.log(`${LOG_PREFIX} Post-load check: i18n pack found via namespace.`);
                dependenciesReady.i18n = true;
            }
        }
        if (dependenciesReady.styles && dependenciesReady.i18n && !initializationAttempted) {
            // console.log(`${LOG_PREFIX} Post-load check: All dependencies ready. Initializing script.`);
            checkDependenciesAndInitialize();
        }
    }
})();