[NDiiong] Bypass Medium

Support for viewing paid articles for medium.com, with auto-redirect option and clear data function.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         [NDiiong] Bypass Medium
// @namespace    medium.com
// @version      1.8.1
// @description  Support for viewing paid articles for medium.com, with auto-redirect option and clear data function.
// @author       NDiiong
// @license      GPLv3
// @match        *://medium.com/*
// @match        *://*.medium.com/*
// @match        *://*.adelaidenow.com.au/*
// @match        *://*.adweek.com/*
// @match        *://*.afr.com/*
// @match        *://*.plainenglish.io/*
// @match        *://*.ambito/*
// @match        *://*.ampproject.org/*
// @match        *://*.baltimoresun.com/*
// @match        *://*.barrons.com/*
// @match        *://*.bizjournals.com/*
// @match        *://*.bloomberg.com/*
// @match        *://*.bloombergquint.com/*
// @match        *://*.bostonglobe.com/*
// @match        *://*.brisbanetimes.com.au/*
// @match        *://*.britannica.com/*
// @match        *://*.businessinsider.com/*
// @match        *://*.caixinglobal.com/*
// @match        *://*.cen.acs.org/*
// @match        *://*.centralwesterndaily.com.au/*
// @match        *://*.chicagobusiness.com/*
// @match        *://*.chicagotribune.com/*
// @match        *://*.corriere.it/*
// @match        *://*.courant.com/*
// @match        *://*.couriermail.com.au/*
// @match        *://*.dailypress.com/*
// @match        *://*.dailytelegraph.com.au/*
// @match        *://*.delfi.ee/*
// @match        *://*.demorgen.be/*
// @match        *://*.denverpost.com/*
// @match        *://*.df.cl/*
// @match        *://*.dynamed.com/*
// @match        *://*.economist.com/*
// @match        *://*.elmercurio.com/*
// @match        *://*.elmundo.es/*
// @match        *://*.elu24.ee/*
// @match        *://*.entreprenal.com/*
// @match        *://*.examiner.com.au/*
// @match        *://*.expansion.com/*
// @match        *://*.fd.nl/*
// @match        *://*.financialpost.com/*
// @match        *://*.fnlondon.com/*
// @match        *://*.foreignpolicy.com/*
// @match        *://*.fortune.com/*
// @match        *://*.ft.com/*
// @match        *://*.gelocal.it/*
// @match        *://*.genomeweb.com/*
// @match        *://*.glassdoor.com/*
// @match        *://*.globes.co.il/*
// @match        *://*.groene.nl/*
// @match        *://*.haaretz.co.il/*
// @match        *://*.haaretz.com/*
// @match        *://*.harpers.org/*
// @match        *://*.hbr.org/*
// @match        *://*.hbrchina.org/*
// @match        *://*.heraldsun.com.au/*
// @match        *://*.historyextra.com/*
// @match        *://*.humo.be/*
// @match        *://*.ilmanifesto.it/*
// @match        *://*.inc.com/*
// @match        *://*.inquirer.com/*
// @match        *://*.interest.co.nz/*
// @match        *://*.investorschronicle.co.uk/*
// @match        *://*.irishtimes.com/*
// @match        *://*.japantimes.co.jp/*
// @match        *://*.journalnow.com/*
// @match        *://*.kansascity.com/*
// @match        *://*.labusinessjournal.com/*
// @match        *://*.lanacion.com.ar/*
// @match        *://*.lastampa.it/*
// @match        *://*.latercera.com/*
// @match        *://*.latimes.com/*
// @match        *://*.lavoixdunord.fr/*
// @match        *://*.lecho.be/*
// @match        *://*.leparisien.fr/*
// @match        *://*.lesechos.fr/*
// @match        *://*.loebclassics.com/*
// @match        *://*.lrb.co.uk/*
// @match        *://*.mcall.com/*
// @match        *://*.medium.com/*
// @match        *://*.medscape.com/*
// @match        *://*.mercurynews.com/*
// @match        *://*.mv-voice.com/*
// @match        *://*.nationalgeographic.com
// @match        *://*.nationalpost.com/*
// @match        *://*.netdna-ssl.com/*
// @match        *://*.news-gazette.com/*
// @match        *://*.newstatesman.com/*
// @match        *://*.newyorker.com/*
// @match        *://*.nrc.nl/*
// @match        *://*.ntnews.com.au/*
// @match        *://*.nydailynews.com/*
// @match        *://*.nymag.com/*
// @match        *://*.nytimes.com/*
// @match        *://*.nzherald.co.nz/*
// @match        *://*.nzz.ch/*
// @match        *://*.ocregister.com/*
// @match        *://*.orlandosentinel.com/*
// @match        *://*.outbrain.com/*
// @match        *://*.paloaltoonline.com/*
// @match        *://*.parool.nl/*
// @match        *://*.piano.io/*
// @match        *://*.poool.fr/*
// @match        *://*.postimees.ee/*
// @match        *://*.qiota.com/*
// @match        *://*.quora.com/*
// @match        *://*.qz.com/*
// @match        *://*.repubblica.it/*
// @match        *://*.republic.ru/*
// @match        *://*.reuters.com/*
// @match        *://*.sandiegouniontribune.com/*
// @match        *://*.scientificamerican.com/*
// @match        *://*.scmp.com/*
// @match        *://*.seattletimes.com/*
// @match        *://*.seekingalpha.com/*
// @match        *://*.slate.com/*
// @match        *://*.smh.com.au/*
// @match        *://*.sofrep.com/*
// @match        *://*.spectator.co.uk/*
// @match        *://*.spectator.com.au/*
// @match        *://*.spectator.us/*
// @match        *://*.speld.nl/
// @match        *://*.startribune.com/*
// @match        *://*.statista.com/*
// @match        *://*.stuff.co.nz/*
// @match        *://*.sueddeutsche.de/*
// @match        *://*.sun-sentinel.com/*
// @match        *://*.techinasia.com/*
// @match        *://*.technologyreview.com/*
// @match        *://*.telegraaf.nl/*
// @match        *://*.telegraph.co.uk/*
// @match        *://*.the-tls.co.uk/*
// @match        *://*.theadvocate.com.au/*
// @match        *://*.theage.com.au/*
// @match        *://*.theathletic.co.uk/*
// @match        *://*.theathletic.com/*
// @match        *://*.theatlantic.com/*
// @match        *://*.theaustralian.com.au/*
// @match        *://*.thediplomat.com/*
// @match        *://*.theglobeandmail.com/*
// @match        *://*.theherald.com.au/*
// @match        *://*.thehindu.com/*
// @match        *://*.themarker.com/*
// @match        *://*.themercury.com.au/*
// @match        *://*.thenation.com/*
// @match        *://*.thenational.scot/*
// @match        *://*.theolivepress.es/*
// @match        *://*.thesaturdaypaper.com.au/*
// @match        *://*.thestar.com/*
// @match        *://*.thewrap.com/*
// @match        *://*.tijd.be/*
// @match        *://*.time.com/*
// @match        *://*.tinypass.com/*
// @match        *://*.towardsdatascience.com/*
// @match        *://*.trouw.nl/*
// @match        *://*.vanityfair.com/*
// @match        *://*.vn.nl/*
// @match        *://*.volkskrant.nl/*
// @match        *://*.washingtonpost.com/*
// @match        *://*.wired.com/*
// @match        *://*.wsj.com/*
// @match        *://*.zeit.de/*
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==


