Sleazy Fork is available in English.

[NDiiong] Bypass Medium

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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 });

})();