Platesmania Lookup Toolbox

Shows lookup buttons on Platesmania upload pages.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Platesmania Lookup Toolbox
// @version      1.14.1
// @description  Shows lookup buttons on Platesmania upload pages.
// @match        https://platesmania.com/*/add*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      opendata.rdw.nl
// @connect      motonet.fi
// @connect      trodo.it
// @license      MIT
// @namespace    https://greasyfork.org/users/976031
// ==/UserScript==

(function () {
    'use strict';

    // --- Settings & Storage ---
    // View size can be Large or Compact (formerly "Small").
    // We support a default and per-country overrides, plus per-country hidden sites.
    const SIZE_DEFAULT_KEY = 'lookup_button_size_default';
    const SIZE_BY_COUNTRY_KEY = 'lookup_button_size_by_country'; // JSON object { code: 'Large'|'Compact' }
    const HIDDEN_SITES_BY_COUNTRY_KEY = 'lookup_hidden_sites_by_country'; // JSON object { code: [siteName, ...] }
    const DISABLE_GOOGLE_IMAGES_KEY = 'lookup_disable_google_images';
    const DISABLE_AUTOGESPOT_KEY = 'lookup_disable_autogespot';

    function getCurrentCountryCode() {
        try {
            const href = window.location.href || '';
            const m = href.match(/platesmania\.com\/([a-z]{2,4})\/add/i);
            return m ? m[1].toLowerCase() : '';
        } catch {
            return '';
        }
    }

    function readJsonFromStorage(key, fallback) {
        try {
            if (typeof GM_getValue === 'function') {
                const raw = GM_getValue(key, '');
                if (!raw) return fallback;
                try { return JSON.parse(raw); } catch {}
            } else if (typeof localStorage !== 'undefined') {
                const raw = localStorage.getItem(key);
                if (!raw) return fallback;
                try { return JSON.parse(raw); } catch {}
            }
        } catch {}
        return fallback;
    }

    function writeJsonToStorage(key, obj) {
        const raw = JSON.stringify(obj || {});
        try {
            if (typeof GM_setValue === 'function') GM_setValue(key, raw);
            else if (typeof localStorage !== 'undefined') localStorage.setItem(key, raw);
        } catch {}
    }

    function readStringFromStorage(key, fallback) {
        try {
            if (typeof GM_getValue === 'function') return GM_getValue(key, fallback);
            if (typeof localStorage !== 'undefined') return localStorage.getItem(key) || fallback;
        } catch {}
        return fallback;
    }

    function writeStringToStorage(key, val) {
        try {
            if (typeof GM_setValue === 'function') GM_setValue(key, val);
            else if (typeof localStorage !== 'undefined') localStorage.setItem(key, val);
        } catch {}
    }

    function migrateLegacySizeDefault() {
        // Migrate from old global key 'lookup_button_size_mode' if present
        const LEGACY_KEY = 'lookup_button_size_mode';
        const legacy = readStringFromStorage(LEGACY_KEY, '');
        if (legacy) {
            const normalized = legacy === 'Small' ? 'Compact' : legacy;
            writeStringToStorage(SIZE_DEFAULT_KEY, normalized);
            try { if (typeof GM_setValue === 'function') GM_setValue(LEGACY_KEY, ''); else if (localStorage) localStorage.removeItem(LEGACY_KEY); } catch {}
        }
    }

    function getDefaultSizeMode() {
        migrateLegacySizeDefault();
        const val = readStringFromStorage(SIZE_DEFAULT_KEY, 'Large');
        return val === 'Small' ? 'Compact' : (val || 'Large');
    }

    function setDefaultSizeMode(mode) {
        const normalized = mode === 'Small' ? 'Compact' : mode;
        writeStringToStorage(SIZE_DEFAULT_KEY, normalized);
    }

    function getSizeModeForCountry(code) {
        const byCountry = readJsonFromStorage(SIZE_BY_COUNTRY_KEY, {});
        const val = byCountry[code];
        if (val) return val === 'Small' ? 'Compact' : val;
        return getDefaultSizeMode();
    }

    function setSizeModeForCountry(code, mode) {
        const normalized = mode === 'Small' ? 'Compact' : mode;
        const byCountry = readJsonFromStorage(SIZE_BY_COUNTRY_KEY, {});
        byCountry[code] = normalized;
        writeJsonToStorage(SIZE_BY_COUNTRY_KEY, byCountry);
    }

    function clearAllCountrySizeOverrides() {
        writeJsonToStorage(SIZE_BY_COUNTRY_KEY, {});
    }

    function isCompactMode() {
        const code = getCurrentCountryCode();
        const mode = code ? getSizeModeForCountry(code) : getDefaultSizeMode();
        return mode === 'Compact';
    }

    function getHiddenSitesForCountry(code) {
        const data = readJsonFromStorage(HIDDEN_SITES_BY_COUNTRY_KEY, {});
        const arr = Array.isArray(data[code]) ? data[code] : [];
        return new Set(arr);
    }

    function setHiddenSitesForCountry(code, hiddenArray) {
        const data = readJsonFromStorage(HIDDEN_SITES_BY_COUNTRY_KEY, {});
        data[code] = Array.from(new Set(hiddenArray || []));
        writeJsonToStorage(HIDDEN_SITES_BY_COUNTRY_KEY, data);
    }

    function isSiteEnabledForCountry(code, siteName) {
        const hidden = getHiddenSitesForCountry(code);
        return !hidden.has(siteName);
    }

    function isGoogleImagesGloballyEnabled() {
        const v = readStringFromStorage(DISABLE_GOOGLE_IMAGES_KEY, '0');
        return String(v) !== '1';
    }
    function setGoogleImagesGloballyEnabled(enabled) {
        writeStringToStorage(DISABLE_GOOGLE_IMAGES_KEY, enabled ? '0' : '1');
    }
    function isAutogespotGloballyEnabled() {
        const v = readStringFromStorage(DISABLE_AUTOGESPOT_KEY, '0');
        return String(v) !== '1';
    }
    function setAutogespotGloballyEnabled(enabled) {
        writeStringToStorage(DISABLE_AUTOGESPOT_KEY, enabled ? '0' : '1');
    }

    // Favicon cache so we don't keep refetching once resolved
    const faviconCache = {};
    function getFaviconConfigForUrl(urlStr) {
        try {
            const u = new URL(urlStr);
            const key = u.hostname;
            const cached = faviconCache[key];
            if (cached) {
                return { key, candidates: [cached] };
            }
            const s2 = `https://www.google.com/s2/favicons?sz=64&domain=${u.hostname}`;
            const ico = `${u.origin}/favicon.ico`;
            return { key, candidates: [ico, s2] };
        } catch {
            return { key: '', candidates: [] };
        }
    }

    const lookupSites = {
        nl: [
            { name: 'Finnik', base: 'https://finnik.nl/kenteken/' },
            { name: 'Finnik (app)', base: 'https://app.finnik.nl/home/vehicle_report?license-plate-number=' },
            { name: 'Finnik (Centraal Beheer)', base: 'https://centraalbeheer.finnik.nl/kenteken/' },
            { name: 'Autoweek', base: 'https://www.autoweek.nl/kentekencheck/' },
            { name: 'voertuig.net', base: 'https://voertuig.net/kenteken/' },
            { name: 'Kentekencheck.info', base: 'https://www.kentekencheck.info/kenteken/' },
            { name: 'Kentekencheck.nu', base: 'https://www.kentekencheck.nu/kenteken/' },
            { name: 'Qenteken', base: 'https://www.qenteken.nl/kentekencheck/' },
            { name: 'RDW (Site)', base: 'https://www.rdwdata.nl/kenteken/' },
        ],
        se: [
            { name: 'car.info', base: 'https://www.car.info/?s=' },
            { name: 'biluppgifter.se', base: 'https://biluppgifter.se/fordon/' },
            { name: 'transportstyrelsen', base: 'https://fordon-fu-regnr.transportstyrelsen.se/?ts-regnr-sok=' },
        ],
        ua: [
            { name: 'carplates.app', base: 'https://ua.carplates.app/en/number/' },
            { name: 'baza-gai.com.ua', base: 'https://baza-gai.com.ua/nomer/' },
            { name: 'auto-inform.com.ua', base: 'https://auto-inform.com.ua/search/' },
        ],
        uk: [
            { name: 'checkcardetails', base: 'https://www.checkcardetails.co.uk/cardetails/' },
            { name: 'totalcarcheck', base: 'https://totalcarcheck.co.uk/FreeCheck?regno=' },
            { name: 'checkhistory', base: 'https://checkhistory.uk/vehicle/' },
            { name: 'carcheck', base: 'https://www.carcheck.co.uk/sendnudes/' },
            { name: 'carhistorycheck', base: 'https://carhistorycheck.co.uk/confirm-vehicle/?vrm=' },
        ],
        dk: [
            { name: 'digitalservicebog.dk', base: 'https://app.digitalservicebog.dk/search?country=dk&Registration=' },
            { name: 'esyn.dk', base: 'https://findsynsrapport.esyn.dk/result?registration=' },
        ],
        no: [{ name: 'vegvesen.no', base: 'https://www.vegvesen.no/en/vehicles/buy-and-sell/vehicle-information/check-vehicle-information/?registreringsnummer=' },
             { name: 'regnr.info', base: 'https://regnr.info/' }

            ],
        fr: [
            { name: 'immatriculation-auto.info', base: 'https://immatriculation-auto.info/vehicle/' },
            { name: 'carter-cash.com', base: 'https://www.carter-cash.com/pieces-auto/?plate=', needsHyphen: true },
        ],
        es: [
            { name: 'carter-cash.es', base: 'https://www.carter-cash.es/piezas-auto/?plate=' },
        ],
        fi: [
            { name: 'Biltema', base: 'https://www.biltema.fi/sv-fi/rekosok-bil/' },
            { name: 'Motonet (in new tab)', base: 'https://www.motonet.fi/api/vehicleInfo/registrationNumber/FI?locale=fi&registrationNumber=' },
        ],
        cz: [{ name: 'uniqa.cz', base: 'https://www.uniqa.cz/online/pojisteni-vozidla/#ecvId=' }],
        sk: [{ name: 'overenie.digital', base: 'https://overenie.digital/over/sk/ecv/' },
             { name: 'stkonline', base: 'https://www.stkonline.sk/spz/' }
            ],
        ie: [{ name: 'cartell.ie', base: 'https://www.cartell.ie/ssl/servlet/beginStarLookup?registration=' },
             { name: 'motorcheck.ie', base: 'https://www.motorcheck.ie/free-car-check/?vrm=' }
            ],
        is: [{ name: 'island.is', base: 'https://island.is/uppfletting-i-oekutaekjaskra?vq=' }],
        it: [{ name: 'carter-cash.it', base: 'https://www.carter-cash.it/ricambi-auto/?plate=' },]
    };

    const url = window.location.href;
    const isPage = (code) => url.includes(`platesmania.com/${code}/add`);
    const supportedCodes = ['nl','ua','no','dk','fr','uk','fi','pl','lt','cz','se','es', 'sk', 'us', 'ie', 'is', 'it'];

    // Platesmania US: mapping of <option value> in #drop_1 to 2-letter state codes
    const usStateValueToCode = {
        '7502': 'AL',
        '7501': 'AK',
        '7504': 'AZ',
        '7503': 'AR',
        '7505': 'CA',
        '7506': 'CO',
        '7507': 'CT',
        '7508': 'DE',
        '7551': 'DC',
        '7509': 'FL',
        '7510': 'GA',
        '7511': 'HI',
        '7513': 'ID',
        '7514': 'IL',
        '7515': 'IN',
        '7512': 'IA',
        '7516': 'KS',
        '7517': 'KY',
        '7518': 'LA',
        '7521': 'ME',
        '7520': 'MD',
        '7519': 'MA',
        '7522': 'MI',
        '7523': 'MN',
        '7525': 'MS',
        '7524': 'MO',
        '7526': 'MT',
        '7529': 'NE',
        '7533': 'NV',
        '7530': 'NH',
        '7531': 'NJ',
        '7532': 'NM',
        '7534': 'NY',
        '7527': 'NC',
        '7528': 'ND',
        '7535': 'OH',
        '7536': 'OK',
        '7537': 'OR',
        '7538': 'PA',
        '7539': 'RI',
        '7540': 'SC',
        '7541': 'SD',
        '7542': 'TN',
        '7543': 'TX',
        '7544': 'UT',
        '7546': 'VT',
        '7545': 'VA',
        '7547': 'WA',
        '7549': 'WV',
        '7548': 'WI',
        '7550': 'WY',
    };

    function getUSStateCode() {
        const sel = document.getElementById('drop_1');
        if (!sel) return '';
        const val = sel.value;
        return usStateValueToCode[val] || '';
    }

    // --- Helpers ---
    function selectedText(id) {
        const el = document.getElementById(id);
        if (!el || !el.options || el.selectedIndex < 0) return '';
        return el.options[el.selectedIndex].text;
    }

    function areFieldsFilled() {
        if (isPage('nl')) return document.getElementById('nomer').value !== '';
        if (isPage('ua')) {
            const region = document.getElementById('region1').value;
            const digits = document.getElementById('digit1').value;
            return region !== '' && digits !== '';
        }
        if (isPage('no') || isPage('dk') || isPage('se')) return document.getElementById('let').value !== '' && document.getElementById('digit').value !== '';
        if (isPage('fr')) {
            const b1 = document.getElementById('b1').value;
            const d2 = document.getElementById('digit2').value;
            const b2 = document.getElementById('b2').value;
            return b1 !== '' && d2 !== '' && b2 !== '';
        }
        if (isPage('es')) {
            const ctype = document.getElementById('ctype')?.value;
            if (!ctype) return false;
            if (ctype === '1') {
                return document.getElementById('digit1').value !== '' && document.getElementById('let').value !== '';
            }
            if (ctype === '2') {
                return selectedText('dip') !== '' && selectedText('region') !== '' && document.getElementById('digit1').value !== '';
            }
            if (ctype === '3') {
                return selectedText('region') !== '' && document.getElementById('digit1').value !== '' && document.getElementById('let').value !== '';
            }
            if (ctype === '4') {
                return selectedText('region') !== '' && document.getElementById('digit2').value !== '' && document.getElementById('let').value !== '';
            }
            if (ctype === '5') {
                return selectedText('region') !== '' && document.getElementById('digit1').value !== '' && document.getElementById('let').value !== '';
            }
            if (ctype === '7') {
                return selectedText('region') !== '' && document.getElementById('digit2').value !== '';
            }
            return false;
        }
        if (isPage('de') || isPage('ch')) return document.getElementById('digit').value !== '';
        if (isPage('us')) {
            const state = getUSStateCode();
            const plate = document.getElementById('nomer').value;
            return !!state && plate !== '';
        }
        if (isPage('pl') || isPage('uk')) {
            const nomerpl = document.getElementById('nomerpl')?.value || '';
            const dip = document.getElementById('dip')?.value || '';
            return nomerpl !== '' || dip !== '';
        }
        if (isPage('fi')) return document.getElementById('digit').value !== '';
        if (isPage('lt')) return document.getElementById('digit2').value !== '';
        if (isPage('cz')) {
            const d1 = document.getElementById('digit1').value;
            const d2 = document.getElementById('digit2').value;
            const d3 = document.getElementById('digit3').value;
            const nomer = document.getElementById('nomer').value;
            return d1 !== '' || d2 !== '' || d3 !== '' || nomer !== '';
        }


        if (isPage('sk')) {
            const digit = document.getElementById('digit').value;
            const nomerpl = document.getElementById('nomerpl').value;
            const police = document.getElementById('police').value;
            return digit !== '' || nomerpl !== '' || police !== '';
        }
        if (isPage('ie')) {
            const digit2 = document.getElementById('digit2').value;
            return digit2;
        }
        if (isPage('is')) {
            const nomer = document.getElementById('nomer').value;
            const b1 = document.getElementById('b1').value;
            return nomer !== '' || b1 !== '';
        }

        if (isPage('it')) {
            const c = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';
            const txt = (id) => (document.getElementById(id)?.options?.[document.getElementById(id).selectedIndex]?.text || '');
            switch (c) {
                case '1': return q('b1') !== '' && q('digit1') !== '' && q('b2') !== '';
                case '2': return q('b1') !== '' && q('digit2') !== '';
                case '3': return q('b1') !== '' && q('digit2') !== '';
                case '4': return q('b1') !== '' && q('digit1') !== '' && q('b2') !== '';
                case '5': return txt('region1') !== '' && q('nomerpl1') !== '';
                case '6': return txt('region1') !== '' && q('nomerpl1') !== '';
                case '7': return txt('region1') !== '' && q('nomerpl1') !== '';
                case '8': return false; // disabled
                case '9': return txt('dipreg') !== '' && q('digit2') !== '' && q('b2') !== '';
                case '10': return q('b1') !== '' && q('digit2') !== '';
                default: return false;
            }
        }

        return false;
    }


    function buildPlateForCurrentPage() {
        if (isPage('nl')) return document.getElementById('nomer').value;

        if (isPage('ua')) {
            const region = document.getElementById('region1').value;
            const digits = document.getElementById('digit1').value;
            const b1 = document.getElementById('b1').value;
            const b2 = document.getElementById('b2').value;
            return `${region}${digits}${b1}${b2}`;
        }

        if (isPage('no') || isPage('dk') || isPage('se')) {
            const letField = document.getElementById('let').value;
            const digitField = document.getElementById('digit').value;
            return `${letField}${digitField}`;
        }

        if (isPage('fr')) {
            const b1 = document.getElementById('b1').value;
            const digit2 = document.getElementById('digit2').value;
            const b2 = document.getElementById('b2').value;
            return `${b1}${digit2}${b2}`;
        }

        if (isPage('us')) {
            const raw = document.getElementById('nomer').value || '';
            return raw.replace(/\s+/g, '');
        }

        if (isPage('es')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';
            if (ctype === '1') {
                return q('digit1') + q('let');
            }
            if (ctype === '2') {
                return selectedText('dip') + selectedText('region') + q('digit1');
            }
            if (ctype === '3') {
                return selectedText('region') + q('digit1') + q('let');
            }
            if (ctype === '4') {
                return selectedText('region') + q('digit2') + q('let');
            }
            if (ctype === '5') {
                return selectedText('region') + q('digit1') + q('let');
            }
            if (ctype === '7') {
                return selectedText('region') + q('digit2');
            }
            return '';
        }

        if (isPage('fi')) {
            const letField = document.getElementById('let1').value;
            const digitField = document.getElementById('digit').value;
            return `${letField}-${digitField}`;
        }

        if (isPage('uk')) {
            return (document.getElementById('nomerpl')?.value || document.getElementById('nomer')?.value || '');
        }

        if (isPage('pl')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '9':
                case '10':
                case '11':
                    return selectedText('region') + q('nomerpl');

                case '6':
                case '7':
                case '8':
                    return selectedText('region') + q('b1') + q('nomerpl');

                case '12':
                    return selectedText('dip') + selectedText('region') + q('digit');

                default:
                    return '';
            }
        }


        if (isPage('lt')) {
            const b1 = document.getElementById('b1');
            const b2 = document.getElementById('b2');
            const b3 = document.getElementById('b3');
            const d1 = document.getElementById('digit1');
            const d2 = document.getElementById('digit2');
            const d3 = document.getElementById('digit3');
            const vanity = document.getElementById('nomer');
            const ctype = document.getElementById('ctype').value;
            if (ctype === '1') return b1.value + b2.value + b3.value + d2.value;
            if (ctype === '2') return d1.value + b1.value + b2.value;
            if (ctype === '3') return b1.value + b2.value + d2.value;
            if (ctype === '4') return d1.value + b1.value + b2.value + b3.value;
            if (['5','6','7','9'].includes(ctype)) return vanity.value;
            if (ctype === '8') return d3.value + b1.value + b2.value;
            return '';
        }

        if (isPage('cz')) {
            const q = (id) => document.getElementById(id)?.value || '';
            const category = q('ctype');
            const regionField = document.getElementById('region');
            const selectedRegionText = regionField.options[regionField.selectedIndex].text;
            const b1 = q('b1'), b2 = q('b2'), b3 = q('b3');
            const d1 = q('digit1'), d2 = q('digit2'), d3 = q('digit3');
            const nomer = q('nomer'), el = q('el');

            switch (category) {
                case '1': return `${b1}${selectedRegionText}${b2}${d1}`;
                case '2': return `${b1}${selectedRegionText}${d1}`;
                case '4': return `${selectedRegionText}${b2}${d1}`;
                case '5': return `${selectedRegionText}${b2}${d1}`;
                case '6': return `${selectedRegionText}${b2}${d1}`;
                case '7': return `${selectedRegionText}${b2}${d2}`;
                case '8': return `${selectedRegionText}${b2}${d2}`;
                case '9': return `${selectedRegionText}${b2}${d2}`;
                case '10': return `${selectedRegionText}${b2}${d2}`;
                case '11': return nomer;
                case '3': return nomer;
                case '12': return `${el}${b3}${d3}`;
                case '13': return `${d1}${d3}`;
                default: return '';
            }
        }
        if (isPage('sk')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                case '2':
                case '12':
                    return selectedText('region') + q('digit') + q('let2');

                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                    return selectedText('region') + q('let1') + q('digit');

                case '9':
                    return q('let2') + q('nomerpl');

                case '10':
                    return q('let1') + q('police');

                case '11':
                    return q('digit') + q('police');

                case '13':
                    return selectedText('dip') + q('police');

                default:
                    return '';
            }


        }
        if (isPage('ie')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                    return q('digit1') + selectedText('region') + q('digit2');
                case '2':
                case '3':
                    return q('let') + q('digit2')

                default:
                    return '';
            }
        }
        if (isPage('is')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '9':
                    return q('nomer').replace(/\s+/g, '');
                case '8':
                    return selectedText('b1') +  selectedText('b2') +  selectedText('b3') +  selectedText('b4') +  selectedText('b5') +  selectedText('b6');
                case '10':
                    return  selectedText('region') + q('nomer').replace(/\s+/g, '');
                default:
                    return '';
            }
        }
        if (isPage('it')) {
            const c = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';
            const txt = (id) => {
                const el = document.getElementById(id);
                if (!el || el.selectedIndex < 0 || !el.options) return '';
                return el.options[el.selectedIndex].text || '';
            };
            const joinClean = (...parts) => parts.join('').replace(/[\s-]+/g, '');
            switch (c) {
                case '1': return joinClean(q('b1'), q('digit1'), q('b2'));
                case '2': return joinClean(q('b1'), q('digit2'));
                case '3': return joinClean(q('b1'), q('digit2'));
                case '4': return joinClean(q('b1'), q('digit1'), q('b2'));
                case '5': return joinClean(txt('region1'), q('nomerpl1'));
                case '6': return joinClean(txt('region1'), q('nomerpl1'));
                case '7': return joinClean(txt('region1'), q('nomerpl1'));
                case '8': return ''; // disabled
                case '9': {
                    const dip = txt('dipreg').replace(/\s*\(.*?\)\s*/g, '');
                    return joinClean(dip, q('digit2'), q('b2'));
                }
                case '10': return joinClean(q('b1'), q('digit2'));
                default: return '';
            }
        }

        return '';
    }

    // For Google Images button we historically insert spaces between chunks on some countries.
    function buildPlateForSearchDisplay() {
        if (isPage('nl')) return document.getElementById('nomer').value;
        if (isPage('ua')) {
            const region = document.getElementById('region1').value;
            const digits = document.getElementById('digit1').value;
            const b1 = document.getElementById('b1').value;
            const b2 = document.getElementById('b2').value;
            return `${region} ${digits} ${b1}${b2}`;
        }
        if (isPage('no') || isPage('dk') || isPage('se')) {
            return document.getElementById('let').value + ' ' + document.getElementById('digit').value;
        }
        if (isPage('fr')) {
            const b1 = document.getElementById('b1').value;
            const d2 = document.getElementById('digit2').value;
            const b2 = document.getElementById('b2').value;
            return `${b1} ${d2} ${b2}`;
        }
        if (isPage('us')) {
            return (document.getElementById('nomer').value || '').replace(/\s+/g, '');
        }
        if (isPage('es')) {
            return buildPlateForCurrentPage();
        }
        if (isPage('de')) {
            const regionFieldBase = document.getElementById('region');
            const regionField = regionFieldBase.options[regionFieldBase.selectedIndex].text;
            const letField = document.getElementById('b1').value;
            const letField2 = document.getElementById('b2').value;
            const digitField = document.getElementById('digit').value;
            return `${regionField} ${letField} ${digitField}${letField2}`;
        }
        if (isPage('ch')) {
            const regionField = document.getElementById('region').value;
            const digitField = document.getElementById('digit').value;
            return `${regionField} ${digitField}`;
        }
        if (isPage('fi')) {
            return document.getElementById('let1').value + '-' + document.getElementById('digit').value;
        }
        if (isPage('pl')) {
            const regionField = document.getElementById('region');
            const selectedRegionText = regionField.options[regionField.selectedIndex].text;
            const digitField = document.getElementById('nomerpl').value;
            return selectedRegionText + ' ' + digitField;
        }
        if (isPage('uk')) {
            return (document.getElementById('nomerpl')?.value || document.getElementById('nomer')?.value || '');
        }
        if (isPage('lt') || isPage('it') || isPage('cz') || isPage('sk')) {
            return buildPlateForCurrentPage();
        }
        if (isPage('ie')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                    return q('digit1') + '-' + selectedText('region') + '-' + q('digit2');
                case '2':
                case '3':
                    return q('let') + q('digit2')

                default:
                    return '';
            }
        }
        if (isPage('is')) {
            const ctype = document.getElementById('ctype')?.value;
            const q = (id) => document.getElementById(id)?.value || '';

            switch (ctype) {
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '9':
                    return q('nomer');
                case '8':
                    return selectedText('b1') +  selectedText('b2') +  selectedText('b3') +  selectedText('b4') +  selectedText('b5') +  selectedText('b6');
                case '10':
                    return  selectedText('region') + ' ' + q('nomer').replace(/\s+/g, '');
                default:
                    return '';
            }
        }

        return '';
    }

    function frWithHyphens(s) {
        return s.replace(/^([A-Z]{2})(\d{3})([A-Z]{2})$/i, '$1-$2-$3');
    }

    function onlyYearFromDate(s) {
        if (!s) return '';
        const m = String(s).match(/(\d{4})/);
        return m ? m[1] : '';
    }

    function showFIWindow(info) {
        const existing = document.getElementById('fiFloatWin');
        if (existing) existing.remove();

        const wrap = document.createElement('div');
        wrap.id = 'fiFloatWin';
        wrap.style.position = 'fixed';
        wrap.style.top = '80px';
        wrap.style.right = '40px';
        wrap.style.zIndex = '99999';
        wrap.style.background = '#fff';
        wrap.style.border = '1px solid #ccc';
        wrap.style.borderRadius = '6px';
        wrap.style.boxShadow = '0 6px 18px rgba(0,0,0,0.18)';
        wrap.style.minWidth = '260px';
        wrap.style.maxWidth = '420px';
        wrap.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif';

        const header = document.createElement('div');
        header.style.padding = '10px 12px';
        header.style.borderBottom = '1px solid #eee';
        header.style.display = 'flex';
        header.style.alignItems = 'center';
        header.style.justifyContent = 'space-between';
        header.style.background = '#f8f9fa';

        const makeModel = `${info.manufacturerName || ''} ${info.model || ''}`.trim().replace(/\s+/g, ' ');
        const makeModelSpanHTML =
              `<span style="border-bottom: 1px dotted;" onclick="$('#markamodtype').val('${escAttr(makeModel)}').autocomplete('search', '${escAttr(makeModel)}'); return false;">${escHtml(makeModel)}</span>`;
        const y = onlyYearFromDate(info.registrationDate);
        const title = document.createElement('div');
        title.innerHTML = `${makeModelSpanHTML}${y ? ` (${escHtml(y)})` : ''}`;
        title.style.fontWeight = '600';

        const close = document.createElement('div');
        close.textContent = '×';
        close.title = 'Close';
        close.style.cursor = 'pointer';
        close.style.fontSize = '18px';
        close.style.lineHeight = '18px';
        close.style.marginLeft = '10px';
        close.onclick = () => wrap.remove();

        header.appendChild(title);
        header.appendChild(close);

        const body = document.createElement('div');
        body.style.padding = '10px 12px';
        body.style.fontSize = '14px';

        const rows = [
            ['Manufacturer', info.manufacturerName],
            ['Model', info.model],
            ['Type', info.type],
            ['VIN', info.VIN],
            ['Registration date', info.registrationDate],
            ['Fuel', info.fuel],
            ['Power', (info.powerKw || info.powerHp) ? `${info.powerKw ?? ''}${info.powerKw ? ' kW' : ''}${(info.powerKw && info.powerHp) ? ' / ' : ''}${info.powerHp ?? ''}${info.powerHp ? ' hp' : ''}` : ''],
        ].filter(([, v]) => v);

        for (const [label, value] of rows) {
            const row = document.createElement('div');
            row.style.display = 'flex';
            row.style.alignItems = 'center';
            row.style.margin = '6px 0';

            const text = document.createElement('div');
            text.textContent = `${label}: ${value}`;
            text.style.flex = '1 1 auto';

            const img = document.createElement('img');
            img.src = 'https://i.imgur.com/RjmoRpu.png';
            img.alt = 'Copy';
            img.title = 'Copy';
            img.style.height = '1em';
            img.style.cursor = 'pointer';
            img.style.marginLeft = '8px';
            img.onclick = () => copyToClipboard(String(value));

            row.appendChild(text);
            row.appendChild(img);
            body.appendChild(row);
        }

        wrap.appendChild(header);
        wrap.appendChild(body);
        document.body.appendChild(wrap);
        makeDraggable(wrap, header);
    }

    function showTrodoWindow(info) {
        const existing = document.getElementById('trodoFloatWin');
        if (existing) existing.remove();

        const wrap = document.createElement('div');
        wrap.id = 'trodoFloatWin';
        wrap.style.position = 'fixed';
        wrap.style.top = '80px';
        wrap.style.right = '40px';
        wrap.style.zIndex = '99999';
        wrap.style.background = '#fff';
        wrap.style.border = '1px solid #ccc';
        wrap.style.borderRadius = '6px';
        wrap.style.boxShadow = '0 6px 18px rgba(0,0,0,0.18)';
        wrap.style.minWidth = '260px';
        wrap.style.maxWidth = '420px';
        wrap.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif';

        const header = document.createElement('div');
        header.style.padding = '10px 12px';
        header.style.borderBottom = '1px solid #eee';
        header.style.display = 'flex';
        header.style.alignItems = 'center';
        header.style.justifyContent = 'space-between';
        header.style.background = '#f8f9fa';

        const makeModel = `${info.manufacturer || ''} ${info.model || ''}`.trim().replace(/\s+/g, ' ');
        const makeModelSpanHTML =
              `<span style="border-bottom: 1px dotted;" onclick="$('#markamodtype').val('${escAttr(makeModel)}').autocomplete('search', '${escAttr(makeModel)}'); return false;">${escHtml(makeModel)}</span>`;
        const y = info.year ? ` (${escHtml(info.year)})` : '';
        const title = document.createElement('div');
        title.innerHTML = `${makeModelSpanHTML}${y}`;
        title.style.fontWeight = '600';

        const close = document.createElement('div');
        close.textContent = '×';
        close.title = 'Close';
        close.style.cursor = 'pointer';
        close.style.fontSize = '18px';
        close.style.lineHeight = '18px';
        close.style.marginLeft = '10px';
        close.onclick = () => wrap.remove();

        header.appendChild(title);
        header.appendChild(close);

        const body = document.createElement('div');
        body.style.padding = '10px 12px';
        body.style.fontSize = '14px';

        const rows = [
            ['Manufacturer', info.manufacturer],
            ['Model', info.model],
            ['Variant', info.variant],
            ['VIN', info.vin],
            ['Year', info.year],
            ['Fuel', info.fuel],
            ['Engine', info.engine],
            ['Power', info.power],
        ].filter(([, v]) => v);

        for (const [label, value] of rows) {
            const row = document.createElement('div');
            row.style.display = 'flex';
            row.style.alignItems = 'center';
            row.style.margin = '6px 0';

            const text = document.createElement('div');
            text.textContent = `${label}: ${value}`;
            text.style.flex = '1 1 auto';

            const img = document.createElement('img');
            img.src = 'https://i.imgur.com/RjmoRpu.png';
            img.alt = 'Copy';
            img.title = 'Copy';
            img.style.height = '1em';
            img.style.cursor = 'pointer';
            img.style.marginLeft = '8px';
            img.onclick = () => copyToClipboard(String(value));

            row.appendChild(text);
            row.appendChild(img);
            body.appendChild(row);
        }

        wrap.appendChild(header);
        wrap.appendChild(body);
        document.body.appendChild(wrap);
        makeDraggable(wrap, header);
    }

    async function fetchMotonetData(fiPlateRaw) {
        const plate = String(fiPlateRaw || '').trim().toUpperCase();
        if (!plate) throw new Error('Empty plate');
        const url = `https://www.motonet.fi/api/vehicleInfo/registrationNumber/FI?locale=fi&registrationNumber=${encodeURIComponent(plate)}`;
        const data = await httpGet(url).catch(() => ({}));
        return data && typeof data === 'object' ? data : {};
    }

    async function fetchTrodoData(itPlateRaw) {
        const plate = String(itPlateRaw || '').trim().toUpperCase();
        if (!plate) throw new Error('Empty plate');
        const url = `https://www.trodo.it/rest/V1/partfinder/search/IT/${encodeURIComponent(plate)}/1`;

        try {
            const response = await new Promise((resolve, reject) => {
                if (typeof GM_xmlhttpRequest === 'function') {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url,
                        headers: { 'Accept': 'application/xml' },
                        onload: (res) => {
                            // Check for captcha response
                            if (res.responseText.includes('Just a moment...')) {
                                reject(new Error('CAPTCHA_REQUIRED'));
                                return;
                            }
                            resolve(res);
                        },
                        onerror: (e) => reject(e),
                    });
                } else {
                    fetch(url, { headers: { 'Accept': 'application/xml' } })
                        .then(r => r.text())
                        .then(text => {
                        if (text.includes('Just a moment...')) {
                            reject(new Error('CAPTCHA_REQUIRED'));
                            return;
                        }
                        resolve({ responseText: text });
                    })
                        .catch(reject);
                }
            });

            // Parse XML response
            const parser = new DOMParser();
            const xmlDoc = parser.parseFromString(response.responseText, 'text/xml');

            // Extract vehicle information from the XML structure
            const values = xmlDoc.querySelectorAll('values item');
            const info = {
                manufacturer: '',
                model: '',
                variant: '',
                year: '',
                fuel: '',
                engine: '',
                power: '',
                vin: ''
            };

            // Extract VIN from attributes
            const vinAttr = xmlDoc.querySelector('attributes vin');
            if (vinAttr) {
                info.vin = vinAttr.textContent || '';
            }

            // Extract vehicle details from values
            for (const item of values) {
                const label = item.querySelector('label name MagentoFrameworkPhrasetext');
                const value = item.querySelector('value');
                const year = item.querySelector('label year');
                const yearFrom = item.querySelector('label year_from');
                const yearTo = item.querySelector('label year_to');
                const fuel = item.querySelector('label engine_fuel MagentoFrameworkPhrasetext');
                const engine = item.querySelector('label engine');
                const power = item.querySelector('label kw_ps');
                const ccm = item.querySelector('label ccm');
                const liters = item.querySelector('label liters');

                if (label && value) {
                    const labelText = label.textContent || '';
                    const valueText = value.textContent || '';

                    // Map different fields based on dropdown_id or label content
                    const dropdownId = item.querySelector('dropdown_id')?.textContent || '';

                    if (dropdownId === '1' || labelText.includes('VOLVO')) {
                        info.manufacturer = labelText;
                    } else if (dropdownId === '2' || (labelText.includes('XC') && !labelText.includes('('))) {
                        info.model = labelText;
                    } else if (dropdownId === '3' || labelText.includes('(')) {
                        info.variant = labelText;
                    }
                }

                if (yearFrom) {
                    info.year = yearFrom.textContent || '';
                }

                if (fuel) {
                    info.fuel = fuel.textContent || '';
                }

                if (engine) {
                    const engineItems = engine.querySelectorAll('item');
                    const engineNames = Array.from(engineItems).map(item => item.textContent).filter(Boolean);
                    if (engineNames.length > 0) {
                        info.engine = engineNames.join(', ');
                    }
                }

                if (power) {
                    info.power = power.textContent || '';
                }
            }

            return info;
        } catch (error) {
            if (error.message === 'CAPTCHA_REQUIRED') {
                throw new Error('CAPTCHA_REQUIRED');
            }
            throw error;
        }
    }

    // --- Utils for RDW API + Floating Window ---
    function httpGet(url) {
        return new Promise((resolve, reject) => {
            if (typeof GM_xmlhttpRequest === 'function') {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url,
                    headers: { 'Accept': 'application/json' },
                    onload: (res) => {
                        try { resolve(JSON.parse(res.responseText)); }
                        catch (e) { reject(e); }
                    },
                    onerror: (e) => reject(e),
                });
            } else {
                fetch(url).then(r => r.json()).then(resolve).catch(reject);
            }
        });
    }

    function onlyYear(yyyymmdd) {
        if (!yyyymmdd || typeof yyyymmdd !== 'string') return 'unknown';
        if (/^\d{8}$/.test(yyyymmdd)) return yyyymmdd.slice(0,4);
        return 'unknown';
    }

    function copyToClipboard(text) {
        const doFallback = () => {
            const ta = document.createElement('textarea');
            ta.value = text;
            document.body.appendChild(ta);
            ta.select();
            try { document.execCommand('copy'); } catch {}
            document.body.removeChild(ta);
        };
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(text).catch(doFallback);
        } else {
            doFallback();
        }
    }

    function escHtml(s) {
        return String(s).replace(/[&<>"']/g, (c) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
    }
    function escAttr(s) {
        return String(s).replace(/['"]/g, (c) => ({ "'": "\\'", '"': '\\"' }[c]));
    }

    function makeDraggable(win, handle) {
        let ox=0, oy=0, dragging=false;
        handle.style.cursor = 'move';
        handle.addEventListener('mousedown', (e) => {
            dragging = true;
            ox = e.clientX - win.offsetLeft;
            oy = e.clientY - win.offsetTop;
            document.addEventListener('mousemove', move);
            document.addEventListener('mouseup', up);
            e.preventDefault();
        });
        function move(e) {
            if (!dragging) return;
            win.style.left = (e.clientX - ox) + 'px';
            win.style.top = (e.clientY - oy) + 'px';
        }
        function up() {
            dragging = false;
            document.removeEventListener('mousemove', move);
            document.removeEventListener('mouseup', up);
        }
    }

    function showRDWWindow(info) {
        const existing = document.getElementById('rdwFloatWin');
        if (existing) existing.remove();

        const wrap = document.createElement('div');
        wrap.id = 'rdwFloatWin';
        wrap.style.position = 'fixed';
        wrap.style.top = '80px';
        wrap.style.right = '40px';
        wrap.style.zIndex = '99999';
        wrap.style.background = '#fff';
        wrap.style.border = '1px solid #ccc';
        wrap.style.borderRadius = '6px';
        wrap.style.boxShadow = '0 6px 18px rgba(0,0,0,0.18)';
        wrap.style.minWidth = '260px';
        wrap.style.maxWidth = '380px';
        wrap.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif';

        const header = document.createElement('div');
        header.style.padding = '10px 12px';
        header.style.borderBottom = '1px solid #eee';
        header.style.display = 'flex';
        header.style.alignItems = 'center';
        header.style.justifyContent = 'space-between';
        header.style.background = '#f8f9fa';

        const makeModel = `${info.make} ${info.model}`.trim().replace(/\s+/g,' ');
        const makeModelSpanHTML =
              `<span style="border-bottom: 1px dotted;" onclick="$('#markamodtype').val('${escAttr(makeModel)}').autocomplete('search', '${escAttr(makeModel)}'); return false;">${escHtml(makeModel)}</span>`;
        const y = info.yFirst && info.yFirst !== 'unknown' ? ` (${escHtml(info.yFirst)})` : '';
        const title = document.createElement('div');
        title.innerHTML = `${makeModelSpanHTML}${y}`;
        title.style.fontWeight = '600';

        const close = document.createElement('div');
        close.textContent = '×';
        close.title = 'Close';
        close.style.cursor = 'pointer';
        close.style.fontSize = '18px';
        close.style.lineHeight = '18px';
        close.style.marginLeft = '10px';
        close.onclick = () => wrap.remove();

        header.appendChild(title);
        header.appendChild(close);

        const body = document.createElement('div');
        body.style.padding = '10px 12px';
        body.style.fontSize = '14px';

        const rows = [
            ['Make', info.make],
            ['Model', info.model],
            ['Variant', info.variant],
            ['First registration', info.yFirst],
            ['First registration in NL', info.yNL],
            ['Fuel', info.fuel],
            ['Doors', info.doors],
            ['Color', info.color],
        ];

        for (const [label, value] of rows) {
            const row = document.createElement('div');
            row.style.display = 'flex';
            row.style.alignItems = 'center';
            row.style.margin = '6px 0';

            const text = document.createElement('div');
            text.textContent = `${label}: ${value}`;
            text.style.flex = '1 1 auto';

            const img = document.createElement('img');
            img.src = 'https://i.imgur.com/RjmoRpu.png';
            img.alt = 'Copy';
            img.title = 'Copy';
            img.style.height = '1em';
            img.style.cursor = 'pointer';
            img.style.marginLeft = '8px';
            img.onclick = () => copyToClipboard(String(value));

            row.appendChild(text);
            row.appendChild(img);
            body.appendChild(row);
        }

        wrap.appendChild(header);
        wrap.appendChild(body);
        document.body.appendChild(wrap);
        makeDraggable(wrap, header);
    }

    async function fetchRDWData(kentekenRaw) {
        const plate = String(kentekenRaw || '').trim().replace(/[\s-]+/g, '').toUpperCase();
        if (!plate) throw new Error('Empty plate');

        const baseUrl = 'https://opendata.rdw.nl/resource/m9d7-ebf2.json';
        const fuelUrl = 'https://opendata.rdw.nl/resource/8ys7-d773.json';
        const qBase = `${baseUrl}?kenteken=${encodeURIComponent(plate)}`;
        const qFuel = `${fuelUrl}?kenteken=${encodeURIComponent(plate)}`;

        const [base, fuel] = await Promise.all([
            httpGet(qBase).catch(() => []),
            httpGet(qFuel).catch(() => []),
        ]);

        const primary = Array.isArray(base) && base.length ? base[0] : {};
        const fuelTypes = (Array.isArray(fuel) ? fuel : [])
        .map(x => x.brandstof_omschrijving || x.brandstof_omschrijving_ || x.brandstof || '')
        .filter(Boolean);

        const info = {
            make: primary.merk || 'unknown',
            model: primary.handelsbenaming || 'unknown',
            variant: primary.variant || 'unknown',
            yFirst: onlyYear(primary.datum_eerste_toelating),
            yNL: onlyYear(primary.datum_eerste_tenaamstelling_in_nederland),
            fuel: fuelTypes.length ? fuelTypes.join('/') : 'unknown',
            doors: primary.aantal_deuren || 'unknown',
            color: primary.eerste_kleur || 'unknown',
        };

        return info;
    }

    // --- UI Button Factory (supports Large/Small modes) ---
    function makeBtn(label, disabled, onclick, iconUrl) {
        const btn = document.createElement('button');
        btn.disabled = !!disabled;
        const compact = isCompactMode();
        if (compact) {
            btn.title = label;
            btn.style.marginBottom = '0';
            btn.style.width = '35px';
            btn.style.height = '35px';
            btn.style.backgroundColor = disabled ? '#95a5a6' : '#3498db';
            btn.style.color = '#ffffff';
            btn.style.border = 'none';
            btn.style.cursor = disabled ? 'default' : 'pointer';
            btn.style.borderRadius = '50%';
            btn.style.display = 'flex';
            btn.style.alignItems = 'center';
            btn.style.justifyContent = 'center';
            btn.style.padding = '0';
            btn.style.gap = '0';
            btn.style.position = 'relative';

            if (iconUrl) {
                const img = document.createElement('img');
                const cfg = (iconUrl && iconUrl.candidates) ? iconUrl : { key: '', candidates: (Array.isArray(iconUrl) ? iconUrl.slice() : [iconUrl]) };
                const candidates = cfg.candidates.slice();
                const tryNext = () => {
                    if (!candidates.length) { img.remove(); return; }
                    img.src = candidates.shift();
                };
                img.alt = '';
                img.style.width = '29px';
                img.style.height = '29px';
                img.style.display = 'block';
                img.style.objectFit = 'contain';
                img.style.transition = 'opacity 120ms ease-in-out';
                img.onerror = tryNext;
                img.onload = () => {
                    if (cfg.key) {
                        faviconCache[cfg.key] = img.src;
                    }
                };
                btn.appendChild(img);
                tryNext();

                // Add a hidden label that shows on hover
                const labelSpan = document.createElement('span');
                labelSpan.innerText = label;
                labelSpan.style.position = 'absolute';
                labelSpan.style.top = '4px';
                labelSpan.style.bottom = '4px';
                labelSpan.style.left = '4px';
                labelSpan.style.right = '4px';
                labelSpan.style.display = 'flex';
                labelSpan.style.alignItems = 'center';
                labelSpan.style.justifyContent = 'center';
                labelSpan.style.textAlign = 'center';
                labelSpan.style.fontSize = '12px';
                labelSpan.style.lineHeight = '1.05';
                labelSpan.style.whiteSpace = 'normal';
                labelSpan.style.overflow = 'hidden';
                labelSpan.style.wordBreak = 'break-word';
                labelSpan.style.overflowWrap = 'anywhere';
                labelSpan.style.opacity = '0';
                labelSpan.style.pointerEvents = 'none';
                labelSpan.style.transition = 'opacity 120ms ease-in-out';
                btn.appendChild(labelSpan);

                // Hover behavior: expand, hide icon, show text
                btn.addEventListener('mouseenter', () => {
                    img.style.opacity = '0';
                    labelSpan.style.opacity = '1';

                    // Auto-fit text size to keep it inside 35x35 (with 4px insets)
                    let size = 12; // start larger for readability
                    const minSize = 7;
                    labelSpan.style.fontSize = size + 'px';
                    // Iterate down until it fits or we hit min
                    // Guard against excessive loops
                    for (let i = 0; i < 20; i++) {
                        const fits = labelSpan.scrollWidth <= labelSpan.clientWidth && labelSpan.scrollHeight <= labelSpan.clientHeight;
                        if (fits || size <= minSize) break;
                        size -= 0.5;
                        labelSpan.style.fontSize = size + 'px';
                    }
                });
                btn.addEventListener('mouseleave', () => {
                    img.style.opacity = '1';
                    labelSpan.style.opacity = '0';
                });
            }
        } else {
            btn.style.marginBottom = '0';
            btn.style.width = '100%';
            btn.style.backgroundColor = disabled ? '#95a5a6' : '#3498db';
            btn.style.color = '#ffffff';
            btn.style.border = 'none';
            btn.style.cursor = disabled ? 'default' : 'pointer';
            btn.style.height = '23px';
            btn.style.borderRadius = '4px';
            btn.style.display = 'flex';
            btn.style.alignItems = 'center';
            btn.style.justifyContent = 'center';
            btn.style.gap = '6px';
            btn.style.padding = '0 8px';

            if (iconUrl) {
                const img = document.createElement('img');
                const cfg = (iconUrl && iconUrl.candidates) ? iconUrl : { key: '', candidates: (Array.isArray(iconUrl) ? iconUrl.slice() : [iconUrl]) };
                const candidates = cfg.candidates.slice();
                const tryNext = () => {
                    if (!candidates.length) { img.remove(); return; }
                    img.src = candidates.shift();
                };
                img.onerror = tryNext;
                img.onload = () => {
                    if (cfg.key) {
                        faviconCache[cfg.key] = img.src;
                    }
                };
                img.alt = '';
                img.style.maxHeight = (23 - 2) + 'px';
                img.style.display = 'inline-block';
                img.style.verticalAlign = 'middle';
                btn.appendChild(img);
                tryNext();
            }

            const span = document.createElement('span');
            span.innerText = label;
            btn.appendChild(span);
        }

        if (onclick && !disabled) btn.onclick = onclick;
        return btn;
    }

    // --- Buttons (Lookup / Google Images / Autogespot) ---
    function createOrUpdateLookupButtons() {
        const host = document.getElementById('zoomimgid');
        if (!host) return;

        // Always ensure a grid container exists (even if there are no site lookups)
        let container = document.getElementById('lookupButtonsContainer');
        if (!container) {
            container = document.createElement('div');
            container.id = 'lookupButtonsContainer';
            container.style.display = 'grid';
            container.style.gap = '6px';
            host.parentNode.insertBefore(container, host);
        } else {
            container.innerHTML = '';
        }

        // Apply grid layout based on size mode
        if (isCompactMode()) {
            container.style.gridTemplateColumns = 'repeat(auto-fill, 35px)';
            container.style.justifyContent = 'center';
            container.style.justifyItems = 'center';
        } else {
            container.style.gridTemplateColumns = '1fr';
            container.style.justifyItems = '';
        }

        const fieldsOk = areFieldsFilled();
        const code = getCurrentCountryCode();
        let sites = lookupSites[code] || [];
        // Filter hidden sites for current country
        if (code) {
            const enabledSites = [];
            for (const s of sites) {
                if (isSiteEnabledForCountry(code, s.name)) enabledSites.push(s);
            }
            sites = enabledSites;
        }

        // Settings button (replaces inline radios)
        let existingControls = document.getElementById('lookupSettingsControls');
        if (existingControls) existingControls.remove();
        const controls = document.createElement('div');
        controls.id = 'lookupSettingsControls';
        controls.style.display = 'flex';
        controls.style.alignItems = 'center';
        controls.style.justifyContent = 'center';
        controls.style.margin = '4px 0 6px 0';
        const settingsBtn = document.createElement('button');
        settingsBtn.textContent = 'Settings';
        settingsBtn.style.width = "100%";
        settingsBtn.style.height = "100%";
        settingsBtn.style.flex = "1";
        settingsBtn.style.border = 'none';
        settingsBtn.style.borderRadius = '4px';
        settingsBtn.style.background = '#6c757d';
        settingsBtn.style.color = '#fff';
        settingsBtn.style.cursor = 'pointer';
        settingsBtn.onclick = openSettingsPanel;
        controls.appendChild(settingsBtn);
        container.parentNode.insertBefore(controls, container);

        // Countries with special flows
        const specialPL = isPage('pl');
        const specialLT = isPage('lt');
        const specialUS = isPage('us');
        const specialIT = isPage('it');

        if (specialPL) {
            const favicon = isCompactMode() ? getFaviconConfigForUrl('https://moj.gov.pl/') : null;
            const btn = makeBtn('Lookup', !fieldsOk, () => {
                const plateNumber = buildPlateForCurrentPage();
                const targetUrl =
                      `https://moj.gov.pl/nforms/engine/ng/index?nfWidReset=true&xFormsAppName=NormaEuro&xFormsOrigin=EXTERNAL&plateNumber=${encodeURIComponent(plateNumber)}#/search`;
                window.open(targetUrl, '_blank');
            }, isCompactMode() ? favicon : null);
            container.appendChild(btn);
            // keep going; Google/Autogespot will also append into this same container
        } else if (specialLT) {
            const favicon = isCompactMode() ? getFaviconConfigForUrl('https://www.cab.lt/') : null;
            const btn = makeBtn('Lookup', !fieldsOk, () => {
                const plateNumber = buildPlateForCurrentPage();
                let form = document.createElement('form');
                form.action = 'https://www.cab.lt/draustumo-patikra/';
                form.method = 'POST';
                form.target = '_blank';
                let inputCountry = document.createElement('input');
                inputCountry.type = 'hidden'; inputCountry.name = 'country'; inputCountry.value = 'LT';
                let inputPlate = document.createElement('input');
                inputPlate.type = 'hidden'; inputPlate.name = 'plate'; inputPlate.value = plateNumber;
                form.appendChild(inputCountry); form.appendChild(inputPlate);
                document.body.appendChild(form); form.submit(); document.body.removeChild(form);
            }, isCompactMode() ? favicon : null);
            container.appendChild(btn);
        } else if (specialUS) {
            const favClearVin = isCompactMode() ? getFaviconConfigForUrl('https://www.clearvin.com/') : null;
            const btn = makeBtn('ClearVin', !fieldsOk, () => {
                const plateRaw = document.getElementById('nomer').value || '';
                const plate = plateRaw.replace(/\s+/g, '');
                const state = getUSStateCode();
                if (!plate || !state) return;
                const targetUrl = `https://www.clearvin.com/en/payment/prepare/${encodeURIComponent(plate)}:${state}/`;
                window.open(targetUrl, '_blank');
            }, isCompactMode() ? favClearVin : null);
            container.appendChild(btn);
            const favFaxVin = isCompactMode() ? getFaviconConfigForUrl('https://www.faxvin.com/') : null;
            const btn2 = makeBtn('FaxVin', !fieldsOk, () => {
                const plateRaw = document.getElementById('nomer').value || '';
                const plate = plateRaw.replace(/\s+/g, '');
                const state = getUSStateCode();
                if (!plate || !state) return;
                const targetUrl = `https://www.faxvin.com/license-plate-lookup/result?plate=${encodeURIComponent(plate)}&state=${state}/`;
                window.open(targetUrl, '_blank');
            }, isCompactMode() ? favFaxVin : null);
            container.appendChild(btn2);
            const favTagNap = isCompactMode() ? getFaviconConfigForUrl('https://tagnap.com/') : null;
            const btn3 = makeBtn('TagNap', !fieldsOk, () => {
                const plateRaw = document.getElementById('nomer').value || '';
                const plate = plateRaw.replace(/\s+/g, '');
                const state = getUSStateCode();
                if (!plate || !state) return;
                const targetUrl = `https://tagnap.com/plates/${encodeURIComponent(plate)}-${state}/`;
                window.open(targetUrl, '_blank');
            }, isCompactMode() ? favTagNap : null);
            container.appendChild(btn3);
            const favInfoTracer = isCompactMode() ? getFaviconConfigForUrl('https://infotracer.com/') : null;
            const btn4 = makeBtn('InfoTracer', !fieldsOk, () => {
                const plateRaw = document.getElementById('nomer').value || '';
                const plate = plateRaw.replace(/\s+/g, '');
                const state = getUSStateCode();
                if (!plate || !state) return;
                const targetUrl = `https://infotracer.com/loading/?type=plate-lookup&s=rw&page=results&mercSubId=plate&state=${state}&tid=tagnap&plate=${encodeURIComponent(plate)}`;
                window.open(targetUrl, '_blank');
            }, isCompactMode() ? favInfoTracer : null);
            container.appendChild(btn4);
        } else if (specialIT) {
            // Trodo (API) button
            const trodoIcon = isCompactMode() ? getFaviconConfigForUrl('https://www.trodo.it/') : null;
            const trodoApiBtn = makeBtn('Trodo (API)', !fieldsOk, async () => {
                const plate = buildPlateForCurrentPage();
                if (!plate) return;
                trodoApiBtn.disabled = true;
                trodoApiBtn.style.opacity = '0.7';
                try {
                    const info = await fetchTrodoData(plate);
                    showTrodoWindow(info);
                } catch (e) {
                    if (e.message === 'CAPTCHA_REQUIRED') {
                        // Show captcha error message
                        const existing = document.getElementById('trodoCaptchaError');
                        if (existing) existing.remove();

                        const errorDiv = document.createElement('div');
                        errorDiv.id = 'trodoCaptchaError';
                        errorDiv.style.position = 'fixed';
                        errorDiv.style.top = '50%';
                        errorDiv.style.left = '50%';
                        errorDiv.style.transform = 'translate(-50%, -50%)';
                        errorDiv.style.zIndex = '99999';
                        errorDiv.style.background = '#fff';
                        errorDiv.style.border = '2px solid #dc3545';
                        errorDiv.style.borderRadius = '8px';
                        errorDiv.style.padding = '20px';
                        errorDiv.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
                        errorDiv.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif';
                        errorDiv.style.maxWidth = '400px';
                        errorDiv.style.textAlign = 'center';

                        errorDiv.innerHTML = `
                            <div style="color: #dc3545; font-size: 18px; font-weight: 600; margin-bottom: 10px;">⚠️ Captcha Required</div>
                            <div style="margin-bottom: 15px;">Cloudflare blocked the request. Please use the "Trodo (link)" button instead.</div>
                            <button onclick="this.parentElement.remove()" style="background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">OK</button>
                        `;

                        document.body.appendChild(errorDiv);

                        // Auto-remove after 5 seconds
                        setTimeout(() => {
                            if (errorDiv.parentNode) {
                                errorDiv.remove();
                            }
                        }, 5000);
                    } else {
                        showTrodoWindow({
                            manufacturer: 'unknown',
                            model: 'unknown',
                            variant: 'unknown',
                            year: 'unknown',
                            fuel: 'unknown',
                            engine: 'unknown',
                            power: 'unknown',
                            vin: 'unknown',
                        });
                    }
                } finally {
                    trodoApiBtn.disabled = false;
                    trodoApiBtn.style.opacity = '1';
                }
            }, trodoIcon);
            container.appendChild(trodoApiBtn);

            // Trodo (link) button
            const trodoLinkBtn = makeBtn('Trodo (link)', !fieldsOk, () => {
                const plate = buildPlateForCurrentPage();
                if (!plate) return;
                const targetUrl = `https://www.trodo.it/rest/V1/partfinder/search/IT/${encodeURIComponent(plate)}/1`;
                window.open(targetUrl, '_blank');
            }, trodoIcon);
            container.appendChild(trodoLinkBtn);
        }

        // Standard flow: add site buttons if present
        if (sites.length === 1) {
            const only = sites[0];
            const favicon = isCompactMode() ? getFaviconConfigForUrl(only.base) : null;
            const btn = makeBtn('Lookup', !fieldsOk, () => {
                const raw = buildPlateForCurrentPage();
                if (!raw) return;
                const finalPlate = (isPage('fr') && only.needsHyphen) ? frWithHyphens(raw) : raw;
                window.open(only.base + finalPlate, '_blank');
            }, favicon);
            container.appendChild(btn);
        } else if (sites.length > 1) {
            for (const site of sites) {
                const favicon = isCompactMode() ? getFaviconConfigForUrl(site.base) : null;
                const btn = makeBtn(site.name, !fieldsOk, () => {
                    const raw = buildPlateForCurrentPage();
                    if (!raw) return;
                    const finalPlate = (isPage('fr') && site.needsHyphen) ? frWithHyphens(raw) : raw;
                    window.open(site.base + finalPlate, '_blank');
                }, favicon);
                container.appendChild(btn);
            }
        }

        // --- NL: RDW API lookup button that opens floating window ---
        if (isPage('nl')) {
            const rdwIcon = isCompactMode() ? getFaviconConfigForUrl('https://www.rdw.nl/') : null;
            const rdwBtn = makeBtn('RDW (API)', !fieldsOk, async () => {
                const plate = buildPlateForCurrentPage();
                if (!plate) return;
                rdwBtn.disabled = true;
                rdwBtn.style.opacity = '0.7';
                try {
                    const info = await fetchRDWData(plate);
                    showRDWWindow(info);
                } catch (e) {
                    showRDWWindow({
                        make: 'unknown',
                        model: 'unknown',
                        variant: 'unknown',
                        yFirst: 'unknown',
                        yNL: 'unknown',
                        fuel: 'unknown',
                        doors: 'unknown',
                        color: 'unknown',
                    });
                } finally {
                    rdwBtn.disabled = false;
                    rdwBtn.style.opacity = '1';
                }
            }, rdwIcon);
            container.appendChild(rdwBtn);
        }

        // --- FI: Motonet API lookup button that opens floating window ---
        if (isPage('fi')) {
            const fiIcon = isCompactMode() ? getFaviconConfigForUrl('https://www.motonet.fi/') : null;
            const fiBtn = makeBtn('Motonet (Direct API)', !fieldsOk, async () => {
                const plate = buildPlateForCurrentPage(); // returns "AAA-123"
                if (!plate) return;
                fiBtn.disabled = true;
                fiBtn.style.opacity = '0.7';
                try {
                    const info = await fetchMotonetData(plate);
                    showFIWindow(info && Object.keys(info).length ? info : {});
                } catch (e) {
                    showFIWindow({});
                } finally {
                    fiBtn.disabled = false;
                    fiBtn.style.opacity = '1';
                }
            }, fiIcon);
            container.appendChild(fiBtn);
        }
    }

function createOrUpdateGoogleImagesButton() {
    const host = document.getElementById('zoomimgid');
    if (!host) return;

    // Respect global disable
    if (!isGoogleImagesGloballyEnabled()) {
        const existing = document.getElementById('googleImagesButton');
        if (existing) existing.remove();
        return;
    }

    let googleBtn = document.getElementById('googleImagesButton');
    if (!googleBtn) {
        googleBtn = makeBtn('Google Images', true, null, 'https://i.imgur.com/5x00UaD.png');
        googleBtn.id = 'googleImagesButton';
        const container = document.getElementById('lookupButtonsContainer');
        if (container) {
            container.appendChild(googleBtn);
        } else {
            host.parentNode.insertBefore(googleBtn, host); // fallback (shouldn’t happen now)
        }
    }

    const fieldsOk = areFieldsFilled();
    googleBtn.disabled = !fieldsOk;
    googleBtn.onclick = !fieldsOk ? null : function () {
        const plateNumber = buildPlateForSearchDisplay();
        if (!plateNumber) return;
        window.open('https://www.google.com/search?tbm=isch&q="' + plateNumber + '"', '_blank');
    };
}

function createOrUpdateAutogespotButton() {
    const host = document.getElementById('zoomimgid');
    if (!host) return;

    // Respect global disable
    if (!isAutogespotGloballyEnabled()) {
        const existing = document.getElementById('autogespotButton');
        if (existing) existing.remove();
        return;
    }

    let agBtn = document.getElementById('autogespotButton');
    if (!agBtn) {
        agBtn = makeBtn('Autogespot', true, null, 'https://i.imgur.com/X8HxriW.png');
        agBtn.id = 'autogespotButton';
        const container = document.getElementById('lookupButtonsContainer');
        if (container) {
            container.appendChild(agBtn);
        } else {
            host.parentNode.insertBefore(agBtn, host); // fallback (shouldn’t happen now)
        }
    }

    const fieldsOk = areFieldsFilled();
    agBtn.disabled = !fieldsOk;
    agBtn.style.marginBottom = '6px';
    agBtn.onclick = !fieldsOk ? null : function () {
        let plateCompact = buildPlateForCurrentPage();
        if (!plateCompact) {
            const display = buildPlateForSearchDisplay();
            plateCompact = (display || '').replace(/\s+/g, '');
        }
        if (!plateCompact) return;
        const target = `https://www.autogespot.com/spots?licenseplate=${encodeURIComponent(plateCompact)}`;
        window.open(target, '_blank');
    };
}

// --- Settings Panel UI ---
function openSettingsPanel() {
    const existing = document.getElementById('lookupSettingsPanel');
    if (existing) existing.remove();

    const code = getCurrentCountryCode();
    const wrapper = document.createElement('div');
    wrapper.id = 'lookupSettingsPanel';
    wrapper.style.position = 'fixed';
    wrapper.style.top = '80px';
    wrapper.style.right = '40px';
    wrapper.style.zIndex = '99999';
    wrapper.style.background = '#fff';
    wrapper.style.border = '1px solid #ccc';
    wrapper.style.borderRadius = '6px';
    wrapper.style.boxShadow = '0 6px 18px rgba(0,0,0,0.18)';
    wrapper.style.minWidth = '280px';
    wrapper.style.maxWidth = '380px';
    wrapper.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif';

    const header = document.createElement('div');
    header.style.padding = '10px 12px';
    header.style.borderBottom = '1px solid #eee';
    header.style.display = 'flex';
    header.style.alignItems = 'center';
    header.style.justifyContent = 'space-between';
    header.style.background = '#f8f9fa';

    const title = document.createElement('div');
    title.textContent = 'Lookup Toolbox Settings';
    title.style.fontWeight = '600';

    const close = document.createElement('div');
    close.textContent = '×';
    close.title = 'Close';
    close.style.cursor = 'pointer';
    close.style.fontSize = '18px';
    close.style.lineHeight = '18px';
    close.style.marginLeft = '10px';
    close.onclick = () => wrapper.remove();

    header.appendChild(title);
    header.appendChild(close);

    const body = document.createElement('div');
    body.style.padding = '10px 12px';
    body.style.fontSize = '14px';
    body.style.maxHeight = '60vh';
    body.style.overflow = 'auto';

    // View size section
    const viewSection = document.createElement('div');
    const currentMode = code ? getSizeModeForCountry(code) : getDefaultSizeMode();
    const viewTitle = document.createElement('div');
    viewTitle.textContent = 'View size';
    viewTitle.style.fontWeight = '600';
    viewTitle.style.marginBottom = '6px';
    const radiosWrap = document.createElement('div');
    radiosWrap.style.display = 'flex';
    radiosWrap.style.gap = '12px';
    const makeRadio = (label, value) => {
        const lab = document.createElement('label');
        lab.style.display = 'inline-flex'; lab.style.alignItems = 'center'; lab.style.gap = '6px';
        const input = document.createElement('input');
        input.type = 'radio'; input.name = 'ltb-view-size'; input.value = value;
        input.checked = currentMode === value;
        const span = document.createElement('span'); span.textContent = label;
        lab.appendChild(input); lab.appendChild(span);
        return lab;
    };
    radiosWrap.appendChild(makeRadio('Large', 'Large'));
    radiosWrap.appendChild(makeRadio('Compact', 'Compact'));

    const applyWrap = document.createElement('div');
    applyWrap.style.display = 'flex';
    applyWrap.style.gap = '10px';
    applyWrap.style.marginTop = '8px';
    const btnApplyCountry = document.createElement('button');
    btnApplyCountry.textContent = `Apply to ${code || 'XX'}`;
    btnApplyCountry.style.padding = '4px 8px';
    btnApplyCountry.style.border = 'none';
    btnApplyCountry.style.borderRadius = '4px';
    btnApplyCountry.style.background = '#3498db';
    btnApplyCountry.style.color = '#fff';
    btnApplyCountry.style.cursor = 'pointer';
    btnApplyCountry.onclick = () => {
        const val = (body.querySelector('input[name="ltb-view-size"]:checked') || {}).value || 'Large';
        if (code) setSizeModeForCountry(code, val);
        render();
    };
    const btnApplyAll = document.createElement('button');
    btnApplyAll.style.padding = '4px 8px';
    btnApplyAll.style.border = 'none';
    btnApplyAll.style.borderRadius = '4px';
    btnApplyAll.style.background = '#dc3545';
    btnApplyAll.style.color = '#fff';
    btnApplyAll.style.cursor = 'pointer';
    btnApplyAll.style.display = 'flex';
    btnApplyAll.style.flexDirection = 'column';
    btnApplyAll.style.alignItems = 'center';

    // main label
    const label = document.createElement('span');
    label.textContent = 'Apply to all countries';

    // smaller warning
    const warning = document.createElement('span');
    warning.textContent = '(will overwrite all custom settings!)';
    warning.style.fontSize = '0.8em';
    warning.style.opacity = '0.8';

    btnApplyAll.appendChild(label);
    btnApplyAll.appendChild(warning);

    btnApplyAll.onclick = () => {
        const val = (body.querySelector('input[name="ltb-view-size"]:checked') || {}).value || 'Large';
        setDefaultSizeMode(val);
        clearAllCountrySizeOverrides();
        render();
    };

    applyWrap.appendChild(btnApplyCountry);
    applyWrap.appendChild(btnApplyAll);


    viewSection.appendChild(viewTitle);
    viewSection.appendChild(radiosWrap);
    viewSection.appendChild(applyWrap);

    // Lookup sites section (per current country)
    const sitesSection = document.createElement('div');
    sitesSection.style.marginTop = '14px';
    const sitesTitle = document.createElement('div');
    sitesTitle.textContent = 'Lookup sites (uncheck to hide)';
    sitesTitle.style.fontWeight = '600';
    sitesTitle.style.marginBottom = '6px';
    const sitesWrap = document.createElement('div');
    sitesWrap.style.display = 'grid';
    sitesWrap.style.gridTemplateColumns = '1fr';
    sitesWrap.style.gap = '6px';
    const allSites = (lookupSites && code && lookupSites[code]) ? lookupSites[code] : [];
    const hiddenSet = code ? getHiddenSitesForCountry(code) : new Set();
    for (const site of allSites) {
        const lab = document.createElement('label');
        lab.style.display = 'flex'; lab.style.alignItems = 'center'; lab.style.gap = '8px';
        const cb = document.createElement('input');
        cb.type = 'checkbox';
        cb.checked = !hiddenSet.has(site.name);
        cb.addEventListener('change', () => {
            const newHidden = getHiddenSitesForCountry(code);
            if (cb.checked) {
                newHidden.delete(site.name);
            } else {
                newHidden.add(site.name);
            }
            setHiddenSitesForCountry(code, Array.from(newHidden));
            render();
        });
        const span = document.createElement('span'); span.textContent = site.name;
        lab.appendChild(cb); lab.appendChild(span);
        sitesWrap.appendChild(lab);
    }
    sitesSection.appendChild(sitesTitle);
    sitesSection.appendChild(sitesWrap);

    // Global toggles section
    const globalSection = document.createElement('div');
    globalSection.style.marginTop = '14px';
    const globalTitle = document.createElement('div');
    globalTitle.textContent = 'Other options';
    globalTitle.style.fontWeight = '600';
    globalTitle.style.marginBottom = '6px';
    const globalWrap = document.createElement('div');
    globalWrap.style.display = 'grid';
    globalWrap.style.gridTemplateColumns = '1fr';
    globalWrap.style.gap = '6px';

    const mkToggle = (label, isEnabled, onChange) => {
        const lab = document.createElement('label');
        lab.style.display = 'flex';
        lab.style.alignItems = 'center';
        lab.style.gap = '8px';
        const cb = document.createElement('input');
        cb.type = 'checkbox';
        cb.checked = isEnabled();
        cb.addEventListener('change', () => {
            onChange(cb.checked);
            render();
        });
        const span = document.createElement('span');
        span.textContent = label + ' (global)';
        lab.appendChild(cb); lab.appendChild(span);
        return lab;
    };

    globalWrap.appendChild(mkToggle('Show Google Images', isGoogleImagesGloballyEnabled, (checked) => setGoogleImagesGloballyEnabled(checked)));
    globalWrap.appendChild(mkToggle('Show Autogespot', isAutogespotGloballyEnabled, (checked) => setAutogespotGloballyEnabled(checked)));

    globalSection.appendChild(globalTitle);
    globalSection.appendChild(globalWrap);

    body.appendChild(viewSection);
    body.appendChild(sitesSection);
    body.appendChild(globalSection);

    wrapper.appendChild(header);
    wrapper.appendChild(body);
    document.body.appendChild(wrapper);
    try { makeDraggable(wrapper, header); } catch {}
}

// --- Initial render + live updates ---
function render() {
    createOrUpdateLookupButtons();
    createOrUpdateGoogleImagesButton();
    createOrUpdateAutogespotButton();
}

render();
setInterval(render, 1000);
})();