(function () {
    'use strict';

    const DEFAULT_SOURCES = [
        { name: 'freedium-mirror.cfd', url: 'freedium-mirror.cfd', enabled: true },
        { name: 'freedium.cfd', url: 'freedium.cfd', enabled: true },
        { name: 'ReadMedium', url: 'readmedium.com', enabled: true },
        { name: 'Scribe', url: 'scribe.rip', enabled: true }
    ];

    const CONFIG = {
        VERSION: '1.8.1',
        STORAGE_KEY: 'medium_bypass_ndiiong',
        MIN_DRAG_THRESHOLD: 5,
        CACHE_DURATION: 5 * 60 * 1000,
        MAX_SOURCES: 20,
        QUICK_ACCESS_COUNT: 3
    };

    let isDropdownOpen = false;

    class StorageManager {
        static get(key, defaultValue = null) {
            try {
                const value = GM_getValue(key, defaultValue);
                return typeof value === 'string' ? JSON.parse(value) : value;
            } catch (e) {
                console.error(`Storage get error for ${key}:`, e);
                return defaultValue;
            }
        }

        static set(key, value) {
            try {
                const serialized = typeof value === 'string' ? value : JSON.stringify(value);
                GM_setValue(key, serialized);
                return true;
            } catch (e) {
                console.error(`Storage set error for ${key}:`, e);
                return false;
            }
        }

        static clearAll() {
            const keys = ['buttonPosition', 'unlockerSources', 'autoRedirectEnabled', 'compactMode',
                          'quickAccessOrder', 'lastUsedSource', 'stats', 'theme'];
            keys.forEach(key => GM_setValue(key, null));
            return true;
        }

        static getAllData() {
            return {
                buttonPosition: this.get('buttonPosition', { right: '20px', bottom: '20px' }),
                unlockerSources: this.get('unlockerSources', DEFAULT_SOURCES),
                autoRedirectEnabled: this.get('autoRedirectEnabled', false),
                compactMode: this.get('compactMode', false),
                quickAccessOrder: this.get('quickAccessOrder', []),
                lastUsedSource: this.get('lastUsedSource', null),
                stats: this.get('stats', { opens: 0, lastOpened: null }),
                theme: this.get('theme', 'light')
            };
        }
    }

    class StatsTracker {
        static recordOpen(sourceName = null) {
            const stats = StorageManager.get('stats', { opens: 0, lastOpened: null, sources: {} });
            stats.opens = (stats.opens || 0) + 1;
            stats.lastOpened = new Date().toISOString();

            if (sourceName) {
                stats.sources = stats.sources || {};
                stats.sources[sourceName] = (stats.sources[sourceName] || 0) + 1;

                StorageManager.set('lastUsedSource', sourceName);
            }

            StorageManager.set('stats', stats);
            return stats;
        }

        static getStats() {
            return StorageManager.get('stats', { opens: 0, lastOpened: null, sources: {} });
        }

        static getMostUsedSource() {
            const stats = this.getStats();
            if (!stats.sources || Object.keys(stats.sources).length === 0) return null;

            return Object.entries(stats.sources)
                .sort(([, a], [, b]) => b - a)[0][0];
        }
    }

    class ThemeManager {
        static getTheme() {
            return StorageManager.get('theme', 'light');
        }

        static setTheme(theme) {
            StorageManager.set('theme', theme);
            this.applyTheme(theme);
        }

        static applyTheme(theme) {
            const root = document.documentElement;
            const isDark = theme === 'dark';

            document.body.classList.toggle('medium-unlock-dark', isDark);

            if (isDark) {
                root.style.setProperty('--mu-bg', '#1a1a1a');
                root.style.setProperty('--mu-text', '#ffffff');
                root.style.setProperty('--mu-border', '#333');
                root.style.setProperty('--mu-card', '#2d2d2d');
                root.style.setProperty('--mu-primary', '#2ecc71');
                root.style.setProperty('--mu-secondary', '#7f8c8d');
                root.style.setProperty('--mu-danger', '#e74c3c');
                root.style.setProperty('--mu-warning', '#f39c12');
                root.style.setProperty('--mu-shadow', '0 4px 12px rgba(0,0,0,0.5)');
            } else {
                root.style.setProperty('--mu-bg', '#ffffff');
                root.style.setProperty('--mu-text', '#333333');
                root.style.setProperty('--mu-border', '#e0e0e0');
                root.style.setProperty('--mu-card', '#f9f9f9');
                root.style.setProperty('--mu-primary', '#1a8917');
                root.style.setProperty('--mu-secondary', '#666666');
                root.style.setProperty('--mu-danger', '#ff4444');
                root.style.setProperty('--mu-warning', '#ff9800');
                root.style.setProperty('--mu-shadow', '0 4px 12px rgba(0,0,0,0.15)');
            }
        }

        static toggleTheme() {
            const current = this.getTheme();
            const newTheme = current === 'light' ? 'dark' : 'light';
            this.setTheme(newTheme);
            return newTheme;
        }
    }

    class QuickAccessManager {
        static getOrder() {
            return StorageManager.get('quickAccessOrder', []);
        }

        static updateOrder(sourceName) {
            let order = this.getOrder();
            order = order.filter(name => name !== sourceName);
            order.unshift(sourceName);

            if (order.length > CONFIG.QUICK_ACCESS_COUNT) {
                order = order.slice(0, CONFIG.QUICK_ACCESS_COUNT);
            }

            StorageManager.set('quickAccessOrder', order);
            return order;
        }

        static getQuickAccessSources(sources) {
            const order = this.getOrder();
            const quickAccess = [];
            const remaining = [...sources];

            order.forEach(name => {
                const index = remaining.findIndex(s => s.name === name);
                if (index !== -1) {
                    quickAccess.push(remaining[index]);
                    remaining.splice(index, 1);
                }
            });

            return { quickAccess, remaining };
        }
    }

    function getButtonPosition() {
        return StorageManager.get('buttonPosition', { right: '20px', bottom: '20px' });
    }

    function saveButtonPosition(position) {
        StorageManager.set('buttonPosition', position);
    }

    function getSources() {
        return StorageManager.get('unlockerSources', DEFAULT_SOURCES);
    }

    function saveSources(sources) {
        StorageManager.set('unlockerSources', sources);
    }

    function getAutoRedirectState() {
        return StorageManager.get('autoRedirectEnabled', false);
    }

    function saveAutoRedirectState(enabled) {
        StorageManager.set('autoRedirectEnabled', enabled);
    }

    function getCompactMode() {
        return StorageManager.get('compactMode', false);
    }

    function saveCompactMode(enabled) {
        StorageManager.set('compactMode', enabled);
    }

    function clearAllData() {
        const confirmed = confirm(`⚠️ VERSION ${CONFIG.VERSION}\n\nThis will reset ALL settings to default:\n\n• Button position\n• Custom sources\n• Auto-redirect\n• Quick access order\n• Statistics\n• Theme preferences\n\nAre you sure?`);

        if (confirmed) {
            StorageManager.clearAll();

            const toast = showToast('All data cleared successfully!', 'success');
            setTimeout(() => {
                toast.remove();
                window.location.reload();
            }, 1500);
        }
    }

    function exportSettings() {
        const data = StorageManager.getAllData();
        data.exportDate = new Date().toISOString();
        data.version = CONFIG.VERSION;

        const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `medium-unlocker-backup-${new Date().toISOString().slice(0, 10)}.json`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);

        showToast('Settings exported successfully!', 'success');
    }

    function importSettings() {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = '.json';

        input.onchange = function (e) {
            const file = e.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = function (e) {
                try {
                    const data = JSON.parse(e.target.result);

                    if (!data.version || !data.unlockerSources) {
                        throw new Error('Invalid backup file');
                    }

                    const confirmed = confirm(`Import settings from ${data.exportDate || 'unknown date'}?\n\nVersion: ${data.version}\nSources: ${data.unlockerSources.length}\n\nThis will overwrite current settings.`);

                    if (confirmed) {
                        Object.keys(data).forEach(key => {
                            StorageManager.set(key, data[key]);
                        });

                        showToast('Settings imported successfully!', 'success');
                        setTimeout(() => window.location.reload(), 1000);
                    }
                } catch (error) {
                    showToast('Error importing settings: Invalid file format', 'error');
                }
            };
            reader.readAsText(file);
        };

        input.click();
    }

    function showToast(message, type = 'info') {
        const toast = document.createElement('div');
        toast.className = 'medium-unlock-toast';
        toast.textContent = message;

        const types = {
            success: '#2ecc71',
            error: '#e74c3c',
            warning: '#f39c12',
            info: '#3498db'
        };

        toast.style.cssText = `
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%) translateY(100px);
            background: ${types[type] || types.info};
            color: white;
            padding: 12px 24px;
            border-radius: 8px;
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
            font-size: 14px;
            font-weight: 500;
            z-index: 10001;
            opacity: 0;
            transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            pointer-events: none;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            max-width: 400px;
            text-align: center;
        `;

        document.body.appendChild(toast);

        requestAnimationFrame(() => {
            toast.style.opacity = '1';
            toast.style.transform = 'translateX(-50%) translateY(0)';
        });

        setTimeout(() => {
            toast.style.opacity = '0';
            toast.style.transform = 'translateX(-50%) translateY(-20px)';
            setTimeout(() => toast.remove(), 300);
        }, 3000);

        return toast;
    }

    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.className = 'medium-unlock-settings';
        panel.innerHTML = `
            <div class="settings-overlay"></div>
            <div class="settings-panel">
                <div class="settings-header">
                    <h3>⚙️ Medium Unlocker v${CONFIG.VERSION}</h3>
                    <button class="settings-close" title="Close">✕</button>
                </div>

                <div class="settings-tabs">
                    <button class="tab-btn active" data-tab="sources">Sources</button>
                    <button class="tab-btn" data-tab="appearance">Appearance</button>
                    <button class="tab-btn" data-tab="advanced">Advanced</button>
                    <button class="tab-btn" data-tab="stats">Statistics</button>
                </div>

                <div class="tab-content active" id="tab-sources">
                    <div class="setting-group">
                        <label class="toggle-switch">
                            <input type="checkbox" id="auto-redirect-checkbox">
                            <span class="toggle-slider"></span>
                            <span class="toggle-label">Auto-redirect to first enabled source</span>
                        </label>
                    </div>

                    <div class="setting-group">
                        <div class="section-header">
                            <h4>Unlocker Sources (${getSources().filter(s => s.enabled).length} enabled)</h4>
                            <span class="hint">Drag to reorder</span>
                        </div>
                        <div class="sources-list" id="sources-list"></div>

                        <div class="add-source-form">
                            <input type="text" id="new-source-name" placeholder="Source name" class="source-input">
                            <input type="text" id="new-source-url" placeholder="Domain (example.com)" class="source-input">
                            <button id="add-source-btn" class="btn-primary">+ Add</button>
                        </div>
                    </div>
                </div>

                <div class="tab-content" id="tab-appearance">
                    <div class="setting-group">
                        <label class="toggle-switch">
                            <input type="checkbox" id="compact-mode-checkbox">
                            <span class="toggle-slider"></span>
                            <span class="toggle-label">Compact button mode</span>
                        </label>
                    </div>

                    <div class="setting-group">
                        <h4>Theme</h4>
                        <div class="theme-selector">
                            <button class="theme-option active" data-theme="light">
                                <div class="theme-preview light"></div>
                                <span>Light</span>
                            </button>
                            <button class="theme-option" data-theme="dark">
                                <div class="theme-preview dark"></div>
                                <span>Dark</span>
                            </button>
                            <button class="theme-option" data-theme="auto">
                                <div class="theme-preview auto"></div>
                                <span>Auto</span>
                            </button>
                        </div>
                    </div>

                    <div class="setting-group">
                        <h4>Quick Access</h4>
                        <p class="setting-description">Most used sources appear first (max ${CONFIG.QUICK_ACCESS_COUNT})</p>
                    </div>
                </div>

                <div class="tab-content" id="tab-advanced">
                    <div class="setting-group">
                        <h4>Backup & Restore</h4>
                        <div class="button-group">
                            <button id="export-btn" class="btn-secondary">📤 Export Settings</button>
                            <button id="import-btn" class="btn-secondary">📥 Import Settings</button>
                        </div>
                    </div>

                    <div class="setting-group">
                        <h4>Danger Zone</h4>
                        <button id="clear-data-btn" class="btn-danger">🗑️ Clear All Data</button>
                        <p class="warning-text">This will reset all settings to default values</p>
                    </div>
                </div>

                <div class="tab-content" id="tab-stats">
                    <div class="stats-grid">
                        <div class="stat-card">
                            <div class="stat-value" id="total-opens">0</div>
                            <div class="stat-label">Total Opens</div>
                        </div>
                        <div class="stat-card">
                            <div class="stat-value" id="last-used">Never</div>
                            <div class="stat-label">Last Used</div>
                        </div>
                        <div class="stat-card">
                            <div class="stat-value" id="top-source">None</div>
                            <div class="stat-label">Top Source</div>
                        </div>
                    </div>

                    <div class="setting-group">
                        <h4>Source Usage</h4>
                        <div id="sources-stats"></div>
                    </div>
                </div>

                <div class="settings-footer">
                    <button id="save-settings-btn" class="btn-primary">Save Settings</button>
                </div>
            </div>
        `;

        document.body.appendChild(panel);
        injectStyles();
        ThemeManager.applyTheme(ThemeManager.getTheme());
        setupEventListeners();
        updateSourcesList(getSources());
        updateStatsDisplay();
    }

    function injectStyles() {
        const style = document.createElement('style');
        style.textContent = `
            :root {
                --mu-primary: #1a8917;
                --mu-primary-dark: #147811;
                --mu-secondary: #666;
                --mu-danger: #ff4444;
                --mu-warning: #ff9800;
                --mu-success: #2ecc71;
                --mu-bg: #fff;
                --mu-text: #333;
                --mu-border: #e0e0e0;
                --mu-card: #f9f9f9;
                --mu-shadow: 0 4px 12px rgba(0,0,0,0.15);
            }

            .medium-unlock-dark {
                --mu-bg: #1a1a1a;
                --mu-text: #fff;
                --mu-border: #333;
                --mu-card: #2d2d2d;
                --mu-primary: #2ecc71;
                --mu-secondary: #7f8c8d;
            }
            .settings-panel {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            }

            .settings-header h3 {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 600;
            }

            .tab-btn {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 500;
            }

            .setting-group h4 {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 600;
            }

            .toggle-label {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            }

            .source-name {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 500;
            }

            .btn-primary, .btn-secondary, .btn-danger {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 500;
            }

            .section-title {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            }

            .dropdown-item {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            }
            .settings-overlay {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0,0,0,0.5);
                z-index: 9998;
                display: none;
            }

            .settings-panel {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: var(--mu-bg);
                border-radius: 16px;
                box-shadow: var(--mu-shadow);
                z-index: 9999;
                width: 90%;
                max-width: 500px;
                max-height: 90vh;
                display: flex;
                flex-direction: column;
                display: none;
            }

            .settings-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 20px;
                border-bottom: 1px solid var(--mu-border);
            }

            .settings-header h3 {
                margin: 0;
                color: var(--mu-text);
                font-size: 18px;
            }

            .settings-close {
                background: none;
                border: none;
                font-size: 20px;
                cursor: pointer;
                color: var(--mu-text);
                opacity: 0.6;
                transition: opacity 0.2s;
            }

            .settings-close:hover {
                opacity: 1;
            }

            .settings-tabs {
                display: flex;
                padding: 0 20px;
                border-bottom: 1px solid var(--mu-border);
                gap: 4px;
            }

            .tab-btn {
                flex: 1;
                padding: 12px;
                background: none;
                border: none;
                border-bottom: 3px solid transparent;
                cursor: pointer;
                color: var(--mu-text);
                font-weight: 500;
                transition: all 0.2s;
            }

            .tab-btn.active {
                border-bottom-color: var(--mu-primary);
                color: var(--mu-primary);
            }

            .tab-content {
                flex: 1;
                overflow-y: auto;
                padding: 20px;
                display: none;
            }

            .tab-content.active {
                display: block;
            }

            .setting-group {
                margin-bottom: 24px;
            }

            .setting-group h4 {
                margin: 0 0 12px 0;
                color: var(--mu-text);
                font-size: 15px;
            }

            .toggle-switch {
                display: flex;
                align-items: center;
                cursor: pointer;
                gap: 12px;
            }

            .toggle-switch input {
                display: none;
            }

            .toggle-slider {
                width: 50px;
                height: 26px;
                background: var(--mu-border);
                border-radius: 13px;
                position: relative;
                transition: background 0.3s;
            }

            .toggle-slider:before {
                content: '';
                position: absolute;
                width: 22px;
                height: 22px;
                border-radius: 50%;
                background: white;
                top: 2px;
                left: 2px;
                transition: transform 0.3s;
                box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            }

            input:checked + .toggle-slider {
                background: var(--mu-primary);
            }

            input:checked + .toggle-slider:before {
                transform: translateX(24px);
            }

            .toggle-label {
                color: var(--mu-text);
                font-size: 14px;
            }

            .sources-list {
                margin: 16px 0;
                max-height: 300px;
                overflow-y: auto;
                border: 1px solid var(--mu-border);
                border-radius: 8px;
            }

            .source-item {
                display: flex;
                align-items: center;
                padding: 12px;
                border-bottom: 1px solid var(--mu-border);
                background: var(--mu-card);
                cursor: move;
                transition: background 0.2s;
            }

            .source-item:last-child {
                border-bottom: none;
            }

            .source-item:hover {
                background: color-mix(in srgb, var(--mu-primary) 10%, transparent);
            }

            .source-item.dragging {
                opacity: 0.5;
                background: color-mix(in srgb, var(--mu-primary) 20%, transparent);
            }

            .source-info {
                flex: 1;
                min-width: 0;
            }

            .source-name {
                font-weight: 500;
                color: var(--mu-text);
                margin-bottom: 2px;
                font-size: 14px;
            }

            .source-url {
                color: var(--mu-secondary);
                font-size: 12px;
                font-family: monospace;
                word-break: break-all;
            }

            .source-actions {
                display: flex;
                gap: 8px;
            }

            .btn-icon {
                width: 32px;
                height: 32px;
                border-radius: 6px;
                border: 1px solid var(--mu-border);
                background: var(--mu-bg);
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.2s;
            }

            .btn-icon:hover {
                border-color: var(--mu-primary);
                color: var(--mu-primary);
            }

            .btn-icon.delete:hover {
                border-color: var(--mu-danger);
                color: var(--mu-danger);
                background: color-mix(in srgb, var(--mu-danger) 10%, transparent);
            }

            .add-source-form {
                display: grid;
                grid-template-columns: 1fr 1fr auto;
                gap: 8px;
                margin-top: 16px;
            }

            .source-input {
                padding: 10px;
                border: 1px solid var(--mu-border);
                border-radius: 6px;
                background: var(--mu-bg);
                color: var(--mu-text);
                font-size: 14px;
            }

            .source-input:focus {
                outline: none;
                border-color: var(--mu-primary);
            }

            .btn-primary {
                background: var(--mu-primary);
                color: white;
                border: none;
                padding: 10px 20px;
                border-radius: 6px;
                cursor: pointer;
                font-weight: 500;
                transition: background 0.2s;
            }

            .btn-primary:hover {
                background: var(--mu-primary-dark);
            }

            .btn-secondary {
                background: var(--mu-card);
                color: var(--mu-text);
                border: 1px solid var(--mu-border);
                padding: 10px 20px;
                border-radius: 6px;
                cursor: pointer;
                font-weight: 500;
                transition: all 0.2s;
            }

            .btn-secondary:hover {
                border-color: var(--mu-primary);
                color: var(--mu-primary);
            }

            .btn-danger {
                background: var(--mu-danger);
                color: white;
                border: none;
                padding: 12px 24px;
                border-radius: 6px;
                cursor: pointer;
                font-weight: 500;
                transition: all 0.2s;
                width: 100%;
            }

            .btn-danger:hover {
                background: #cc0000;
                transform: translateY(-2px);
                box-shadow: 0 4px 8px rgba(255, 68, 68, 0.3);
            }

            .theme-selector {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 12px;
                margin-top: 12px;
            }

            .theme-option {
                background: var(--mu-card);
                border: 2px solid var(--mu-border);
                border-radius: 8px;
                padding: 12px;
                cursor: pointer;
                transition: all 0.2s;
                text-align: center;
            }

            .theme-option.active {
                border-color: var(--mu-primary);
                background: color-mix(in srgb, var(--mu-primary) 10%, transparent);
            }

            .theme-preview {
                width: 40px;
                height: 40px;
                border-radius: 6px;
                margin: 0 auto 8px;
            }

            .theme-preview.light {
                background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%);
                border: 1px solid #e0e0e0;
            }

            .theme-preview.dark {
                background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
                border: 1px solid #333;
            }

            .theme-preview.auto {
                background: linear-gradient(135deg, #ffffff 0%, #1a1a1a 100%);
                border: 1px solid #666;
            }

            .stats-grid {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 12px;
                margin-bottom: 24px;
            }

            .stat-card {
                background: var(--mu-card);
                border-radius: 8px;
                padding: 16px;
                text-align: center;
            }

            .stat-value {
                font-size: 24px;
                font-weight: 600;
                color: var(--mu-primary);
                margin-bottom: 4px;
            }

            .stat-label {
                font-size: 12px;
                color: var(--mu-secondary);
                text-transform: uppercase;
                letter-spacing: 0.5px;
            }

            .settings-footer {
                padding: 20px;
                border-top: 1px solid var(--mu-border);
                text-align: right;
            }

            .setting-description {
                color: var(--mu-secondary);
                font-size: 13px;
                margin: 8px 0 0;
            }

            .warning-text {
                color: var(--mu-danger);
                font-size: 12px;
                text-align: center;
                margin-top: 8px;
            }

            .section-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 12px;
            }

            .hint {
                color: var(--mu-secondary);
                font-size: 11px;
                font-style: italic;
            }

            .button-group {
                display: flex;
                gap: 12px;
                margin-top: 12px;
            }
        `;
        document.head.appendChild(style);
    }

    function setupEventListeners() {
        const overlay = document.querySelector('.settings-overlay');
        const panel = document.querySelector('.settings-panel');
        const closeBtn = document.querySelector('.settings-close');
        const saveBtn = document.getElementById('save-settings-btn');

        overlay.addEventListener('click', closeSettings);
        closeBtn.addEventListener('click', closeSettings);
        saveBtn.addEventListener('click', saveSettings);

        document.getElementById('add-source-btn').addEventListener('click', addNewSource);
        document.getElementById('clear-data-btn').addEventListener('click', clearAllData);
        document.getElementById('export-btn').addEventListener('click', exportSettings);
        document.getElementById('import-btn').addEventListener('click', importSettings);

        document.querySelectorAll('.tab-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const tab = btn.dataset.tab;
                switchTab(tab);
            });
        });

        document.querySelectorAll('.theme-option').forEach(option => {
            option.addEventListener('click', () => {
                document.querySelectorAll('.theme-option').forEach(o => o.classList.remove('active'));
                option.classList.add('active');
            });
        });

        document.getElementById('new-source-name').addEventListener('keypress', e => {
            if (e.key === 'Enter') addNewSource();
        });

        document.getElementById('new-source-url').addEventListener('keypress', e => {
            if (e.key === 'Enter') addNewSource();
        });
    }

    function switchTab(tabName) {
        document.querySelectorAll('.tab-btn').forEach(btn => {
            btn.classList.toggle('active', btn.dataset.tab === tabName);
        });

        document.querySelectorAll('.tab-content').forEach(content => {
            content.classList.toggle('active', content.id === `tab-${tabName}`);
        });
    }

    function updateStatsDisplay() {
        const stats = StatsTracker.getStats();
        const mostUsed = StatsTracker.getMostUsedSource();

        document.getElementById('total-opens').textContent = stats.opens || 0;
        document.getElementById('last-used').textContent = stats.lastOpened
            ? new Date(stats.lastOpened).toLocaleDateString()
        : 'Never';
        document.getElementById('top-source').textContent = mostUsed || 'None';

        const sourcesStats = document.getElementById('sources-stats');
        if (sourcesStats) {
            if (stats.sources && Object.keys(stats.sources).length > 0) {
                sourcesStats.innerHTML = Object.entries(stats.sources)
                    .sort(([, a], [, b]) => b - a)
                    .map(([name, count]) => `
                        <div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--mu-border);">
                            <span>${name}</span>
                            <span style="color:var(--mu-primary);font-weight:500;">${count}</span>
                        </div>
                    `).join('');
            } else {
                sourcesStats.innerHTML = '<p style="color:var(--mu-secondary);text-align:center;">No usage data yet</p>';
            }
        }
    }

    function addNewSource() {
        const nameInput = document.getElementById('new-source-name');
        const urlInput = document.getElementById('new-source-url');

        if (nameInput.value && urlInput.value) {
            const sources = getSources();

            if (sources.length >= CONFIG.MAX_SOURCES) {
                showToast(`Maximum ${CONFIG.MAX_SOURCES} sources allowed`, 'error');
                return;
            }

            let url = urlInput.value.trim();
            url = url.replace(/^(https?:\/\/)?(www\.)?/i, '');

            sources.push({
                name: nameInput.value.trim(),
                url: url,
                enabled: true
            });

            updateSourcesList(sources);
            nameInput.value = '';
            urlInput.value = '';
            nameInput.focus();
            showToast('Source added successfully', 'success');
        } else {
            showToast('Please enter both name and URL', 'error');
        }
    }

    function closeSettings() {
        document.querySelector('.settings-overlay').style.display = 'none';
        document.querySelector('.settings-panel').style.display = 'none';
        document.getElementById('new-source-name').value = '';
        document.getElementById('new-source-url').value = '';
    }

    function saveSettings() {
        const autoRedirectEnabled = document.getElementById('auto-redirect-checkbox').checked;
        saveAutoRedirectState(autoRedirectEnabled);

        const compactMode = document.getElementById('compact-mode-checkbox').checked;
        saveCompactMode(compactMode);

        const activeTheme = document.querySelector('.theme-option.active').dataset.theme;
        ThemeManager.setTheme(activeTheme);

        const sources = [];
        document.querySelectorAll('.source-item').forEach(item => {
            sources.push({
                name: item.querySelector('.source-name').textContent,
                url: item.querySelector('.source-url').textContent,
                enabled: item.querySelector('input[type="checkbox"]').checked
            });
        });

        saveSources(sources);
        closeSettings();
        updateUnlockButton();
        showToast('Settings saved successfully!', 'success');
    }

    function deleteSource(index) {
        const sources = getSources();
        const sourceName = sources[index].name;

        if (sources.length <= 1) {
            showToast('You must have at least one source', 'error');
            return;
        }

        if (confirm(`Delete "${sourceName}"?`)) {
            sources.splice(index, 1);
            updateSourcesList(sources);
            showToast(`"${sourceName}" deleted`, 'success');
        }
    }

    function updateSourcesList(sources) {
        const list = document.getElementById('sources-list');
        if (!list) return;

        if (sources.length === 0) {
            list.innerHTML = '<div class="empty-state">No sources added yet</div>';
            return;
        }

        list.innerHTML = sources.map((source, index) => `
            <div class="source-item" data-index="${index}">
                <div class="source-info">
                    <div class="source-name">${source.name}</div>
                    <div class="source-url">${source.url}</div>
                </div>
                <div class="source-actions">
                    <label class="toggle-switch" style="margin:0;">
                        <input type="checkbox" ${source.enabled ? 'checked' : ''}>
                        <span class="toggle-slider"></span>
                    </label>
                    <button class="btn-icon delete" onclick="(${deleteSource.toString()})(${index})" title="Delete">
                        🗑️
                    </button>
                </div>
            </div>
        `).join('');

        setupDragAndDrop();
    }

    function setupDragAndDrop() {
        const list = document.getElementById('sources-list');
        if (!list) return;

        let draggedItem = null;

        list.querySelectorAll('.source-item').forEach(item => {
            item.setAttribute('draggable', 'true');

            item.addEventListener('dragstart', e => {
                draggedItem = item;
                setTimeout(() => item.classList.add('dragging'), 0);
                e.dataTransfer.effectAllowed = 'move';
                e.dataTransfer.setData('text/plain', item.dataset.index);
            });

            item.addEventListener('dragend', () => {
                item.classList.remove('dragging');
                draggedItem = null;
            });
        });

        list.addEventListener('dragover', e => {
            e.preventDefault();
            const afterElement = getDragAfterElement(list, e.clientY);
            const draggable = document.querySelector('.dragging');

            if (afterElement == null) {
                list.appendChild(draggable);
            } else {
                list.insertBefore(draggable, afterElement);
            }
        });

        list.addEventListener('drop', e => {
            e.preventDefault();
            const sources = getSources();
            const newSources = [];

            list.querySelectorAll('.source-item').forEach(item => {
                const index = parseInt(item.dataset.index);
                newSources.push(sources[index]);
            });

            saveSources(newSources);
            updateSourcesList(newSources);
        });
    }

    function getDragAfterElement(container, y) {
        const draggableElements = [...container.querySelectorAll('.source-item:not(.dragging)')];

        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 createUnlockButton() {
        const sources = getSources().filter(s => s.enabled);
        if (sources.length === 0) return;

        const { quickAccess, remaining } = QuickAccessManager.getQuickAccessSources(sources);
        const compactMode = getCompactMode();
        const position = getButtonPosition();

        const button = document.createElement('div');
        button.id = 'medium-unlock-button';
        button.className = compactMode ? 'compact' : '';
        button.innerHTML = `
            <div class="unlock-button-main">
                <button class="unlock-button-toggle" title="Medium Unlocker">
                    <span class="toggle-icon">🔓</span>
                    ${!compactMode ? '<span class="toggle-text">Unlock</span>' : ''}
                </button>

                <div class="unlock-dropdown" style="display: none;">
                    ${quickAccess.length > 0 ? `
                        <div class="dropdown-section">
                            <div class="section-title">Quick Access</div>
                            ${quickAccess.map(source => `
                                <button class="dropdown-item" data-source="${source.name}">
                                    <span class="item-name">${source.name}</span>
                                    <span class="item-hint">${StatsTracker.getStats().sources?.[source.name] || 0} uses</span>
                                </button>
                            `).join('')}
                        </div>
                    ` : ''}

                    ${remaining.length > 0 ? `
                        <div class="dropdown-section">
                            <div class="section-title">Other Sources</div>
                            ${remaining.map(source => `
                                <button class="dropdown-item" data-source="${source.name}">
                                    <span class="item-name">${source.name}</span>
                                </button>
                            `).join('')}
                        </div>
                    ` : ''}

                    <div class="dropdown-section">
                        <button class="dropdown-item settings-btn">
                            <span class="item-name">⚙️ Settings</span>
                        </button>
                        <button class="dropdown-item stats-btn">
                            <span class="item-name">📊 Statistics</span>
                        </button>
                    </div>
                </div>
            </div>
        `;

        button.style.cssText = `
            position: fixed;
            top: ${position.top || '381px'};
            left: ${position.left || '17px'};
            z-index: 9999;
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
        `;

        document.body.appendChild(button);
        injectButtonStyles();
        setupButtonEvents(button, sources);
    }

    function injectButtonStyles() {
        const style = document.createElement('style');
        style.textContent = `
            #medium-unlock-button {
                --mu-btn-bg: var(--mu-primary);
                --mu-btn-text: white;
                --mu-dropdown-bg: var(--mu-bg);
                --mu-dropdown-border: var(--mu-border);
                --mu-dropdown-shadow: var(--mu-shadow);
            }
            .unlock-button-toggle {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-weight: 500;
            }
            .unlock-button-main {
                position: relative;
            }

            .unlock-button-toggle {
                background: var(--mu-btn-bg);
                color: var(--mu-btn-text);
                border: none;
                border-radius: 24px;
                padding: ${getCompactMode() ? '10px' : '10px 16px'};
                cursor: pointer;
                display: flex;
                align-items: center;
                gap: 8px;
                font-size: 14px;
                font-weight: 500;
                box-shadow: 0 2px 8px rgba(26, 137, 23, 0.3);
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                min-width: ${getCompactMode() ? '40px' : '100px'};
                justify-content: center;
            }

            .unlock-button-toggle:hover {
                transform: translateY(-2px);
                box-shadow: 0 4px 12px rgba(26, 137, 23, 0.4);
            }

            .toggle-icon {
                font-size: 16px;
            }

            .toggle-text {
                display: ${getCompactMode() ? 'none' : 'block'};
            }

            .unlock-dropdown {
                position: absolute;
                bottom: 100%;
                left: 0;
                margin-bottom: 8px;
                background: var(--mu-dropdown-bg);
                border-radius: 12px;
                box-shadow: var(--mu-dropdown-shadow);
                border: 1px solid var(--mu-dropdown-border);
                min-width: 200px;
                z-index: 10000;
                overflow: hidden;
                animation: slideDown 0.2s ease-out;
            }

            .compact .unlock-dropdown {
                left: 50%;
                transform: translateX(-50%);
            }

            .compact .unlock-dropdown:before {
                content: '';
                position: absolute;
                bottom: -6px;
                left: 50%;
                transform: translateX(-50%);
                width: 0;
                height: 0;
                border-left: 6px solid transparent;
                border-right: 6px solid transparent;
                border-top: 6px solid var(--mu-dropdown-bg);
            }

            @keyframes slideDown {
                from {
                    opacity: 0;
                    transform: translateY(-10px);
                }
                to {
                    opacity: 1;
                    transform: translateY(0);
                }
            }

            .dropdown-section {
                padding: 8px 0;
                border-bottom: 1px solid var(--mu-dropdown-border);
            }

            .dropdown-section:last-child {
                border-bottom: none;
            }

            .section-title {
                padding: 8px 16px;
                font-size: 11px;
                text-transform: uppercase;
                letter-spacing: 0.5px;
                color: var(--mu-secondary);
                font-weight: 600;
            }

            .dropdown-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                padding: 10px 16px;
                border: none;
                background: none;
                cursor: pointer;
                color: var(--mu-text);
                font-size: 14px;
                text-align: left;
                transition: background 0.2s;
            }

            .dropdown-item:hover {
                background: color-mix(in srgb, var(--mu-primary) 10%, transparent);
            }

            .item-name {
                flex: 1;
            }

            .item-hint {
                font-size: 11px;
                color: var(--mu-secondary);
                background: var(--mu-card);
                padding: 2px 6px;
                border-radius: 10px;
                margin-left: 8px;
            }
        `;
        document.head.appendChild(style);
    }

    function setupButtonEvents(button, sources) {
        const toggle = button.querySelector('.unlock-button-toggle');
        const dropdown = button.querySelector('.unlock-dropdown');
        const settingsBtn = button.querySelector('.settings-btn');
        const statsBtn = button.querySelector('.stats-btn');

        // Toggle dropdown on click
        toggle.addEventListener('click', (e) => {
            e.stopPropagation();
            isDropdownOpen = !isDropdownOpen;
            dropdown.style.display = isDropdownOpen ? 'block' : 'none';
        });

        // Close dropdown when clicking outside
        document.addEventListener('click', (e) => {
            if (isDropdownOpen && !button.contains(e.target)) {
                isDropdownOpen = false;
                dropdown.style.display = 'none';
            }
        });

        // Close dropdown on escape key
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && isDropdownOpen) {
                isDropdownOpen = false;
                dropdown.style.display = 'none';
            }
        });

        // Source click events
        button.querySelectorAll('.dropdown-item[data-source]').forEach(item => {
            item.addEventListener('click', (e) => {
                e.stopPropagation();
                const sourceName = item.dataset.source;
                const source = sources.find(s => s.name === sourceName);
                if (source) {
                    isDropdownOpen = false;
                    dropdown.style.display = 'none';
                    openWithSource(source);
                }
            });
        });

        // Settings button
        settingsBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            isDropdownOpen = false;
            dropdown.style.display = 'none';
            openSettings();
        });

        // Stats button
        statsBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            isDropdownOpen = false;
            dropdown.style.display = 'none';
            openStats();
        });

        // Make draggable (only on the toggle button, not dropdown)
        makeDraggable(button, toggle);
    }

    function openWithSource(source) {
        const currentUrl = window.location.href;
        const unlockUrl = `https://${source.url}/${currentUrl}`;

        StatsTracker.recordOpen(source.name);
        QuickAccessManager.updateOrder(source.name);

        window.open(unlockUrl, '_blank');
        showToast(`Opening with ${source.name}...`, 'info');
    }

    function openSettings() {
        document.querySelector('.settings-overlay').style.display = 'block';
        document.querySelector('.settings-panel').style.display = 'flex';

        document.getElementById('auto-redirect-checkbox').checked = getAutoRedirectState();
        document.getElementById('compact-mode-checkbox').checked = getCompactMode();

        const currentTheme = ThemeManager.getTheme();
        document.querySelectorAll('.theme-option').forEach(option => {
            option.classList.toggle('active', option.dataset.theme === currentTheme);
        });

        updateSourcesList(getSources());
    }

    function openStats() {
        openSettings();
        switchTab('stats');
        updateStatsDisplay();
    }

    function makeDraggable(element, handle) {
        handle = handle || element;
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        handle.addEventListener('mousedown', startDrag);

        function startDrag(e) {
            if (e.target.closest('.dropdown-item')) return;

            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            startLeft = parseInt(element.style.left) || 17;
            startTop = parseInt(element.style.top) || 381;

            document.addEventListener('mousemove', drag);
            document.addEventListener('mouseup', stopDrag);

            element.style.cursor = 'grabbing';
            element.style.transition = 'none';
        }

        function drag(e) {
            if (!isDragging) return;

            const dx = e.clientX - startX;
            const dy = e.clientY - startY;

            if (Math.abs(dx) > CONFIG.MIN_DRAG_THRESHOLD || Math.abs(dy) > CONFIG.MIN_DRAG_THRESHOLD) {
                element.style.left = `${startLeft + dx}px`;
                element.style.top = `${startTop + dy}px`;
            }
        }

        function stopDrag() {
            if (!isDragging) return;

            isDragging = false;
            element.style.cursor = '';
            element.style.transition = '';

            document.removeEventListener('mousemove', drag);
            document.removeEventListener('mouseup', stopDrag);

            saveButtonPosition({
                left: element.style.left,
                top: element.style.top
            });

            showToast('Position saved', 'info');
        }
    }

    function isMediumArticle() {
        const checks = [
            () => document.querySelector('article') !== null,
            () => document.querySelector('meta[name="generator"][content*="medium"]') !== null,
            () => document.querySelector('.progressiveMedia, .graf--title, .section-content') !== null,
            () => Array.from(document.scripts).some(s => s.src && (s.src.includes('medium.com') || s.src.includes('cdn-client.medium.com'))),
            () => Array.from(document.links).some(l => l.href && (l.href.includes('medium.com') || l.href.includes('cdn-static-1.medium.com')))
        ];

        return checks.filter(check => check()).length >= 2;
    }

    function updateUnlockButton() {
        const oldButton = document.getElementById('medium-unlock-button');
        if (oldButton) oldButton.remove();
        createUnlockButton();
    }

    function init() {
        setTimeout(() => {
            if (isMediumArticle()) {
                if (getAutoRedirectState()) {
                    const excludePaths = ['/', '/search', '/me/', '/new-story'];
                    const path = window.location.pathname;

                    if (!excludePaths.some(p => path.startsWith(p))) {
                        const enabledSources = getSources().filter(s => s.enabled);
                        if (enabledSources.length > 0) {
                            const primarySource = enabledSources[0];
                            const currentUrl = window.location.href;
                            const redirectUrl = `https://${primarySource.url}/${currentUrl}`;

                            console.log(`[Medium Unlocker] Auto-redirecting to ${primarySource.name}...`);
                            window.location.href = redirectUrl;
                            return;
                        }
                    }
                }

                if (!document.querySelector('.medium-unlock-settings')) {
                    createSettingsPanel();
                }
                if (!document.getElementById('medium-unlock-button')) {
                    createUnlockButton();
                }
            }
        }, 100);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    let lastUrl = location.href;
    new MutationObserver(() => {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            init();
        }

        if (!document.getElementById('medium-unlock-button') && isMediumArticle()) {
            init();
        }
    }).observe(document, { subtree: true, childList: true });

})();