💪 Anaboles farmen

FarmGod vollständig integriert – keine externe Abhängigkeit

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/587597/1880936/%F0%9F%92%AA%20Anaboles%20farmen.js

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         💪 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');

})();