💪 Anaboles farmen

FarmGod vollständig integriert – keine externe Abhängigkeit

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/587597/1880936/%F0%9F%92%AA%20Anaboles%20farmen.js을(를) 사용하여 포함하는 라이브러리입니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         💪 Anaboles farmen
// @version      25
// @include      https://*/game.php*screen=am_farm*
// @namespace    https://greasyfork.org/users/1388863
// @description  FarmGod vollständig integriert – keine externe Abhängigkeit
// @grant        unsafeWindow
// ==/UserScript==

(function () {
    'use strict';

    // Hub-Check: respektiert den Ein/Aus-Schalter im IntelliScripts Hub (id: 'autofarm')
    function isEnabledInHub() {
        try {
            const reg = JSON.parse(localStorage.getItem('intellifarm_registry') || '{}');
            return reg.autofarm !== false; // Default: aktiviert, falls Hub fehlt/Eintrag fehlt
        } catch { return true; }
    }
    if (!isEnabledInHub()) {
        return;
    }

    // Screen-Guard: läuft jetzt evtl. per @require unter dem breiteren @match des Hubs,
    // daher muss die ursprüngliche @include-Einschränkung (screen=am_farm) hier im Code selbst geprüft werden.
    if (typeof game_data === 'undefined' || game_data.screen !== 'am_farm') {
        return;
    }

    const domain = window.location.hostname.split('.')[0];

    const DEFAULTS = {
        enterMin: 250, enterMax: 500,
        reloadMin: 600, reloadMax: 900,
        webhookUrl: '',
        holdEnter: false, holdInterval: 50,
        optionGroup: 0, optionDistance: 25,
        optionTime: 10, optionLosses: false,
        optionMaxloot: true, optionNewbarbs: true,
        villageDistances: {},
        villageSettings: {},
        // Nachtmodus
        nightModeEnabled: false,
        nightStart: '23:00',
        nightEnd: '07:00',
        nightReloadMin: 300,
        nightReloadMax: 600,
        nightOptionTime: 20,
        nightOptionMaxloot: true,
        nightOptionLosses: false,
        // Zufällige Pausen
        randomPauseEnabled: false,
        randomPauseIntervalMin: 20,
        randomPauseIntervalMax: 40,
        randomPauseDurationMin: 5,
        randomPauseDurationMax: 15,
        kattaEnabled: false,
        kattaGroup: 0,
        kattaAxe: 0,
        kattaRam: 0,
        kattaSpy: 0,
        kattaCata: 1,
        kattaTarget: 'wall',
        kattaOnRed: true,
        kattaOnYellow: false,
    };

    function loadSettings() {
        try {
            const saved = JSON.parse(localStorage.getItem(domain + '_autofarm_settings') || '{}');
            const merged = Object.assign({}, DEFAULTS, saved);
            // Objekt-Felder explizit mergen damit sie nicht von DEFAULTS überschrieben werden
            merged.villageSettings = saved.villageSettings || {};
            merged.villageDistances = saved.villageDistances || {};
            return merged;
        }
        catch (e) { return Object.assign({}, DEFAULTS); }
    }
    function saveSettings(s) { localStorage.setItem(domain + '_autofarm_settings', JSON.stringify(s)); }
    function loadPosition() { try { return JSON.parse(localStorage.getItem(domain + '_autofarm_pos') || 'null'); } catch (e) { return null; } }
    function savePosition(x, y) { localStorage.setItem(domain + '_autofarm_pos', JSON.stringify({ x, y })); }
    function loadMinimized() { return localStorage.getItem(domain + '_autofarm_minimized') === 'true'; }
    function saveMinimized(val) { localStorage.setItem(domain + '_autofarm_minimized', val); }

    let settings = loadSettings();
    let isRunning = JSON.parse(localStorage.getItem(domain + '_isRunning')) || false;
    let isMinimized = loadMinimized();
    let intervalId, holdIntervalId, countdownInterval, hourlyAlertInterval;
    let emptyFarmChecks = 0;
    let captchaAlertSent = localStorage.getItem(domain + '_captcha_paused') === 'true';
    let currentStatus = 'Bereit', currentCountdownText = '';
    let holdRepeatCount = 0;
    let todayLoot = null;
    let todayVillages = null;
    const EMPTY_FARM_THRESHOLD = 4;

    function randomDelay(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }

    // =====================================================================
    // DISCORD
    // =====================================================================
    function sendDiscordCaptchaAlert() {
        settings = loadSettings();
        if (!settings.webhookUrl) return;
        fetch(settings.webhookUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                username: 'Anaboles farmen',
                embeds: [{
                    title: '⚠️ Captcha erkannt!',
                    description: 'Anaboles farmen wurde gestoppt.',
                    color: 0xe74c3c,
                    fields: [
                        { name: '🌐 Server', value: domain, inline: true },
                        { name: '🕒 Zeit', value: new Date().toLocaleString('de-DE'), inline: true },
                        { name: '🔗 Seite', value: window.location.href, inline: false }
                    ],
                    footer: { text: 'Anaboles farmen – Captcha Alert' }
                }]
            })
        }).catch(e => console.error('[Autofarm] Webhook Fehler:', e));
    }

    function sendDiscordHourlyUpdate() {
        settings = loadSettings();
        if (!settings.webhookUrl) return;
        const avg = (todayLoot && todayVillages && todayVillages.value > 0)
            ? formatLoot(Math.round(todayLoot.value / todayVillages.value)) + ' / Angriff'
            : '–';
        fetch(settings.webhookUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                username: 'Anaboles farmen',
                embeds: [{
                    title: '📊 Stündliches Update',
                    description: `Anaboles farmen läuft auf **${domain}**`,
                    color: 0x27ae60,
                    fields: [
                        { name: '📦 Rohstoffe heute', value: todayLoot ? `${todayLoot.value.toLocaleString('de-DE')} (Rang ${todayLoot.rank})` : '–', inline: false },
                        { name: '🏘️ Dörfer heute', value: todayVillages ? `${todayVillages.value.toLocaleString('de-DE')} (Rang ${todayVillages.rank})` : '–', inline: false },
                        { name: '⚡ Schnitt', value: avg, inline: false },
                        { name: '🕒 Zeit', value: new Date().toLocaleString('de-DE'), inline: true },
                        { name: '⚙️ Status', value: isRunning ? '✅ Läuft' : '⏹ Gestoppt', inline: true }
                    ],
                    footer: { text: 'Anaboles farmen – Stündliches Update' }
                }]
            })
        }).catch(e => console.error('[Autofarm] Webhook Fehler:', e));
    }

    const HOURLY_ALERT_KEY = domain + '_autofarm_last_hourly_alert';

    function checkAndSendHourlyAlert() {
        if (!isRunning) return;
        if (!loadSettings().webhookUrl) return;
        const last = parseInt(localStorage.getItem(HOURLY_ALERT_KEY) || '0');
        const now = Date.now();
        if (now - last >= 60 * 60 * 1000) {
            localStorage.setItem(HOURLY_ALERT_KEY, now);
            // Daten laden, dann erst senden wenn Daten wirklich da sind
            fetchTodayLoot(sendDiscordHourlyUpdate);
        }
    }

    function startHourlyAlert() {
        clearInterval(hourlyAlertInterval);
        // Timestamp nur setzen wenn noch keiner vorhanden (überlebt Reloads)
        if (!localStorage.getItem(HOURLY_ALERT_KEY)) {
            localStorage.setItem(HOURLY_ALERT_KEY, Date.now());
        }
        // Jede Minute prüfen ob eine Stunde vergangen ist (reload-sicher)
        hourlyAlertInterval = setInterval(checkAndSendHourlyAlert, 60 * 1000);
    }

    function stopHourlyAlert() {
        clearInterval(hourlyAlertInterval);
        // Timestamp zurücksetzen damit beim nächsten Start neu gezählt wird
        localStorage.removeItem(HOURLY_ALERT_KEY);
    }

    // =====================================================================
    // TAGESERGEBNIS
    // =====================================================================
    function parseRankingPage(html) {
        const $html = $(html);
        const bodyText = $html.text ? $html.text() : $(html).text();
        const matchDE = bodyText.match(/Mein heutiges Ergebnis[^\d]*([\d\.]+)/i);
        const matchEN = bodyText.match(/My result today[^\d]*([\d,]+)/i);
        const matchRaw = matchDE || matchEN;
        let value = null;
        if (matchRaw) value = parseInt(matchRaw[1].replace(/\./g, '').replace(/,/g, ''));
        let rank = null;
        $html.find('tr').each((i, row) => {
            const $row = $(row);
            if ($row.find('b, strong').length > 0) {
                const cells = $row.find('td');
                if (cells.length >= 2) {
                    const r = parseInt(cells.eq(0).text().trim());
                    if (!isNaN(r) && r > 0) rank = r;
                }
            }
        });
        return { value, rank };
    }

    function fetchTodayLoot(callback) {
        const urlRes = TribalWars.buildURL('GET', 'ranking', { mode: 'in_a_day', type: 'loot_res' });
        const urlVil = TribalWars.buildURL('GET', 'ranking', { mode: 'in_a_day', type: 'loot_vil' });
        Promise.all([$.get(urlRes), $.get(urlVil)]).then(([htmlRes, htmlVil]) => {
            const res = parseRankingPage(htmlRes);
            const vil = parseRankingPage(htmlVil);
            if (res.value && !isNaN(res.value)) todayLoot = { value: res.value, rank: res.rank || '?', updated: new Date() };
            if (vil.value && !isNaN(vil.value)) todayVillages = { value: vil.value, rank: vil.rank || '?', updated: new Date() };
            updateLootDisplay();
            console.log(`[Autofarm] Rohstoffe: ${res.value} | Dörfer: ${vil.value}`);
            if (typeof callback === 'function') callback();
        }).catch(e => console.warn('[Autofarm] Loot-Abruf fehlgeschlagen:', e));
    }

    function formatLoot(val) {
        if (!val || isNaN(val)) return '–';
        if (val >= 1000000) return (val / 1000000).toFixed(1) + 'M';
        if (val >= 1000) return (val / 1000).toFixed(1) + 'K';
        return val.toLocaleString('de-DE');
    }

    function updateLootDisplay() {
        const mini = document.getElementById('af-loot-mini');
        const fullRes = document.getElementById('af-loot-res');
        const fullVil = document.getElementById('af-loot-vil');
        const fullAvg = document.getElementById('af-loot-avg');
        const resText = todayLoot ? `📦 ${formatLoot(todayLoot.value)} (Rang ${todayLoot.rank})` : '📦 –';
        const vilText = todayVillages ? `🏘️ ${todayVillages.value.toLocaleString('de-DE')} Dörfer (Rang ${todayVillages.rank})` : '🏘️ –';
        const avg = (todayLoot && todayVillages && todayVillages.value > 0) ? Math.round(todayLoot.value / todayVillages.value) : null;
        const avgText = avg ? `⚡ ${formatLoot(avg)} / Angriff` : '⚡ –';
        if (mini) mini.innerHTML = `${resText}<br>${vilText}<br>${avgText}`;
        if (fullRes) fullRes.innerText = todayLoot ? `${todayLoot.value.toLocaleString('de-DE')} (Rang ${todayLoot.rank})` : '–';
        if (fullVil) fullVil.innerText = todayVillages ? `${todayVillages.value.toLocaleString('de-DE')} (Rang ${todayVillages.rank})` : '–';
        if (fullAvg) fullAvg.innerText = avg ? `${avg.toLocaleString('de-DE')} / Angriff` : '–';
    }

    // =====================================================================
    // GRUPPE ZURÜCKSETZEN
    // =====================================================================
    function resetGroupToAll() {
        if (settings.optionGroup === 0) return;
        // Gruppe im Farm-Assistenten zurück auf "Alle" (group_id 0)
        const url = TribalWars.buildURL('GET', 'am_farm', { ajax: 'change_group', group: 0 });
    }

    // =====================================================================
    // NACHTMODUS & ZUFÄLLIGE PAUSEN
    // =====================================================================
    let randomPauseTimeout = null;
    let isRandomPausing = false;

    function isNightMode() {
        settings = loadSettings();
        if (!settings.nightModeEnabled) return false;
        const now = new Date();
        const currentMinutes = now.getHours() * 60 + now.getMinutes();
        const [startH, startM] = settings.nightStart.split(':').map(Number);
        const [endH, endM] = settings.nightEnd.split(':').map(Number);
        const startMinutes = startH * 60 + startM;
        const endMinutes = endH * 60 + endM;
        // Über Mitternacht
        if (startMinutes > endMinutes) {
            return currentMinutes >= startMinutes || currentMinutes < endMinutes;
        }
        return currentMinutes >= startMinutes && currentMinutes < endMinutes;
    }

    function getReloadDelay() {
        settings = loadSettings();
        if (isNightMode()) {
            return randomDelay(settings.nightReloadMin, settings.nightReloadMax);
        }
        return randomDelay(settings.reloadMin, settings.reloadMax);
    }

    const RANDOM_PAUSE_KEY = domain + '_autofarm_next_pause';
    const RANDOM_PAUSE_ACTIVE_KEY = domain + '_autofarm_pause_active';

    // Gibt die Reload-Zeit zurück – bei fälliger Pause: längeres Intervall
    function getRandomPauseReloadDelay(normalDelay) {
        settings = loadSettings();
        if (!settings.randomPauseEnabled) return normalDelay;
        // Zufällige Pausen nur im Nachtmodus wenn nightModeEnabled
        if (settings.nightModeEnabled && !isNightMode()) return normalDelay;

        const now = Date.now();
        const nextPause = parseInt(localStorage.getItem(RANDOM_PAUSE_KEY) || '0');

        if (nextPause === 0) {
            // Ersten Pause-Zeitpunkt setzen
            const intervalSec = randomDelay(
                settings.randomPauseIntervalMin * 60,
                settings.randomPauseIntervalMax * 60
            );
            localStorage.setItem(RANDOM_PAUSE_KEY, now + intervalSec * 1000);
            console.log(`[Autofarm] Nächste Pause in ${Math.round(intervalSec/60)} Min`);
            return normalDelay;
        }

        if (now >= nextPause) {
            // Pause fällig – längeres Reload-Intervall verwenden
            const durationSec = randomDelay(
                settings.randomPauseDurationMin * 60,
                settings.randomPauseDurationMax * 60
            );
            // Nächste Pause einplanen ab jetzt + Pausendauer + normales Intervall
            const intervalSec = randomDelay(
                settings.randomPauseIntervalMin * 60,
                settings.randomPauseIntervalMax * 60
            );
            localStorage.setItem(RANDOM_PAUSE_KEY, now + durationSec * 1000 + intervalSec * 1000);
            localStorage.setItem(RANDOM_PAUSE_ACTIVE_KEY, '1');
            console.log(`[Autofarm] Pause fällig: ${Math.round(durationSec/60)} Min längeres Reload`);
            isRandomPausing = true;
            return durationSec; // Reload-Intervall = Pausendauer
        }

        return normalDelay;
    }

    function scheduleRandomPause(callback) {
        callback(); // direkt aufrufen – Pause-Logik ist in startCountdown integriert
    }

    function clearRandomPause() {
        if (randomPauseTimeout) { clearTimeout(randomPauseTimeout); randomPauseTimeout = null; }
        localStorage.removeItem(RANDOM_PAUSE_KEY);
        localStorage.removeItem(RANDOM_PAUSE_ACTIVE_KEY);
        isRandomPausing = false;
    }

    // =====================================================================
    // FARMGOD LIBRARY
    // =====================================================================
    const FarmLib = (function () {
        if (typeof window.twLib === 'undefined') {
            window.twLib = {
                queues: null,
                init: function () { if (this.queues === null) this.queues = this.queueLib.createQueues(5); },
                queueLib: {
                    maxAttempts: 3,
                    Item: function (action, arg, promise = null) { this.action = action; this.arguments = arg; this.promise = promise; this.attempts = 0; },
                    Queue: function () {
                        this.list = []; this.working = false; this.length = 0;
                        this.doNext = function () {
                            let item = this.dequeue(), self = this;
                            $[item.action](...item.arguments)
                                .done(function () { item.promise.resolve.apply(null, arguments); self.start(); })
                                .fail(function () { item.attempts += 1; if (item.attempts < twLib.queueLib.maxAttempts) self.enqueue(item, true); else item.promise.reject.apply(null, arguments); self.start(); });
                        };
                        this.start = function () { if (this.length) { this.working = true; this.doNext(); } else this.working = false; };
                        this.dequeue = function () { this.length -= 1; return this.list.shift(); };
                        this.enqueue = function (item, front = false) { front ? this.list.unshift(item) : this.list.push(item); this.length += 1; if (!this.working) this.start(); };
                    },
                    createQueues: function (amount) { let arr = []; for (let i = 0; i < amount; i++) arr[i] = new twLib.queueLib.Queue(); return arr; },
                    addItem: function (item) { let min = twLib.queues.map(q => q.length).reduce((n, c) => (c < n ? c : n), 0); twLib.queues[min].enqueue(item); },
                    orchestrator: function (type, arg) { let p = $.Deferred(); twLib.queueLib.addItem(new twLib.queueLib.Item(type, arg, p)); return p; },
                },
                ajax: function () { return twLib.queueLib.orchestrator('ajax', arguments); },
                get: function () { return twLib.queueLib.orchestrator('get', arguments); },
                post: function () { return twLib.queueLib.orchestrator('post', arguments); },
            };
            twLib.init();
        }

        const setUnitSpeeds = () => $.when($.get('/interface.php?func=get_unit_info')).then((xml) => {
            let u = {}; $(xml).find('config').children().map((i, el) => { u[$(el).prop('nodeName')] = $(el).find('speed').text().toNumber(); });
            localStorage.setItem('FarmGod_unitSpeeds', JSON.stringify(u));
        });
        const getUnitSpeeds = () => JSON.parse(localStorage.getItem('FarmGod_unitSpeeds')) || false;
        if (!getUnitSpeeds()) setUnitSpeeds();

        const determineNextPage = (page, $html) => {
            let vl = $html.find('#scavenge_mass_screen').length > 0 ? $html.find('tr[id*="scavenge_village"]').length : $html.find('tr.row_a, tr.row_ax, tr.row_b, tr.row_bx').length;
            let ns = $html.find('.paged-nav-item').first().closest('td').find('select').first();
            let nl = $html.find('#am_widget_Farm').length > 0
                ? parseInt($('#plunder_list_nav').first().find('a.paged-nav-item, strong.paged-nav-item')[$('#plunder_list_nav').first().find('a.paged-nav-item, strong.paged-nav-item').length - 1].textContent.replace(/\D/g, '')) - 1
                : ns.length > 0 ? ns.find('option').length - 1 : $html.find('.paged-nav-item').not('[href*="page=-1"]').length;
            let ps = $('#mobileHeader').length > 0 ? 10 : parseInt($html.find('input[name="page_size"]').val());
            if (page == -1 && vl == 1000) return Math.floor(1000 / ps);
            else if (page < nl) return page + 1;
            return false;
        };

        const processPage = (url, page, wrapFn) => {
            const fullUrl = url + (url.match('am_farm') ? `&Farm_page=${page}` : `&page=${page}`);
                        return twLib.ajax({ url: fullUrl }).then((html) => {
                return wrapFn(page, $(html));
            }).catch(e => {
                console.error(`[AF] processPage ERROR p=${page}:`, e);
                throw e;
            });
        };
        const processAllPages = (url, processorFn) => {
            let page = url.match('am_farm') || url.match('scavenge_mass') ? 0 : -1;
            let wrapFn = (page, $html) => { let dnp = determineNextPage(page, $html);if (dnp) { processorFn($html); return processPage(url, dnp, wrapFn); } else return processorFn($html); };
            return processPage(url, page, wrapFn);
        };

        const getDistance = (o, t) => Math.hypot(o.toCoord(true).x - t.toCoord(true).x, o.toCoord(true).y - t.toCoord(true).y);
        const subtractArrays = (a1, a2) => { let r = a1.map((v, i) => v - a2[i]); return r.some(v => v < 0) ? false : r; };
        const getCurrentServerTime = () => { let [h, m, s, d, mo, y] = $('#serverTime').closest('p').text().match(/\d+/g); return new Date(y, mo - 1, d, h, m, s).getTime(); };
        const timestampFromString = (timestr) => {
            try {
            const lang = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window).lang;
            if (!lang) return 0;
            let d = $('#serverDate').text().split('/').map(x => +x);
            let tp = new RegExp(lang['aea2b0aa9ae1534226518faaefffdaad'].replace('%s', '([\\d+|:]+)')).exec(timestr);
            let tom = new RegExp(lang['57d28d1b211fddbb7a499ead5bf23079'].replace('%s', '([\\d+|:]+)')).exec(timestr);
            let lat = new RegExp(lang['0cb274c906d622fa8ce524bcfbb7552d'].replace('%1', '([\\d+|\\.]+)').replace('%2', '([\\d+|:]+)')).exec(timestr);
            let t, date;
            if (tp) { t = tp[1].split(':'); date = new Date(d[2], d[1]-1, d[0], t[0], t[1], t[2], t[3]||0); }
            else if (tom) { t = tom[1].split(':'); date = new Date(d[2], d[1]-1, d[0]+1, t[0], t[1], t[2], t[3]||0); }
            else if (lat) { d = (lat[1]+d[2]).split('.').map(x=>+x); t = lat[2].split(':'); date = new Date(d[2], d[1]-1, d[0], t[0], t[1], t[2], t[3]||0); }
            else return 0;
            return date.getTime();
            } catch(e) { return 0; }
        };

        if (!String.prototype.toCoord) String.prototype.toCoord = function (o) { let c = (this.match(/\d{1,3}\|\d{1,3}/g)||[false]).pop(); return c && o ? {x:c.split('|')[0],y:c.split('|')[1]} : c; };
        if (!String.prototype.toNumber) String.prototype.toNumber = function () { return parseFloat(this); };
        if (!Number.prototype.toNumber) Number.prototype.toNumber = function () { return parseFloat(this); };

        return { getUnitSpeeds, processPage, processAllPages, getDistance, subtractArrays, getCurrentServerTime, timestampFromString };
    })();


    // =====================================================================
    // KATTA ENGINE — Rot/Gelb Angriffe
    // =====================================================================
    const KATTA_SENT_KEY = domain + '_katta_sent';
    let kattaAllFarms = {}; // wird nach getData befüllt // {coord: timestamp}

    function kattaLoadSent() {
        try { return JSON.parse(localStorage.getItem(KATTA_SENT_KEY) || '{}'); } catch(e) { return {}; }
    }
    function kattaSaveSent(obj) { localStorage.setItem(KATTA_SENT_KEY, JSON.stringify(obj)); }

    function kattaShouldAttack(coord, color) {
        const s = loadSettings();
        if (!s.kattaEnabled) return false;
        if (color === 'red' && !s.kattaOnRed) return false;
        if (color === 'yellow' && !s.kattaOnYellow) return false;
        if (color !== 'red' && color !== 'yellow') return false;
        const sent = kattaLoadSent();
        if (!sent[coord]) return true; // noch nie geschickt
        const age = Date.now() - sent[coord];
        return age >= 24 * 60 * 60 * 1000; // älter als 24h
    }

    function kattaMarkSent(coord) {
        const sent = kattaLoadSent();
        sent[coord] = Date.now();
        kattaSaveSent(sent);
    }

    // Wählt nächstes Dorf aus Gruppe das genug Truppen hat
    // Lädt unitMap mit direktem fetch (nicht twLib-Queue) um Farm-Loop nicht zu blockieren
    async function kattaLoadUnitMap(group) {
        const gd = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window).game_data;
        const csrf = gd && gd.csrf;
        const villages = await FarmGodCore.loadVillages(group);
        const unitMap = {};

        const fetchPage = async (page) => {
            const url = `/game.php?village=${gd.village.id}&screen=overview_villages&mode=combined&group=${group}&page=${page}&h=${csrf}`;
            const html = await fetch(url, { credentials: 'include' }).then(r => r.text());
            const $html = $(html);
            const units = gd.units; // ["spear","sword","axe",...]
            $html.find('#combined_table').find('.row_a,.row_b').each((i, el) => {
                const $el = $(el);
                const coord = $el.find('.quickedit-label').first().text().trim().match(/\d+\|\d+/)?.[0];
                if (!coord) return;
                const unitItems = $el.find('.unit-item');
                unitMap[coord] = {
                    id:       villages[coord] && villages[coord].id,
                    axe:      parseInt(unitItems.eq(units.indexOf('axe')).text())      || 0,
                    ram:      parseInt(unitItems.eq(units.indexOf('ram')).text())      || 0,
                    spy:      parseInt(unitItems.eq(units.indexOf('spy')).text())      || 0,
                    catapult: parseInt(unitItems.eq(units.indexOf('catapult')).text()) || 0,
                };
            });
            // Nächste Seite prüfen
            const nextMatch = $html.find('#pager_form').find('input[name=page]').attr('max');
            return nextMatch ? parseInt(nextMatch) : page;
        };

        let page = 0;
        const maxPage = await fetchPage(0);
        for (let p = 1; p <= maxPage; p++) {
            await fetchPage(p);
        }
        return unitMap;
    }

    // Wählt nächstes Dorf aus unitMap das genug Truppen hat (nach Distanz)
    function kattaFindVillage(targetCoord, troops, unitMap) {
        const candidates = Object.entries(unitMap)
            .filter(([, u]) =>
                u.id &&
                u.axe      >= troops.axe &&
                u.ram      >= troops.ram &&
                u.spy      >= troops.spy &&
                u.catapult >= troops.catapult
            )
            .map(([coord, u]) => ({ coord, id: u.id, dist: FarmLib.getDistance(coord, targetCoord) }))
            .sort((a, b) => a.dist - b.dist);
        return candidates[0] || null;
    }

    // Zieht verplante Truppen lokal ab
    function kattaReserveTroops(unitMap, coord, troops) {
        if (!unitMap[coord]) return;
        unitMap[coord].axe      -= troops.axe;
        unitMap[coord].ram      -= troops.ram;
        unitMap[coord].spy      -= troops.spy;
        unitMap[coord].catapult -= troops.catapult;
    }

    async function kattaSendAttack(villageId, target, troops, building) {
        const base = window.location.origin + '/game.php';
        const gd = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window).game_data;
        const csrf = gd && gd.csrf;
        if (!csrf) throw new Error('CSRF fehlt');

        // Schritt 1: Versammlungsplatz im iframe laden → CSRF-Feldname holen
        const { tokenField, tokenValue } = await new Promise((resolve, reject) => {
            const iframe = document.createElement('iframe');
            iframe.style.cssText = 'display:none;width:0;height:0;border:0;';
            iframe.src = `${base}?village=${villageId}&screen=place`;
            iframe.onload = () => {
                try {
                    const iDoc = iframe.contentDocument || iframe.contentWindow.document;
                    let tokenField = null, tokenValue = null;
                    iDoc.querySelectorAll('input[type=hidden]').forEach(inp => {
                        if (/^[0-9a-f]{10,}$/.test(inp.name)) { tokenField = inp.name; tokenValue = inp.value; }
                    });
                    document.body.removeChild(iframe);
                    if (tokenField) resolve({ tokenField, tokenValue });
                    else reject(new Error('Token nicht im iframe'));
                } catch(e) { document.body.removeChild(iframe); reject(e); }
            };
            iframe.onerror = () => { document.body.removeChild(iframe); reject(new Error('iframe Fehler')); };
            document.body.appendChild(iframe);
        });

        // Schritt 2: try=confirm
        const step2params = new URLSearchParams({
            source_village: villageId,
            x: target.x,
            y: target.y,
            target_type: 'coord',
            input: `${target.x}|${target.y}`,
            attack: 'Angreifen',
            template_id: '',
            spy:      troops.spy      || 0,
            axe:      troops.axe      || 0,
            ram:      troops.ram      || 0,
            catapult: troops.catapult || 0,
            spear: 0, sword: 0, archer: 0, light: 0, marcher: 0, heavy: 0, snob: 0,
        });
        step2params.set(tokenField, tokenValue);

        const step2html = await fetch(`${base}?village=${villageId}&screen=place&try=confirm&h=${csrf}`, {
            method: 'POST', credentials: 'include',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: step2params.toString(),
        }).then(r => r.text());

        // Schritt 3: Bestätigung
        const step3params = new URLSearchParams();
        for (const [, name, value] of step2html.matchAll(/name="([^"]+)"\s+value="([^"]*)"/g)) {
            step3params.set(name, value);
        }
        if (!step3params.has('ch')) throw new Error('ch-Token fehlt in Bestätigungsseite');

        // Katapult-Zielgebäude setzen falls in Bestätigungsseite ein building-Select vorhanden
        step3params.set('building', building || 'wall');
        step3params.set('h', csrf);
        step3params.set('cb', 'troop_confirm_submit');
        step3params.set('submit_confirm', 'Angreifen');
        step3params.set('attack_name', '');

        const step3res = await fetch(`${base}?village=${villageId}&screen=place&action=command`, {
            method: 'POST', credentials: 'include',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Referer': `${base}?village=${villageId}&screen=place&try=confirm`,
            },
            body: step3params.toString(),
        });
        if (!step3res.ok) throw new Error(`HTTP ${step3res.status}`);
    }

    // Hauptfunktion: alle rot/gelben Farmen prüfen und Katta-Angriffe schicken
    let kattaPhaseRunning = false;
    async function runKattaPhase(farms) {
        const s = loadSettings();
        if (!s.kattaEnabled) return;
        if (kattaPhaseRunning) { console.log('[Katta] Phase läuft bereits — übersprungen'); return; }
        kattaPhaseRunning = true;

        const troops = {
            axe:      parseInt(s.kattaAxe)  || 0,
            ram:      parseInt(s.kattaRam)  || 0,
            spy:      parseInt(s.kattaSpy)  || 0,
            catapult: parseInt(s.kattaCata) || 0,
        };

        // Katta-Log bereinigen: Dörfer die jetzt grün/blau sind rausnehmen
        const sent = kattaLoadSent();
        let cleaned = 0;
        Object.entries(farms).forEach(([coord, f]) => {
            if (sent[coord] && (f.color === 'green' || f.color === 'blue')) {
                delete sent[coord];
                cleaned++;
            }
        });
        if (cleaned > 0) { kattaSaveSent(sent); console.log(`[Katta] ${cleaned} Dörfer aus Log entfernt (jetzt grün/blau)`); }

        // Duplikate vermeiden: Set der bereits in dieser Runde angegriffenen Coords
        const attackedThisRound = new Set();
        const targets = Object.entries(farms)
            .filter(([coord, f]) => kattaShouldAttack(coord, f.color))
            .filter(([coord]) => { if (attackedThisRound.has(coord)) return false; attackedThisRound.add(coord); return true; })
            .map(([coord, f]) => ({ coord, f }));

        if (!targets.length) return;

        setStatus(`⚔️ ${targets.length} Katta-Angriffe werden geprüft...`, '#e67e22');

        // Einmalig unitMap laden — Truppen werden lokal abgezogen
        const unitMap = await kattaLoadUnitMap(s.kattaGroup);

        let sentCount = 0;
        for (const { coord } of targets) {
            try {
                const [x, y] = coord.split('|');
                const village = kattaFindVillage(coord, troops, unitMap);
                if (!village) {
                    console.log(`[Katta] Kein Dorf mit Truppen für ${coord} — übersprungen`);
                    continue;
                }

                await kattaSendAttack(village.id, { x, y }, troops, s.kattaTarget);
                kattaReserveTroops(unitMap, village.coord, troops); // lokal abziehen
                kattaMarkSent(coord);
                sentCount++;
                console.log(`[Katta] ✓ ${coord} von ${village.coord} (${sentCount}/${targets.length})`);

                if (targets.indexOf(targets.find(t => t.coord === coord)) < targets.length - 1) {
                    await new Promise(r => setTimeout(r, 500 + Math.random() * 200));
                }
            } catch(e) {
                console.error(`[Katta] Fehler bei ${coord}:`, e);
            }
        }
        setStatus(`✅ ${sentCount} Katta-Angriffe gesendet`, '#27ae60');
        kattaPhaseRunning = false;
    }

    // =====================================================================
    // FARMGOD KERN
    // =====================================================================
    const FarmGodCore = (function (lib) {
        let farmBusy = false;

        const buildGroupSelect = (id) => $.get(TribalWars.buildURL('GET', 'groups', { ajax: 'load_group_menu' })).then((groups) => {
            let html = `<select class="optionGroup" style="background:#1a1a1a;color:#fff;border:1px solid #555;border-radius:4px;padding:2px;width:100%;">`;
            groups.result.forEach(val => { if (val.type == 'separator') html += `<option disabled=""/>`; else html += `<option value="${val.group_id}" ${val.group_id == id ? 'selected' : ''}>${val.name}</option>`; });
            return html + `</select>`;
        });

        const loadVillages = (group) => {
            let villages = {};
            let processor = ($html) => {
                const mobile = $('#mobileHeader').length > 0;
                if (mobile) {
                    jQuery($html).find('.overview-container > div').each((i, el) => {
                        try { const id=jQuery(el).find('.quickedit-vn').data('id'),name=jQuery(el).find('.quickedit-label').attr('data-text'),coord=jQuery(el).find('.quickedit-label').text().toCoord(); if(coord)villages[coord]={name,id}; } catch(e){}
                    });
                } else {
                    $html.find('#combined_table').find('.row_a,.row_b').filter((i,el)=>$(el).find('.bonus_icon_33').length==0).map((i,el)=>{
                        let $qel=$(el).find('.quickedit-label').first(),coord=$qel.text().toCoord();
                        if(coord)villages[coord]={name:$qel.data('text'),id:parseInt($(el).find('.quickedit-vn').first().data('id'))};
                    });
                }
                return villages;
            };
            return lib.processAllPages(TribalWars.buildURL('GET','overview_villages',{mode:'combined',group,page_size:1000}),processor).then(()=>villages);
        };

        const getData = (group, newbarbs, losses) => {
            const game_data = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window).game_data;
            let data = { villages:{}, commands:{}, farms:{templates:{},farms:{}} };
            let skipUnits = ['ram','catapult','knight','snob','militia'];

            let villagesProcessor = ($html) => {
                const mobile = $('#mobileHeader').length > 0;
                if (mobile) {
                    jQuery($html).find('.overview-container > div').each((i,el)=>{
                        try {
                            const vid=jQuery(el).find('.quickedit-vn').data('id'),name=jQuery(el).find('.quickedit-label').attr('data-text'),coord=jQuery(el).find('.quickedit-label').text().toCoord();
                            const units=new Array(game_data.units.length).fill(0);
                            jQuery(el).find('.overview-units-row > div.unit-row-item').each((_,ue)=>{const img=jQuery(ue).find('img'),span=jQuery(ue).find('span.unit-row-name');if(img.length&&span.length){let ut=img.attr('src').split('unit_')[1].replace('@2x.webp','').replace('.webp','').replace('.png','');const ui=game_data.units.indexOf(ut);if(ui!==-1)units[ui]=parseInt(span.text())||0;}});
                            data.villages[coord]={name,id:vid,units:units.filter((_,i)=>skipUnits.indexOf(game_data.units[i])===-1)};
                        } catch(e){}
                    });
                } else {
                    $html.find('#combined_table').find('.row_a,.row_b').filter((i,el)=>$(el).find('.bonus_icon_33').length==0).map((i,el)=>{
                        let $el=$(el),$qel=$el.find('.quickedit-label').first();
                        let units=$el.find('.unit-item').filter((idx)=>skipUnits.indexOf(game_data.units[idx])==-1).map((idx,e)=>$(e).text().toNumber()).get();
                        return(data.villages[$qel.text().toCoord()]={name:$qel.data('text'),id:parseInt($el.find('.quickedit-vn').first().data('id')),units});
                    });
                }
                return data;
            };

            let commandsProcessor = ($html) => {
                $html.find('#commands_table').find('.row_a,.row_ax,.row_b,.row_bx').map((i,el)=>{
                    let $el=$(el),coord=$el.find('.quickedit-label').first().text().toCoord();
                    if(coord){if(!data.commands[coord])data.commands[coord]=[];data.commands[coord].push(Math.round(lib.timestampFromString($el.find('td').eq(2).text().trim())/1000));}
                });
                return data;
            };

            let farmProcessor = ($html) => {
                if($.isEmptyObject(data.farms.templates)){
                    let us=lib.getUnitSpeeds();
                    $html.find('form[action*="action=edit_all"]').find('input[type="hidden"][name*="template"]').closest('tr').map((i,el)=>{
                        let $el=$(el);
                        return(data.farms.templates[$el.prev('tr').find('a.farm_icon').first().attr('class').match(/farm_icon_(.*)\s/)[1]]={
                            id:$el.find('input[type="hidden"][name*="template"][name*="[id]"]').first().val().toNumber(),
                            units:$el.find('input[type="text"],input[type="number"]').map((i,e)=>$(e).val().toNumber()).get(),
                            speed:Math.max(...$el.find('input[type="text"],input[type="number"]').map((i,e)=>$(e).val().toNumber()>0?us[$(e).attr('name').trim().split('[')[0]]:0).get()),
                        });
                    });
                }
                $html.find('#plunder_list').find('tr[id^="village_"]').map((i,el)=>{
                    let $el=$(el);
                    return(data.farms.farms[$el.find('a[href*="screen=report&mode=all&view="]').first().text().toCoord()]={
                        id:$el.attr('id').split('_')[1].toNumber(),
                        color:$el.find('img[src*="graphic/dots/"]').attr('src').match(/dots\/(green|yellow|red|blue|red_blue)/)[1],
                        max_loot:$el.find('img[src*="max_loot/1"]').length>0,
                    });
                });
                return data;
            };

            let findNewbarbs = () => newbarbs ? twLib.get('/map/village.txt').then((all)=>{
                all.match(/[^\r\n]+/g).forEach(vd=>{let [id,name,x,y,pid]=vd.split(','),coord=`${x}|${y}`;if(pid==0&&!data.farms.farms[coord])data.farms.farms[coord]={id:id.toNumber()};});
                return data;
            }) : data;

            let filterFarms = () => {
                data.farms.allFarms = Object.assign({}, data.farms.farms); // alle Farmen vor Filter merken
                data.farms.farms=Object.fromEntries(Object.entries(data.farms.farms).filter(([k,v])=>!v.color||(v.color!='red'&&v.color!='red_blue'&&(v.color!='yellow'||losses))));
                return data;
            };

            return Promise.all([
                lib.processAllPages(TribalWars.buildURL('GET','overview_villages',{mode:'combined',group}),villagesProcessor).catch(e=>console.warn('[AF] villages error:',e)),
                lib.processAllPages(TribalWars.buildURL('GET','overview_villages',{mode:'commands',type:'attack'}),commandsProcessor).catch(e=>console.warn('[AF] commands error:',e)),
                lib.processAllPages(TribalWars.buildURL('GET','am_farm'),farmProcessor).catch(e=>console.warn('[AF] farm error:',e)),
                Promise.resolve(findNewbarbs()).catch(e=>console.warn('[AF] newbarbs error:',e)),
            ]).then(filterFarms).then(()=>data);
        };

        const isInArea = (farmCoord, x1, y1, x2, y2) => {
            const fc = farmCoord.toCoord(true);
            if (!fc) return false;
            const fx = parseFloat(fc.x), fy = parseFloat(fc.y);
            const minX = Math.min(x1, x2), maxX = Math.max(x1, x2);
            const minY = Math.min(y1, y2), maxY = Math.max(y1, y2);
            return fx >= minX && fx <= maxX && fy >= minY && fy <= maxY;
        };

        const farmAllowed = (farmCoord, dist, vs, optionDistance) => {
            if (!vs) return dist < optionDistance; // kein individuelles Setting → Standard
            const mode = vs.mode || 'distance';
            const maxDist = vs.distance !== undefined ? parseFloat(vs.distance) : optionDistance;
            const areaOk = isInArea(farmCoord,
                vs.x1 !== undefined ? vs.x1 : 0,
                vs.y1 !== undefined ? vs.y1 : 0,
                vs.x2 !== undefined ? vs.x2 : 999,
                vs.y2 !== undefined ? vs.y2 : 999);
            if (mode === 'distance') return dist < maxDist;
            if (mode === 'area')     return areaOk;
            if (mode === 'both')     return dist < maxDist && areaOk;
            return dist < optionDistance;
        };

        const createPlanning = (optionDistance, optionTime, optionMaxloot, data, villageDistances, villageSettings) => {
            let plan={counter:0,farms:{}};
            let serverTime=Math.round(lib.getCurrentServerTime()/1000);
            for(let prop in data.villages){
                const vs = villageSettings && villageSettings[prop] ? villageSettings[prop] : null;
                // Fallback auf altes villageDistances System
                let distForVillage;
                if (!vs && villageDistances && villageDistances[prop] !== undefined) {
                    distForVillage = parseFloat(villageDistances[prop]);
                } else {
                    distForVillage = optionDistance;
                }
                let orderedFarms=Object.keys(data.farms.farms).map(k=>({coord:k,dis:lib.getDistance(prop,k)})).sort((a,b)=>a.dis>b.dis?1:-1);
                orderedFarms.forEach(el=>{
                    let fi=data.farms.farms[el.coord],tn=optionMaxloot&&fi.max_loot?'b':'a',tmpl=data.farms.templates[tn];
                    let unitsLeft=lib.subtractArrays(data.villages[prop].units,tmpl.units);
                    let dist=lib.getDistance(prop,el.coord),arrival=Math.round(serverTime+dist*tmpl.speed*60+Math.round(plan.counter/5));
                    let maxTD=Math.round(optionTime*60),timeDiff=true;
                    if(data.commands[el.coord]){if(!fi.color&&data.commands[el.coord].length>0)timeDiff=false;data.commands[el.coord].forEach(ts=>{if(Math.abs(ts-arrival)<maxTD){timeDiff=false;}});}
                    else data.commands[el.coord]=[];
                    const allowed = vs ? farmAllowed(el.coord, dist, vs, optionDistance) : dist < distForVillage;
                    if(unitsLeft&&timeDiff&&allowed){
                        plan.counter++;
                        if(!plan.farms[prop])plan.farms[prop]=[];
                        plan.farms[prop].push({origin:{coord:prop,name:data.villages[prop].name,id:data.villages[prop].id},target:{coord:el.coord,id:fi.id},fields:dist,template:{name:tn,id:tmpl.id}});
                        data.villages[prop].units=unitsLeft;data.commands[el.coord].push(arrival);
                    }
                });
            }
            return plan;
        };

        const buildTable = (plan) => {
            let html=`<div class="vis farmGodContent"><h4>FarmGod</h4><table class="vis" width="100%">
                <tr><div id="FarmGodProgessbar" class="progress-bar live-progress-bar progress-bar-alive" style="width:98%;margin:5px auto;"><div style="background:rgb(146,194,0);"></div><span class="label" style="margin-top:0px;"></span></div></tr>
                <tr><th style="text-align:center;">Herkunft</th><th style="text-align:center;">Ziel</th><th style="text-align:center;">Felder</th><th style="text-align:center;">Farm</th></tr>`;
            if(!$.isEmptyObject(plan)){for(let prop in plan)plan[prop].forEach((val,i)=>{html+=`<tr class="farmRow row_${i%2==0?'a':'b'}"><td style="text-align:center;"><a href="${game_data.link_base_pure}info_village&id=${val.origin.id}">${val.origin.name} (${val.origin.coord})</a></td><td style="text-align:center;"><a href="${game_data.link_base_pure}info_village&id=${val.target.id}">${val.target.coord}</a></td><td style="text-align:center;">${val.fields.toFixed(2)}</td><td style="text-align:center;"><a href="#" data-origin="${val.origin.id}" data-target="${val.target.id}" data-template="${val.template.id}" class="farmGod_icon farm_icon farm_icon_${val.template.name}" style="margin:auto;"></a></td></tr>`;});}
            else html+=`<tr><td colspan="4" style="text-align:center;">Keine Farms mit den aktuellen Einstellungen möglich.</td></tr>`;
            return html+`</table></div>`;
        };

        const sendFarm = ($this) => {
            let n=Timing.getElapsedTimeSinceLoad();
            if(!farmBusy&&!(Accountmanager.farm.last_click&&n-Accountmanager.farm.last_click<200)){
                farmBusy=true;Accountmanager.farm.last_click=n;let $pb=$('#FarmGodProgessbar');
                TribalWars.post(Accountmanager.send_units_link.replace(/village=(\d+)/,'village='+$this.data('origin')),null,
                    {target:$this.data('target'),template_id:$this.data('template'),source:$this.data('origin')},
                    (r)=>{UI.SuccessMessage(r.success);$pb.data('current',$pb.data('current')+1);UI.updateProgressBar($pb,$pb.data('current'),$pb.data('max'));$this.closest('.farmRow').remove();farmBusy=false;},
                    (r)=>{UI.ErrorMessage(r||'Fehler!');$pb.data('current',$pb.data('current')+1);UI.updateProgressBar($pb,$pb.data('current'),$pb.data('max'));$this.closest('.farmRow').remove();farmBusy=false;}
                );
            }
        };

        const bindEventHandlers = () => {
            $('.farmGod_icon').off('click').on('click',function(){sendFarm($(this));});
            $(document).off('keydown.farmgod').on('keydown.farmgod',(e)=>{if((e.keyCode||e.which)==13)$('.farmGod_icon').first().trigger('click');});
        };

        const planAndRender = () => {
                        settings=loadSettings();
            const gd2 = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window).game_data;
            if(!gd2||!gd2.features.Premium.active||!gd2.features.FarmAssistent.active){UI.ErrorMessage('Farmgod benötigt Premium + Farm-Assistent!');return Promise.reject();}
            setStatus('⏳ Lade Daten...','#f0a500');
            const effectiveTime = isNightMode() ? settings.nightOptionTime : settings.optionTime;
                const effectiveMaxloot = isNightMode() ? settings.nightOptionMaxloot : settings.optionMaxloot;
                const effectiveLosses = isNightMode() ? settings.nightOptionLosses : settings.optionLosses;
                return getData(settings.optionGroup,settings.optionNewbarbs,effectiveLosses).then(data=>{
                let plan=createPlanning(settings.optionDistance,effectiveTime,effectiveMaxloot,data,settings.villageDistances,settings.villageSettings);
                $('.farmGodContent').remove();$('#am_widget_Farm').first().before(buildTable(plan.farms));
                bindEventHandlers();UI.InitProgressBars();UI.updateProgressBar($('#FarmGodProgessbar'),0,plan.counter);$('#FarmGodProgessbar').data('current',0).data('max',plan.counter);
                setStatus(`✅ ${plan.counter} Farms geplant`,'#27ae60');
                kattaAllFarms = data.farms.allFarms || {}; // für Katta-Phase merken
                return plan;
            });
        };

        return { planAndRender, buildGroupSelect, loadVillages };
    })(FarmLib);

    // =====================================================================
    // ENTER / STATUS
    // =====================================================================
    function fireEnterDown(r){const o={key:'Enter',code:'Enter',keyCode:13,which:13,bubbles:true,cancelable:true,repeat:r};document.dispatchEvent(new KeyboardEvent('keydown',o));document.dispatchEvent(new KeyboardEvent('keypress',o));}
    function fireEnterUp(){document.dispatchEvent(new KeyboardEvent('keyup',{key:'Enter',code:'Enter',keyCode:13,which:13,bubbles:true,cancelable:true,repeat:false}));}

    function setStatus(text,color){
        currentStatus=text;
        const el=document.getElementById('af-status-mini');if(el){el.innerText=text;el.style.color=color||'#f0a500';}
        const full=document.getElementById('af-countdown');if(full){full.innerText=text;full.style.color=color||'#f0a500';full.style.display='block';}
        // Tag/Nacht Modus anzeigen
        const modeEl=document.getElementById('af-mode-mini');
        if(modeEl){
            if(isNightMode()){modeEl.innerText='🌙 Nachtmodus';modeEl.style.color='#8e44ad';}
            else{modeEl.innerText='☀️ Tagmodus';modeEl.style.color='#f0a500';}
        }
    }
    function setCountdown(text){
        currentCountdownText=text;
        const el=document.getElementById('af-timer-mini');if(el)el.innerText=text;
        const full=document.getElementById('af-countdown');if(full){full.innerText=text;full.style.display='block';}
    }

    function getFarmGodProgress(){
        const pb=document.getElementById('FarmGodProgessbar');
        if(!pb)return null;
        const label=pb.querySelector('.label');
        if(!label)return null;
        const text=(label.innerText||label.textContent||'').trim().replace(/\./g,'');
        const m=text.match(/^(\d+)\s*\/\s*(\d+)$/);
        if(m)return{current:parseInt(m[1]),total:parseInt(m[2])};
        return null;
    }
    function hasFarmsRemaining(){
        const p=getFarmGodProgress();
        if(p!==null){
            if(p.total===0)return false;
            return p.current<p.total;
        }
        const visibleRows=document.querySelectorAll('.farmRow');
        return visibleRows.length>0;
    }

    function startHoldEnter(){
        holdRepeatCount=0;fireEnterDown(false);holdRepeatCount++;
        holdIntervalId=setInterval(()=>{
            if(!isRunning){stopHoldEnter();return;}
            fireEnterDown(true);holdRepeatCount++;
            const p=getFarmGodProgress();
            if(p){
                if(p.total===0){stopHoldEnter();setStatus('😴 Keine Farms','#aaa');triggerEndOfLoop();return;}
                if(p.current>=p.total){stopHoldEnter();setStatus('✅ Alle erledigt','#27ae60');triggerEndOfLoop();return;}
                setStatus(`⚔️ Farmt... ${p.current}/${p.total}`,'#27ae60');
            }
        },settings.holdInterval);
    }
    function stopHoldEnter(){clearInterval(holdIntervalId);fireEnterUp();holdRepeatCount=0;}

    function pressEnterRandomly(){
        settings=loadSettings();const delay=randomDelay(settings.enterMin,settings.enterMax);
        fireEnterDown(false);fireEnterUp();
        setTimeout(()=>{
            if(!hasFarmsRemaining()){emptyFarmChecks++;if(emptyFarmChecks>=EMPTY_FARM_THRESHOLD){clearTimeout(intervalId);setStatus('🔄 Fertig','#e74c3c');triggerEndOfLoop();return;}}
            else{emptyFarmChecks=0;const p=getFarmGodProgress();setStatus(p&&p.total>0?`⚔️ Farmt... ${p.current}/${p.total}`:'⚔️ Farmt...','#27ae60');}
        },300);
        intervalId=setTimeout(pressEnterRandomly,delay);
    }

    function triggerEndOfLoop(){
        resetGroupToAll();
        fetchTodayLoot();
        scheduleRandomPause(startCountdown);
    }

    // =====================================================================
    // CAPTCHA
    // =====================================================================
    function isCaptchaPresent(){return['#captcha','.captcha','iframe[src*="captcha"]','iframe[src*="recaptcha"]','.g-recaptcha','#recaptcha','[class*="captcha"]','[id*="captcha"]'].some(s=>document.querySelector(s));}
    function handleCaptchaDetected(){
        if(captchaAlertSent)return;
        captchaAlertSent=true;
        localStorage.setItem(domain + '_captcha_paused', 'true');
        sendDiscordCaptchaAlert();

        // Pausieren statt stoppen
        clearTimeout(intervalId);
        stopHoldEnter();
        clearInterval(countdownInterval);
        clearRandomPause();
        // isRunning bleibt true – Script ist pausiert nicht gestoppt

        setStatus('⚠️ Captcha – bitte lösen!','#e74c3c');

        // Fortsetzen Button anzeigen
        const resumeBtn = document.getElementById('af-resume-btn');
        if(resumeBtn) resumeBtn.style.display = 'block';
        const miniResumeBtn2 = document.getElementById('af-mini-resume-btn');
        if(miniResumeBtn2) miniResumeBtn2.style.display = 'inline-block';

        // Automatisch prüfen ob Captcha weg ist nach jedem Reload
        // (wird durch startCaptchaObserver + isCaptchaPresent beim nächsten Seitenload gehandelt)
        console.log('[Autofarm] Captcha erkannt – pausiert. Bitte lösen und Seite neu laden oder Fortsetzen klicken.');
    }

    function resumeAfterCaptcha(){
        if(isCaptchaPresent()){
            setStatus('⚠️ Captcha noch vorhanden!','#e74c3c');
            return;
        }
        // Alle laufenden Intervalle/Timeouts sauber stoppen
        clearTimeout(intervalId);
        clearInterval(countdownInterval);
        clearInterval(holdIntervalId);
        stopHoldEnter();
        clearRandomPause();

        captchaAlertSent=false;
        localStorage.removeItem(domain + '_captcha_paused');
        emptyFarmChecks=0;

        const resumeBtn=document.getElementById('af-resume-btn');
        if(resumeBtn)resumeBtn.style.display='none';
        const miniResumeBtn=document.getElementById('af-mini-resume-btn');
        if(miniResumeBtn)miniResumeBtn.style.display='none';

        setStatus('⏳ Fortsetze...','#f0a500');
        startProcess();
        updateButtonState();
    }
    function startCaptchaObserver(){
        if(isCaptchaPresent()){
            // Captcha noch da – pausieren
            handleCaptchaDetected();
            return;
        }
        if(captchaAlertSent && isRunning){
            // Captcha war da aber ist jetzt weg nach Reload
            // startProcess läuft bereits (wegen isRunning=true aus localStorage)
            // nur State zurücksetzen, kein erneuter startProcess
            console.log('[Autofarm] Captcha gelöst – setze State zurück.');
            captchaAlertSent=false;
            localStorage.removeItem(domain + '_captcha_paused');
            const resumeBtn=document.getElementById('af-resume-btn');
            if(resumeBtn)resumeBtn.style.display='none';
            const miniResumeBtn=document.getElementById('af-mini-resume-btn');
            if(miniResumeBtn)miniResumeBtn.style.display='none';
        }
        const o=new MutationObserver(()=>{if(isRunning&&isCaptchaPresent())handleCaptchaDetected();});
        o.observe(document.body,{childList:true,subtree:true});
    }

    // =====================================================================
    // PROZESS
    // =====================================================================
    function checkFarmsAndStart(){
        const p=getFarmGodProgress();
        if(p&&p.total===0){setStatus('😴 Keine Farms','#aaa');triggerEndOfLoop();return;}
        settings=loadSettings();
        if(settings.holdEnter){setStatus('⚔️ Farmt...','#27ae60');startHoldEnter();}
        else{setStatus('⚔️ Farmt...','#27ae60');pressEnterRandomly();}
    }

    function startProcess(){
        captchaAlertSent=false;
        localStorage.removeItem(domain + '_captcha_paused');
        emptyFarmChecks=0;
        startHourlyAlert();
        // Reihenfolge: 1) am_farm laden+filtern (in planAndRender via kattaAllFarms)
        //              2) Katta-Angriffe planen+senden
        //              3) Farm-Angriffe planen+senden
        setStatus('⏳ Lade Farm-Daten...','#f0a500');
        FarmGodCore.planAndRender()
            .then(() => {
                const targets = Object.entries(kattaAllFarms).filter(([coord,f]) => kattaShouldAttack(coord, f.color));
                if (targets.length === 0) return;
                return runKattaPhase(kattaAllFarms);
            })
            .catch(e => console.error('[Katta] Fehler:', e))
            .then(()=>setTimeout(()=>checkFarmsAndStart(),randomDelay(1000,2000)))
            .catch(e=>{console.error(e);setStatus('❌ Fehler','#e74c3c');});
    }

    function stopProcess(){
        clearTimeout(intervalId);
        stopHoldEnter();
        clearInterval(countdownInterval);
        stopHourlyAlert();
        clearRandomPause();
        isRunning=false;
        localStorage.setItem(domain+'_isRunning',false);
        updateButtonState();
        if(!captchaAlertSent){
            setStatus('⏹ Gestoppt','#aaa');
            const cd=document.getElementById('af-countdown');
            if(cd)cd.style.display='none';
        }
    }

    function toggleProcess(){
        if(isRunning)stopProcess();
        else{
            isRunning=true;
            localStorage.setItem(domain+'_isRunning',true);
            updateButtonState();
            startProcess();
        }
    }

    function startCountdown(){
        clearInterval(countdownInterval); // sicherstellen kein doppelter Countdown
        settings=loadSettings();
        const nightMode = isNightMode();
        let normalDelay = nightMode
            ? randomDelay(settings.nightReloadMin, settings.nightReloadMax)
            : randomDelay(settings.reloadMin, settings.reloadMax);
        let t = getRandomPauseReloadDelay(normalDelay);
        const isPause = localStorage.getItem(RANDOM_PAUSE_ACTIVE_KEY) === '1';
        if (isPause) localStorage.removeItem(RANDOM_PAUSE_ACTIVE_KEY);
        const modeLabel = isPause ? '😴' : (nightMode ? '🌙' : '🔁');
        if (isPause) setStatus(`😴 Pause ${Math.round(t/60)} Min...`, '#8e44ad');
        countdownInterval=setInterval(()=>{
            if(t<=0){
                clearInterval(countdownInterval);
                isRandomPausing=false;
                location.reload();
            } else {
                setCountdown(`${modeLabel} ${isPause?'Pause':'Loop'} in: ${Math.floor(t/60)}m ${t%60}s`);
                t--;
            }
        },1000);
    }

    function updateButtonState(){
        const btn=document.getElementById('af-toggle-btn');
        const miniBtn=document.getElementById('af-mini-toggle-btn');
        const label=isRunning?'Stop Looting':'Start Looting';
        const color=isRunning?'#c0392b':'#27ae60';
        if(btn){btn.textContent=label;btn.style.backgroundColor=color;}
        if(miniBtn){miniBtn.textContent=isRunning?'Stop':'Start';miniBtn.style.backgroundColor=color;}
    }
    function toggleMinimize(){isMinimized=!isMinimized;saveMinimized(isMinimized);applyMinimizeState();}
    function applyMinimizeState(){
        const body=document.getElementById('af-body');
        const mini=document.getElementById('af-mini-status');
        const arrow=document.getElementById('af-arrow');
        const miniToggle=document.getElementById('af-mini-toggle-btn');
        if(!body||!mini||!arrow)return;
        if(isMinimized){
            body.style.display='none';
            mini.style.display='flex';
            arrow.textContent='▼';
            if(miniToggle)miniToggle.style.display='block';
        }else{
            body.style.display='block';
            mini.style.display='none';
            arrow.textContent='▲';
            if(miniToggle)miniToggle.style.display='none';
        }
    }

    function makeDraggable(container,handle){
        let d=false,sx,sy,sl,st;
        handle.style.cursor='grab';
        handle.addEventListener('mousedown',(e)=>{if(['af-arrow','af-toggle-btn'].includes(e.target.id))return;d=true;sx=e.clientX;sy=e.clientY;sl=parseInt(container.style.left)||0;st=parseInt(container.style.top)||0;handle.style.cursor='grabbing';e.preventDefault();});
        document.addEventListener('mousemove',(e)=>{if(!d)return;container.style.left=Math.max(0,Math.min(window.innerWidth-container.offsetWidth,sl+e.clientX-sx))+'px';container.style.top=Math.max(0,Math.min(window.innerHeight-container.offsetHeight,st+e.clientY-sy))+'px';container.style.bottom='auto';container.style.right='auto';});
        document.addEventListener('mouseup',()=>{if(!d)return;d=false;handle.style.cursor='grab';savePosition(parseInt(container.style.left),parseInt(container.style.top));});
        handle.addEventListener('touchstart',(e)=>{if(e.target.id==='af-arrow')return;const t=e.touches[0];d=true;sx=t.clientX;sy=t.clientY;sl=parseInt(container.style.left)||0;st=parseInt(container.style.top)||0;e.preventDefault();},{passive:false});
        document.addEventListener('touchmove',(e)=>{if(!d)return;const t=e.touches[0];container.style.left=Math.max(0,Math.min(window.innerWidth-container.offsetWidth,sl+t.clientX-sx))+'px';container.style.top=Math.max(0,Math.min(window.innerHeight-container.offsetHeight,st+t.clientY-sy))+'px';container.style.bottom='auto';container.style.right='auto';e.preventDefault();},{passive:false});
        document.addEventListener('touchend',()=>{if(!d)return;d=false;savePosition(parseInt(container.style.left),parseInt(container.style.top));});
    }

    // =====================================================================
    // UI
    // =====================================================================
    function mk(tag,styles,text){const e=document.createElement(tag);if(styles)Object.assign(e.style,styles);if(text!==undefined)e.textContent=text;return e;}
    function inp(id,type,value,min,width){const e=document.createElement('input');e.id=id;e.type=type;e.value=value;if(min!==undefined)e.min=min;Object.assign(e.style,{width:width||'70px',padding:'3px 5px',borderRadius:'4px',border:'1px solid #555',backgroundColor:'#1a1a1a',color:'#fff',fontSize:'12px',boxSizing:'border-box'});return e;}

    function buildTabSettings(body){
        const s=document.createElement('div');s.id='af-tab-settings';
        s.style.maxHeight='340px';s.style.overflowY='auto';s.style.paddingRight='4px';
        const add=(label,children)=>{s.appendChild(mk('div',{color:'#aaa',fontSize:'12px',marginBottom:'2px'},label));const row=mk('div',{display:'flex',gap:'6px',alignItems:'center',marginBottom:'6px'});children.forEach(c=>row.appendChild(c));s.appendChild(row);};
        const sep=()=>s.appendChild(mk('div',{borderTop:'1px solid #444',margin:'8px 0'}));
        const sec=(t)=>s.appendChild(mk('div',{color:'#f0a500',fontWeight:'bold',fontSize:'12px',marginBottom:'6px'},t));
        const addCb=(id,label,checked)=>{const w=mk('div',{display:'flex',alignItems:'center',gap:'6px',marginBottom:'5px'});const cb=document.createElement('input');cb.id=id;cb.type='checkbox';cb.checked=checked;const l=mk('label',{color:'#fff',fontSize:'12px',cursor:'pointer'},label);l.htmlFor=id;w.appendChild(cb);w.appendChild(l);s.appendChild(w);};

        sec('🌾 FarmGod Einstellungen');
        s.appendChild(mk('div',{color:'#aaa',fontSize:'12px',marginBottom:'2px'},'Gruppe:'));
        const gw=mk('div',{marginBottom:'6px'});gw.id='af-group-wrapper';gw.innerHTML='<span style="color:#aaa;font-size:11px;">Lädt...</span>';s.appendChild(gw);
        FarmGodCore.buildGroupSelect(settings.optionGroup).then(html=>{gw.innerHTML=html;});

        add('Standard-Reichweite (Felder):',[inp('af-distance','number',settings.optionDistance,1,'80px'),mk('span',{color:'#aaa'},'Felder')]);
        add('Mindestzeit zwischen Farms:',[inp('af-time','number',settings.optionTime,0,'80px'),mk('span',{color:'#aaa'},'min')]);
        addCb('af-losses','Farms mit Teilverlusten senden',settings.optionLosses);
        addCb('af-maxloot','B-Farm bei vollem Loot senden',settings.optionMaxloot);
        addCb('af-newbarbs','Neue Barbarendörfer hinzufügen',settings.optionNewbarbs);

        sep();
        sec('⌨️ Enter-Einstellungen');
        add('Reload-Intervall:',[inp('af-reload-min','number',settings.reloadMin,10,'65px'),mk('span',{color:'#aaa'},'–'),inp('af-reload-max','number',settings.reloadMax,10,'65px'),mk('span',{color:'#aaa'},'s')]);
        add('Enter-Intervall (Einzel):',[inp('af-enter-min','number',settings.enterMin,50,'65px'),mk('span',{color:'#aaa'},'–'),inp('af-enter-max','number',settings.enterMax,50,'65px'),mk('span',{color:'#aaa'},'ms')]);
        addCb('af-hold-enter','Enter gedrückt halten (Schnellmodus)',settings.holdEnter);
        add('Wiederholrate (Hold):',[inp('af-hold-interval','number',settings.holdInterval,10,'65px'),mk('span',{color:'#666',fontSize:'11px'},'ms (OS Key-Repeat)')]);

        sep();
        sec('🔔 Discord');
        s.appendChild(mk('div',{color:'#aaa',fontSize:'12px',marginBottom:'2px'},'Webhook URL:'));
        const wi=inp('af-webhook-url','text',settings.webhookUrl||'',undefined,'100%');wi.placeholder='https://discord.com/api/webhooks/...';wi.style.marginBottom='8px';s.appendChild(wi);

        sep();
        sec('📊 Tagesergebnis');
        const lootTable=mk('div',{fontSize:'12px',marginBottom:'4px'});
        const lootRes=mk('div',{color:'#27ae60',marginBottom:'2px'},'📦 –');lootRes.id='af-loot-res';
        const lootVil=mk('div',{color:'#3498db',marginBottom:'2px'},'🏘️ –');lootVil.id='af-loot-vil';
        const lootAvg=mk('div',{color:'#e67e22',marginBottom:'4px'},'⚡ –');lootAvg.id='af-loot-avg';
        lootTable.appendChild(lootRes);lootTable.appendChild(lootVil);lootTable.appendChild(lootAvg);
        s.appendChild(lootTable);
        s.appendChild(mk('div',{color:'#666',fontSize:'10px'},'Aktualisierung nach jedem Loop'));

        const saveBtn=mk('button',{display:'block',width:'100%',marginBottom:'8px',marginTop:'8px'},'Einstellungen speichern'); saveBtn.className='af-btn af-btn-primary';
        saveBtn.addEventListener('click',()=>{
            const sel=document.querySelector('#af-group-wrapper select');
            const ns={
                optionGroup:sel?parseInt(sel.value):settings.optionGroup,
                optionDistance:Math.max(1,parseFloat(document.getElementById('af-distance').value)||DEFAULTS.optionDistance),
                optionTime:Math.max(0,parseFloat(document.getElementById('af-time').value)||DEFAULTS.optionTime),
                optionLosses:document.getElementById('af-losses').checked,
                optionMaxloot:document.getElementById('af-maxloot').checked,
                optionNewbarbs:document.getElementById('af-newbarbs').checked,
                villageDistances:loadSettings().villageDistances,
                villageSettings:loadSettings().villageSettings,
                enterMin:Math.max(50,parseInt(document.getElementById('af-enter-min').value)||DEFAULTS.enterMin),
                enterMax:Math.max(50,parseInt(document.getElementById('af-enter-max').value)||DEFAULTS.enterMax),
                reloadMin:Math.max(10,parseInt(document.getElementById('af-reload-min').value)||DEFAULTS.reloadMin),
                reloadMax:Math.max(10,parseInt(document.getElementById('af-reload-max').value)||DEFAULTS.reloadMax),
                holdEnter:document.getElementById('af-hold-enter').checked,
                holdInterval:Math.max(10,parseInt(document.getElementById('af-hold-interval').value)||DEFAULTS.holdInterval),
                webhookUrl:document.getElementById('af-webhook-url').value.trim(),
            };
            if(ns.enterMin>ns.enterMax)[ns.enterMin,ns.enterMax]=[ns.enterMax,ns.enterMin];
            if(ns.reloadMin>ns.reloadMax)[ns.reloadMin,ns.reloadMax]=[ns.reloadMax,ns.reloadMin];
            saveSettings(ns);settings=ns;
            saveBtn.textContent='✓ Gespeichert!';setTimeout(()=>{saveBtn.textContent='Einstellungen speichern';},1500);
        });
        s.appendChild(saveBtn);
        body.appendChild(s);
    }

    // Gecachte Dorfliste für Village-Tab
    let cachedVillages = {};

    function buildTabVillages(body) {
        const s = document.createElement('div');
        s.id = 'af-tab-villages';
        s.style.display = 'none';

        // Header mit + Button
        const header = mk('div', {display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:'8px'});
        header.appendChild(mk('div', {color:'#aaa', fontSize:'11px'}, 'Dörfer mit individuellen Einstellungen:'));
        const addBtn = mk('button', {border:'none', borderRadius:'50%', width:'22px', height:'22px', backgroundColor:'#27ae60', color:'#fff', fontSize:'16px', cursor:'pointer', lineHeight:'20px', padding:'0', flexShrink:'0'}, '+');
        header.appendChild(addBtn);
        s.appendChild(header);

        // Liste der konfigurierten Dörfer
        const configList = mk('div', {maxHeight:'300px', overflowY:'auto'});
        configList.id = 'af-vs-config-list';
        s.appendChild(configList);

        // Modal Overlay
        // Altes Modal entfernen falls vorhanden
        const existingModal = document.getElementById('af-vs-modal');
        if (existingModal) existingModal.remove();

        const modal = mk('div', {
            display:'none', position:'fixed', top:'0', left:'0', width:'100%', height:'100%',
            backgroundColor:'rgba(0,0,0,0.7)', zIndex:'20000', justifyContent:'center', alignItems:'center'
        });
        modal.id = 'af-vs-modal';

        const modalBox = mk('div', {
            backgroundColor:'#2c2c2c', borderRadius:'8px', padding:'16px', minWidth:'260px', maxWidth:'300px',
            maxHeight:'80vh', overflowY:'auto', color:'#fff', fontFamily:'Arial, sans-serif', fontSize:'13px',
            position:'relative', margin:'auto', top:'50%', transform:'translateY(-50%)'
        });

        // Modal Titel
        const modalTitle = mk('div', {fontWeight:'bold', fontSize:'14px', marginBottom:'12px', borderBottom:'1px solid #555', paddingBottom:'6px'}, 'Dorf konfigurieren');
        modalBox.appendChild(modalTitle);

        // Dorf-Auswahl Dropdown
        modalBox.appendChild(mk('div', {color:'#aaa', fontSize:'12px', marginBottom:'2px'}, 'Dorf:'));
        const villageSelect = document.createElement('select');
        villageSelect.id = 'af-vs-modal-village';
        Object.assign(villageSelect.style, {width:'100%', backgroundColor:'#1a1a1a', color:'#fff', border:'1px solid #555', borderRadius:'4px', padding:'4px', fontSize:'12px', marginBottom:'10px', boxSizing:'border-box'});
        modalBox.appendChild(villageSelect);

        // Modus
        modalBox.appendChild(mk('div', {color:'#aaa', fontSize:'12px', marginBottom:'2px'}, 'Modus:'));
        const modeSelect = document.createElement('select');
        modeSelect.id = 'af-vs-modal-mode';
        Object.assign(modeSelect.style, {width:'100%', backgroundColor:'#1a1a1a', color:'#fff', border:'1px solid #555', borderRadius:'4px', padding:'4px', fontSize:'12px', marginBottom:'10px', boxSizing:'border-box'});
        [['distance','Nur Distanz'], ['area','Nur Bereich'], ['both','Distanz + Bereich']].forEach(([val, label]) => {
            const opt = document.createElement('option'); opt.value = val; opt.textContent = label;
            modeSelect.appendChild(opt);
        });
        modalBox.appendChild(modeSelect);

        // Distanz-Zeile
        const distRow = mk('div', {marginBottom:'10px'});
        distRow.id = 'af-vs-modal-distrow';
        distRow.appendChild(mk('div', {color:'#aaa', fontSize:'12px', marginBottom:'2px'}, 'Reichweite:'));
        const distRowInner = mk('div', {display:'flex', alignItems:'center', gap:'6px'});
        const distInp = inp('af-vs-modal-dist', 'number', settings.optionDistance, 1, '80px');
        distRowInner.appendChild(distInp);
        distRowInner.appendChild(mk('span', {color:'#aaa', fontSize:'12px'}, 'Felder'));
        distRow.appendChild(distRowInner);
        modalBox.appendChild(distRow);

        // Bereich
        const areaDiv = mk('div', {marginBottom:'10px'});
        areaDiv.id = 'af-vs-modal-areadiv';
        areaDiv.appendChild(mk('div', {color:'#aaa', fontSize:'12px', marginBottom:'4px'}, 'Farmbereich:'));

        const makeCoordRow = (label, xId, yId) => {
            const row = mk('div', {display:'flex', alignItems:'center', gap:'4px', marginBottom:'4px'});
            row.appendChild(mk('span', {color:'#aaa', fontSize:'11px', minWidth:'28px'}, label));
            const xi = inp(xId, 'number', 0, 0, '60px'); xi.placeholder = 'X';
            const yi = inp(yId, 'number', 0, 0, '60px'); yi.placeholder = 'Y';
            row.appendChild(xi);
            row.appendChild(mk('span', {color:'#555', fontSize:'12px'}, '|'));
            row.appendChild(yi);
            return row;
        };
        areaDiv.appendChild(makeCoordRow('Von:', 'af-vs-modal-x1', 'af-vs-modal-y1'));
        areaDiv.appendChild(makeCoordRow('Bis:', 'af-vs-modal-x2', 'af-vs-modal-y2'));
        modalBox.appendChild(areaDiv);

        // Modus-Visibility
        const updateModalVisibility = () => {
            const mode = modeSelect.value;
            distRow.style.display = (mode === 'distance' || mode === 'both') ? 'block' : 'none';
            areaDiv.style.display = (mode === 'area' || mode === 'both') ? 'block' : 'none';
        };
        modeSelect.addEventListener('change', updateModalVisibility);
        updateModalVisibility();

        // Buttons
        const btnRow = mk('div', {display:'flex', gap:'8px', marginTop:'4px'});
        const saveModalBtn = mk('button', {flex:'1', padding:'6px', border:'none', borderRadius:'4px', cursor:'pointer', backgroundColor:'#27ae60', color:'#fff', fontSize:'12px', fontWeight:'bold'}, 'Speichern');
        const cancelModalBtn = mk('button', {flex:'1', padding:'6px', border:'none', borderRadius:'4px', cursor:'pointer', backgroundColor:'#555', color:'#fff', fontSize:'12px'}, 'Abbrechen');
        btnRow.appendChild(saveModalBtn);
        btnRow.appendChild(cancelModalBtn);
        modalBox.appendChild(btnRow);

        modal.appendChild(modalBox);
        document.body.appendChild(modal);

        // Modal schließen
        cancelModalBtn.addEventListener('click', () => { modal.style.display = 'none'; });
        modal.addEventListener('click', (e) => { if (e.target === modal) modal.style.display = 'none'; });

        // Konfigurations-Liste neu rendern
        function renderConfigList() {
            configList.innerHTML = '';
            settings = loadSettings();
            const vs = settings.villageSettings || {};
                const entries = Object.entries(vs);
            if (entries.length === 0) {
                configList.appendChild(mk('div', {color:'#666', fontSize:'11px', padding:'8px 0'}, 'Keine individuellen Einstellungen vorhanden.'));
                return;
            }
            entries.forEach(([coord, cfg]) => {
                const vName = cachedVillages[coord] ? cachedVillages[coord].name : coord;
                const row = mk('div', {
                    display:'flex', alignItems:'center', gap:'6px', marginBottom:'6px',
                    padding:'5px', borderRadius:'4px', backgroundColor:'#1e1e1e', border:'1px solid #333'
                });
                // Info
                const info = mk('div', {flex:'1', overflow:'hidden'});
                info.appendChild(mk('div', {fontSize:'11px', color:'#ccc', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}, `${vName} (${coord})`));
                const modeLabels = {distance:'Distanz', area:'Bereich', both:'Distanz + Bereich'};
                let detail = modeLabels[cfg.mode] || cfg.mode;
                if ((cfg.mode === 'distance' || cfg.mode === 'both') && cfg.distance) detail += ` · ${cfg.distance} Felder`;
                if (cfg.mode === 'area' || cfg.mode === 'both') detail += ` · (${cfg.x1}|${cfg.y1})-(${cfg.x2}|${cfg.y2})`;
                info.appendChild(mk('div', {fontSize:'10px', color:'#888', marginTop:'2px'}, detail));
                row.appendChild(info);

                // Bearbeiten
                const editBtn = mk('button', {border:'none', borderRadius:'4px', padding:'3px 7px', cursor:'pointer', backgroundColor:'#2980b9', color:'#fff', fontSize:'11px'}, '✏️');
                editBtn.addEventListener('click', () => openModal(coord, cfg));
                row.appendChild(editBtn);

                // Löschen
                const delBtn = mk('button', {border:'none', borderRadius:'4px', padding:'3px 7px', cursor:'pointer', backgroundColor:'#c0392b', color:'#fff', fontSize:'11px'}, '🗑');
                delBtn.addEventListener('click', () => {
                    settings = loadSettings();
                    delete settings.villageSettings[coord];
                    delete settings.villageDistances[coord];
                    saveSettings(settings);
                    renderConfigList();
                });
                row.appendChild(delBtn);
                configList.appendChild(row);
            });
        }

        // Modal öffnen (neu oder bearbeiten)
        function openModal(editCoord, existingCfg) {
            // Dorf-Dropdown befüllen
            villageSelect.innerHTML = '';
            settings = loadSettings();
            const usedCoords = Object.keys(settings.villageSettings || {});

            if (Object.keys(cachedVillages).length === 0) {
                // Dörfer noch nicht geladen
                const loadingOpt = document.createElement('option');
                loadingOpt.textContent = '⏳ Wird geladen...';
                loadingOpt.disabled = true;
                villageSelect.appendChild(loadingOpt);
                FarmGodCore.loadVillages(settings.optionGroup).then(villages => {
                    cachedVillages = villages;
                    openModal(editCoord, existingCfg);
                });
                modal.style.display = 'flex';
                return;
            }

            Object.entries(cachedVillages).sort(([,a],[,b])=>a.name.localeCompare(b.name)).forEach(([coord, v]) => {
                // Beim Bearbeiten: alle anzeigen; beim Neuanlegen: bereits konfigurierte ausblenden
                if (!editCoord && usedCoords.includes(coord)) return;
                const opt = document.createElement('option');
                opt.value = coord;
                opt.textContent = `${v.name} (${coord})`;
                if (coord === editCoord) opt.selected = true;
                villageSelect.appendChild(opt);
            });

            if (villageSelect.options.length === 0) {
                const opt = document.createElement('option');
                opt.textContent = 'Alle Dörfer bereits konfiguriert';
                opt.disabled = true;
                villageSelect.appendChild(opt);
            }

            // Felder vorausfüllen wenn bearbeiten
            if (existingCfg) {
                modeSelect.value = existingCfg.mode || 'distance';
                distInp.value = existingCfg.distance || settings.optionDistance;
                document.getElementById('af-vs-modal-x1').value = existingCfg.x1 !== undefined ? existingCfg.x1 : 0;
                document.getElementById('af-vs-modal-y1').value = existingCfg.y1 !== undefined ? existingCfg.y1 : 0;
                document.getElementById('af-vs-modal-x2').value = existingCfg.x2 !== undefined ? existingCfg.x2 : 999;
                document.getElementById('af-vs-modal-y2').value = existingCfg.y2 !== undefined ? existingCfg.y2 : 999;
            } else {
                modeSelect.value = 'distance';
                distInp.value = settings.optionDistance;
                document.getElementById('af-vs-modal-x1').value = 0;
                document.getElementById('af-vs-modal-y1').value = 0;
                document.getElementById('af-vs-modal-x2').value = 999;
                document.getElementById('af-vs-modal-y2').value = 999;
            }
            updateModalVisibility();
            villageSelect.disabled = !!editCoord;
            modal.style.display = 'flex';
        }

        // Speichern aus Modal
        saveModalBtn.addEventListener('click', () => {
            const coord = villageSelect.value;
            if (!coord) return;
            const mode = modeSelect.value;
            const entry = { mode };
            if (mode === 'distance' || mode === 'both') {
                const d = parseFloat(distInp.value);
                if (!isNaN(d) && d > 0) entry.distance = d;
            }
            if (mode === 'area' || mode === 'both') {
                entry.x1 = parseFloat(document.getElementById('af-vs-modal-x1').value) || 0;
                entry.y1 = parseFloat(document.getElementById('af-vs-modal-y1').value) || 0;
                entry.x2 = parseFloat(document.getElementById('af-vs-modal-x2').value) || 999;
                entry.y2 = parseFloat(document.getElementById('af-vs-modal-y2').value) || 999;
            }
            settings = loadSettings();
            if (!settings.villageSettings) settings.villageSettings = {};
            settings.villageSettings[coord] = entry;
            // villageDistances sync
            if (!settings.villageDistances) settings.villageDistances = {};
            if ((mode === 'distance' || mode === 'both') && entry.distance) {
                settings.villageDistances[coord] = entry.distance;
            } else {
                delete settings.villageDistances[coord];
            }
            saveSettings(settings);
            modal.style.display = 'none';
            renderConfigList();
        });

        // + Button öffnet Modal
        addBtn.addEventListener('click', () => openModal(null, null));

        // Beim Tab-Klick Dörfer im Hintergrund laden
        if (tabBtn) {
            tabBtn.addEventListener('click', () => {
                if (Object.keys(cachedVillages).length === 0) {
                    settings = loadSettings();
                    FarmGodCore.loadVillages(settings.optionGroup).then(v => { cachedVillages = v; });
                }
            });
        }

        // Dörfer im Hintergrund laden, dann Liste rendern
        function loadAndRender() {
            settings = loadSettings();
            if (Object.keys(cachedVillages).length === 0) {
                FarmGodCore.loadVillages(settings.optionGroup).then(v => {
                    cachedVillages = v;
                    renderConfigList();
                }).catch(() => renderConfigList()); // Fallback: Koordinaten zeigen
            } else {
                renderConfigList();
            }
        }

        setTimeout(() => loadAndRender(), 100);

        // Custom Event für switchTab
        document.addEventListener('af-render-config-list', () => loadAndRender());

        body.appendChild(s);
    }

    function switchTab(tab){
        ['settings','katta'].forEach(t=>{
            const el=document.getElementById(`af-tab-${t}`),btn=document.getElementById(`af-tabbt-${t}`);
            if(el)el.style.display=t===tab?'block':'none';
            if(btn)btn.className='af-tab-btn'+(t===tab?' active':'');
        });
        // Bij wisselen naar dörfer tab: lijst opnieuw renderen
        if(tab==='villages'||tab==='night'){
            const cl=document.getElementById('af-vs-config-list');
            if(cl){
                const s=loadSettings();
                const vs=s.villageSettings||{};
                    cl.innerHTML='';
                const entries=Object.entries(vs);
                if(entries.length===0){
                    cl.appendChild(document.createElement('div')).innerText='Keine individuellen Einstellungen vorhanden.';
                    cl.firstChild.style.cssText='color:#666;font-size:11px;padding:8px 0';
                } else {
                    // renderConfigList existiert im closure von buildTabVillages
                    // daher rufen wir es über ein Custom Event auf
                    document.dispatchEvent(new CustomEvent('af-render-config-list'));
                }
            }
        }
    }

    function initializeUI(){
        const existing=document.getElementById('af-container');if(existing)existing.remove();

        // ── CSS-Variablen & globale Styles ──────────────────────────────
        if (!document.getElementById('af-styles')) {
            const style = document.createElement('style');
            style.id = 'af-styles';
            style.textContent = `
                #af-container * { box-sizing: border-box; }
                #af-container { --bg0: #0d1117; --bg1: #161b22; --bg2: #21262d; --bg3: #30363d;
                    --gold: #c9a84c; --gold-dim: #8a6f2e; --blue: #4a9eff; --green: #3fb950;
                    --red: #f85149; --orange: #f0883e; --text: #e6edf3; --muted: #8b949e; }
                #af-container input[type=number], #af-container input[type=text],
                #af-container select {
                    background: var(--bg0) !important; color: var(--text) !important;
                    border: 1px solid var(--bg3) !important; border-radius: 6px !important;
                    padding: 5px 8px !important; font-size: 12px !important;
                    transition: border-color .15s;
                }
                #af-container input:focus, #af-container select:focus {
                    outline: none !important; border-color: var(--blue) !important;
                }
                #af-container input[type=checkbox] { accent-color: var(--gold); width:14px; height:14px; }
                .af-btn {
                    border: none; border-radius: 6px; cursor: pointer; font-weight: 600;
                    font-size: 12px; padding: 6px 12px; transition: opacity .15s, transform .1s;
                    letter-spacing: .3px;
                }
                .af-btn:hover { opacity: .85; }
                .af-btn:active { transform: scale(.97); }
                .af-btn-primary { background: var(--blue); color: #fff; }
                .af-btn-gold { background: linear-gradient(135deg, var(--gold), var(--gold-dim)); color: #0d1117; }
                .af-btn-danger { background: var(--red); color: #fff; }
                .af-btn-ghost { background: var(--bg3); color: var(--text); }
                .af-tab-btn {
                    flex: 1; padding: 6px 8px; border: none; background: transparent;
                    color: var(--muted); font-size: 11px; font-weight: 600; cursor: pointer;
                    border-bottom: 2px solid transparent; transition: color .15s, border-color .15s;
                    letter-spacing: .4px; text-transform: uppercase;
                }
                .af-tab-btn.active { color: var(--gold); border-bottom-color: var(--gold); }
                .af-section-label {
                    font-size: 10px; font-weight: 700; letter-spacing: 1px;
                    text-transform: uppercase; color: var(--muted); margin: 12px 0 6px;
                }
                .af-section-label:first-child { margin-top: 0; }
                .af-divider { border: none; border-top: 1px solid var(--bg3); margin: 10px 0; }
                .af-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
                .af-label { color: var(--muted); font-size: 12px; flex: 1; }
                .af-pulse { display: inline-block; width: 7px; height: 7px; border-radius: 50%;
                    background: var(--green); box-shadow: 0 0 0 0 rgba(63,185,80,.4);
                    animation: af-pulse 2s infinite; }
                .af-pulse.paused { background: var(--orange); box-shadow: none; animation: none; }
                .af-pulse.stopped { background: var(--muted); box-shadow: none; animation: none; }
                @keyframes af-pulse {
                    0% { box-shadow: 0 0 0 0 rgba(63,185,80,.4); }
                    70% { box-shadow: 0 0 0 6px rgba(63,185,80,0); }
                    100% { box-shadow: 0 0 0 0 rgba(63,185,80,0); }
                }
                .af-progress-bar { height: 3px; background: var(--bg3); border-radius: 2px; overflow: hidden; margin: 4px 0 8px; }
                .af-progress-fill { height: 100%; background: linear-gradient(90deg, var(--gold), var(--blue));
                    border-radius: 2px; transition: width .4s ease; width: 0%; }
            `;
            document.head.appendChild(style);
        }

        const mk2 = (tag, styles, text) => {
            const e = document.createElement(tag);
            if (styles) Object.assign(e.style, styles);
            if (text !== undefined) e.textContent = text;
            return e;
        };

        const container = mk('div', {
            backgroundColor: 'var(--bg1)', color: 'var(--text)',
            fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
            fontSize: '13px', margin: '0 0 10px 0', borderRadius: '10px',
            boxShadow: '0 4px 24px rgba(0,0,0,.6)', overflow: 'hidden',
            border: '1px solid var(--bg3)'
        });
        container.id = 'af-container';

        // ── Header ───────────────────────────────────────────────────────
        const titleBar = mk('div', {
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            padding: '10px 14px', backgroundColor: 'var(--bg0)',
            borderBottom: '1px solid var(--bg3)', userSelect: 'none', cursor: 'pointer'
        });

        const titleLeft = mk('div', { display: 'flex', alignItems: 'center', gap: '10px' });

        // Logo + Name
        const logoWrap = mk('div', { display: 'flex', alignItems: 'center', gap: '8px' });
        const pulse = mk('div'); pulse.className = 'af-pulse' + (isRunning ? '' : ' stopped');
        pulse.id = 'af-pulse';
        const nameEl = mk('span', { fontWeight: '700', fontSize: '14px', letterSpacing: '.5px',
            background: 'linear-gradient(90deg, var(--gold), #e8c97a)', WebkitBackgroundClip: 'text',
            WebkitTextFillColor: 'transparent' }, 'Anaboles farmen');
        logoWrap.appendChild(pulse);
        logoWrap.appendChild(nameEl);
        titleLeft.appendChild(logoWrap);

        // Mini-Status
        const miniStatus = mk('div', { display: isMinimized ? 'flex' : 'none', alignItems: 'center', gap: '12px', fontSize: '12px' });
        miniStatus.id = 'af-mini-status';
        const statusMini = mk('span', { color: 'var(--gold)', fontWeight: '600' }, currentStatus); statusMini.id = 'af-status-mini';
        const timerMini = mk('span', { color: 'var(--muted)' }, currentCountdownText); timerMini.id = 'af-timer-mini';
        const modeMini = mk('span', { fontSize: '11px' }, ''); modeMini.id = 'af-mode-mini';
        const lootMini = mk('span', { color: 'var(--green)', fontSize: '11px' }, ''); lootMini.id = 'af-loot-mini';
        miniStatus.appendChild(statusMini);
        miniStatus.appendChild(timerMini);
        miniStatus.appendChild(modeMini);
        miniStatus.appendChild(lootMini);

        const miniResumeBtn = mk('button', {
            display: captchaAlertSent ? 'inline-block' : 'none',
            marginLeft: '8px', padding: '2px 8px', border: 'none', borderRadius: '4px',
            cursor: 'pointer', color: '#fff', fontSize: '11px', backgroundColor: 'var(--orange)'
        }, '▶ Fortsetzen');
        miniResumeBtn.id = 'af-mini-resume-btn';
        miniResumeBtn.addEventListener('click', (e) => { e.stopPropagation(); resumeAfterCaptcha(); });
        miniStatus.appendChild(miniResumeBtn);
        titleLeft.appendChild(miniStatus);
        titleBar.appendChild(titleLeft);

        const titleRight = mk('div', { display: 'flex', alignItems: 'center', gap: '6px' });

        const miniToggleBtn = mk('button', {
            display: isMinimized ? 'block' : 'none',
            padding: '3px 10px', border: 'none', borderRadius: '4px',
            cursor: 'pointer', color: '#0d1117', fontSize: '11px', fontWeight: '700',
            background: 'linear-gradient(135deg, var(--gold), var(--gold-dim))'
        });
        miniToggleBtn.id = 'af-mini-toggle-btn';
        miniToggleBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleProcess(); });
        titleRight.appendChild(miniToggleBtn);

        const arrowBtn = mk('button', {
            background: 'none', border: 'none', color: 'var(--muted)',
            cursor: 'pointer', fontSize: '12px', padding: '2px 4px',
            borderRadius: '4px', transition: 'color .15s'
        }, isMinimized ? '▼' : '▲');
        arrowBtn.id = 'af-arrow';
        arrowBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleMinimize(); });
        titleRight.appendChild(arrowBtn);
        titleBar.appendChild(titleRight);
        titleBar.addEventListener('click', () => toggleMinimize());
        container.appendChild(titleBar);

        // ── Body ─────────────────────────────────────────────────────────
        const body = mk('div', { display: isMinimized ? 'none' : 'block', padding: '12px 14px' });
        body.id = 'af-body';

        // Progress bar (dekorativ, zeigt Farm-Fortschritt)
        const progressWrap = mk('div'); progressWrap.className = 'af-progress-bar';
        const progressFill = mk('div'); progressFill.className = 'af-progress-fill'; progressFill.id = 'af-progress-fill';
        progressWrap.appendChild(progressFill);
        body.appendChild(progressWrap);

        const countdown = mk('div', { display: 'none', marginBottom: '8px', color: 'var(--gold)', fontWeight: '600', fontSize: '12px' });
        countdown.id = 'af-countdown'; body.appendChild(countdown);

        // Tabs
        const tabBar = mk('div', {
            display: 'flex', gap: '0', marginBottom: '12px',
            borderBottom: '1px solid var(--bg3)'
        });
        [['settings', '⚙ Einstellungen'], ['katta', '⚒ Rammen']].forEach(([id, label]) => {
            const btn = mk('button', {}, label);
            btn.className = 'af-tab-btn';
            btn.id = `af-tabbt-${id}`;
            btn.addEventListener('click', () => switchTab(id));
            tabBar.appendChild(btn);
        });
        body.appendChild(tabBar);
        buildTabSettings(body);
        buildTabKatta(body);

        // Start/Stop Button
        const controlButton = mk('button', { display: 'block', width: '100%', padding: '8px', marginTop: '10px' });
        controlButton.className = 'af-btn af-btn-gold';
        controlButton.id = 'af-toggle-btn';
        controlButton.addEventListener('click', toggleProcess);
        body.appendChild(controlButton);

        const resumeButton = mk('button', {
            display: captchaAlertSent ? 'block' : 'none',
            width: '100%', padding: '8px', marginTop: '6px'
        }, '▶ Fortsetzen (Captcha gelöst)');
        resumeButton.className = 'af-btn af-btn-ghost';
        resumeButton.id = 'af-resume-btn';
        resumeButton.addEventListener('click', resumeAfterCaptcha);
        body.appendChild(resumeButton);

        container.appendChild(body);

        const farmWidget = document.getElementById('am_widget_Farm');
        if (farmWidget) {
            farmWidget.parentNode.insertBefore(container, farmWidget);
        } else {
            document.body.appendChild(container);
        }

        updateButtonState();
        ['settings', 'katta'].forEach(t => {
            const btn = document.getElementById(`af-tabbt-${t}`);
            if (btn) btn.className = 'af-tab-btn' + (t === 'settings' ? ' active' : '');
        });
    }


    function buildTabKatta(body) {
        const s = document.createElement('div');
        s.id = 'af-tab-katta';
        s.style.display = 'none';
        s.style.maxHeight = '340px';
        s.style.overflowY = 'auto';
        s.style.paddingRight = '4px';

        const mk2 = (tag, styles, text) => { const e = document.createElement(tag); if(styles)Object.assign(e.style,styles); if(text!==undefined)e.textContent=text; return e; };
        const sec = (t) => { const d=mk2('div',{}); d.className='af-section-label'; d.textContent=t; s.appendChild(d); };
        const sep = () => { const d=document.createElement('hr'); d.className='af-divider'; s.appendChild(d); };
        const addCb = (id, label, checked) => {
            const w = mk2('div',{display:'flex',alignItems:'center',gap:'6px',marginBottom:'5px'});
            const cb = document.createElement('input'); cb.id=id; cb.type='checkbox'; cb.checked=checked;
            const l = mk2('label',{color:'#fff',fontSize:'12px',cursor:'pointer'},label); l.htmlFor=id;
            w.appendChild(cb); w.appendChild(l); s.appendChild(w);
        };
        const addRow = (label, children) => {
            s.appendChild(mk2('div',{color:'#aaa',fontSize:'12px',marginBottom:'2px'},label));
            const row = mk2('div',{display:'flex',gap:'6px',alignItems:'center',marginBottom:'6px'});
            children.forEach(c=>row.appendChild(c)); s.appendChild(row);
        };
        const numInp = (id, val, w) => {
            const e = document.createElement('input'); e.id=id; e.type='number'; e.value=val; e.min=0;
            Object.assign(e.style,{width:w||'60px',padding:'3px 5px',borderRadius:'4px',border:'1px solid #555',backgroundColor:'#1a1a1a',color:'#fff',fontSize:'12px',boxSizing:'border-box'});
            return e;
        };

        const st = loadSettings();

        sec('🔨 Rammen bei roten/gelben Berichten');
        addCb('katta-enabled', 'Aktiviert', st.kattaEnabled);
        addCb('katta-on-red', 'Bei roten Berichten', st.kattaOnRed);
        addCb('katta-on-yellow', 'Bei gelben Berichten', st.kattaOnYellow);

        sep();
        sec('🏘️ Angreifende Gruppe');
        const gw = mk2('div',{marginBottom:'6px'}); gw.id='katta-group-wrapper';
        gw.innerHTML='<span style="color:#aaa;font-size:11px;">Lädt...</span>';
        s.appendChild(gw);
        FarmGodCore.buildGroupSelect(st.kattaGroup).then(html=>{gw.innerHTML=html;});

        sep();
        sec('⚔️ Truppenanzahl');
        addRow('Axtkämpfer:', [numInp('katta-axe', st.kattaAxe)]);
        addRow('Rammbock:', [numInp('katta-ram', st.kattaRam)]);
        addRow('Späher:', [numInp('katta-spy', st.kattaSpy)]);
        addRow('Katapult:', [numInp('katta-cata', st.kattaCata)]);

        sep();
        sec('🎯 Katapult-Zielgebäude');
        const buildingSelect = document.createElement('select');
        Object.assign(buildingSelect.style, {width:'100%',backgroundColor:'#1a1a1a',color:'#fff',border:'1px solid #555',borderRadius:'4px',padding:'4px',fontSize:'12px',marginBottom:'10px',boxSizing:'border-box'});
        buildingSelect.id = 'katta-building';
        const buildings = [
            ['wall','Wall'],['main','Hauptgebäude'],['barracks','Kaserne'],
            ['stable','Stall'],['garage','Werkstatt'],['market','Marktplatz'],
            ['wood','Holzfäller'],['stone','Lehmgrube'],['iron','Eisenmine'],
            ['farm','Bauernhof'],['storage','Speicher'],['hide','Versteck'],
        ];
        buildings.forEach(([val, label]) => {
            const o = document.createElement('option'); o.value=val; o.textContent=label;
            if (val === st.kattaTarget) o.selected=true;
            buildingSelect.appendChild(o);
        });
        s.appendChild(buildingSelect);

        sep();
        // Gesendete Koordinaten anzeigen
        const sentInfo = mk2('div',{color:'#666',fontSize:'10px',marginBottom:'6px'});
        const sent = kattaLoadSent();
        const sentCount = Object.keys(sent).length;
        sentInfo.textContent = `Gespeicherte Katta-Logs: ${sentCount} Dörfer`;
        s.appendChild(sentInfo);
        const clearBtn = mk2('button',{display:'block',width:'100%',marginBottom:'8px'},'🗑 Ramm-Log löschen'); clearBtn.className='af-btn af-btn-danger';
        clearBtn.addEventListener('click', () => {
            localStorage.removeItem(KATTA_SENT_KEY);
            sentInfo.textContent = 'Gespeicherte Katta-Logs: 0 Dörfer';
        });
        s.appendChild(clearBtn);

        const saveBtn = mk2('button',{display:'block',width:'100%'},'Einstellungen speichern'); saveBtn.className='af-btn af-btn-primary';
        saveBtn.addEventListener('click', () => {
            const sel = document.querySelector('#katta-group-wrapper select');
            const ns = Object.assign(loadSettings(), {
                kattaEnabled:   document.getElementById('katta-enabled').checked,
                kattaOnRed:     document.getElementById('katta-on-red').checked,
                kattaOnYellow:  document.getElementById('katta-on-yellow').checked,
                kattaGroup:     sel ? parseInt(sel.value) : st.kattaGroup,
                kattaAxe:       parseInt(document.getElementById('katta-axe').value) || 0,
                kattaRam:       parseInt(document.getElementById('katta-ram').value) || 0,
                kattaSpy:       parseInt(document.getElementById('katta-spy').value) || 0,
                kattaCata:      parseInt(document.getElementById('katta-cata').value) || 0,
                kattaTarget:    document.getElementById('katta-building').value,
            });
            saveSettings(ns); settings = ns;
            saveBtn.textContent = '✓ Gespeichert!';
            setTimeout(()=>{ saveBtn.textContent='Einstellungen speichern'; }, 1500);
        });
        s.appendChild(saveBtn);
        body.appendChild(s);
    }

    // =====================================================================
    // START
    // =====================================================================
    function buildTabNight(body) {
        const s = document.createElement('div');
        s.id = 'af-tab-night';
        s.style.display = 'none';

        const sep = () => s.appendChild(mk('div', {borderTop:'1px solid #444', margin:'8px 0'}));
        const sec = (t) => s.appendChild(mk('div', {color:'#f0a500', fontWeight:'bold', fontSize:'12px', marginBottom:'6px'}, t));
        const addCb = (id, label, checked) => {
            const w = mk('div', {display:'flex', alignItems:'center', gap:'6px', marginBottom:'6px'});
            const cb = document.createElement('input'); cb.id = id; cb.type = 'checkbox'; cb.checked = checked;
            const l = mk('label', {color:'#fff', fontSize:'12px', cursor:'pointer'}, label); l.htmlFor = id;
            w.appendChild(cb); w.appendChild(l); s.appendChild(w);
        };
        const add = (label, children) => {
            s.appendChild(mk('div', {color:'#aaa', fontSize:'12px', marginBottom:'2px'}, label));
            const row = mk('div', {display:'flex', gap:'6px', alignItems:'center', marginBottom:'6px'});
            children.forEach(c => row.appendChild(c));
            s.appendChild(row);
        };

        // Nachtmodus
        sec('🌙 Nachtmodus');
        addCb('af-night-enabled', 'Nachtmodus aktivieren', settings.nightModeEnabled);

        add('Nacht von:', [
            inp('af-night-start', 'time', settings.nightStart, undefined, '90px'),
            mk('span', {color:'#aaa'}, 'bis'),
            inp('af-night-end', 'time', settings.nightEnd, undefined, '90px')
        ]);

        add('Reload-Intervall (Nacht):', [
            inp('af-night-reload-min', 'number', settings.nightReloadMin, 10, '65px'),
            mk('span', {color:'#aaa'}, '–'),
            inp('af-night-reload-max', 'number', settings.nightReloadMax, 10, '65px'),
            mk('span', {color:'#aaa'}, 's')
        ]);

        add('Mindestzeit zwischen Farms (Nacht):', [
            inp('af-night-option-time', 'number', settings.nightOptionTime, 0, '80px'),
            mk('span', {color:'#aaa'}, 'min')
        ]);
        addCb('af-night-maxloot', 'B-Farm bei vollem Loot senden (Nacht)', settings.nightOptionMaxloot);
        addCb('af-night-losses', 'Farms mit Teilverlusten senden (Nacht)', settings.nightOptionLosses);

        // Nachtmodus Status
        const nightStatusEl = mk('div', {fontSize:'11px', marginBottom:'6px', padding:'4px', borderRadius:'4px', backgroundColor:'#1e1e1e'});
        nightStatusEl.id = 'af-night-status';
        nightStatusEl.innerText = isNightMode() ? '🌙 Nachtmodus aktiv' : '☀️ Tagmodus aktiv';
        nightStatusEl.style.color = isNightMode() ? '#8e44ad' : '#f0a500';
        s.appendChild(nightStatusEl);

        sep();

        // Zufällige Pausen
        sec('⏸ Zufällige Pausen');
        addCb('af-pause-enabled', 'Zufällige Pausen aktivieren', settings.randomPauseEnabled);

        add('Pause alle:', [
            inp('af-pause-interval-min', 'number', settings.randomPauseIntervalMin, 1, '55px'),
            mk('span', {color:'#aaa'}, '–'),
            inp('af-pause-interval-max', 'number', settings.randomPauseIntervalMax, 1, '55px'),
            mk('span', {color:'#aaa'}, 'Min')
        ]);

        add('Pausendauer:', [
            inp('af-pause-duration-min', 'number', settings.randomPauseDurationMin, 1, '55px'),
            mk('span', {color:'#aaa'}, '–'),
            inp('af-pause-duration-max', 'number', settings.randomPauseDurationMax, 1, '55px'),
            mk('span', {color:'#aaa'}, 'Min')
        ]);

        sep();

        const saveBtn = mk('button', {display:'block', width:'100%', padding:'5px', border:'none', borderRadius:'4px', cursor:'pointer', backgroundColor:'#2980b9', color:'#fff', fontSize:'12px'}, 'Einstellungen speichern');
        saveBtn.addEventListener('click', () => {
            const cur = loadSettings();
            const ns = Object.assign({}, cur, {
                nightModeEnabled: document.getElementById('af-night-enabled').checked,
                nightStart: document.getElementById('af-night-start').value,
                nightEnd: document.getElementById('af-night-end').value,
                nightReloadMin: Math.max(10, parseInt(document.getElementById('af-night-reload-min').value) || DEFAULTS.nightReloadMin),
                nightReloadMax: Math.max(10, parseInt(document.getElementById('af-night-reload-max').value) || DEFAULTS.nightReloadMax),
                nightOptionTime: Math.max(0, parseFloat(document.getElementById('af-night-option-time').value) || DEFAULTS.nightOptionTime),
                nightOptionMaxloot: document.getElementById('af-night-maxloot').checked,
                nightOptionLosses: document.getElementById('af-night-losses').checked,
                randomPauseEnabled: document.getElementById('af-pause-enabled').checked,
                randomPauseIntervalMin: Math.max(1, parseInt(document.getElementById('af-pause-interval-min').value) || DEFAULTS.randomPauseIntervalMin),
                randomPauseIntervalMax: Math.max(1, parseInt(document.getElementById('af-pause-interval-max').value) || DEFAULTS.randomPauseIntervalMax),
                randomPauseDurationMin: Math.max(1, parseInt(document.getElementById('af-pause-duration-min').value) || DEFAULTS.randomPauseDurationMin),
                randomPauseDurationMax: Math.max(1, parseInt(document.getElementById('af-pause-duration-max').value) || DEFAULTS.randomPauseDurationMax),
            });
            if (ns.nightReloadMin > ns.nightReloadMax) [ns.nightReloadMin, ns.nightReloadMax] = [ns.nightReloadMax, ns.nightReloadMin];
            if (ns.randomPauseIntervalMin > ns.randomPauseIntervalMax) [ns.randomPauseIntervalMin, ns.randomPauseIntervalMax] = [ns.randomPauseIntervalMax, ns.randomPauseIntervalMin];
            if (ns.randomPauseDurationMin > ns.randomPauseDurationMax) [ns.randomPauseDurationMin, ns.randomPauseDurationMax] = [ns.randomPauseDurationMax, ns.randomPauseDurationMin];
            saveSettings(ns); settings = ns;
            // Status aktualisieren
            const el = document.getElementById('af-night-status');
            if (el) { el.innerText = isNightMode() ? '🌙 Nachtmodus aktiv' : '☀️ Tagmodus aktiv'; el.style.color = isNightMode() ? '#8e44ad' : '#f0a500'; }
            saveBtn.textContent = '✓ Gespeichert!';
            setTimeout(() => { saveBtn.textContent = 'Einstellungen speichern'; }, 1500);
        });
        s.appendChild(saveBtn);
        body.appendChild(s);
    }

    initializeUI();
    startCaptchaObserver();
    fetchTodayLoot();

    if(isRunning)startProcess();
    else setStatus('⏹ Bereit','#aaa');

})();