Bonk Toolkit

Adds new features to modify the game's STATE, you can type ;help to get info about the commands :)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bonk Toolkit
// @namespace    https://bonk.io/
// @version      1.0.0
// @description  Adds new features to modify the game's STATE, you can type ;help to get info about the commands :)
// @author       Meleko
// @match        https://bonk.io/gameframe-release.html
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const SM_VERSION = '1.0.0';

    // --- GLOBAL STATE ---
    window.__SUMMON__ = {
        state: null,
        cbs: {},
        applyState: false,
        savedState: null,
        noJoin: null,
        extraTime: 30,
        _frozen: false,
    };

    // --- CODE INJECTOR ---
    if (!window.bonkCodeInjectors) window.bonkCodeInjectors = [];
    window.bonkCodeInjectors.push(function (src) {
        try {
            const sm = src.match(/[A-Z]\[[A-Za-z0-9\$_]{3}(\[[0-9]{1,3}\]){2}\]={discs/i);
            if (sm) {
                const stStr = sm[0];
                let sci = null;
                const scStr = src.match(/[A-Za-z]\[...(\[[0-9]{1,4}\]){2}\]\(\[\{/)?.[0];
                if (scStr) {
                    const ns = scStr.match(/[0-9]{1,4}/g);
                    sci = ns[ns.length - 1];
                }
                let scv = '';
                if (sci) {
                    const re = new RegExp(
                        `[A-Za-z0-9\\$_]{3}\\[[0-9]{1,3}\\]=[A-Za-z0-9\\$_]{3}\\[[0-9]{1,4}\\]\\[[A-Za-z0-9\\$_]{3}\\[[0-9]{1,4}\\]\\[${sci}\\]\\].+?(?=;);`
                    );
                    const sc2 = src.match(re)?.[0];
                    if (sc2) scv = sc2.split(']')[0] + ']';
                }
                const ap = scv
                    ? `
if(window.__SUMMON__.savedState&&window.__SUMMON__.applyState){
    const _ya=${scv};
    for(let _i in window.__SUMMON__.savedState.discs){if(window.__SUMMON__.savedState.discs[_i]!=undefined)_ya.discs[_i]=window.__SUMMON__.savedState.discs[_i];}
    for(let _i in _ya.discs){if(!window.__SUMMON__.savedState.discs[_i])_ya.discs[_i]=undefined;}
    for(let _i in window.__SUMMON__.savedState.discDeaths){if(window.__SUMMON__.savedState.discDeaths[_i]!=undefined)_ya.discDeaths[_i]=window.__SUMMON__.savedState.discDeaths[_i];}
    _ya.ftu=window.__SUMMON__.extraTime||30;_ya.physics=window.__SUMMON__.savedState.physics;
    _ya.seed=window.__SUMMON__.savedState.seed;_ya.rc=window.__SUMMON__.savedState.rc;
    if(window.__SUMMON__.savedState.scores&&window.__SUMMON__.savedState.scores.length>0&&_ya.scores&&_ya.scores.length>0)_ya.scores=window.__SUMMON__.savedState.scores;
    _ya.capZones=window.__SUMMON__.savedState.capZones;_ya.projectiles=window.__SUMMON__.savedState.projectiles;
    if(window.__SUMMON__.savedState.ms)_ya.ms=window.__SUMMON__.savedState.ms;
    window.__SUMMON__.applyState=false;window.__SUMMON__.savedState=null;window.__SUMMON__.noJoin=null;
}`
                    : '';
                src = src.replace(stStr, `window.__SUMMON__.state=arguments[0];` + stStr);
                if (scv) {
                    const tr = src.match(
                        /\* 999\),[A-Za-z0-9\$_]{3}\[[0-9]{1,3}\],null,[A-Za-z0-9\$_]{3}\[[0-9]{1,3}\],true\);/i
                    )?.[0];
                    if (tr) src = src.replace(tr, tr + ap);
                }
            }
            for (const cb of [...src.matchAll(/[A-Za-z0-9\$_]{3}\(\.\.\./g)].map(m => m[0])) {
                const fn = cb.split('(')[0];
                src = src.replace(`function ${cb}`, `window.__SUMMON__.cbs["${fn}"]=${fn};function ${cb}`);
            }
            return src;
        } catch (e) {
            console.warn('[SummonMod]', e);
            return src;
        }
    });

    // --- START GAME ---
    function startGame(state, noJoin, extraTime) {
        // If the game is currently frozen, always keep the huge freeze time
        // so that commands like ;spin, ;exp, etc. don't accidentally unfreeze it.
        if (window.__SUMMON__?._frozen) {
            extraTime = 1000000;
        }
        if (state) {
            window.__SUMMON__.applyState = true;
            window.__SUMMON__.savedState = state;
            window.__SUMMON__.noJoin = noJoin;
            window.__SUMMON__.extraTime = extraTime || 30;
            for (const cb of Object.keys(window.__SUMMON__.cbs)) window.__SUMMON__.cbs[cb]('startGame');
        } else {
            const e = document.getElementById('newbonklobby_editorbutton'),
                m = document.getElementById('mapeditorcontainer'),
                t = document.getElementById('mapeditor_midbox_testbutton'),
                c = document.getElementById('mapeditor_close');
            if (e && m && t && c) {
                e.click();
                m.style.display = 'none';
                c.click();
                t.click();
            } else for (const cb of Object.keys(window.__SUMMON__.cbs)) window.__SUMMON__.cbs[cb]('startGame');
        }
    }

    // --- STATE HELPER ---
    // Returns window.__SUMMON__ if state exists, otherwise shows error and returns null.
    function _requireState() {
        const S = window.__SUMMON__;
        if (!S?.state) { showToast('⚠️ No state', 'err'); return null; }
        return S;
    }

    // ===
    // --- SUMMONABLES ---
    // Divided into 3 categories. Add your items in the corresponding section.

    // --- MAPDATA EXPAND (restores defaults stripped to save space) ---
    function expandMapData(s) {
        const data = JSON.parse(s);
        const FZ = { on: false, x: 0, y: 0, d: true, p: true, a: true, t: 0, cf: 0 };
        const CF = { x: 0, y: 0, w: true, ct: 0 };
        const SD = {
            ld: 0,
            ad: 0,
            fr: false,
            bu: false,
            fricp: false,
            f_c: 1,
            f_p: true,
            f_1: false,
            f_2: false,
            f_3: false,
            f_4: false,
            n: '',
        };
        const FX = {
            n: 'Unnamed Shape',
            fr: null,
            fp: null,
            re: null,
            de: null,
            d: false,
            np: false,
            ng: false,
            ig: false,
        };
        const JD = { mmt: 0, ms: 0, em: false, cc: false, bf: 0 };
        function fill(o, d) {
            for (const k in d) if (!(k in o)) o[k] = d[k];
        }
        for (const b of data.bodies || []) {
            if (!('a' in b)) b.a = 0;
            if (!('av' in b)) b.av = 0;
            if (!('lv' in b)) b.lv = [0, 0];
            if (b.fz) fill(b.fz, FZ);
            else b.fz = { ...FZ };
            if (b.cf) fill(b.cf, CF);
            else b.cf = { ...CF };
            if (b.s) fill(b.s, SD);
        }
        for (const sh of data.shapes || []) {
            if (!('a' in sh)) sh.a = 0;
            if (!('sk' in sh)) sh.sk = false;
            if (!('c' in sh)) sh.c = [0, 0];
        }
        for (const fx of data.fixtures || []) fill(fx, FX);
        for (const j of data.joints || []) {
            if (!('ra' in j)) j.ra = 0;
            if (!('aa' in j)) j.aa = [0, 0];
            if (!('bb' in j)) j.bb = -1;
            if (j.d) fill(j.d, JD);
        }
        return JSON.stringify(data);
    }

    // -- Category 1: Objects (regular objects + full maps) ---
    const SUMMONABLES_OBJ = {
        jail: {
            icon: '🏛️',
            mapData: `{"bodies":[{"p":[0,0],"fx":[0,1,2,3],"s":{"type":"s","fric":0.5,"fricp":true,"de":0.3,"re":-1}}],"shapes":[{"type":"bx","w":3.416667,"h":0.416667,"c":[0,1.083333]},{"type":"bx","w":3.416667,"h":0.416667,"c":[0,-1.833333]},{"type":"bx","w":3.5,"h":0.416667,"c":[1.25,-0.375],"a":1.570796},{"type":"bx","w":3.5,"h":0.416667,"c":[-1.25,-0.375],"a":1.570796}],"fixtures":[{"f":7368816,"sh":0},{"f":7368816,"sh":1},{"f":7368816,"sh":2},{"f":7368816,"sh":3}],"joints":[]}`,
        },
        wdb: {
            icon: '⚫',
            mapData: `{"bodies":[{"p":[30.416667,45.75],"lv":[0,13],"cf":{"y":-0.333333},"fx":[0,1,2],"s":{"type":"d","bu":true,"fric":-1,"de":0.05,"re":-1,"f_c":4,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1.041667},{"type":"bx","w":1.666667,"h":1.666667,"a":0.785398},{"type":"bx","w":1.666667,"h":1.666667}],"fixtures":[{"sh":0,"f":591631,"d":true},{"sh":1,"f":591631,"np":true},{"sh":2,"f":591631,"np":true}],"joints":[]}`,
        },

        anvil: {
            icon: '⚓',
            mapData: `{"bodies":[{"p":[0,0],"fx":[0,1,2,3],"s":{"type":"d","bu":true,"fric":0.5,"de":9999,"re":-1}}],"shapes":[{"type":"bx","w":2.583333,"h":0.416667,"c":[0.083333,-41.666667]},{"type":"po","v":[[2.416667,-41.333333],[-2.083333,-41.333333],[-1.333333,-41.666667],[-0.916667,-42.083333],[-0.833333,-42.5],[-0.833333,-43.083333],[-1,-43.416667],[-1.166667,-43.75],[-1.666667,-44],[-2.416667,-44.083333],[1.583333,-44.083333],[1.583333,-43.583333],[2.833333,-43.583333],[2.833333,-43],[2.75,-42.666667],[2.5,-42.583333],[2,-42.583333],[1.416667,-42.666667],[1.083333,-42.666667],[1,-42.5],[1.083333,-42],[1.5,-41.75]],"s":1,"c":[0,0.166667]},{"type":"bx","w":83.333333,"h":0.083333,"c":[-0.416667,-1.083333],"a":1.570796},{"type":"bx","w":83.333333,"h":0.083333,"c":[0.416667,-1.083333],"a":1.570796}],"fixtures":[{"sh":0,"f":592137,"d":true,"ng":true},{"sh":1,"f":592137,"np":true,"ng":true},{"sh":2,"f":14767680,"ng":true},{"sh":3,"f":14767680,"ng":true}],"joints":[]}`,
        },

        balloon: {
            icon: '🎈',
            mapData: `{"bodies":[{"p":[0,0],"cf":{"y":-0.833333},"fx":[0,1,2,3,4,5,6],"s":{"type":"d","ld":2,"bu":true,"fric":0.5,"de":5,"re":-1}}],"shapes":[{"type":"ci","r":0.083333,"c":[0.666667,0.666667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.666667]},{"type":"ci","r":0.083333,"c":[-0.666667,0.666667]},{"type":"ci","r":0.083333,"c":[0.666667,-0.666667]},{"type":"bx","w":3,"h":0.083333,"c":[0,-1.333333],"a":-1.570796},{"type":"po","v":[[-0.083333,-2.833333],[0,-2.833333],[0.25,-2.583333],[-0.25,-2.583333]],"s":1},{"type":"ci","r":1.666667,"c":[0,-4.5]}],"fixtures":[{"sh":0,"f":5209260},{"sh":1,"f":5209260},{"sh":2,"f":5209260},{"sh":3,"f":5209260},{"sh":4,"f":15987699,"np":true},{"sh":5,"f":14829383,"np":true},{"sh":6,"f":14829383}],"joints":[]}`,
        },

        skateboard: {
            icon: '🛹',
            mapData: `{"bodies":[{"p":[0,0],"lv":[0,-100],"cf":{"y":-0.5},"fx":[0,1,2,3,4],"s":{"type":"d","bu":true,"fric":0,"fricp":true,"de":0.05,"re":-1,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"bx","w":2.083333,"h":0.416667,"c":[0,1.25]},{"type":"bx","w":1,"h":0.5,"c":[1.111667,0.6],"a":-0.888003},{"type":"bx","w":1,"h":0.5,"c":[-1.111667,0.6],"a":0.888003},{"type":"ci","r":0.416667,"c":[1,1.633333]},{"type":"ci","r":0.416667,"c":[-1,1.633333]}],"fixtures":[{"sh":0,"fr":0.01,"re":-1,"f":8421504},{"sh":1,"fr":6,"f":8421504},{"sh":2,"fr":6,"f":8421504},{"sh":3,"f":1644825},{"sh":4,"f":1644825}],"joints":[]}`,
        },

        gift: {
            icon: '🎁',
            mapData: `{"bodies":[{"p":[0,0],"av":-0.1,"lv":[0,-10],"cf":{"y":-0.6},"fx":[0,1,2,3,4,5,6,7,8,9,10],"s":{"type":"d","fric":5,"fricp":true,"de":0.5,"re":-1,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"bx","w":4.166667,"h":0.416667,"c":[2.083333,0],"a":-1.570796},{"type":"bx","w":4.583333,"h":0.416667,"c":[0,1.875]},{"type":"bx","w":4.166667,"h":0.416667,"c":[-2.083333,0],"a":-1.570796},{"type":"bx","w":4.583333,"h":0.416667,"c":[0,-2.291667]},{"type":"bx","w":4.166667,"h":4.166667,"c":[0,-0.166667]},{"type":"bx","w":4.916667,"h":0.5,"c":[0.083333,-0.25],"a":1.570796},{"type":"bx","w":4.916667,"h":0.5,"c":[1.333333,-0.25],"a":1.570796},{"type":"bx","w":4.916667,"h":0.5,"c":[-1.333333,-0.25],"a":1.570796},{"type":"ci","r":0.333333,"c":[0.083333,-2.833333]},{"type":"po","v":[[0.083333,-2.916667],[-0.333333,-3.333333],[-0.583333,-3.416667],[-0.916667,-3.416667],[-1,-3.166667],[-1,-2.833333],[-0.75,-2.5],[-0.25,-2.416667],[-0.083333,-2.666667],[-0.083333,-2.75],[-0.416667,-2.666667],[-0.583333,-2.666667],[-0.75,-2.833333],[-0.75,-3.083333],[-0.416667,-3.083333],[-0.083333,-3]],"s":1,"c":[0.083333,-0.083333]},{"type":"po","v":[[0.083333,-2.916667],[0.333333,-3.083333],[0.75,-3.333333],[1,-3.166667],[1.083333,-3],[1.083333,-2.666667],[0.916667,-2.5],[0.666667,-2.416667],[0.5,-2.333333],[0.166667,-2.5],[0.166667,-2.666667],[0.333333,-2.666667],[0.583333,-2.583333],[0.75,-2.666667],[0.833333,-2.75],[0.916667,-2.833333],[0.833333,-3],[0.75,-3.083333],[0.416667,-3]],"s":1}],"fixtures":[{"sh":0,"f":14895235},{"sh":1,"f":14895235},{"sh":2,"f":14895235},{"sh":3,"f":14895236,"np":true},{"sh":4,"f":12262751,"np":true},{"sh":5,"f":13574188,"np":true},{"sh":6,"f":13574188,"np":true},{"sh":7,"f":13574188,"np":true},{"sh":8,"f":13574188,"np":true},{"sh":9,"f":13574188,"np":true},{"sh":10,"f":13574188,"np":true}],"joints":[]}`,
        },

        spinner: {
            icon: '🌀',
            mapData: `{"bodies":[{"p":[0,0],"av":333,"cf":{"y":-666.666667,"w":false,"ct":33.3},"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],"s":{"type":"d","fric":0.5,"de":1,"re":-1}}],"shapes":[{"type":"ci","r":1.083333},{"type":"ci","r":0.166667,"c":[-0.78125,-0.78125]},{"type":"ci","r":0.166667,"c":[0.78125,-0.78125]},{"type":"ci","r":0.166667,"c":[0.78125,0.78125]},{"type":"ci","r":0.166667,"c":[-0.78125,0.78125]},{"type":"ci","r":1,"c":[0,-2.166667]},{"type":"ci","r":1,"c":[2,1.25]},{"type":"ci","r":1,"c":[-2,1.25]},{"type":"po","v":[[-2.083333,0.260417],[-1.822917,0.260417],[-1.302083,0],[-0.78125,-0.78125],[-0.78125,-1.041667],[-1.041667,-2.083333],[1.041667,-2.083333],[0.78125,-1.041667],[0.78125,-0.78125],[1.302083,0],[1.822917,0.260417],[2.083333,0.260417],[2.864583,1.5625],[1.822917,2.083333],[1.041667,1.5625],[0.78125,1.302083],[0.260417,1.041667],[-0.260417,1.041667],[-0.78125,1.302083],[-1.041667,1.5625],[-1.822917,2.083333],[-2.864583,1.5625]],"s":1},{"type":"ci","r":0.75,"c":[-2,1.25]},{"type":"ci","r":0.75,"c":[2,1.25]},{"type":"ci","r":0.583333,"c":[2,1.25]},{"type":"ci","r":0.583333,"c":[-2,1.25]},{"type":"ci","r":0.583333,"c":[-2,1.25]},{"type":"ci","r":0.583333,"c":[0,-2.166667]},{"type":"ci","r":0.75,"c":[0,-2.166667]}],"fixtures":[{"sh":0,"f":8421504,"np":true},{"sh":1,"f":14492194},{"sh":2,"f":14492194},{"sh":3,"f":14492194},{"sh":4,"f":14492194},{"sh":5,"f":14492194,"np":true},{"sh":6,"f":14492194,"np":true},{"sh":7,"f":14492194,"np":true},{"sh":8,"f":14492194,"np":true},{"sh":9,"f":513,"np":true},{"sh":10,"f":513,"np":true},{"sh":11,"f":8421504,"np":true},{"sh":12,"f":8421504,"np":true},{"sh":13,"f":8421504,"np":true},{"sh":14,"f":8421504,"np":true},{"sh":15,"f":513,"np":true}],"joints":[]}`,
        },

        parachute: {
            icon: '🪂',
            mapData: `{"bodies":[{"p":[0,0],"cf":{"y":-166.666667},"fx":[0,1,2,3,4,5,6,7,8],"s":{"type":"d","ld":999,"ad":600,"bu":true,"fric":0.5,"de":0.3,"re":-1}}],"shapes":[{"type":"ci","r":0.166667,"c":[0.916667,0.916667]},{"type":"ci","r":0.166667,"c":[-0.916667,0.916667]},{"type":"ci","r":0.833333,"c":[0.083333,-2.083333]},{"type":"ci","r":0.833333,"c":[0.75,-1.916667]},{"type":"ci","r":0.833333,"c":[-0.75,-1.916667]},{"type":"ci","r":0.833333,"c":[-1.25,-1.5]},{"type":"ci","r":0.833333,"c":[1.333333,-1.5]},{"type":"bx","w":0.416667,"h":0.416667,"c":[-0.916667,-0.916667],"a":-1.570796},{"type":"bx","w":0.416667,"h":0.416667,"c":[0.916667,-0.916667],"a":-1.570796}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":5209260,"ng":true},{"sh":2,"f":8577870,"np":true,"ng":true},{"sh":3,"f":2284123,"np":true,"ng":true},{"sh":4,"f":2284123,"np":true,"ng":true},{"sh":5,"f":1612084,"np":true,"ng":true},{"sh":6,"f":1612084,"np":true,"ng":true},{"sh":7,"f":5209260,"ng":true},{"sh":8,"f":5209260,"ng":true}],"joints":[]}`,
        },

        sword: {
            icon: '⚔️',
            mapData: `{"bodies":[{"p":[0,0],"fx":[0,1,2,3,4,5],"s":{"type":"d","fric":0,"de":0.01,"re":-1}}],"shapes":[{"type":"ci","r":0.08,"c":[0.76,0.76]},{"type":"ci","r":0.08,"c":[-0.76,-0.76]},{"type":"ci","r":0.08,"c":[-0.76,0.76]},{"type":"ci","r":0.08,"c":[0.76,-0.76]},{"type":"bx","w":1.36,"h":0.52,"c":[0,-1.92],"a":1.570796},{"type":"bx","w":2.04,"h":0.2,"c":[0,-3.6],"a":-1.570796}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":5209260,"ng":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":5209260,"ng":true},{"sh":4,"f":0,"ng":true},{"sh":5,"f":15461355,"d":true,"ng":true}],"joints":[]}`,
        },

        car: {
            icon: '🚗',
            mapData: `{"bodies":[{"p":[0,0],"fx":[0,1,2,3,4,5,6,7,8,9],"s":{"type":"d","bu":true,"fric":0,"de":0.1,"re":-1,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":0.083333,"c":[0.666667,0.666667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.666667]},{"type":"ci","r":0.083333,"c":[-0.666667,0.666667]},{"type":"ci","r":0.083333,"c":[0.666667,-0.666667]},{"type":"bx","w":6.25,"h":0.416667,"c":[0,1.166667]},{"type":"po","v":[[-3.083333,1],[-3.083333,-0.083333],[-1.166667,-0.333333],[-1.083333,-1.416667],[1.583333,-1.416667],[1.666667,-0.5],[3.083333,-0.25],[3.083333,1.083333]],"s":1,"c":[0,0.083333]},{"type":"ci","r":0.5,"c":[2.5,1.666667]},{"type":"ci","r":0.5,"c":[-2.5,1.666667]},{"type":"bx","w":1.416667,"h":0.416667,"c":[2.916667,0.5],"a":1.570796},{"type":"bx","w":1.416667,"h":0.416667,"c":[-2.916667,0.5],"a":1.570796}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":5209260,"ng":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":5209260,"ng":true},{"sh":4,"f":8421504,"ng":true},{"sh":5,"f":11869276,"np":true,"ng":true},{"sh":6,"f":1906437,"ng":true},{"sh":7,"f":1906437,"ng":true},{"sh":8,"f":8421504,"d":true,"ng":true},{"sh":9,"f":8421504,"d":true,"ng":true}],"joints":[]}`,
        },

        jetpack: {
            icon: '🚀',
            mapData: `{"bodies":[{"p":[0,0],"cf":{"y":-4,"w":false},"fx":[0,1,2,3,4,5,6,7],"s":{"type":"d","ad":2,"bu":true,"fric":0,"de":0.2,"re":-1}}],"shapes":[{"type":"ci","r":0.083333,"c":[-0.833333,-0.75]},{"type":"ci","r":0.083333,"c":[0.833333,-0.75]},{"type":"bx","w":1.333333,"h":0.416667,"c":[-1.25,0],"a":-1.570796},{"type":"bx","w":1.333333,"h":0.416667,"c":[1.25,0],"a":-1.570796},{"type":"bx","w":0.333333,"h":0.25,"c":[-1.25,0.833333]},{"type":"bx","w":0.333333,"h":0.25,"c":[1.25,0.833333]},{"type":"bx","w":0.166667,"h":0.166667,"c":[-0.75,0.75]},{"type":"bx","w":0.166667,"h":0.166667,"c":[0.75,0.75]}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":5209260,"ng":true},{"sh":2,"f":10329501,"ng":true},{"sh":3,"f":10329501,"ng":true},{"sh":4,"f":919298,"d":true,"ng":true},{"sh":5,"f":919298,"d":true,"ng":true},{"sh":6,"f":10329501,"ng":true},{"sh":7,"f":10329501,"ng":true}],"joints":[]}`,
        },

        clown: {
            icon: '🤡',
            mapData: `{"bodies":[{"p":[0,0],"lv":[-5,0],"cf":{"x":0.333333,"y":-0.666667},"fx":[0,1,2,3,4,5,6,7,8,9,10,11],"s":{"type":"d","fr":true,"fric":0.5,"de":552,"re":-1}}],"shapes":[{"type":"ci","r":0.166667},{"type":"ci","r":0.166667,"c":[1.583333,0]},{"type":"po","v":[[-2.604167,0.520833],[5.46875,0.520833],[5.46875,-1.302083],[3.125,-1.822917],[2.604167,-3.645833],[-4.427083,-3.645833],[-4.427083,0.520833]],"s":1},{"type":"po","v":[[-4.166667,0.260417],[4.947917,0.260417],[5.208333,0.260417],[5.208333,-1.041667],[2.864583,-1.5625],[2.34375,-3.385417],[-3.90625,-3.385417],[-4.166667,-3.125]],"s":1},{"type":"bx","w":9.416667,"h":0.416667,"c":[0.5,0.25]},{"type":"bx","w":1.583333,"h":0.416667,"c":[5.25,-0.5],"a":-1.570796},{"type":"bx","w":2.416667,"h":0.25,"c":[4,-1.333333],"a":0.218669},{"type":"bx","w":2.083333,"h":0.416667,"c":[2.5,-2.5],"a":1.165905},{"type":"bx","w":6.25,"h":0.416667,"c":[-1,-3.416667]},{"type":"bx","w":3.666667,"h":0.416667,"c":[-4.166667,-1.583333],"a":-1.570796},{"type":"ci","r":0.916667,"c":[-2.885417,0.520833]},{"type":"ci","r":0.916667,"c":[3.802083,0.520833]}],"fixtures":[{"sh":0,"f":16711422},{"sh":1,"f":16711422},{"sh":2,"f":16711422,"np":true},{"sh":3,"f":12040119,"np":true},{"sh":4,"f":16711422},{"sh":5,"f":16711422},{"sh":6,"f":16711422},{"sh":7,"f":16711422},{"sh":8,"f":16711422},{"sh":9,"f":16711422},{"sh":10,"f":131586,"np":true},{"sh":11,"f":131586,"np":true}],"joints":[]}`,
        },

        centro: {
            icon: '💗',
            mapData: `{"bodies":[{"p":[21.133333,17.8],"fx":[0],"s":{"type":"s","fric":0.5,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":0.2}],"fixtures":[{"sh":0,"f":14626944}],"joints":[]}`,
        },

        furry_1: {
            icon: '🐶',
            mapData: `{"bodies":[{"p":[-0.417295,-0.237529],"a":-0.279105,"av":2.412086,"lv":[0.586697,-0.695705],"cf":{"y":-1},"fx":[0,1],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":0},{"p":[0.416023,-0.242523],"a":-0.286679,"av":2.418575,"lv":[0.578152,-0.703735],"cf":{"y":-1},"fx":[2,3],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":1},{"p":[0.580689,-0.576849],"a":0.671979,"av":-2.613507,"lv":[-0.041557,-0.940918],"cf":{"y":-1},"fx":[4,5,6,7,8],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":2},{"p":[-0.585957,-0.569857],"a":-0.485774,"av":2.24048,"lv":[0.293925,-0.688351],"cf":{"y":-1},"fx":[9,10,11,12,13],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":3},{"p":[0.000363,-0.073362],"a":-0.005993,"lv":[-0.00074,0.598579],"fx":[14,15,16,17,18,19,20],"s":{"type":"d","n":"kevin","fric":0,"re":0.95,"de":1.177747,"ad":99999,"f_c":3,"f_1":true,"f_4":true},"id":4},{"p":[0.001402,0.357584],"lv":[-0.000659,-0.215432],"cf":{"y":-0.666667},"fx":[21,22],"s":{"type":"d","n":"detw4y2","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":5},{"p":[0.001498,0.402816],"lv":[-0.002551,-1.140847],"cf":{"y":-0.666667},"fx":[23,24],"s":{"type":"d","n":"detw4y3","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":6},{"p":[0.001696,0.493652],"lv":[-0.005102,-2.168616],"cf":{"y":-0.666667},"fx":[25,26],"s":{"type":"d","n":"detw4y5","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":7},{"p":[0.001581,0.446068],"lv":[-0.004082,-1.760061],"cf":{"y":-0.666667},"fx":[27,28],"s":{"type":"d","n":"detw4y4","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":8}],"shapes":[{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"po","v":[[0.583333,-1.25],[0.833333,-1],[0.416667,0.333333],[-0.25,-0.416667]],"s":1,"c":[-1,1.083333]},{"type":"ci","r":0.5,"c":[-0.083333,0.083333]},{"type":"po","v":[[0.583333,-1.333333],[0.833333,-1],[0.583333,-0.333333],[0.083333,-0.75]],"s":1},{"type":"po","v":[[0.583333,-1],[0.25,0.083333],[0,-0.25]],"s":1},{"type":"ci","r":8.3e-05,"c":[0.416667,-0.416667]},{"type":"po","v":[[-0.833333,-1],[-0.583333,-1.25],[0.25,-0.416667],[-0.416667,0.333333]],"s":1,"c":[1,1.083333]},{"type":"ci","r":0.5,"c":[0.083333,0.083333]},{"type":"po","v":[[-0.825,-1],[-0.575,-1.25],[-0.158333,-0.75],[-0.658333,-0.333333]],"s":1,"c":[0.008333,0]},{"type":"po","v":[[-0.583333,-1],[0,-0.25],[-0.25,0.083333]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[-0.666667,0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,0.833333]},{"type":"po","v":[[-0.333333,-0.416667],[-0.5,-0.25],[-0.75,-0.083333],[-1.083333,0.083333],[-1.333333,0.083333],[-1.166667,0.25],[-0.916667,0.25],[-1.083333,0.333333],[-1.166667,0.333333],[-1,0.583333],[-0.666667,0.666667],[-0.75,0.75],[-0.833333,0.75],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[1.083333,0.75],[0.916667,0.75],[0.666667,0.75],[0.583333,0.75],[0.916667,0.5],[1.333333,0.333333],[1.25,0.333333],[1,0.333333],[1.083333,0.166667],[1.333333,-0.083333],[0.916667,0]],"s":1},{"type":"po","v":[[0.25,0.083333],[0.083333,0],[-0.166667,-0.083333],[-0.25,0.083333],[-0.333333,0.25],[-0.5,0.333333],[-0.583333,0.333333],[-0.416667,0.333333],[-0.416667,0.416667],[-0.5,0.5],[-0.666667,0.583333],[-0.583333,0.583333],[-0.5,0.583333],[-0.75,0.666667],[-0.75,0.75],[-0.5,0.916667],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[0.666667,0.75],[0.5,0.75],[0.583333,0.75],[0.75,0.583333],[0.833333,0.5],[0.666667,0.5],[0.583333,0.5],[0.916667,0.416667],[0.416667,0.25],[0.333333,0.166667]],"s":1},{"type":"po","v":[[-0.25,0],[-0.166667,0],[0,0],[0.166667,0],[0.166667,0.083333],[0.083333,0.166667],[0,0.166667],[-0.083333,0.166667]],"s":1,"c":[0,0.083333]},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":1e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":6426641,"np":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":6426641,"np":true},{"sh":4,"f":11357255,"np":true},{"sh":5,"f":11357255,"np":true},{"sh":6,"f":6426641,"np":true},{"sh":7,"f":15252920,"np":true},{"sh":8,"f":5209260,"ng":true},{"sh":9,"f":11357255,"np":true},{"sh":10,"f":11357255,"np":true},{"sh":11,"f":6426641,"np":true},{"sh":12,"f":15252920,"np":true},{"sh":13,"f":5209260,"ng":true},{"sh":14,"f":2308173,"ng":true},{"sh":15,"f":2308173,"ng":true},{"sh":16,"f":2308173,"ng":true},{"sh":17,"f":2308173,"ng":true},{"sh":18,"f":11357255,"np":true,"ng":true},{"sh":19,"f":15252920,"np":true,"ng":true},{"sh":20,"f":6426641,"np":true,"ng":true},{"sh":21,"f":5209260,"ng":true},{"sh":22,"f":11357255,"np":true},{"sh":23,"f":5209260,"ng":true},{"sh":24,"f":11357255,"np":true},{"sh":25,"f":5209260,"ng":true},{"sh":26,"f":6426641,"np":true},{"sh":27,"f":5209260,"ng":true},{"sh":28,"f":6426641,"np":true}],"joints":[{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":0,"bb":4,"ab":[-0.416667,-0.166667]},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":1,"bb":4,"ab":[0.416667,-0.166667]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":2,"bb":4,"ab":[0.583333,-0.5]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":3,"bb":4,"ab":[-0.583333,-0.5]},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":5,"bb":4,"ab":[0,0],"len":0.416667},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":6,"bb":5,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":7,"bb":8,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":8,"bb":6,"ab":[0,0],"len":0.01}]}`,
        },
        furry_2: {
            icon: '🦄',
            mapData: `{"bodies":[{"p":[-0.47955,-0.023448],"a":0.430845,"av":-0.15599,"lv":[-0.01129,4.909706],"cf":{"y":-1},"fx":[0,1],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":0},{"p":[0.353774,-0.029052],"a":0.407259,"av":0.073502,"lv":[-0.010549,4.953889],"cf":{"y":-1},"fx":[2,3],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":1},{"p":[0.518185,-0.363504],"a":-0.685155,"av":-3.386757,"lv":[-1.961451,4.533888],"cf":{"y":-1},"fx":[4,5,6,7,8],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":2},{"p":[-0.648455,-0.35562],"a":0.711795,"av":2.892395,"lv":[1.66913,4.667246],"cf":{"y":-1},"fx":[9,10,11,12,13],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":3},{"p":[-0.061757,0.140427],"a":-0.006757,"av":-2e-06,"lv":[-0.010815,4.955166],"fx":[14,15,16,17,18,19,20],"s":{"type":"d","n":"kevin","fric":0,"re":0.95,"de":1.177747,"ad":99999,"f_c":3,"f_1":true,"f_4":true},"id":4},{"p":[0.168679,0.32066],"lv":[2.109282,2.905626],"cf":{"y":-0.666667},"fx":[21,22],"s":{"type":"d","n":"detw4y2","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":5},{"p":[0.079303,0.18699],"lv":[1.022004,2.734325],"cf":{"y":-0.666667},"fx":[23,24],"s":{"type":"d","n":"detw4y3","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":6},{"p":[0.027343,0.032229],"lv":[0.378394,2.046709],"cf":{"y":-0.666667},"fx":[25,26],"s":{"type":"d","n":"detw4y5","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":7},{"p":[0.042477,0.091318],"lv":[0.580641,2.372294],"cf":{"y":-0.666667},"fx":[27,28],"s":{"type":"d","n":"detw4y4","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":8}],"shapes":[{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"po","v":[[0.583333,-1.25],[0.833333,-1],[0.416667,0.333333],[-0.25,-0.416667]],"s":1,"c":[-1,1.083333]},{"type":"ci","r":0.5,"c":[-0.083333,0.083333]},{"type":"po","v":[[0.583333,-1.333333],[0.833333,-1],[0.583333,-0.333333],[0.083333,-0.75]],"s":1},{"type":"po","v":[[0.583333,-1],[0.25,0.083333],[0,-0.25]],"s":1},{"type":"ci","r":8.3e-05,"c":[0.416667,-0.416667]},{"type":"po","v":[[-0.833333,-1],[-0.583333,-1.25],[0.25,-0.416667],[-0.416667,0.333333]],"s":1,"c":[1,1.083333]},{"type":"ci","r":0.5,"c":[0.083333,0.083333]},{"type":"po","v":[[-0.825,-1],[-0.575,-1.25],[-0.158333,-0.75],[-0.658333,-0.333333]],"s":1,"c":[0.008333,0]},{"type":"po","v":[[-0.583333,-1],[0,-0.25],[-0.25,0.083333]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[-0.666667,0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,0.833333]},{"type":"po","v":[[-0.333333,-0.416667],[-0.5,-0.25],[-0.75,-0.083333],[-1.083333,0.083333],[-1.333333,0.083333],[-1.166667,0.25],[-0.916667,0.25],[-1.083333,0.333333],[-1.166667,0.333333],[-1,0.583333],[-0.666667,0.666667],[-0.75,0.75],[-0.833333,0.75],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[1.083333,0.75],[0.916667,0.75],[0.666667,0.75],[0.583333,0.75],[0.916667,0.5],[1.333333,0.333333],[1.25,0.333333],[1,0.333333],[1.083333,0.166667],[1.333333,-0.083333],[0.916667,0]],"s":1},{"type":"po","v":[[0.25,0.083333],[0.083333,0],[-0.166667,-0.083333],[-0.25,0.083333],[-0.333333,0.25],[-0.5,0.333333],[-0.583333,0.333333],[-0.416667,0.333333],[-0.416667,0.416667],[-0.5,0.5],[-0.666667,0.583333],[-0.583333,0.583333],[-0.5,0.583333],[-0.75,0.666667],[-0.75,0.75],[-0.5,0.916667],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[0.666667,0.75],[0.5,0.75],[0.583333,0.75],[0.75,0.583333],[0.833333,0.5],[0.666667,0.5],[0.583333,0.5],[0.916667,0.416667],[0.416667,0.25],[0.333333,0.166667]],"s":1},{"type":"po","v":[[-0.25,0],[-0.166667,0],[0,0],[0.166667,0],[0.166667,0.083333],[0.083333,0.166667],[0,0.166667],[-0.083333,0.166667]],"s":1,"c":[0,0.083333]},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":1e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":9835706,"np":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":9835706,"np":true},{"sh":4,"f":16056528,"np":true},{"sh":5,"f":16056528,"np":true},{"sh":6,"f":9835706,"np":true},{"sh":7,"f":15970303,"np":true},{"sh":8,"f":5209260,"ng":true},{"sh":9,"f":16056528,"np":true},{"sh":10,"f":16056528,"np":true},{"sh":11,"f":9835706,"np":true},{"sh":12,"f":16770768,"np":true},{"sh":13,"f":5209260,"ng":true},{"sh":14,"f":2308173,"ng":true},{"sh":15,"f":2308173,"ng":true},{"sh":16,"f":2308173,"ng":true},{"sh":17,"f":2308173,"ng":true},{"sh":18,"f":16056528,"np":true,"ng":true},{"sh":19,"f":15970303,"np":true,"ng":true},{"sh":20,"f":9835706,"np":true,"ng":true},{"sh":21,"f":5209260,"ng":true},{"sh":22,"f":16056528,"np":true},{"sh":23,"f":5209260,"ng":true},{"sh":24,"f":16056528,"np":true},{"sh":25,"f":5209260,"ng":true},{"sh":26,"f":9835706,"np":true},{"sh":27,"f":5209260,"ng":true},{"sh":28,"f":9835706,"np":true}],"joints":[{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":0,"bb":4,"ab":[-0.416667,-0.166667]},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":1,"bb":4,"ab":[0.416667,-0.166667]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":2,"bb":4,"ab":[0.583333,-0.5]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":3,"bb":4,"ab":[-0.583333,-0.5]},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":5,"bb":4,"ab":[0,0],"len":0.416667},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":6,"bb":5,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":7,"bb":8,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":8,"bb":6,"ab":[0,0],"len":0.01}]}`,
        },
        furry_3: {
            icon: '🦊',
            mapData: `{"bodies":[{"p":[-0.416667,-0.203704],"cf":{"y":-1},"fx":[0,1],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":0},{"p":[0.416667,-0.203704],"cf":{"y":-1},"fx":[2,3],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":1},{"p":[0.583333,-0.537037],"cf":{"y":-1},"fx":[4,5,6,7,8],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":2},{"p":[-0.583333,-0.537037],"cf":{"y":-1},"fx":[9,10,11,12,13],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":3},{"p":[0,-0.037037],"fx":[14,15,16,17,18,19,20],"s":{"type":"d","n":"kevin","fric":0,"re":0.95,"de":1.177747,"ad":99999,"f_c":3,"f_1":true,"f_4":true},"id":4},{"p":[0,0.37963],"cf":{"y":-0.666667},"fx":[21,22],"s":{"type":"d","n":"detw4y2","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":5},{"p":[0,0.37963],"cf":{"y":-0.666667},"fx":[23,24],"s":{"type":"d","n":"detw4y3","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":6},{"p":[0,0.37963],"cf":{"y":-0.666667},"fx":[25,26],"s":{"type":"d","n":"detw4y5","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":7},{"p":[0,0.37963],"cf":{"y":-0.666667},"fx":[27,28],"s":{"type":"d","n":"detw4y4","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":8}],"shapes":[{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"po","v":[[0.583333,-1.25],[0.833333,-1],[0.416667,0.333333],[-0.25,-0.416667]],"s":1,"c":[-1,1.083333]},{"type":"ci","r":0.5,"c":[-0.083333,0.083333]},{"type":"po","v":[[0.583333,-1.333333],[0.833333,-1],[0.583333,-0.333333],[0.083333,-0.75]],"s":1},{"type":"po","v":[[0.583333,-1],[0.25,0.083333],[0,-0.25]],"s":1},{"type":"ci","r":8.3e-05,"c":[0.416667,-0.416667]},{"type":"po","v":[[-0.833333,-1],[-0.583333,-1.25],[0.25,-0.416667],[-0.416667,0.333333]],"s":1,"c":[1,1.083333]},{"type":"ci","r":0.5,"c":[0.083333,0.083333]},{"type":"po","v":[[-0.825,-1],[-0.575,-1.25],[-0.158333,-0.75],[-0.658333,-0.333333]],"s":1,"c":[0.008333,0]},{"type":"po","v":[[-0.583333,-1],[0,-0.25],[-0.25,0.083333]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[-0.666667,0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,0.833333]},{"type":"po","v":[[-0.333333,-0.416667],[-0.5,-0.25],[-0.75,-0.083333],[-1.083333,0.083333],[-1.333333,0.083333],[-1.166667,0.25],[-0.916667,0.25],[-1.083333,0.333333],[-1.166667,0.333333],[-1,0.583333],[-0.666667,0.666667],[-0.75,0.75],[-0.833333,0.75],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[1.083333,0.75],[0.916667,0.75],[0.666667,0.75],[0.583333,0.75],[0.916667,0.5],[1.333333,0.333333],[1.25,0.333333],[1,0.333333],[1.083333,0.166667],[1.333333,-0.083333],[0.916667,0]],"s":1},{"type":"po","v":[[0.25,0.083333],[0.083333,0],[-0.166667,-0.083333],[-0.25,0.083333],[-0.333333,0.25],[-0.5,0.333333],[-0.583333,0.333333],[-0.416667,0.333333],[-0.416667,0.416667],[-0.5,0.5],[-0.666667,0.583333],[-0.583333,0.583333],[-0.5,0.583333],[-0.75,0.666667],[-0.75,0.75],[-0.5,0.916667],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[0.666667,0.75],[0.5,0.75],[0.583333,0.75],[0.75,0.583333],[0.833333,0.5],[0.666667,0.5],[0.583333,0.5],[0.916667,0.416667],[0.416667,0.25],[0.333333,0.166667]],"s":1},{"type":"po","v":[[-0.25,0],[-0.166667,0],[0,0],[0.166667,0],[0.166667,0.083333],[0.083333,0.166667],[0,0.166667],[-0.083333,0.166667]],"s":1,"c":[0,0.083333]},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":1e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":15169055,"np":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":15169055,"np":true},{"sh":4,"f":16756838,"np":true},{"sh":5,"f":16756838,"np":true},{"sh":6,"f":15169055,"np":true},{"sh":7,"f":16770768,"np":true},{"sh":8,"f":5209260,"ng":true},{"sh":9,"f":16756838,"np":true},{"sh":10,"f":16756838,"np":true},{"sh":11,"f":15169055,"np":true},{"sh":12,"f":16770768,"np":true},{"sh":13,"f":5209260,"ng":true},{"sh":14,"f":2308173,"ng":true},{"sh":15,"f":2308173,"ng":true},{"sh":16,"f":2308173,"ng":true},{"sh":17,"f":2308173,"ng":true},{"sh":18,"f":16756838,"np":true,"ng":true},{"sh":19,"f":16770768,"np":true,"ng":true},{"sh":20,"f":15169055,"np":true,"ng":true},{"sh":21,"f":5209260,"ng":true},{"sh":22,"f":16756838,"np":true},{"sh":23,"f":5209260,"ng":true},{"sh":24,"f":16756838,"np":true},{"sh":25,"f":5209260,"ng":true},{"sh":26,"f":16777215,"np":true},{"sh":27,"f":5209260,"ng":true},{"sh":28,"f":15169055,"np":true}],"joints":[{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":0,"bb":4,"ab":[-0.416667,-0.166667]},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":1,"bb":4,"ab":[0.416667,-0.166667]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":2,"bb":4,"ab":[0.583333,-0.5]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":3,"bb":4,"ab":[-0.583333,-0.5]},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":5,"bb":4,"ab":[0,0],"len":0.416667},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":6,"bb":5,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":7,"bb":8,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":8,"bb":6,"ab":[0,0],"len":0.01}]}`,
        },
        furry_4: {
            icon: '🦝',
            mapData: `{"bodies":[{"p":[-0.431988,-0.203401],"a":-0.205712,"av":3.005571,"lv":[0.816587,-0.304622],"cf":{"y":-1},"fx":[0,1],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":0},{"p":[0.401324,-0.209395],"a":-0.078326,"av":3.131343,"lv":[1.045543,-0.253244],"cf":{"y":-1},"fx":[2,3],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":1},{"p":[0.565589,-0.543918],"a":-0.557746,"av":-0.896906,"lv":[-0.512061,1.131813],"cf":{"y":-1},"fx":[4,5,6,7,8],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":2},{"p":[-0.601047,-0.535527],"a":0.738663,"av":0.180056,"lv":[0.104966,1.260835],"cf":{"y":-1},"fx":[9,10,11,12,13],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":3},{"p":[-0.014133,-0.039735],"a":-0.007193,"av":-1e-06,"lv":[-0.001002,1.266441],"fx":[14,15,16,17,18,19,20],"s":{"type":"d","n":"kevin","fric":0,"re":0.95,"de":1.177747,"ad":99999,"f_c":3,"f_1":true,"f_4":true},"id":4},{"p":[0.018871,0.359573],"lv":[0.037493,0.512579],"cf":{"y":-0.666667},"fx":[21,22],"s":{"type":"d","n":"detw4y2","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":5},{"p":[0.019204,0.368951],"lv":[-0.022087,-0.306845],"cf":{"y":-0.666667},"fx":[23,24],"s":{"type":"d","n":"detw4y3","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":6},{"p":[0.021995,0.41628],"lv":[-0.092415,-1.143418],"cf":{"y":-0.666667},"fx":[25,26],"s":{"type":"d","n":"detw4y5","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":7},{"p":[0.020185,0.387172],"lv":[-0.060993,-0.836724],"cf":{"y":-0.666667},"fx":[27,28],"s":{"type":"d","n":"detw4y4","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":8}],"shapes":[{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"po","v":[[0.583333,-1.25],[0.833333,-1],[0.416667,0.333333],[-0.25,-0.416667]],"s":1,"c":[-1,1.083333]},{"type":"ci","r":0.5,"c":[-0.083333,0.083333]},{"type":"po","v":[[0.583333,-1.333333],[0.833333,-1],[0.583333,-0.333333],[0.083333,-0.75]],"s":1},{"type":"po","v":[[0.583333,-1],[0.25,0.083333],[0,-0.25]],"s":1},{"type":"ci","r":8.3e-05,"c":[0.416667,-0.416667]},{"type":"po","v":[[-0.833333,-1],[-0.583333,-1.25],[0.25,-0.416667],[-0.416667,0.333333]],"s":1,"c":[1,1.083333]},{"type":"ci","r":0.5,"c":[0.083333,0.083333]},{"type":"po","v":[[-0.825,-1],[-0.575,-1.25],[-0.158333,-0.75],[-0.658333,-0.333333]],"s":1,"c":[0.008333,0]},{"type":"po","v":[[-0.583333,-1],[0,-0.25],[-0.25,0.083333]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"ci","r":0.083333,"c":[-0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[-0.666667,0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,0.833333]},{"type":"po","v":[[-0.333333,-0.416667],[-0.5,-0.25],[-0.75,-0.083333],[-1.083333,0.083333],[-1.333333,0.083333],[-1.166667,0.25],[-0.916667,0.25],[-1.083333,0.333333],[-1.166667,0.333333],[-1,0.583333],[-0.666667,0.666667],[-0.75,0.75],[-0.833333,0.75],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[1.083333,0.75],[0.916667,0.75],[0.666667,0.75],[0.583333,0.75],[0.916667,0.5],[1.333333,0.333333],[1.25,0.333333],[1,0.333333],[1.083333,0.166667],[1.333333,-0.083333],[0.916667,0]],"s":1},{"type":"po","v":[[0.25,0.083333],[0.083333,0],[-0.166667,-0.083333],[-0.25,0.083333],[-0.333333,0.25],[-0.5,0.333333],[-0.583333,0.333333],[-0.416667,0.333333],[-0.416667,0.416667],[-0.5,0.5],[-0.666667,0.583333],[-0.583333,0.583333],[-0.5,0.583333],[-0.75,0.666667],[-0.75,0.75],[-0.5,0.916667],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[0.666667,0.75],[0.5,0.75],[0.583333,0.75],[0.75,0.583333],[0.833333,0.5],[0.666667,0.5],[0.583333,0.5],[0.916667,0.416667],[0.416667,0.25],[0.333333,0.166667]],"s":1},{"type":"po","v":[[-0.25,0],[-0.166667,0],[0,0],[0.166667,0],[0.166667,0.083333],[0.083333,0.166667],[0,0.166667],[-0.083333,0.166667]],"s":1,"c":[0,0.083333]},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":1e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1}],"fixtures":[{"sh":0,"f":5209260,"ng":true},{"sh":1,"f":0,"np":true},{"sh":2,"f":5209260,"ng":true},{"sh":3,"f":0,"np":true},{"sh":4,"f":11842740,"np":true},{"sh":5,"f":11842740,"np":true},{"sh":6,"f":0,"np":true},{"sh":7,"f":16777215,"np":true},{"sh":8,"f":5209260,"ng":true},{"sh":9,"f":11842740,"np":true},{"sh":10,"f":11842740,"np":true},{"sh":11,"f":0,"np":true},{"sh":12,"f":16777215,"np":true},{"sh":13,"f":5209260,"ng":true},{"sh":14,"f":2308173,"ng":true},{"sh":15,"f":2308173,"ng":true},{"sh":16,"f":2308173,"ng":true},{"sh":17,"f":2308173,"ng":true},{"sh":18,"f":11842740,"np":true,"ng":true},{"sh":19,"f":16777215,"np":true,"ng":true},{"sh":20,"f":0,"np":true,"ng":true},{"sh":21,"f":5209260,"ng":true},{"sh":22,"f":11842740,"np":true},{"sh":23,"f":5209260,"ng":true},{"sh":24,"f":11842740,"np":true},{"sh":25,"f":5209260,"ng":true},{"sh":26,"f":0,"np":true},{"sh":27,"f":5209260,"ng":true},{"sh":28,"f":0,"np":true}],"joints":[{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":0,"bb":4,"ab":[-0.416667,-0.166667]},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":1,"bb":4,"ab":[0.416667,-0.166667]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":2,"bb":4,"ab":[0.583333,-0.5]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":3,"bb":4,"ab":[-0.583333,-0.5]},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":5,"bb":4,"ab":[0,0],"len":0.416667},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":6,"bb":5,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":7,"bb":8,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":8,"bb":6,"ab":[0,0],"len":0.01}]}`,
        },
        furry_5: {
            icon: '🐺',
            mapData: `{"bodies":[{"p":[-0.06238,-0.077845],"a":-0.005447,"lv":[-0.000622,-0.079924],"fx":[0,1,2,3,4,5,6],"s":{"type":"d","n":"kevin","fric":0,"re":0.95,"de":1.177747,"ad":99999,"f_c":3,"f_1":true,"f_4":true},"id":0},{"p":[-0.648429,-0.57466],"a":0.743673,"av":0.042928,"lv":[0.024652,-0.081017],"cf":{"y":-1},"fx":[7,8,9,10,11],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":1},{"p":[0.518221,-0.581015],"a":-0.706239,"av":-0.112038,"lv":[-0.066412,-0.085391],"cf":{"y":-1},"fx":[12,13,14,15,16],"s":{"type":"d","n":"detw4y","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":2},{"p":[0.061795,0.358193],"lv":[-0.22093,-0.729184],"cf":{"y":-0.666667},"fx":[17,18],"s":{"type":"d","n":"detw4y2","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":3},{"p":[0.07695,0.413216],"lv":[-0.404886,-1.291302],"cf":{"y":-0.666667},"fx":[19,20],"s":{"type":"d","n":"detw4y3","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":4},{"p":[0.086924,0.452153],"lv":[-0.467824,-1.560282],"cf":{"y":-0.666667},"fx":[21,22],"s":{"type":"d","n":"detw4y4","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":5},{"p":[0.093497,0.498978],"lv":[-0.671138,-1.781946],"cf":{"y":-0.666667},"fx":[23,24],"s":{"type":"d","n":"detw4y5","fric":0.5,"re":0.8,"de":0,"ld":20,"ad":10,"f_p":false},"id":6},{"p":[0.353372,-0.246779],"a":0.02932,"av":0.836088,"lv":[0.348257,-0.427787],"cf":{"y":-1},"fx":[25,26],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":7},{"p":[-0.479949,-0.24224],"a":0.012369,"av":0.849294,"lv":[0.347569,-0.439391],"cf":{"y":-1},"fx":[27,28],"s":{"type":"d","n":"EYE KEVIN","fric":0.5,"re":0.8,"de":0,"ld":10,"f_p":false},"id":8}],"shapes":[{"type":"ci","r":0.083333,"c":[-0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[-0.666667,0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,-0.833333]},{"type":"ci","r":0.083333,"c":[0.666667,0.833333]},{"type":"po","v":[[-0.333333,-0.416667],[-0.5,-0.25],[-0.75,-0.083333],[-1.083333,0.083333],[-1.333333,0.083333],[-1.166667,0.25],[-0.916667,0.25],[-1.083333,0.333333],[-1.166667,0.333333],[-1,0.583333],[-0.666667,0.666667],[-0.75,0.75],[-0.833333,0.75],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[1.083333,0.75],[0.916667,0.75],[0.666667,0.75],[0.583333,0.75],[0.916667,0.5],[1.333333,0.333333],[1.25,0.333333],[1,0.333333],[1.083333,0.166667],[1.333333,-0.083333],[0.916667,0]],"s":1},{"type":"po","v":[[0.25,0.083333],[0.083333,0],[-0.166667,-0.083333],[-0.25,0.083333],[-0.333333,0.25],[-0.5,0.333333],[-0.583333,0.333333],[-0.416667,0.333333],[-0.416667,0.416667],[-0.5,0.5],[-0.666667,0.583333],[-0.583333,0.583333],[-0.5,0.583333],[-0.75,0.666667],[-0.75,0.75],[-0.5,0.916667],[-0.333333,0.916667],[0.083333,1],[0.583333,0.916667],[0.666667,0.75],[0.5,0.75],[0.583333,0.75],[0.75,0.583333],[0.833333,0.5],[0.666667,0.5],[0.583333,0.5],[0.916667,0.416667],[0.416667,0.25],[0.333333,0.166667]],"s":1},{"type":"po","v":[[-0.25,0],[-0.166667,0],[0,0],[0.166667,0],[0.166667,0.083333],[0.083333,0.166667],[0,0.166667],[-0.083333,0.166667]],"s":1,"c":[0,0.083333]},{"type":"po","v":[[-0.833333,-1],[-0.583333,-1.25],[0.25,-0.416667],[-0.416667,0.333333]],"s":1,"c":[1,1.083333]},{"type":"ci","r":0.5,"c":[0.083333,0.083333]},{"type":"po","v":[[-0.825,-1],[-0.575,-1.25],[-0.158333,-0.75],[-0.658333,-0.333333]],"s":1,"c":[0.008333,0]},{"type":"po","v":[[-0.583333,-1],[0,-0.25],[-0.25,0.083333]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"po","v":[[0.583333,-1.25],[0.833333,-1],[0.416667,0.333333],[-0.25,-0.416667]],"s":1,"c":[-1,1.083333]},{"type":"ci","r":0.5,"c":[-0.083333,0.083333]},{"type":"po","v":[[0.583333,-1.333333],[0.833333,-1],[0.583333,-0.333333],[0.083333,-0.75]],"s":1},{"type":"po","v":[[0.583333,-1],[0.25,0.083333],[0,-0.25]],"s":1},{"type":"ci","r":8.3e-05,"c":[0.416667,-0.416667]},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":1e-06},{"type":"po","v":[[0,-0.75],[-0.333333,-1.166667],[-0.25,-0.916667],[-0.666667,-0.916667],[-0.5,-0.75],[-0.75,-0.75],[-0.583333,-0.5],[-0.916667,-0.5],[-0.583333,-0.416667],[-0.916667,-0.25],[-0.583333,-0.25],[-0.833333,0],[-0.5,0],[-0.583333,0.166667],[-0.333333,0.166667],[-0.333333,0.416667],[-0.083333,0.416667],[0.083333,0.416667],[0.333333,0.5],[0.25,0.333333],[0.666667,0.416667],[0.5,0.166667],[0.833333,0.166667],[0.75,0],[1.166667,-0.083333],[0.833333,-0.166667],[0.916667,-0.5],[0.75,-0.5],[0.833333,-0.75],[0.583333,-0.75],[0.583333,-1.083333],[0.416667,-0.75],[0.333333,-1],[0.25,-1.083333],[0.166667,-1.25],[0.166667,-1.083333],[-0.083333,-1.083333],[-0.083333,-0.916667]],"s":1},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667},{"type":"ci","r":8.3e-05,"c":[-0.416667,-0.416667]},{"type":"bx","w":0.166667,"h":0.416667}],"fixtures":[{"sh":0,"f":2308173,"ng":true},{"sh":1,"f":2308173,"ng":true},{"sh":2,"f":2308173,"ng":true},{"sh":3,"f":2308173,"ng":true},{"sh":4,"f":12089183,"np":true,"ng":true},{"sh":5,"f":15522243,"np":true,"ng":true},{"sh":6,"f":5060387,"np":true,"ng":true},{"sh":7,"f":12089183,"np":true},{"sh":8,"f":12089183,"np":true},{"sh":9,"f":3219482,"np":true},{"sh":10,"f":15522243,"np":true},{"sh":11,"f":5209260,"ng":true},{"sh":12,"f":12089183,"np":true},{"sh":13,"f":12089183,"np":true},{"sh":14,"f":3219482,"np":true},{"sh":15,"f":15522243,"np":true},{"sh":16,"f":5209260,"ng":true},{"sh":17,"f":5209260,"ng":true},{"sh":18,"f":7818815,"np":true},{"sh":19,"f":5209260,"ng":true},{"sh":20,"f":7818815,"np":true},{"sh":21,"f":5209260,"ng":true},{"sh":22,"f":3219482,"np":true},{"sh":23,"f":5209260,"ng":true},{"sh":24,"f":3219482,"np":true},{"sh":25,"f":5209260,"ng":true},{"sh":26,"f":3219482,"np":true},{"sh":27,"f":5209260,"ng":true},{"sh":28,"f":3219482,"np":true}],"joints":[{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":1,"bb":0,"ab":[-0.583333,-0.5]},{"type":"rv","d":{"la":-0.785398,"ua":0.785398,"el":true,"dl":true},"ba":2,"bb":0,"ab":[0.583333,-0.5]},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":3,"bb":0,"ab":[0,0],"len":0.416667},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":4,"bb":3,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":5,"bb":4,"ab":[0,0],"len":0.01},{"type":"d","d":{"fh":5,"dr":0,"dl":true},"ba":6,"bb":5,"ab":[0,0],"len":0.01},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":7,"bb":0,"ab":[0.416667,-0.166667]},{"type":"rv","d":{"la":-0.392699,"ua":0.392699,"el":true,"dl":true},"ba":8,"bb":0,"ab":[-0.416667,-0.166667]}]}`,
        },
        fake_lag: {
            icon: '⁉️',
            mapData: `{"bodies":[{"p":[0,-0.892856],"fx":[0],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":0},{"p":[0,10.357144],"fx":[1],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":1},{"p":[0,-12.142856],"fx":[2],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":2},{"p":[0,-0.892861],"fx":[3],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":3},{"p":[16.666667,-0.892856],"fx":[4],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":4},{"p":[0,5.357144],"fx":[5],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":5},{"p":[-16.666667,-0.892856],"fx":[6],"s":{"type":"d","n":"fake lag","fric":0.5,"re":0.8,"de":0},"id":6}],"shapes":[{"type":"bx","w":8e-06,"h":72.916667,"a":-1.570796},{"type":"bx","w":8e-06,"h":72.916667,"a":-1.570796},{"type":"bx","w":8e-06,"h":72.916667,"a":-1.570796},{"type":"bx","w":8e-06,"h":72.916667},{"type":"bx","w":8e-06,"h":72.916667},{"type":"bx","w":8e-06,"h":72.916667,"a":1.570796},{"type":"bx","w":8e-06,"h":72.916667}],"fixtures":[{"sh":0,"f":8553090},{"sh":1,"f":8618883},{"sh":2,"f":15000662},{"sh":3,"f":8553090},{"sh":4,"f":8618883},{"sh":5,"f":8618883},{"sh":6,"f":15000662}],"joints":[{"type":"lpj","d":{"dl":false},"ba":0,"pax":0,"pay":-0.892856,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0},{"type":"lpj","d":{"dl":false},"ba":1,"pax":0,"pay":10.357144,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0},{"type":"lpj","d":{"dl":false},"ba":2,"pax":0,"pay":-12.142856,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0},{"type":"lpj","d":{"dl":false},"ba":3,"pax":0,"pay":-0.892856,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0},{"type":"lpj","d":{"dl":false},"ba":4,"pax":16.666667,"pay":-0.892856,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":5,"ab":[30.416667,27.083333]},{"type":"lpj","d":{"dl":false},"ba":6,"pax":-16.666667,"pay":-0.892856,"pa":1.570796,"pf":0,"pl":0,"pu":0,"plen":0,"pms":0}]}`,
        },
        prototype: {
            icon: '🤡',
            mapData: `{"bodies":[{"p":[1.079471,-7.420775],"lv":[-3.728688,0],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],"s":{"type":"d","n":"head","fric":0.5,"re":-4.2,"de":0.15,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":0},{"p":[1.076765,-5.497699],"a":2.3e-05,"av":8e-06,"lv":[-4.143903,1.9e-05],"fx":[20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"s":{"type":"d","n":"torso","fric":0.5,"re":-2,"de":5,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":1},{"p":[-0.307085,-5.883606],"a":-1.942434,"av":-0.507449,"lv":[-3.370198,-1.357668],"fx":[36,37,38,39,40],"s":{"type":"d","n":"antebrazo 1","fric":0.5,"re":0.8,"de":0.05,"fr":true,"bu":true,"f_2":true,"f_3":true,"f_4":true},"id":2},{"p":[2.023627,-4.544494],"a":-2.280571,"av":-2.965493,"lv":[0.102347,3.061959],"fx":[41,42,43,44,45],"s":{"type":"d","n":"brazo","fric":0.5,"re":0.8,"de":0,"f_2":true,"f_3":true,"f_4":true},"id":3},{"p":[1.751592,-1.674013],"a":-2.846978,"av":-6.089286,"lv":[15.924585,1.640227],"fx":[46,47,48,49,50,51,52],"s":{"type":"d","n":"Unnamed","fric":0.5,"re":0.8,"de":-0.3,"f_2":true,"f_3":true,"f_4":true},"id":4},{"p":[2.384233,-5.882208],"a":1.172963,"av":0.934484,"lv":[-6.468969,-0.666077],"fx":[53,54,55,56,57],"s":{"type":"d","n":"antebrazo 2","fric":0.5,"re":0.8,"de":0.05,"fr":true,"bu":true,"f_2":true,"f_3":true,"f_4":true},"id":5},{"p":[1.662388,-3.224554],"a":2.160946,"av":4.17131,"lv":[-10.954413,4.215044],"fx":[58,59,60,61,62],"s":{"type":"d","n":"brazo 2","fric":0.5,"re":0.8,"de":0,"f_2":true,"f_3":true,"f_4":true},"id":6},{"p":[2.360657,-0.534884],"a":2.440418,"av":3.431844,"lv":[-22.853048,7.315755],"fx":[63,64,65,66,67,68,69],"s":{"type":"d","n":"Unnamed","fric":0.5,"re":0.8,"de":-0.3,"f_2":true,"f_3":true,"f_4":true},"id":7},{"p":[0.303847,-1.264764],"a":0.454991,"av":2.81084,"lv":[-7.97544,0.351549],"fx":[70,71,72,73,74,75],"s":{"type":"d","n":"torso inferior","fric":0.5,"re":0.8,"de":0.1,"f_2":true,"f_3":true,"f_4":true},"id":8},{"p":[-0.983577,2.845981],"a":0.417964,"av":1.210284,"lv":[-13.851521,-0.260994],"fx":[76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114],"s":{"type":"d","n":"no se KJAKAJKAJ","fric":0.5,"re":0.8,"de":0.1,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":9},{"p":[-6.63217,-2.392253],"a":0.794642,"av":7.601493,"lv":[9.563,-6.042586],"cf":{"y":-10},"fx":[115,116,117],"s":{"type":"d","n":"mommy","fric":0.5,"re":0.8,"de":0,"f_2":true,"f_3":true,"f_4":true},"id":10},{"p":[4.00034,6.406303],"a":1.38525,"av":-7.32393,"lv":[-3.417907,11.800592],"cf":{"x":0.333333,"y":33.333333},"fx":[118,119,120,121,122,123,124,125,126,127,128,129],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true},"id":11},{"p":[2.180521,5.766219],"a":0.689162,"av":-5.74501,"lv":[-5.996668,-0.86767],"cf":{"x":0.333333,"y":3.333333},"fx":[130,131,132,133,134,135,136,137,138,139,140,141],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true,"f_4":true},"id":12},{"p":[0.266884,5.337164],"a":0.603157,"av":-5.794998,"lv":[-5.817101,-4.160988],"cf":{"x":0.333333,"y":3.333333},"fx":[142,143,144,145,146,147,148,149,150,151,152,153],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true},"id":13},{"p":[-2.207428,4.295163],"a":-0.46759,"av":3.547801,"lv":[-21.510561,-4.204681],"cf":{"x":0.333333,"y":3.333333},"fx":[154,155,156,157,158,159,160,161,162,163],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true},"id":14},{"p":[0.262046,5.367872],"a":-1.791204,"av":-3.516116,"lv":[-8.272251,-3.860931],"fx":[164,165,166,167,168,169,170,171,172],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true},"id":15},{"p":[2.731464,6.440536],"a":-1.873755,"av":-3.451815,"lv":[-8.698161,-4.273078],"fx":[173,174,175,176,177,178,179,180,181],"s":{"type":"d","n":"pata","fric":0.5,"re":0.8,"de":-0.2,"f_c":4,"f_2":true,"f_3":true},"id":16},{"p":[-3.20207,0.920664],"a":0.891281,"av":1.023153,"lv":[-12.53094,1.889098],"cf":{"x":-0.366667,"y":-0.7},"fx":[182,183,184,185,186,187],"s":{"type":"d","n":"huggy","fric":0.5,"re":0.8,"de":0,"f_p":false,"f_2":true,"f_3":true,"f_4":true},"id":17},{"p":[-8.751505,0.939348],"a":-0.794701,"av":-2.133139,"lv":[-17.601394,-1.046852],"cf":{"x":-1.033333,"y":-1.566667},"fx":[188,189,190,191,192,193],"s":{"type":"d","n":"mommy","fric":0.5,"re":0.8,"de":0,"f_p":false,"f_2":true,"f_3":true,"f_4":true},"id":18}],"shapes":[{"type":"po","v":[[-0.923077,-0.307692],[0,-2.384615],[0.153846,-2.538462],[1.230769,-2.846154],[1.692308,-2.769231],[2,-2.615385],[2.153846,-2.384615],[2.076923,-2.307692],[1.538462,-2.230769],[1.076923,-2.076923],[0.923077,-1.615385],[0.923077,-1.153846],[0.923077,-0.615385],[0.923077,-0.230769],[0.769231,-0.076923],[-0.076923,-0.153846]],"s":1},{"type":"po","v":[[0.615385,-0.692308],[1.230769,-0.923077],[2.230769,-1],[2.538462,-0.846154],[2.923077,-0.615385],[3.076923,-0.307692],[3.230769,0],[3.230769,0.230769],[3.076923,0.307692],[2.846154,0.076923],[2.384615,0],[1.923077,0.076923],[1.384615,0.230769],[1,0.384615],[0.692308,0.538462],[0.230769,0.692308],[-0.153846,0],[0.076923,-0.307692]],"s":1},{"type":"po","v":[[-0.461538,-0.769231],[-1.076923,-0.923077],[-1.384615,-0.846154],[-1.692308,-0.692308],[-2.076923,-0.461538],[-2.153846,-0.230769],[-2.153846,-0.153846],[-1.923077,-0.153846],[-1.538462,0],[-1.076923,0.230769],[-0.769231,0.461538],[-0.538462,0.615385],[-0.230769,0.461538],[-0.076923,0.307692],[-0.230769,-0.384615]],"s":1},{"type":"po","v":[[-1.307692,0],[-1.076923,0.384615],[-0.923077,0.615385],[-0.692308,0.692308],[-0.538462,0.769231],[-0.076923,0.923077],[0.384615,0.846154],[0.692308,0.692308],[0.769231,0.615385],[1,0.461538],[1.153846,0.307692],[1.230769,0.230769],[1.384615,-0.153846],[1.307692,-0.307692],[0.923077,-0.769231],[0.846154,-0.692308],[-0.461538,-0.153846],[-0.538462,-0.153846],[-0.769231,0],[-0.923077,-0.076923]],"s":1},{"type":"po","v":[[0.461538,0.923077],[0.923077,0.692308],[1.076923,0.615385],[1.153846,0.538462],[1.307692,0.384615],[1.307692,0.230769],[1.384615,-0.076923],[1.384615,-0.153846],[1.461538,-0.307692],[1.384615,-0.461538],[1.384615,-0.692308],[1.230769,-0.769231],[1.153846,-0.846154],[1,-0.923077],[0.923077,-0.846154],[0.615385,-0.692308],[0.615385,-0.461538],[0.615385,0],[0.615385,0.461538]],"s":1,"c":[0.076923,0]},{"type":"po","v":[[0.923077,-0.038462],[0.384615,0.961538],[1,0.730769],[1.307692,0.423077],[1.307692,0.269231],[1.461538,0.192308],[2.153846,0.038462],[2,-0.038462],[1.769231,-0.115385],[1.615385,-0.192308],[1.307692,-0.192308],[1.153846,-0.884615],[1.076923,-0.115385]],"s":1,"c":[0,0.038462]},{"type":"po","v":[[-1.769231,-0.076923],[-1.461538,-0.076923],[-1.307692,-0.153846],[-1.153846,-0.384615],[-1.076923,-0.692308],[-0.846154,-0.846154],[-0.923077,-0.615385],[-1,-0.307692],[-1.076923,-0.076923],[-1,0.076923],[-0.923077,0.153846],[-0.846154,0.307692],[-0.692308,0.538462],[-0.692308,0.615385],[-0.615385,0.692308],[-0.923077,0.615385],[-1.230769,0.153846]],"s":1},{"type":"ci","r":1},{"type":"ci","r":0.230769,"c":[3.230769,0.307692]},{"type":"ci","r":0.192308,"c":[-2.153846,0]},{"type":"ci","r":0.192308,"c":[2.230769,-2.307692]},{"type":"po","v":[[-0.307692,-1.692308],[-0.307692,-1.461538],[-0.153846,-1.230769],[0,-1.153846],[0.230769,-1.153846],[0.461538,-1.307692],[0.692308,-1.461538],[0.769231,-1.538462],[0.923077,-1.692308],[0.923077,-1.769231],[0.923077,-1.461538],[0.923077,-0.846154],[0.461538,-0.615385],[-0.153846,-0.769231],[-0.384615,-0.692308],[-0.692308,-0.846154]],"s":1},{"type":"ci","r":0.923077},{"type":"po","v":[[-0.961538,-0.192308],[-0.730769,-0.115385],[-0.730769,-0.038462],[-0.653846,0.115385],[-0.576923,0.192308],[-0.423077,0.192308],[-0.346154,0.192308],[-0.115385,0.192308],[0.192308,0.192308],[0.346154,0.192308],[0.576923,0.115385],[0.653846,0.115385],[0.653846,-0.038462],[0.730769,-0.115385],[0.807692,-0.192308],[0.884615,-0.346154],[0.807692,-0.576923],[0.576923,-0.807692],[0.192308,-0.961538],[-0.192308,-0.961538],[-0.576923,-0.807692],[-0.884615,-0.5]],"s":1,"c":[-0.038462,-0.038462]},{"type":"po","v":[[-0.230769,-0.076923],[-0.307692,0],[-0.384615,0.076923],[-0.538462,0.076923],[-0.615385,0],[-0.692308,-0.153846],[-0.692308,-0.307692],[-0.692308,-0.384615],[-0.615385,-0.538462],[-0.538462,-0.615385],[-0.384615,-0.692308],[-0.307692,-0.692308],[-0.230769,-0.692308],[-0.153846,-0.692308],[-0.076923,-0.538462],[-0.076923,-0.461538],[-0.153846,-0.230769],[-0.153846,-0.153846]],"s":1},{"type":"ci","r":0.115385,"c":[-0.384615,-0.153846]},{"type":"po","v":[[-0.846154,0],[-0.769231,0.384615],[-0.615385,0.615385],[-0.384615,0.769231],[-0.076923,0.846154],[0.384615,0.769231],[0.615385,0.615385],[0.846154,0.230769],[0.846154,-0.153846],[0.692308,0],[0.692308,0.230769],[0.384615,0.307692],[-0.538462,0.307692],[-0.692308,0.230769]],"s":1},{"type":"po","v":[[0.076923,-0.538462],[0.076923,-0.153846],[0.230769,0],[0.307692,0],[0.461538,0],[0.538462,-0.076923],[0.615385,-0.153846],[0.615385,-0.307692],[0.615385,-0.384615],[0.538462,-0.461538],[0.461538,-0.615385],[0.307692,-0.692308],[0.153846,-0.692308]],"s":1},{"type":"po","v":[[-0.846154,0],[-0.692308,0.230769],[-0.538462,0.307692],[0.384615,0.307692],[0.692308,0.230769],[0.692308,0],[0.846154,-0.153846],[0.846154,0.153846],[0.538462,0.461538],[0.307692,0.538462],[-0.230769,0.538462],[-0.538462,0.461538],[-0.692308,0.384615]],"s":1},{"type":"po","v":[[-0.615385,0.615385],[-0.153846,0.538462],[0.307692,0.538462],[0.615385,0.615385],[0.384615,0.769231],[-0.076923,0.846154],[-0.384615,0.769231]],"s":1},{"type":"po","v":[[-1,-0.384615],[0.923077,-0.384615],[-0.153846,3.153846]],"s":1,"c":[-1,-0.384615]},{"type":"po","v":[[-0.769231,-0.384615],[-1,-0.923077],[-0.769231,-1.076923],[-0.538462,-1.153846],[-0.153846,-1.153846],[0.307692,-1.230769],[0.615385,-1.153846],[0.846154,-1.076923],[0.923077,-1],[0.692308,-0.384615]],"s":1},{"type":"bx","w":0.615385,"h":0.615385,"c":[0,-0.692308],"a":-1.570796},{"type":"bx","w":0.615385,"h":0.307692,"c":[-0.153846,-0.692308],"a":-1.570796},{"type":"po","v":[[-0.769231,-0.384615],[-1,-0.923077],[-0.769231,-1.076923],[-0.307692,-1.153846],[0.615385,-1.153846],[0.846154,-1.076923],[0.923077,-1],[0.692308,-0.384615],[0.615385,-0.384615],[0.846154,-1],[0.615385,-1.076923],[-0.384615,-1.076923],[-0.692308,-1],[-0.923077,-0.923077]],"s":1},{"type":"po","v":[[-0.307692,-0.384615],[-0.076923,-0.384615],[0,-0.692308],[0.076923,-0.384615],[0.307692,-0.384615],[0.307692,-0.769231],[-0.307692,-0.769231]],"s":1},{"type":"po","v":[[-2.084615,-0.615385],[-0.853846,-0.615385],[-0.7,-0.153846],[-1.392308,-0.076923]],"s":1,"c":[-0.007692,0]},{"type":"po","v":[[0.801353,-0.667544],[1.881019,-0.665931],[1.606271,-0.184053],[0.674711,-0.273134]],"s":1,"a":-0.069813,"c":[0.076923,0]},{"type":"po","v":[[-1.538462,-0.153846],[-0.923077,0.846154],[-0.769231,2.615385],[-0.769231,4.076923],[-0.153846,3.076923],[0.538462,4.230769],[0.538462,2.615385],[0.692308,0.846154],[1.615385,-0.230769],[1.076923,-0.307692],[-0.153846,-0.153846],[-0.538462,-0.230769],[-0.923077,-0.307692],[-1.230769,-0.461538]],"s":1},{"type":"po","v":[[-0.076923,-0.538462],[0.153846,1.769231],[-0.076923,2.384615],[-0.230769,1.769231]],"s":1,"c":[0.076923,0]},{"type":"po","v":[[-0.769231,-0.461538],[-1.153846,0.307692],[-0.384615,0.692308],[-0.769231,1.461538],[-0.384615,1.846154],[-0.307692,1.769231],[-0.692308,1.461538],[-0.307692,0.615385],[-1.076923,0.307692]],"s":1,"c":[0,-0.076923]},{"type":"po","v":[[0.153846,1.846154],[0.538462,1.461538],[0.153846,0.692308],[0.923077,0.307692],[0.538462,-0.461538],[0.846154,0.307692],[0,0.692308],[0.461538,1.461538]],"s":1,"c":[-0.230769,-0.076923]},{"type":"po","v":[[-0.307692,-0.384615],[-0.538462,0.076923],[-0.615385,0.307692],[-0.692308,0.538462],[-0.692308,0.846154],[-0.615385,0.923077],[-0.307692,1.076923],[-0.307692,0.307692],[-0.230769,0.153846],[-0.153846,-0.076923],[0,-0.153846],[0.153846,0.384615],[0.230769,0.538462],[0.230769,0.769231],[0.153846,1.153846],[0.384615,1],[0.538462,0.923077],[0.538462,0.846154],[0.538462,0.538462],[0.538462,0.230769],[0.384615,-0.076923],[0.230769,-0.230769],[0.153846,-0.384615],[0.076923,-0.461538]],"s":1},{"type":"po","v":[[-0.076923,2.307692],[-0.153846,3.076923],[-0.769231,4.076923],[-0.076923,3.153846],[0.538462,4.230769],[-0.076923,3.076923]],"s":1},{"type":"po","v":[[-1.076923,-0.769231],[-1.076923,-0.615385],[-1.769231,-0.615385]],"s":1},{"type":"po","v":[[1.076923,-0.819231],[1.692308,-0.665385],[1.076923,-0.665385]],"s":1,"c":[0,0.026923]},{"type":"bx","w":0.538462,"h":0.153846,"c":[-1.769231,1.538462],"a":0.912981},{"type":"po","v":[[-1.076923,0.846154],[-1.846154,0.923077],[-1.923077,0.846154],[-2,1.076923],[-1.846154,1.307692],[-1.461538,1.923077],[-1.307692,1.923077],[-1.230769,1.692308],[-1.230769,1.615385],[-1.230769,1.384615]],"s":1,"c":[-1.076923,0.846154]},{"type":"po","v":[[-0.538462,0.461538],[-1.307692,0.538462],[-1.384615,0.461538],[-1.461538,0.692308],[-1.307692,0.923077],[-0.923077,1.538462],[-0.769231,1.538462],[-0.692308,1.307692],[-0.692308,1.230769],[-0.692308,1]],"s":1,"c":[-0.538462,0.461538]},{"type":"po","v":[[0,0],[-0.769231,0.076923],[-0.846154,0],[-0.923077,0.230769],[-0.769231,0.461538],[-0.384615,1.076923],[-0.230769,1.076923],[-0.153846,0.846154],[-0.153846,0.769231],[-0.153846,0.538462]],"s":1},{"type":"bx","w":0.538462,"h":0.307692,"c":[-1.961538,1.676923],"a":0.912981},{"type":"bx","w":0.307692,"h":0.230769,"a":0.588003},{"type":"bx","w":2.692308,"h":0.153846,"c":[-1.076923,-1.076923],"a":0.851966},{"type":"bx","w":2.692308,"h":0.153846,"c":[-0.846154,-1.230769],"a":0.851966},{"type":"po","v":[[-0.230769,0],[-0.384615,-0.153846],[-0.384615,-0.307692],[-0.461538,-0.538462],[-0.461538,-0.615385],[-0.615385,-0.769231],[-0.692308,-0.846154],[-0.846154,-0.846154],[-1.076923,-0.923077],[-1.153846,-1],[-1.307692,-1.230769],[-1.307692,-1.461538],[-1.307692,-1.615385],[-1.538462,-1.692308],[-1.769231,-1.769231],[-1.923077,-1.846154],[-2,-1.923077],[-2,-2],[-1.846154,-1.923077],[-1.692308,-1.846154],[-1.538462,-1.769231],[-1.384615,-1.769231],[-1.230769,-1.692308],[-1.153846,-1.538462],[-1.153846,-1.307692],[-1.153846,-1.076923],[-0.923077,-1],[-0.692308,-0.923077],[-0.615385,-0.846154],[-0.461538,-0.692308],[-0.384615,-0.538462],[-0.230769,-0.230769]],"s":1},{"type":"po","v":[[-1.692308,-2.307692],[-1.538462,-2.153846],[-1.538462,-2],[-1.461538,-1.769231],[-1.461538,-1.692308],[-1.307692,-1.538462],[-1.230769,-1.461538],[-1.076923,-1.461538],[-0.846154,-1.384615],[-0.769231,-1.307692],[-0.615385,-1.076923],[-0.615385,-0.846154],[-0.615385,-0.692308],[-0.384615,-0.615385],[-0.153846,-0.538462],[0,-0.461538],[0.076923,-0.384615],[0.076923,-0.307692],[-0.076923,-0.384615],[-0.230769,-0.461538],[-0.384615,-0.538462],[-0.538462,-0.538462],[-0.692308,-0.615385],[-0.769231,-0.769231],[-0.769231,-1],[-0.769231,-1.230769],[-1,-1.307692],[-1.230769,-1.384615],[-1.307692,-1.461538],[-1.461538,-1.615385],[-1.538462,-1.769231],[-1.692308,-2.076923]],"s":1,"a":3.141593,"c":[-1.923077,-2.307692]},{"type":"po","v":[[0.153846,-0.538462],[0.384615,-0.153846],[0,0],[-0.230769,-0.230769]],"s":1},{"type":"po","v":[[-0.230769,-0.230769],[0.153846,-0.538462],[0,-0.846154],[-0.076923,-0.923077],[-0.307692,-0.923077],[-0.307692,-0.846154],[-0.538462,-0.769231],[-0.538462,-0.538462],[-0.461538,-0.384615]],"s":1,"c":[0,0.076923]},{"type":"po","v":[[-3.615385,-3.769231],[-0.307692,-0.846154],[-0.538462,-0.769231]],"s":1},{"type":"po","v":[[-2.901778,-4.235713],[-0.03309,-0.880742],[-0.272319,-0.836684]],"s":1,"a":0.139626,"c":[0.153846,0]},{"type":"po","v":[[-4.266005,-2.782628],[-0.34941,-0.746582],[-0.554715,-0.616116]],"s":1,"a":-0.244346,"c":[0.153846,0]},{"type":"po","v":[[-1.954351,-4.624606],[0.154112,-0.746515],[-0.08905,-0.753159]],"s":1,"a":0.349066,"c":[0.153846,0.153846]},{"type":"po","v":[[-3.461538,-0.923077],[-0.384615,-0.461538],[-0.153846,-0.230769]],"s":1,"c":[0,-0.076923]},{"type":"bx","w":0.538462,"h":0.153846,"c":[1.769231,1.538462],"a":-0.912981},{"type":"po","v":[[1.076923,0.846154],[1.846154,0.923077],[1.923077,0.846154],[2,1.076923],[1.846154,1.307692],[1.461538,1.923077],[1.307692,1.923077],[1.230769,1.692308],[1.230769,1.615385],[1.230769,1.384615]],"s":1,"c":[1.076923,0.846154]},{"type":"po","v":[[0.538462,0.461538],[1.307692,0.538462],[1.384615,0.461538],[1.461538,0.692308],[1.307692,0.923077],[0.923077,1.538462],[0.769231,1.538462],[0.692308,1.307692],[0.692308,1.230769],[0.692308,1]],"s":1,"c":[0.538462,0.461538]},{"type":"po","v":[[0,0],[0.769231,0.076923],[0.846154,0],[0.923077,0.230769],[0.769231,0.461538],[0.384615,1.076923],[0.230769,1.076923],[0.153846,0.846154],[0.153846,0.769231],[0.153846,0.538462]],"s":1},{"type":"bx","w":0.538462,"h":0.307692,"c":[1.961538,1.676923],"a":-0.912981},{"type":"bx","w":0.307692,"h":0.230769,"a":-0.588003},{"type":"bx","w":2.692308,"h":0.153846,"c":[1.076923,-1.076923],"a":-0.851966},{"type":"bx","w":2.692308,"h":0.153846,"c":[0.846154,-1.230769],"a":-0.851966},{"type":"po","v":[[0.230769,0],[0.384615,-0.153846],[0.384615,-0.307692],[0.461538,-0.538462],[0.461538,-0.615385],[0.615385,-0.769231],[0.692308,-0.846154],[0.846154,-0.846154],[1.076923,-0.923077],[1.153846,-1],[1.307692,-1.230769],[1.307692,-1.461538],[1.307692,-1.615385],[1.538462,-1.692308],[1.769231,-1.769231],[1.923077,-1.846154],[2,-1.923077],[2,-2],[1.846154,-1.923077],[1.692308,-1.846154],[1.538462,-1.769231],[1.384615,-1.769231],[1.230769,-1.692308],[1.153846,-1.538462],[1.153846,-1.307692],[1.153846,-1.076923],[0.923077,-1],[0.692308,-0.923077],[0.615385,-0.846154],[0.461538,-0.692308],[0.384615,-0.538462],[0.230769,-0.230769]],"s":1},{"type":"po","v":[[1.692308,-2.307692],[1.538462,-2.153846],[1.538462,-2],[1.461538,-1.769231],[1.461538,-1.692308],[1.307692,-1.538462],[1.230769,-1.461538],[1.076923,-1.461538],[0.846154,-1.384615],[0.769231,-1.307692],[0.615385,-1.076923],[0.615385,-0.846154],[0.615385,-0.692308],[0.384615,-0.615385],[0.153846,-0.538462],[0,-0.461538],[-0.076923,-0.384615],[-0.076923,-0.307692],[0.076923,-0.384615],[0.230769,-0.461538],[0.384615,-0.538462],[0.538462,-0.538462],[0.692308,-0.615385],[0.769231,-0.769231],[0.769231,-1],[0.769231,-1.230769],[1,-1.307692],[1.230769,-1.384615],[1.307692,-1.461538],[1.461538,-1.615385],[1.538462,-1.769231],[1.692308,-2.076923]],"s":1,"a":-3.141593,"c":[1.923077,-2.307692]},{"type":"po","v":[[-0.153846,-0.538462],[0.230769,-0.230769],[0,0],[-0.384615,-0.153846]],"s":1},{"type":"po","v":[[0.230769,-0.230769],[-0.153846,-0.538462],[0,-0.846154],[0.076923,-0.923077],[0.307692,-0.923077],[0.307692,-0.846154],[0.538462,-0.769231],[0.538462,-0.538462],[0.461538,-0.384615]],"s":1,"c":[0,0.076923]},{"type":"po","v":[[3.615385,-3.769231],[0.538462,-0.769231],[0.307692,-0.846154]],"s":1},{"type":"po","v":[[2.901778,-4.235713],[0.272319,-0.836684],[0.03309,-0.880742]],"s":1,"a":-0.139626,"c":[-0.153846,0]},{"type":"po","v":[[4.266005,-2.782628],[0.554715,-0.616116],[0.34941,-0.746582]],"s":1,"a":0.244346,"c":[-0.153846,0]},{"type":"po","v":[[1.954351,-4.624606],[0.08905,-0.753159],[-0.154112,-0.746515]],"s":1,"a":-0.349066,"c":[-0.153846,0.153846]},{"type":"po","v":[[3.461538,-0.923077],[0.153846,-0.230769],[0.384615,-0.461538]],"s":1,"c":[0,-0.076923]},{"type":"po","v":[[0,0],[0.615385,-2],[2,2.461538],[-0.692308,2.461538]],"s":1},{"type":"po","v":[[-0.307692,1.692308],[-0.076923,0.692308],[0.307692,0.923077],[0.461538,1],[0.692308,0.846154],[1.153846,0.923077],[1.076923,1.384615],[1.153846,1.615385],[1.076923,1.846154],[0.923077,2],[0.615385,2],[0.384615,1.923077]],"s":1},{"type":"po","v":[[-0.015385,0.353846],[0.169231,-0.446154],[0.476923,-0.261538],[0.6,-0.2],[0.784615,-0.323077],[1.153846,-0.261538],[1.092308,0.107692],[1.153846,0.292308],[1.092308,0.476923],[0.969231,0.6],[0.723077,0.6],[0.538462,0.538462]],"s":0.8,"c":[0.230769,-1]},{"type":"po","v":[[1.230769,2.538462],[1.230769,1.153846],[1.230769,0.846154],[1.230769,0.384615],[1.307692,0.384615],[1.384615,0.923077],[1.538462,1.384615],[1.615385,1.923077],[1.846154,2.307692],[1.923077,2.769231],[1.923077,2.923077]],"s":1},{"type":"po","v":[[-0.538462,2.769231],[-0.461538,2.307692],[-0.307692,2.153846],[-0.153846,2.230769],[0.307692,2.307692],[0.846154,2.230769],[1.076923,2.384615],[1,3],[0.307692,3.153846]],"s":1},{"type":"po","v":[[1.307692,0.307692],[1.307692,-0.076923],[1.153846,-0.384615],[1,-0.538462],[0.923077,-0.769231],[0.846154,-1],[0.692308,-1.230769],[0.384615,-1.076923],[0.153846,-0.538462],[0,-0.307692],[0,-0.153846],[0,0.076923],[0,0.230769],[0.153846,-0.076923],[0.307692,-0.307692],[0.461538,-0.538462],[0.538462,-0.846154],[0.769231,-0.538462],[0.923077,-0.384615],[1,-0.153846],[1.153846,0.076923]],"s":1},{"type":"ci","r":1.923077,"c":[0,-0.307692]},{"type":"po","v":[[-1.769231,2.153846],[-1.307692,3.153846],[4.307692,2.846154],[5.769231,2.307692],[7.461538,1.461538],[8.153846,1],[9.153846,-1.461538],[7.615385,-2.230769],[6.153846,-2],[4.769231,-1.307692],[3.615385,-0.692308],[1,0.230769],[-1.153846,1.307692],[-1.692308,1.615385]],"s":1},{"type":"po","v":[[0.769231,1.846154],[-0.076923,1.846154],[-0.769231,1.769231],[-1.384615,1.692308],[-1.846154,1.461538],[-2.307692,1.153846],[-2.384615,0.538462],[-2.307692,0],[-2.230769,-0.538462],[-2.076923,-1.076923],[-1,-1.923077],[0.076923,-2],[0.769231,-1.923077],[1.692308,-1.461538],[2.153846,-0.846154],[2.846154,-0.076923],[2.384615,1],[1.923077,1.538462]],"s":1},{"type":"po","v":[[-0.461538,-0.692308],[-1.076923,-0.769231],[-1.384615,-0.692308],[-1.538462,-0.692308],[-1.692308,-0.923077],[-1.615385,-1.230769],[-1.153846,-1.384615],[-0.846154,-1.461538],[-0.384615,-1.307692],[0,-1.307692],[0.384615,-1.461538],[0.846154,-1.307692],[1.384615,-1.076923],[1.615385,-1.076923],[1.692308,-0.923077],[1.615385,-0.384615],[1.076923,-0.307692],[1,-0.384615],[0.538462,-0.461538],[-0.153846,-0.538462]],"s":1,"c":[0,-0.153846]},{"type":"po","v":[[-0.461538,0.307692],[-1.076923,0.230769],[-1.384615,0.307692],[-1.538462,0.307692],[-1.692308,0.076923],[-1.615385,-0.230769],[-1.153846,-0.384615],[-0.846154,-0.461538],[-0.384615,-0.307692],[0,-0.307692],[0.384615,-0.461538],[0.846154,-0.307692],[1.384615,-0.076923],[1.615385,-0.076923],[1.692308,0.076923],[1.615385,0.615385],[1.076923,0.692308],[1,0.615385],[0.538462,0.538462],[-0.153846,0.461538]],"s":1,"c":[0,0.846154]},{"type":"po","v":[[-0.461538,1.384615],[-1.076923,1.307692],[-1.384615,1.384615],[-1.538462,1.384615],[-1.692308,1.153846],[-1.615385,0.846154],[-1.153846,0.692308],[-0.846154,0.615385],[-0.384615,0.769231],[0,0.769231],[0.384615,0.615385],[0.846154,0.769231],[1.384615,1],[1.615385,1],[1.692308,1.153846],[1.615385,1.692308],[1.076923,1.769231],[1,1.692308],[0.538462,1.615385],[-0.153846,1.538462]],"s":1,"c":[0,1.923077]},{"type":"po","v":[[-2.076923,-0.923077],[-1.769231,-1.153846],[-1.538462,-1.230769],[-1.384615,-0.692308],[-1.769231,-0.384615],[-2,-0.461538],[-2.153846,-0.692308]],"s":1,"c":[0,0.076923]},{"type":"po","v":[[-2.076923,0.076923],[-1.769231,-0.153846],[-1.538462,-0.230769],[-1.384615,0.307692],[-1.769231,0.615385],[-2,0.538462],[-2.153846,0.307692]],"s":1,"c":[0,1.076923]},{"type":"po","v":[[-2.076923,1.153846],[-1.769231,0.923077],[-1.538462,0.846154],[-1.384615,1.384615],[-1.769231,1.692308],[-2,1.615385],[-2.153846,1.384615]],"s":1,"c":[0,2.153846]},{"type":"po","v":[[-0.307692,1.615385],[-0.461538,1.076923],[-0.538462,0.769231],[-0.538462,0.538462],[-0.538462,0.307692],[-0.384615,-0.076923],[-0.461538,-0.461538],[-0.538462,-0.846154],[-0.461538,-1.230769],[-0.384615,-1.307692],[-0.384615,-1],[-0.384615,-0.769231],[-0.384615,-0.461538],[-0.307692,-0.230769],[-0.307692,0.076923],[-0.384615,0.615385],[-0.384615,1]],"s":1,"c":[0,0.230769]},{"type":"po","v":[[5.230769,1],[4.846154,1.769231],[4.230769,1.923077],[3.461538,1.769231],[3.076923,1.461538],[2.769231,1.153846],[2.692308,1],[2.846154,0.846154],[3,0.923077],[3.153846,1.076923],[3.461538,1.076923],[3.769231,1.076923],[4.076923,0.923077],[4.153846,0.846154],[4.307692,0.923077],[4.307692,1],[4.153846,1.230769],[4,1.230769],[3.769231,1.230769],[3.384615,1.307692],[3.153846,1.230769],[3,1],[2.846154,1],[3,1.307692],[3.307692,1.538462],[3.461538,1.615385],[3.846154,1.769231],[4.076923,1.846154],[4.384615,1.769231],[4.538462,1.692308],[4.769231,1.615385],[4.846154,1.461538]],"s":1,"c":[0,-0.461538]},{"type":"po","v":[[0.461538,1.769231],[1.538462,1.307692],[1.692308,1.230769],[2,0.923077],[2.153846,0.538462],[2.230769,-0.076923],[2.153846,-0.615385],[1.923077,-0.923077],[1.692308,-1.230769],[1.384615,-1.461538],[0.923077,-1.615385],[0.384615,-1.692308],[0.153846,-1.923077],[0.153846,-2.230769],[0.538462,-2.461538],[1,-2.307692],[1.384615,-2.153846],[1.769231,-1.692308],[2,-1.538462],[2.615385,-1.538462],[3,-1.692308],[3.615385,-1.538462],[4,-0.923077],[4.153846,-0.230769],[4.076923,0.307692],[3.538462,0.923077],[3,1.307692],[2.230769,1.692308],[1.769231,1.846154],[1.230769,2],[0.461538,2.384615],[0.307692,2.230769]],"s":1},{"type":"po","v":[[-1.769231,2.230769],[-1.538462,1.692308],[-1.692308,1.384615],[-1.846154,1.153846],[-2.153846,0.076923],[-2.153846,-0.307692],[-2.076923,-0.769231],[-1.923077,-1.076923],[-1.461538,-1.384615],[-1.076923,-1.538462],[-0.692308,-1.692308],[-0.615385,-2],[-0.846154,-2.230769],[-1.461538,-2.076923],[-2,-1.769231],[-2.153846,-1.384615],[-2.230769,-1],[-2.384615,-0.153846],[-2.461538,0.461538],[-2.461538,1.230769],[-2.307692,1.846154]],"s":1,"c":[0.076923,0]},{"type":"po","v":[[-1.076923,-2],[-0.923077,-2.923077],[-0.846154,-3],[-0.461538,-3],[0.153846,-3.153846],[0.307692,-3.153846],[0.384615,-2.846154],[0.461538,-2.153846],[0.384615,-2],[0.307692,-1.846154],[-0.538462,-1.846154]],"s":1},{"type":"ci","r":0.461538,"c":[-0.307692,-2.461538]},{"type":"ci","r":0.423077,"c":[-0.307692,-2.461538]},{"type":"po","v":[[-1.692308,2],[-1.923077,1.461538],[-2.076923,1.307692],[-2.230769,1],[-2.307692,0.307692],[-2.384615,0.076923],[-2.384615,-0.153846],[-2.307692,-0.461538],[-2.307692,-0.692308],[-2.153846,-0.923077],[-1.846154,-1.153846],[-0.769231,-1.692308],[-0.692308,-1.769231],[-0.461538,-1.846154],[-1.384615,-1.615385],[-2,-1.230769],[-2.230769,-1],[-2.307692,-0.692308],[-2.461538,-0.076923],[-2.461538,0.307692],[-2.461538,0.692308],[-2.461538,1.153846],[-2.461538,1.384615],[-2.384615,1.923077],[-2.230769,2.076923]],"s":1},{"type":"bx","w":1.923077,"h":1,"c":[9.769231,-1.307692],"a":1.341564},{"type":"bx","w":2.384615,"h":1.769231,"c":[9,-1.153846],"a":1.341564},{"type":"po","v":[[2.153846,-1.615385],[3,-1.692308],[3.846154,-1.923077],[4.230769,-2.076923],[4.846154,-2.230769],[5.153846,-2.384615],[5.846154,-2.538462],[6.384615,-2.615385],[7.615385,-2.692308],[7.923077,-2.769231],[8.461538,-2.692308],[9.076923,-2.153846],[9.307692,-2],[9.307692,-1.538462],[9,-1.384615],[8.384615,-1.461538],[7.538462,-1.769231],[7.076923,-1.615385],[6.615385,-1],[5.923077,-0.692308],[5.153846,-0.692308],[4.615385,0.230769],[4.384615,0.307692],[4.230769,0.230769],[4.153846,0.153846],[4.076923,-0.230769],[4.076923,-0.615385],[3.923077,-1],[3.692308,-1.307692],[3.461538,-1.538462],[3.384615,-1.615385],[3,-1.615385],[2.923077,-1.615385]],"s":1},{"type":"bx","w":1.076923,"h":0.076923,"c":[10.692308,-1.923077],"a":-0.432408},{"type":"bx","w":2.153846,"h":0.615385,"c":[4.461538,0.307692],"a":-1.352127},{"type":"bx","w":2.153846,"h":0.615385,"c":[5.307692,0.076923],"a":-1.352127},{"type":"bx","w":0.846154,"h":0.615385,"c":[5.161538,0.730769],"a":-1.352127},{"type":"bx","w":0.846154,"h":0.615385,"c":[4.315385,0.961538],"a":-1.352127},{"type":"po","v":[[6.769231,-1.076923],[7.230769,-1],[7.615385,-1.076923],[7.846154,-1.230769],[8.076923,-1.384615],[8.692308,-1.538462],[8.846154,-1.461538],[8.538462,-0.615385],[8.461538,-0.384615],[8.076923,-0.153846],[7.384615,0.307692],[6.923077,0.538462],[6.230769,0.307692],[6.076923,0],[5.846154,-0.230769],[5.846154,-0.307692],[5.769231,-0.461538],[5.769231,-0.538462],[5.846154,-0.846154]],"s":1},{"type":"po","v":[[1.153846,2.153846],[1.692308,2.153846],[2.153846,2.307692],[2.615385,2.230769],[3.538462,2.076923],[4.307692,2.076923],[4.923077,1.846154],[5.538462,1.692308],[6,1.461538],[7,1.230769],[7.538462,1],[8.076923,0.692308],[8.230769,0.384615],[8.384615,0.307692],[8.384615,0.153846],[8.307692,0.076923],[8.076923,0.230769],[8.076923,0.538462],[7.846154,0.692308],[7.461538,0.846154],[6.846154,1.153846],[6.230769,1.307692],[5.538462,1.461538],[4.923077,1.692308],[4.615385,1.923077],[3.692308,2.076923],[2.846154,2.076923],[2.307692,2.076923],[2.076923,2.153846],[1.692308,1.923077],[1.615385,1.846154]],"s":1},{"type":"po","v":[[2.384615,1.692308],[3.153846,1.769231],[3.538462,1.923077],[4,2],[4.923077,2],[5.846154,1.923077],[6.461538,1.461538],[6.769231,1.076923],[7.307692,0.769231],[7.538462,0.538462],[7.461538,0.307692],[7.307692,0.384615],[7.076923,0.615385],[6.846154,0.923077],[6.461538,1.153846],[6.230769,1.461538],[5.923077,1.692308],[5.230769,1.923077],[4.538462,1.846154],[3.538462,1.846154],[3.307692,1.769231],[2.923077,1.615385],[2.769231,1.461538],[2.692308,1.461538]],"s":1},{"type":"po","v":[[3.846154,-1.615385],[4.384615,-1.692308],[4.615385,-1.692308],[4.692308,-1.538462],[4.846154,-1.461538],[5.230769,-1.461538],[5.461538,-1.538462],[5.538462,-1.615385],[5.692308,-1.692308],[5.692308,-1.846154],[5.923077,-1.538462],[5.923077,-1.461538],[5.923077,-1.615385],[6,-1.615385],[5.846154,-1.461538],[5.615385,-1.461538],[5.384615,-1.384615],[5.307692,-1.384615],[5.153846,-1.230769],[4.846154,-1.230769],[4.769231,-1.384615],[5.076923,-1.384615],[4.769231,-1.307692],[4.615385,-1.153846],[4.692308,-1.307692],[4.538462,-1.230769],[4.615385,-1.230769],[4.538462,-1.384615],[4.461538,-1.615385],[4.307692,-1.615385]],"s":1},{"type":"po","v":[[5.461538,-2.307692],[6,-2.384615],[6.230769,-2.384615],[6.307692,-2.230769],[6.461538,-2.153846],[6.846154,-2.153846],[7.076923,-2.230769],[7.153846,-2.307692],[7.307692,-2.384615],[7.307692,-2.538462],[7.538462,-2.230769],[7.538462,-2.153846],[7.538462,-2.307692],[7.615385,-2.307692],[7.461538,-2.153846],[7.230769,-2.153846],[7,-2.076923],[6.923077,-2.076923],[6.769231,-1.923077],[6.461538,-1.923077],[6.384615,-2.076923],[6.692308,-2.076923],[6.384615,-2],[6.230769,-1.846154],[6.307692,-2],[6.153846,-1.923077],[6.230769,-1.923077],[6.153846,-2.076923],[6.076923,-2.307692],[5.923077,-2.307692]],"s":1,"c":[1.615385,-0.692308]},{"type":"po","v":[[8.461538,-1.461538],[7.692308,-1.769231],[7.692308,-1.692308],[7.307692,-1.692308],[7.153846,-1.538462],[6.846154,-1.153846],[6.538462,-1.076923],[6.153846,-1.384615],[6,-1.153846],[5.846154,-1.076923],[5.615385,-1.076923],[5.384615,-1],[6,-1.230769],[6.076923,-1.538462],[6.461538,-1.384615],[6.769231,-1.384615],[7.153846,-1.846154],[7.615385,-2],[8.076923,-2],[8.230769,-2],[8.230769,-2.153846],[8.461538,-2]],"s":1,"c":[0,0.076923]},{"type":"po","v":[[3.846154,-1.384615],[3.692308,-1.769231],[4,-2],[4.769231,-2],[4.923077,-1.846154],[5.307692,-1.923077],[5.461538,-2.076923],[5.230769,-2.076923],[5.076923,-2.076923],[4.769231,-2.076923],[4.461538,-2.076923],[3.615385,-1.923077],[3.615385,-1.846154],[3.461538,-1.692308],[3.384615,-1.692308]],"s":1},{"type":"bx","w":3.769231,"h":0.538462,"c":[-1.846154,2.384615],"a":0.141897},{"type":"bx","w":3.307692,"h":0.615385,"c":[-3.076923,1.923077],"a":0.165149},{"type":"po","v":[[-3.461538,2.461538],[-3.153846,2.769231],[-2.692308,2.769231],[-2.384615,2.692308],[-2.307692,2.615385],[-2,2.615385],[-1.769231,2.692308],[-1.384615,2.923077],[-0.769231,2.846154],[-0.615385,2.923077],[-0.307692,2.769231],[-0.153846,2.769231],[-0.076923,2.538462],[-0.076923,2.461538],[-0.230769,2.230769],[-0.692308,2.307692],[-1,2.153846],[-1.384615,2.153846],[-1.769231,2.076923],[-2.307692,2],[-2.384615,1.923077],[-2.846154,1.923077],[-2.923077,1.923077],[-3,1.923077],[-3.076923,1.923077],[-3.538462,2.153846]],"s":1,"c":[0,-0.076923]},{"type":"po","v":[[6.076923,-0.307692],[6.538462,-0.461538],[6.615385,-0.230769],[6.769231,-0.230769],[7,-0.307692],[7.153846,-0.384615],[7.307692,-0.307692],[7.307692,-0.538462],[7.538462,-0.538462],[7.538462,-0.307692],[7.923077,-0.846154],[7.923077,-0.692308],[8,-0.923077],[8.153846,-0.846154],[8.307692,-1.384615],[8.153846,-0.846154],[8.538462,-0.923077],[8.461538,-1.076923],[8.538462,-1.076923],[8.384615,-0.615385],[8.230769,-0.384615],[8.230769,-0.615385],[8.076923,-0.615385],[8.076923,-0.384615],[7.538462,-1],[7.692308,-0.615385],[7.615385,-0.384615],[7.538462,0.076923],[7.384615,0.076923],[7.307692,-0.692308],[7.076923,-0.384615],[7,0],[7,0.307692],[6.692308,0.153846],[6.846154,-0.384615],[6.692308,-0.769231],[6.692308,0],[6.538462,0.153846]],"s":1,"c":[0,0.076923]},{"type":"po","v":[[0.461538,2.384615],[1,2],[1.615385,1.615385],[1.846154,1.307692],[2.230769,1],[2.384615,0.615385],[2.538462,0.076923],[2.692308,-0.461538],[2.538462,-0.769231],[2.615385,-1.076923],[2.923077,-1.384615],[3.384615,-1.538462],[3.461538,-1.461538],[3.923077,-1.076923],[4.076923,-0.461538],[4.153846,0],[4.076923,0.461538],[3.923077,0.615385],[2.692308,1.692308],[1.692308,2.384615],[1.307692,2.307692]],"s":1},{"type":"po","v":[[8.230769,-0.307692],[8.461538,-0.461538],[8.769231,-1.461538],[8.923077,-1.384615],[9.230769,-1.538462],[9.230769,-1.615385],[9.461538,-1.615385],[9.538462,-1.230769],[9.307692,-0.769231],[9.307692,-0.461538],[9.230769,-0.307692],[9.076923,-0.076923],[9,0],[9,0.076923],[8.307692,0.230769]],"s":1,"c":[-0.076923,0]},{"type":"bx","w":5.615385,"h":0.230769,"c":[-3.153846,1.153846],"a":0.261479},{"type":"bx","w":6.769231,"h":0.230769,"c":[0,0.076923],"a":0.737049},{"type":"po","v":[[-2.653846,-2.153846],[-3.884615,-2.730769],[-3.923077,-2.769231],[-3.923077,-2.807692],[-3.923077,-2.846154],[-3.884615,-2.846154],[-3.038462,-2.461538],[-3.884615,-3.384615],[-3.884615,-3.423077],[-3.846154,-3.423077],[-3.807692,-3.384615],[-3.192308,-2.769231],[-3.153846,-2.884615],[-3.769231,-3.538462],[-3.769231,-3.576923],[-3.769231,-3.615385],[-3.730769,-3.615385],[-3.115385,-3],[-3.038462,-3.076923],[-3.653846,-3.692308],[-3.615385,-3.730769],[-3.538462,-3.730769],[-3.423077,-3.615385],[-3,-3.153846],[-2.884615,-3.192308],[-3.423077,-3.769231],[-3.384615,-3.807692],[-3.307692,-3.807692],[-3.153846,-3.653846],[-2.730769,-3.230769],[-2.615385,-3.038462],[-2.461538,-2.692308],[-2.461538,-2.615385],[-2.384615,-2.461538]],"s":0.5,"c":[-1.230769,-1.153846]},{"type":"bx","w":1.153846,"h":0.230769,"c":[-2.538462,-2.230769],"a":-0.851966},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[1.153846,0]},{"type":"po","v":[[2.307692,0.461538],[3.461538,0.076923],[2.461538,2.538462],[2.153846,2.538462]],"s":1},{"type":"po","v":[[2.461538,2],[4.153846,2],[4.230769,2.230769],[2.230769,2.538462]],"s":1},{"type":"ci","r":0.769231,"c":[2.692308,0]},{"type":"po","v":[[3.923077,1.692308],[4.615385,1.538462],[3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[2.692308,0]},{"type":"ci","r":0.461538,"c":[2.692308,0]},{"type":"po","v":[[0.230769,0.384615],[0.461538,0.307692],[0.692308,0.307692],[0.846154,0.307692],[1,0.230769],[1.230769,0.230769],[1.384615,0.230769],[1.846154,0.307692],[2.153846,0.461538],[2.230769,0.615385],[2.307692,0.692308],[2.153846,0.692308],[2.076923,0.615385],[2,0.538462],[1.846154,0.384615],[1.615385,0.384615],[1.307692,0.307692],[1.076923,0.307692],[0.846154,0.384615],[0.538462,0.461538]],"s":1},{"type":"po","v":[[2.384615,-0.692308],[2.153846,-0.538462],[2,-0.307692],[1.692308,-0.076923],[1.384615,0],[1,0],[0.615385,-0.076923],[0.230769,-0.307692],[0.230769,-0.384615],[0.076923,-0.384615],[0.076923,-0.461538],[-0.076923,-0.615385],[-0.153846,-0.692308],[-0.230769,-0.692308],[-0.307692,-0.692308],[-0.153846,-0.692308],[-0.076923,-0.538462],[0.230769,-0.461538],[0.461538,-0.307692],[0.769231,-0.153846],[1.076923,-0.076923],[1.538462,-0.153846],[1.769231,-0.230769],[2,-0.461538]],"s":1},{"type":"po","v":[[3.153846,0.692308],[3,0.846154],[2.846154,1.153846],[2.538462,1.538462],[2.538462,1.769231],[2.538462,1.923077],[2.461538,2.076923],[2.692308,2.230769],[3.076923,2.153846],[3.461538,2.153846],[3.692308,2.076923],[3.923077,2.076923],[3.769231,2.153846],[3.461538,2.153846],[2.846154,2.230769],[2.615385,2.230769],[2.461538,2.230769],[2.384615,2.076923],[2.461538,1.846154],[2.461538,1.461538],[2.538462,1.230769],[2.769231,1.076923]],"s":1},{"type":"po","v":[[2.384615,0.769231],[2.461538,1],[2.615385,1.615385],[2.615385,2],[2.615385,2.153846],[2.692308,2.384615],[3,2.461538],[3.461538,2.307692],[3.615385,2.153846],[3.846154,2],[3.923077,2],[3.692308,2.076923],[3.538462,2.153846],[3.384615,2.307692],[3.076923,2.384615],[2.923077,2.384615],[2.769231,2.307692],[2.692308,2.230769],[2.615385,2],[2.615385,1.692308],[2.615385,1.461538],[2.615385,1.153846]],"s":1},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[1.153846,0]},{"type":"po","v":[[2.307692,0.461538],[3.461538,0.076923],[2.461538,2.538462],[2.153846,2.538462]],"s":1},{"type":"po","v":[[2.461538,2],[4.153846,2],[4.230769,2.230769],[2.230769,2.538462]],"s":1},{"type":"ci","r":0.769231,"c":[2.692308,0]},{"type":"po","v":[[3.923077,1.692308],[4.615385,1.538462],[3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[2.692308,0]},{"type":"ci","r":0.461538,"c":[2.692308,0]},{"type":"po","v":[[2.230769,0.615385],[2,0.384615],[1.846154,0.153846],[1.692308,-0.076923],[1.307692,0],[1,0.076923],[0.769231,-0.230769],[0.461538,-0.307692],[0.153846,-0.384615],[0,-0.538462],[-0.153846,-0.615385],[-0.307692,-0.692308],[-0.384615,-0.615385],[-0.461538,-0.615385],[-0.076923,-0.538462],[0.076923,-0.384615],[0.230769,-0.307692],[0.461538,-0.230769],[0.769231,-0.153846],[0.923077,0],[1.076923,0.153846],[1.461538,0.076923],[1.615385,0],[1.846154,0.153846]],"s":1},{"type":"po","v":[[2.307692,-0.615385],[1.615385,-0.153846],[1.538462,0.307692],[1.153846,0.384615],[0.769231,0.307692],[0.615385,0.153846],[0.461538,0.230769],[0.461538,0.307692],[0.384615,0.307692],[0.384615,0.230769],[0.692308,0.076923],[0.846154,0.153846],[1.076923,0.307692],[1.461538,0.307692],[1.538462,0.076923],[1.538462,-0.153846],[1.846154,-0.384615]],"s":1},{"type":"po","v":[[3.846154,2.153846],[3.153846,2.230769],[2.692308,2.307692],[2.461538,2.307692],[2.384615,2.076923],[2.461538,1.615385],[2.692308,1.230769],[3.076923,0.923077],[3.076923,0.692308],[3.153846,0.538462],[3.153846,0.769231],[3,1.076923],[2.692308,1.307692],[2.615385,1.615385],[2.538462,2],[2.538462,2.307692],[3,2.307692],[3.384615,2.153846]],"s":1},{"type":"po","v":[[2.384615,0.692308],[2.384615,1.461538],[2.384615,1.846154],[2.307692,2.230769],[2.461538,2.461538],[2.769231,2.153846],[3.076923,2.307692],[3.307692,2.230769],[3.615385,2.076923],[4,2.307692],[3.923077,2.153846],[3.692308,2],[3.538462,2],[3.153846,2.153846],[3.076923,2.230769],[2.692308,2.076923],[2.615385,2.230769],[2.538462,2.307692],[2.384615,2.076923],[2.461538,1.692308],[2.461538,1.461538]],"s":1},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[1.153846,0]},{"type":"po","v":[[2.307692,0.461538],[3.461538,0.076923],[2.461538,2.538462],[2.153846,2.538462]],"s":1},{"type":"po","v":[[2.461538,2],[4.153846,2],[4.230769,2.230769],[2.230769,2.538462]],"s":1},{"type":"ci","r":0.769231,"c":[2.692308,0]},{"type":"po","v":[[3.923077,1.692308],[4.615385,1.538462],[3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[2.692308,0]},{"type":"ci","r":0.461538,"c":[2.692308,0]},{"type":"po","v":[[2.307692,0.692308],[2,0.538462],[1.615385,0.230769],[1.307692,0],[0.923077,-0.230769],[0.230769,-0.538462],[-0.230769,-0.615385],[-0.461538,-0.538462],[-0.615385,-0.461538],[-0.538462,-0.461538],[-0.307692,-0.692308],[0,-0.692308],[0.384615,-0.538462],[0.692308,-0.461538],[1.153846,-0.153846],[1.538462,0.076923],[1.769231,0.230769]],"s":1},{"type":"po","v":[[-0.538462,0.538462],[0.076923,0.769231],[0.230769,0.692308],[0.538462,0.538462],[0.846154,0.307692],[1.307692,-0.076923],[1.692308,-0.384615],[1.923077,-0.538462],[2.153846,-0.692308],[2.307692,-0.769231],[2.461538,-0.692308],[2.153846,-0.615385],[1.923077,-0.461538],[1.769231,-0.230769],[1.615385,0.076923],[1.307692,0.230769],[1.076923,0.153846],[0.923077,0.384615],[0.461538,0.384615],[0.461538,0.615385],[0.384615,0.769231],[-0.076923,0.692308]],"s":1},{"type":"po","v":[[3.230769,0.538462],[2.923077,0.769231],[2.769231,0.846154],[2.461538,1.076923],[2.461538,1.461538],[2.538462,1.615385],[2.307692,2.153846],[2.615385,2.461538],[2.923077,2.153846],[3.153846,2],[3.307692,2.076923],[3.538462,2.307692],[3.769231,2.153846],[4,2.076923],[3.923077,2.153846],[3.692308,2.307692],[3.461538,2.307692],[3.230769,2.076923],[3.076923,2.153846],[2.769231,2.461538],[2.615385,2.461538],[2.307692,2.307692],[2.230769,2.153846],[2.461538,1.846154],[2.461538,1.538462],[2.307692,1.230769],[2.461538,0.923077],[2.769231,0.769231]],"s":1},{"type":"po","v":[[2.461538,0.769231],[2.769231,1],[2.692308,1.307692],[2.461538,1.615385],[2.384615,1.846154],[2.538462,2.076923],[2.769231,2.307692],[2.923077,2],[3.076923,2.307692],[3.461538,2.153846],[3.615385,2.153846],[3.846154,2.153846],[3.923077,2.076923],[3.692308,2.076923],[3.384615,2.076923],[3.153846,2.153846],[2.846154,2.076923],[2.692308,2],[2.538462,1.846154],[2.692308,1.538462],[2.769231,1.076923],[2.769231,0.692308]],"s":1},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[-1.153846,0]},{"type":"po","v":[[-3.461538,0.076923],[-2.307692,0.461538],[-2.153846,2.538462],[-2.461538,2.538462]],"s":1},{"type":"po","v":[[-4.153846,2],[-2.461538,2],[-2.230769,2.538462],[-4.230769,2.230769]],"s":1},{"type":"ci","r":0.769231,"c":[-2.692308,0]},{"type":"po","v":[[-4.615385,1.538462],[-3.923077,1.692308],[-3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[-2.692308,0]},{"type":"ci","r":0.461538,"c":[-2.692308,0]},{"type":"po","v":[[-3.769231,2.153846],[-3.307692,2.153846],[-2.692308,2.230769],[-2.461538,2.230769],[-2.461538,1.846154],[-2.692308,1.384615],[-2.923077,1.153846],[-3.076923,0.846154],[-3,0.769231],[-2.923077,0.692308],[-2.923077,0.769231],[-2.923077,0.923077],[-2.923077,1.076923],[-2.846154,1.153846],[-2.615385,1.076923],[-2.461538,0.846154],[-2.384615,0.692308],[-2.307692,0.846154],[-2.615385,1.307692],[-2.307692,1.692308],[-2.307692,1.769231],[-2.230769,2.076923],[-2.307692,2.538462],[-2.384615,2.538462],[-2.538462,2.384615],[-2.692308,2.153846],[-3.076923,2.384615],[-3.076923,2.307692],[-2.692308,2.384615],[-2.538462,2.153846],[-2.769231,2],[-3.076923,2.153846],[-3.230769,2.384615],[-3.461538,2.153846],[-4.076923,2.230769],[-4.076923,2.076923]],"s":1},{"type":"po","v":[[-1.923077,-0.153846],[-1.384615,-0.461538],[-0.384615,-0.153846],[-0.230769,-0.076923],[-0.692308,0.538462],[-1,0.538462],[-1.384615,0.230769],[-1.461538,0.076923],[-1.307692,0],[-1.461538,-0.153846]],"s":1},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[-1.153846,0]},{"type":"po","v":[[-3.461538,0.076923],[-2.307692,0.461538],[-2.153846,2.538462],[-2.461538,2.538462]],"s":1},{"type":"po","v":[[-4.153846,2],[-2.461538,2],[-2.230769,2.538462],[-4.230769,2.230769]],"s":1},{"type":"ci","r":0.769231,"c":[-2.692308,0]},{"type":"po","v":[[-4.615385,1.538462],[-3.923077,1.692308],[-3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[-2.692308,0]},{"type":"ci","r":0.461538,"c":[-2.692308,0]},{"type":"po","v":[[-3.615385,2.230769],[-3.153846,2.230769],[-2.307692,2.384615],[-2.230769,2.230769],[-2.461538,1.769231],[-2.384615,1.615385],[-2.615385,1.307692],[-2.692308,1.076923],[-2.384615,1.153846],[-2.307692,1.307692],[-2.230769,1.615385],[-2.230769,1.846154],[-2.538462,2.153846],[-2.307692,2.538462],[-2.538462,2.538462],[-2.846154,2.230769],[-2.923077,2.230769],[-3,2.461538],[-3.384615,2.076923]],"s":1,"c":[0.153846,0.076923]},{"type":"ci","r":0.769231},{"type":"bx","w":2.692308,"h":1.538462,"c":[-1.153846,0]},{"type":"po","v":[[-3.461538,0.076923],[-2.307692,0.461538],[-2.153846,2.538462],[-2.461538,2.538462]],"s":1},{"type":"po","v":[[-4.153846,2],[-2.461538,2],[-2.230769,2.538462],[-4.230769,2.230769]],"s":1},{"type":"ci","r":0.769231,"c":[-2.692308,0]},{"type":"po","v":[[-4.615385,1.538462],[-3.923077,1.692308],[-3.846154,4.461538]],"s":1},{"type":"ci","r":0.692308,"c":[-2.692308,0]},{"type":"ci","r":0.461538,"c":[-2.692308,0]},{"type":"po","v":[[-3.769231,2.153846],[-3.230769,2.076923],[-2.923077,2.230769],[-2.769231,2.230769],[-2.615385,2.076923],[-2.538462,1.846154],[-2.692308,1.615385],[-2.923077,1.230769],[-2.923077,1],[-2.769231,1.307692],[-2.538462,1.692308],[-2.538462,1.076923],[-2.461538,1],[-2.461538,1.615385],[-2.384615,2.076923],[-2.307692,2.230769],[-2.307692,2.461538],[-2.615385,2.307692],[-2.692308,2.307692],[-3.153846,2.307692],[-3.384615,2.153846],[-4.076923,2.230769]],"s":1},{"type":"bx","w":6.230769,"h":0.692308,"c":[0.076923,0.153846],"a":1.346773},{"type":"po","v":[[-0.6,-2],[-0.846154,-2],[-1.215385,-2.676923],[-1.153846,-2.923077],[-0.907692,-3.046154],[-0.6,-3.169231],[-0.353846,-3.169231],[-0.230769,-3.107692],[-0.169231,-3.107692],[-0.107692,-2.923077],[-0.107692,-2.8],[-0.046154,-2.615385],[0.015385,-2.984615],[0.2,-2.984615],[0.323077,-2.861538],[0.323077,-2.553846],[0.261538,-2.369231],[0.138462,-2.123077],[0.015385,-2.061538],[-0.169231,-1.938462]],"s":0.8,"c":[0.076923,0.153846]},{"type":"bx","w":0.384615,"h":0.076923,"c":[-0.846154,-2.846154],"a":1.19029},{"type":"bx","w":0.384615,"h":0.076923,"c":[-0.538462,-3],"a":1.19029},{"type":"po","v":[[0.384615,3.307692],[0.307692,2.769231],[0.153846,2.538462],[0.230769,2.384615],[0.076923,2.230769],[0.076923,2.076923],[0,1.846154],[-0.076923,1.615385],[-0.076923,1.461538],[-0.153846,1.153846],[-0.230769,1],[-0.153846,0.769231],[-0.307692,0.461538],[-0.384615,0.307692],[-0.307692,0.076923],[-0.461538,-0.076923],[-0.538462,-0.230769],[-0.461538,-0.538462],[-0.538462,-0.692308],[-0.538462,-0.923077],[-0.615385,-1.076923],[-0.692308,-1.230769],[-0.769231,-1.461538],[-0.769231,-1.538462],[-0.846154,-1.692308],[-0.769231,-1.846154],[-0.769231,-1.923077],[-0.692308,-1.923077],[-0.615385,-1.769231]],"s":1,"c":[-0.076923,0.076923]},{"type":"po","v":[[-0.076923,-2.153846],[0,-1.615385],[0.153846,-1.384615],[0.076923,-1.230769],[0.230769,-1.076923],[0.230769,-0.923077],[0.307692,-0.692308],[0.384615,-0.461538],[0.384615,-0.307692],[0.461538,0],[0.538462,0.153846],[0.461538,0.384615],[0.615385,0.692308],[0.692308,0.846154],[0.615385,1.076923],[0.769231,1.230769],[0.846154,1.384615],[0.769231,1.692308],[0.846154,1.846154],[0.846154,2.076923],[0.923077,2.230769],[1,2.384615],[1.076923,2.615385],[1.076923,2.692308],[1.153846,2.846154],[1.076923,3],[1.076923,3.076923],[1,3.076923],[0.923077,2.923077]],"s":1,"a":3.141593,"c":[0.384615,1.076923]},{"type":"bx","w":6.230769,"h":0.692308,"c":[0.076923,0.153846],"a":1.346773},{"type":"po","v":[[-0.6,-2],[-0.846154,-2],[-1.215385,-2.676923],[-1.153846,-2.923077],[-0.907692,-3.046154],[-0.6,-3.169231],[-0.353846,-3.169231],[-0.230769,-3.107692],[-0.169231,-3.107692],[-0.107692,-2.923077],[-0.107692,-2.8],[-0.046154,-2.615385],[0.015385,-2.984615],[0.2,-2.984615],[0.323077,-2.861538],[0.323077,-2.553846],[0.261538,-2.369231],[0.138462,-2.123077],[0.015385,-2.061538],[-0.169231,-1.938462]],"s":0.8,"c":[0.076923,0.153846]},{"type":"bx","w":0.384615,"h":0.076923,"c":[-0.846154,-2.846154],"a":1.19029},{"type":"bx","w":0.384615,"h":0.076923,"c":[-0.538462,-3],"a":1.19029},{"type":"po","v":[[0.384615,3.307692],[0.307692,2.769231],[0.153846,2.538462],[0.230769,2.384615],[0.076923,2.230769],[0.076923,2.076923],[0,1.846154],[-0.076923,1.615385],[-0.076923,1.461538],[-0.153846,1.153846],[-0.230769,1],[-0.153846,0.769231],[-0.307692,0.461538],[-0.384615,0.307692],[-0.307692,0.076923],[-0.461538,-0.076923],[-0.538462,-0.230769],[-0.461538,-0.538462],[-0.538462,-0.692308],[-0.538462,-0.923077],[-0.615385,-1.076923],[-0.692308,-1.230769],[-0.769231,-1.461538],[-0.769231,-1.538462],[-0.846154,-1.692308],[-0.769231,-1.846154],[-0.769231,-1.923077],[-0.692308,-1.923077],[-0.615385,-1.769231]],"s":1,"c":[-0.076923,0.076923]},{"type":"po","v":[[-0.076923,-2.153846],[0,-1.615385],[0.153846,-1.384615],[0.076923,-1.230769],[0.230769,-1.076923],[0.230769,-0.923077],[0.307692,-0.692308],[0.384615,-0.461538],[0.384615,-0.307692],[0.461538,0],[0.538462,0.153846],[0.461538,0.384615],[0.615385,0.692308],[0.692308,0.846154],[0.615385,1.076923],[0.769231,1.230769],[0.846154,1.384615],[0.769231,1.692308],[0.846154,1.846154],[0.846154,2.076923],[0.923077,2.230769],[1,2.384615],[1.076923,2.615385],[1.076923,2.692308],[1.153846,2.846154],[1.076923,3],[1.076923,3.076923],[1,3.076923],[0.923077,2.923077]],"s":1,"a":3.141593,"c":[0.384615,1.076923]}],"fixtures":[{"sh":0,"f":5209260,"np":true},{"sh":1,"f":10526810,"np":true},{"sh":2,"f":8538458,"np":true},{"sh":3,"f":8538458,"np":true,"ng":true},{"sh":4,"fp":false,"f":10526810,"np":true,"ng":true},{"sh":5,"f":8816203,"np":true,"ng":true},{"sh":6,"f":7027779,"np":true,"ng":true},{"sh":7,"f":15526614,"d":true},{"sh":8,"f":12892543,"np":true,"ng":true},{"sh":9,"f":12892543,"np":true,"ng":true},{"sh":10,"f":12892543,"np":true,"ng":true},{"sh":11,"f":4548502,"np":true,"ng":true},{"sh":12,"f":11833784,"np":true,"ng":true},{"sh":13,"f":15526614,"np":true,"ng":true},{"sh":14,"f":2760724,"np":true,"ng":true},{"sh":15,"f":14460962,"np":true,"ng":true},{"sh":16,"f":3088918,"np":true,"ng":true},{"sh":17,"f":2760724,"np":true,"ng":true},{"sh":18,"f":14145495,"np":true,"ng":true},{"sh":19,"f":14145495},{"sh":20,"f":5139598},{"sh":21,"f":6896453,"np":true,"ng":true},{"sh":22,"f":12892543,"np":true,"ng":true},{"sh":23,"f":8538458,"np":true,"ng":true},{"sh":24,"f":11513169,"np":true,"ng":true},{"sh":25,"f":6458008,"np":true},{"sh":26,"f":5139598},{"sh":27,"f":5139598},{"sh":28,"f":5139598,"np":true},{"sh":29,"f":14079173,"np":true,"ng":true},{"sh":30,"f":14460706,"np":true},{"sh":31,"f":14460706,"np":true},{"sh":32,"f":8538458,"np":true},{"sh":33,"f":14460706,"np":true,"ng":true},{"sh":34,"f":14460706,"np":true,"ng":true},{"sh":35,"f":14460706,"np":true,"ng":true},{"sh":36,"f":9539985,"ng":true},{"sh":37,"f":12892543,"np":true,"ng":true},{"sh":38,"f":5139598,"np":true,"ng":true},{"sh":39,"f":9392219,"np":true,"ng":true},{"sh":40,"f":6645093,"ng":true},{"sh":41,"f":5209260},{"sh":42,"f":14670520,"ng":true},{"sh":43,"f":14670520,"ng":true},{"sh":44,"f":12216656,"np":true,"ng":true},{"sh":45,"f":12216656,"np":true,"ng":true},{"sh":46,"f":9343374,"ng":true},{"sh":47,"f":6645093,"np":true,"ng":true},{"sh":48,"f":4737096,"ng":true},{"sh":49,"f":4737096,"ng":true},{"sh":50,"f":4737096,"ng":true},{"sh":51,"f":4737096,"ng":true},{"sh":52,"f":4737096},{"sh":53,"f":9539985,"ng":true},{"sh":54,"f":12892543,"np":true,"ng":true},{"sh":55,"f":5139598,"np":true,"ng":true},{"sh":56,"f":9392219,"np":true,"ng":true},{"sh":57,"f":6645093,"ng":true},{"sh":58,"f":5209260},{"sh":59,"f":14670520,"ng":true},{"sh":60,"f":14670520,"ng":true},{"sh":61,"f":12216656,"np":true,"ng":true},{"sh":62,"f":12216656,"np":true,"ng":true},{"sh":63,"f":9343374,"ng":true},{"sh":64,"f":6645093,"np":true,"ng":true},{"sh":65,"f":4737096,"ng":true},{"sh":66,"f":4737096,"ng":true},{"sh":67,"f":4737096,"ng":true},{"sh":68,"f":4737096,"ng":true},{"sh":69,"f":4737096},{"sh":70,"f":3815994,"ng":true},{"sh":71,"f":5263440,"np":true,"ng":true},{"sh":72,"f":5263440,"np":true,"ng":true},{"sh":73,"f":5263440,"np":true,"ng":true},{"sh":74,"f":5263440,"np":true,"ng":true},{"sh":75,"f":2697513,"np":true,"ng":true},{"sh":76,"f":1842204,"ng":true},{"sh":77,"f":4013373,"np":true,"ng":true},{"sh":78,"f":1842204,"np":true,"ng":true},{"sh":79,"f":12499880,"np":true,"ng":true},{"sh":80,"f":12499880,"np":true,"ng":true},{"sh":81,"f":12499880,"np":true,"ng":true},{"sh":82,"f":12499880,"np":true,"ng":true},{"sh":83,"f":12499880,"np":true,"ng":true},{"sh":84,"f":12499880,"np":true,"ng":true},{"sh":85,"f":1842204,"np":true,"ng":true},{"sh":86,"f":7356479,"np":true,"ng":true},{"sh":87,"f":8538458,"np":true,"ng":true},{"sh":88,"f":8538458,"np":true,"ng":true},{"sh":89,"f":8538458,"np":true,"ng":true},{"sh":90,"f":1842204,"np":true,"ng":true},{"sh":91,"f":14460706,"np":true,"ng":true},{"sh":92,"f":7027779,"np":true,"ng":true},{"sh":93,"f":5526612,"np":true,"ng":true},{"sh":94,"f":5855577,"np":true,"ng":true},{"sh":95,"f":6312581,"np":true},{"sh":96,"f":9343374,"np":true,"ng":true},{"sh":97,"f":6776679,"np":true,"ng":true},{"sh":98,"f":6776679,"np":true,"ng":true},{"sh":99,"f":9782870,"np":true,"ng":true},{"sh":100,"f":9782870,"np":true,"ng":true},{"sh":101,"f":5720954,"np":true,"ng":true},{"sh":102,"f":9782356,"np":true,"ng":true},{"sh":103,"f":9782356,"np":true,"ng":true},{"sh":104,"f":5720954,"np":true,"ng":true},{"sh":105,"f":5720954,"np":true,"ng":true},{"sh":106,"f":5720954,"np":true,"ng":true},{"sh":107,"f":5720954,"np":true,"ng":true},{"sh":108,"f":4548502,"np":true,"ng":true},{"sh":109,"f":11957184,"np":true,"ng":true},{"sh":110,"f":4548502,"np":true,"ng":true},{"sh":111,"f":5129842,"np":true,"ng":true},{"sh":112,"f":7881551,"np":true,"ng":true},{"sh":113,"f":4605510,"np":true,"ng":true},{"sh":114,"f":13341133,"ng":true},{"sh":115,"f":13341133,"d":true,"ng":true},{"sh":116,"f":12018615,"np":true},{"sh":117,"f":9848200,"np":true,"ng":true},{"sh":118,"f":6513507,"np":true},{"sh":119,"f":6513507},{"sh":120,"f":6513507},{"sh":121,"f":6513507},{"sh":122,"f":5329233,"np":true},{"sh":123,"f":6513507},{"sh":124,"f":6513507,"np":true,"ng":true},{"sh":125,"f":4737096,"np":true,"ng":true},{"sh":126,"f":9392219,"np":true,"ng":true},{"sh":127,"f":9392219,"np":true,"ng":true},{"sh":128,"f":9392219,"np":true},{"sh":129,"f":9392219,"np":true,"ng":true},{"sh":130,"f":6513507,"np":true},{"sh":131,"f":6513507},{"sh":132,"f":6513507},{"sh":133,"f":6513507},{"sh":134,"f":5329233,"np":true},{"sh":135,"f":6513507},{"sh":136,"f":6513507,"np":true,"ng":true},{"sh":137,"f":4737096,"np":true,"ng":true},{"sh":138,"f":9392219,"np":true,"ng":true},{"sh":139,"f":9392219,"np":true,"ng":true},{"sh":140,"f":9392219,"np":true,"ng":true},{"sh":141,"f":9392219,"np":true,"ng":true},{"sh":142,"f":6513507,"np":true},{"sh":143,"f":6513507},{"sh":144,"f":6513507},{"sh":145,"f":6513507},{"sh":146,"f":5329233,"np":true},{"sh":147,"f":6513507},{"sh":148,"f":6513507,"np":true,"ng":true},{"sh":149,"f":4737096,"np":true,"ng":true},{"sh":150,"f":9392219,"np":true,"ng":true},{"sh":151,"f":9392219,"np":true},{"sh":152,"f":9392219,"np":true,"ng":true},{"sh":153,"f":9392219,"np":true,"ng":true},{"sh":154,"f":5592405,"np":true},{"sh":155,"f":5592405},{"sh":156,"f":5592405},{"sh":157,"f":5592405},{"sh":158,"f":5329233,"np":true},{"sh":159,"f":5592405},{"sh":160,"f":5592405,"np":true,"ng":true},{"sh":161,"f":3881787,"np":true,"ng":true},{"sh":162,"f":7027779,"np":true,"ng":true},{"sh":163,"f":7027779,"np":true,"ng":true},{"sh":164,"f":5592405,"np":true},{"sh":165,"f":5592405},{"sh":166,"f":5592405},{"sh":167,"f":5592405},{"sh":168,"f":5329233,"np":true},{"sh":169,"f":5592405},{"sh":170,"f":5592405,"np":true,"ng":true},{"sh":171,"f":3881787,"np":true,"ng":true},{"sh":172,"f":7027779,"np":true,"ng":true},{"sh":173,"f":5592405,"np":true},{"sh":174,"f":5592405},{"sh":175,"f":5592405},{"sh":176,"f":5592405},{"sh":177,"f":5329233,"np":true},{"sh":178,"f":5592405},{"sh":179,"f":5592405,"np":true,"ng":true},{"sh":180,"f":3881787,"np":true,"ng":true},{"sh":181,"f":7027779,"np":true,"ng":true},{"sh":182,"f":4548502},{"sh":183,"f":12632699,"np":true,"ng":true},{"sh":184,"f":2502482,"np":true,"ng":true},{"sh":185,"f":2502482,"np":true,"ng":true},{"sh":186,"f":4548502,"np":true,"ng":true},{"sh":187,"f":4548502,"np":true,"ng":true},{"sh":188,"f":11957184},{"sh":189,"f":12632699,"np":true,"ng":true},{"sh":190,"f":2502482,"np":true,"ng":true},{"sh":191,"f":2502482,"np":true,"ng":true},{"sh":192,"f":11957184,"np":true,"ng":true},{"sh":193,"f":11957184,"np":true,"ng":true}],"joints":[{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":2,"bb":1,"ab":[-1.384615,-0.384615]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":3,"bb":2,"ab":[-2.076923,1.692308]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":4,"bb":3,"ab":[-2,-2.076923]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":5,"bb":1,"ab":[1.307692,-0.384615]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":6,"bb":5,"ab":[2.153846,1.692308]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":7,"bb":6,"ab":[1.846154,-2.076923]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":8,"bb":1,"ab":[-0.769231,4.230769]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":11,"bb":9,"ab":[6,1.230769]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":12,"bb":9,"ab":[4.076923,1.384615]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":13,"bb":9,"ab":[2.153846,1.769231]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":8,"bb":9,"ab":[-0.538462,-4.153846]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":9,"bb":8,"ab":[0.538462,4.153846]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":14,"bb":9,"ab":[-0.538462,1.769231]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":15,"bb":9,"ab":[2.153846,1.769231]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":16,"bb":9,"ab":[4.846154,1.769231]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":10,"bb":9,"aa":[2.461538,2.307692],"ab":[-5.846154,0.461538]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"em":true,"dl":true},"ba":17,"bb":9,"aa":[0.769231,3.076923],"ab":[-3.538462,2.153846]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"em":true,"dl":true},"ba":18,"bb":9,"aa":[0.769231,3.076923],"ab":[-4.692308,1.769231]},{"type":"lpj","d":{"dl":false},"ba":9,"pax":3.671303,"pay":2.886917,"pa":0,"pf":591.715976,"pl":0,"pu":0,"plen":30.769231,"pms":15.384615},{"type":"lpj","d":{"dl":false},"ba":0,"pax":3.902072,"pay":-7.420775,"pa":0,"pf":0,"pl":0,"pu":0,"plen":30.769231,"pms":0},{"type":"lpj","d":{"dl":false},"ba":1,"pax":3.902072,"pay":-5.497698,"pa":0,"pf":0,"pl":0,"pu":0,"plen":30.769231,"pms":0},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":0,"bb":1,"ab":[0,-1.923077]}]}`,
        },
        paragoomba1: {
            icon: '🪽',
            mapData: `{"bodies":[{"p":[0.09248,-0.01769],"a":0.004634,"av":0.359997,"lv":[0.130228,0.612445],"cf":{"y":-0.166667},"fx":[0,1,2,3,4,5,6],"s":{"type":"d","n":"Paragoomba1","fric":1,"re":1.5,"de":0.6,"ld":0,"ad":2,"f_c":2,"f_1":true,"f_2":true,"f_4":true},"id":0},{"p":[-1.069069,0.256367],"a":-0.574411,"av":-46.949465,"lv":[-19.42595,13.195916],"cf":{"x":-66.666667,"y":-40,"ct":-333.3},"fx":[7,8,9],"s":{"type":"d","n":"1\u2190","fric":1,"re":0,"de":0.3,"ld":0.5,"f_p":false},"id":1},{"p":[0.976589,-0.238677],"a":-0.651906,"av":-47.12389,"lv":[-0.425176,-29.624559],"cf":{"x":66.666667,"y":-40,"ct":-333.3},"fx":[10,11,12],"s":{"type":"d","n":"1\u2192","fric":1,"re":0,"de":0.3,"ld":0.5,"f_p":false},"id":2}],"shapes":[{"type":"po","v":[[-0.555556,0],[0.555556,0],[0.555556,0.833333],[0.277778,1.111111],[-0.277778,1.111111],[-0.555556,0.833333]],"s":0.5},{"type":"po","v":[[-0.277778,-0.972222],[0.277778,-0.972222],[0.972222,-0.277778],[0.972222,0.277778],[-0.972222,0.277778],[-0.972222,-0.277778]],"s":0.5},{"type":"po","v":[[-0.277778,-1.111111],[0.277778,-1.111111],[0.972222,-0.416667],[1.111111,-0.138889],[1.111111,0.277778],[0.972222,0.416667],[0.416667,0.416667],[0.277778,0.277778],[-0.277778,0.277778],[-0.416667,0.416667],[-0.972222,0.416667],[-1.111111,0.277778],[-1.111111,-0.138889],[-0.972222,-0.416667]],"s":0.5},{"type":"po","v":[[-0.555556,0],[-0.416667,0.138889],[-0.138889,0.138889],[-0.138889,-0.138889],[0.138889,-0.138889],[0.138889,0.138889],[0.416667,0.138889],[0.555556,0],[0.555556,-0.555556],[0.138889,-0.138889],[-0.138889,-0.138889],[-0.555556,-0.555556]],"s":0.5},{"type":"po","v":[[-0.416667,0],[-0.277778,0],[-0.277778,-0.138889],[0,0],[0.277778,-0.138889],[0.277778,0],[0.416667,0],[0.416667,-0.416667],[0.694444,-0.555556],[0.694444,-0.694444],[0.416667,-0.555556],[0.277778,-0.277778],[0,-0.138889],[-0.277778,-0.277778],[-0.416667,-0.555556],[-0.694444,-0.694444],[-0.694444,-0.555556],[-0.416667,-0.416667]],"s":0.5},{"type":"po","v":[[-0.972222,0.694444],[-0.833333,0.555556],[-0.555556,0.555556],[-0.138889,0.833333],[-0.138889,1.111111],[-0.833333,1.111111],[-0.972222,0.972222]],"s":0.5},{"type":"po","v":[[0.555556,0.555556],[0.833333,0.555556],[0.972222,0.694444],[0.972222,0.972222],[0.833333,1.111111],[0.138889,1.111111],[0.138889,0.833333]],"s":0.5},{"type":"ci","r":0.111111},{"type":"po","v":[[0.025804,0.418641],[0.222222,0.025804],[-0.072406,-0.465243],[-0.661661,-0.661661],[-0.563452,-0.563452],[-1.349126,-0.563452],[-1.447335,-0.268824],[-1.054498,-0.072406],[-0.759871,-0.170615],[-0.85808,-0.072406],[-0.759871,0.222222],[-0.268824,0.124013],[-0.563452,0.222222],[-0.661661,0.320431],[-0.563452,0.418641],[-0.268824,0.51685]],"s":0.5,"a":-0.785398,"c":[0.222222,0.222222]},{"type":"po","v":[[-0.759871,-0.367033],[-0.465243,-0.465243],[-0.072406,-0.268824],[0.124013,0.124013],[0.025804,0.222222],[-0.170615,-0.170615],[-0.563452,-0.367033]],"s":0.5,"a":-0.785398,"c":[0.222222,0.222222]},{"type":"ci","r":0.111111},{"type":"po","v":[[-0.025804,0.418641],[-0.222222,0.025804],[0.072406,-0.465243],[0.661661,-0.661661],[0.563452,-0.563452],[1.349126,-0.563452],[1.447335,-0.268824],[1.054498,-0.072406],[0.759871,-0.170615],[0.85808,-0.072406],[0.759871,0.222222],[0.268824,0.124013],[0.563452,0.222222],[0.661661,0.320431],[0.563452,0.418641],[0.268824,0.51685]],"s":0.5,"a":0.785398,"c":[-0.222222,0.222222]},{"type":"po","v":[[0.759871,-0.367033],[0.465243,-0.465243],[0.072406,-0.268824],[-0.124013,0.124013],[-0.025804,0.222222],[0.170615,-0.170615],[0.563452,-0.367033]],"s":0.5,"a":0.785398,"c":[-0.222222,0.222222]}],"fixtures":[{"sh":0,"n":"","re":1.2,"f":13933714,"d":true},{"sh":1,"n":"","f":9062454},{"sh":2,"n":"","f":9062454,"np":true},{"sh":3,"n":"","f":13933714,"np":true},{"sh":4,"n":"","f":4201497,"np":true},{"sh":5,"n":"","f":4201497,"np":true},{"sh":6,"n":"","f":4201497,"np":true},{"sh":7,"f":9062454,"ng":true},{"sh":8,"f":16181736,"np":true},{"sh":9,"f":14335904,"np":true},{"sh":10,"n":"","f":16181736,"ng":true},{"sh":11,"n":"","f":16181736,"np":true},{"sh":12,"n":"","f":14335904,"np":true}],"joints":[{"type":"d","d":{"fh":0.2,"dr":0,"cc":true,"dl":false},"ba":0,"ab":[49.444444,34.444444],"len":0.01},{"type":"rv","d":{"la":-0.349066,"ua":0.349066,"el":true,"dl":true},"ba":1,"bb":0,"aa":[0.444444,0.222222],"ab":[-0.666667,0.222222]},{"type":"rv","d":{"la":0.087266,"ua":0.523599,"el":true,"dl":true},"ba":2,"bb":0,"aa":[-0.444444,0.222222],"ab":[0.666667,0.222222]}]}`,
        },
        dinosaur: {
            icon: '🦖',
            mapData: `{"bodies":[{"p":[-0.166963,0.000642],"a":0.038541,"av":-0.21347,"lv":[-3.498799,0.921073],"cf":{"y":-0.833333},"fx":[0],"s":{"type":"d","n":"3b","fric":1.2,"fricp":true,"re":1.2,"de":2,"ad":99999,"f_c":3,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":0},{"p":[-0.167173,0.000742],"a":0.440622,"av":4.329418,"lv":[-3.404354,0.998349],"cf":{"x":0.033333,"y":-0.8,"ct":1.2},"fx":[1],"s":{"type":"d","n":"3_","fric":0.5,"re":1,"de":37.5,"ad":1,"f_c":3,"f_p":false,"f_4":true},"id":1},{"p":[-0.190703,0.054111],"a":0.079627,"av":0.717301,"lv":[-3.323711,0.70195],"cf":{"x":-0.5,"y":-0.666667},"fx":[2],"s":{"type":"d","n":"3t","fric":0.5,"fricp":true,"re":1.5,"de":1.2,"ad":5},"id":2},{"p":[-0.095109,-0.381115],"a":0.330692,"av":1.73582,"lv":[-1.698116,0.631898],"cf":{"y":-0.833333},"fx":[3,4,5],"s":{"type":"d","n":"3n","fric":0.5,"fricp":true,"re":2,"de":1,"f_c":3,"f_3":true,"f_4":true},"id":3},{"p":[1.003754,0.6153],"a":-0.247083,"av":-0.537534,"lv":[-0.181557,1.529212],"cf":{"x":0.166667,"y":-0.666667},"fx":[6,7],"s":{"type":"d","n":"3h","fric":0.5,"fricp":true,"re":3,"de":1,"ad":9999,"f_c":3,"f_1":true},"id":4},{"p":[-0.049324,-0.291256],"a":0.295008,"av":1.719486,"lv":[-0.696361,3.178165],"cf":{"x":-0.166667,"y":-0.666667},"fx":[8],"s":{"type":"d","n":"3j","fric":0.5,"fricp":true,"re":2,"de":1,"f_c":3},"id":5},{"p":[0.586498,0.356203],"a":-0.171909,"av":-0.243424,"lv":[-4.199077,4.440076],"cf":{"x":1,"y":5.166667},"fx":[9,10,11],"s":{"type":"d","n":"3pR","fric":9999,"fricp":true,"re":6,"de":1.2,"ad":999,"f_c":3,"f_1":true,"f_2":true,"f_4":true},"id":6},{"p":[-0.920981,-0.354629],"a":-0.073569,"av":0.677255,"lv":[-2.989602,-2.254908],"cf":{"x":1,"y":5.166667},"fx":[12,13,14],"s":{"type":"d","n":"3pL","fric":9999,"fricp":true,"re":6,"de":1.2,"ad":999,"f_c":3,"f_1":true,"f_2":true,"f_4":true},"id":7}],"shapes":[{"type":"po","v":[[-1.666667,-1],[0.333333,-1.333333],[1.333333,-1],[1.333333,0],[0.333333,0.333333],[-1.333333,0]],"s":1},{"type":"bx","w":1.666667,"h":0.333333},{"type":"po","v":[[-4,-1.333333],[-1,-1],[-0.666667,-0.333333],[-1.333333,0],[-3,-0.666667]],"s":1},{"type":"po","v":[[1.502838,-0.971528],[1.701834,-0.890745],[2.041593,-0.830837],[2.228385,-1.026371],[2.355232,-1.141087],[2.438655,-1.095915],[2.360477,-0.911691],[2.253612,-0.823915],[2.515157,-0.838723],[2.660237,-1.056844],[2.674117,-1.221942],[2.765354,-1.221086],[2.808814,-1.122034],[2.78712,-0.91262],[2.689817,-0.792694],[2.86619,-0.92914],[2.892238,-1.076862],[2.981727,-1.152471],[3.054731,-1.04821],[2.960925,-0.775354],[2.824515,-0.692786],[3.05912,-0.727576],[3.109502,-0.840544],[3.211158,-0.898776],[3.278953,-0.764971],[3.178189,-0.539036],[2.82976,-0.463391],[3.056551,-0.453864],[3.22165,-0.439984],[3.282486,-0.3531],[3.143472,-0.255761],[2.825443,-0.266144],[2.453535,-0.31649],[2.176326,-0.471989],[2.012084,-0.577106],[1.661015,-0.745629],[1.519359,-0.892458],[1.556717,-0.931565]],"s":0.18,"a":0.174533,"c":[1.25,-0.833333]},{"type":"po","v":[[1.666667,-2],[2,-2],[1.666667,-0.666667],[1.333333,0],[0.333333,0],[0.333333,-0.666667]],"s":1},{"type":"po","v":[[1.568198,-0.797978],[1.697834,-0.597631],[1.968892,-0.326573],[2.26352,-0.385499],[2.452082,-0.409069],[2.499222,-0.314788],[2.31066,-0.196937],[2.157454,-0.185152],[2.404941,-0.031946],[2.675999,-0.138012],[2.79385,-0.279433],[2.876346,-0.220507],[2.852775,-0.102656],[2.699569,0.07412],[2.534577,0.121261],[2.782065,0.109476],[2.899916,-0.008375],[3.029552,-0.020161],[3.029552,0.121261],[2.77028,0.309823],[2.593503,0.298038],[2.829205,0.415889],[2.947056,0.345178],[3.076693,0.356963],[3.053122,0.521955],[2.81742,0.663376],[2.452082,0.51017],[2.652428,0.663376],[2.79385,0.781227],[2.79385,0.899078],[2.605288,0.899078],[2.322445,0.686946],[2.016032,0.404104],[1.862826,0.085906],[1.78033,-0.114441],[1.568198,-0.491565],[1.532843,-0.715482],[1.591768,-0.727267]],"s":0.2,"a":0.785398,"c":[1.25,-0.833333]},{"type":"po","v":[[2,-2.666667],[2.666667,-2.666667],[3.333333,-2.333333],[3.666667,-2],[3.333333,-1.666667],[2,-1.666667],[1.666667,-2]],"s":1},{"type":"ci","r":0.083333,"c":[2.5,-2.333333]},{"type":"po","v":[[1.666667,-2],[2,-2.333333],[3.333333,-1.666667],[2.333333,-1.333333]],"s":1},{"type":"bx","w":1,"h":0.5,"c":[0.166667,1.666667]},{"type":"ci","r":0.333333},{"type":"po","v":[[0.166667,0.833333],[-0.166667,1.833333],[0.166667,1.833333],[0.5,1.166667],[0.833333,0.166667],[0.166667,-0.833333],[-0.5,-0.5]],"s":1,"c":[0,0.166667]},{"type":"bx","w":1,"h":0.5,"c":[0.166667,1.666667]},{"type":"ci","r":0.333333},{"type":"po","v":[[0.166667,0.833333],[-0.166667,1.833333],[0.166667,1.833333],[0.5,1.166667],[0.833333,0.166667],[0.166667,-0.833333],[-0.5,-0.5]],"s":1,"c":[0,0.166667]}],"fixtures":[{"sh":0,"f":3774562},{"sh":1,"f":3774562},{"sh":2,"f":3774562},{"sh":3,"f":3046489,"np":true},{"sh":4,"f":3774562},{"sh":5,"f":3774562,"np":true},{"sh":6,"f":3774562},{"sh":7,"f":256,"np":true},{"sh":8,"f":3774562},{"sh":9,"f":3774562},{"sh":10,"re":1,"f":3774562},{"sh":11,"f":3774562,"np":true},{"sh":12,"f":3774562},{"sh":13,"re":1,"f":3774562},{"sh":14,"f":3774562,"np":true}],"joints":[{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":1,"bb":0,"ab":[0,0]},{"type":"rv","d":{"la":-1.047198,"ua":0.523599,"el":true,"dl":false},"ba":2,"bb":0,"aa":[-1.333333,-0.5],"ab":[-1.333333,-0.5]},{"type":"rv","d":{"la":-1.047198,"ua":0.261799,"el":true,"dl":false},"ba":3,"bb":0,"aa":[1.333333,0],"ab":[1.333333,0]},{"type":"rv","d":{"la":0.261799,"ua":1.047198,"el":true,"dl":false},"ba":4,"bb":3,"aa":[1.666667,-2],"ab":[1.666667,-2]},{"type":"rv","d":{"la":-0.174533,"ua":0,"el":true,"dl":false},"ba":5,"bb":3,"aa":[2,-2],"ab":[2,-2]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":6,"bb":1,"ab":[0.833333,0]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":7,"bb":1,"ab":[-0.833333,0]}]}`,
        },
        goofy: {
            icon: '🤖',
            mapData: `{"bodies":[{"p":[-3.933333,4.486667],"cf":{"x":-0.166667,"y":-0.333333},"fx":[0,1,2,3],"s":{"type":"d","n":"leg 1","fric":0.5,"re":-1,"de":0.1,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":0},{"p":[-2.266667,1.153333],"cf":{"y":-1.666667},"fx":[4,5],"s":{"type":"d","n":"Leg 2-1","fric":0.5,"re":-1,"de":0.01,"f_2":true,"f_3":true,"f_4":true},"id":1},{"p":[4.4,1.153333],"cf":{"y":-1.666667},"fx":[6,7],"s":{"type":"d","n":"Leg 2-2","fric":0.5,"re":-1,"de":0.01,"f_2":true,"f_3":true,"f_4":true},"id":2},{"p":[1.066667,0.286667],"cf":{"y":-5},"fx":[8,9,10,11,12,13,14,15,16,17],"s":{"type":"d","n":"suit","fric":0.5,"re":-1,"de":0.01,"f_c":2,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":3},{"p":[-3.6,-1.713333],"cf":{"y":-0.666667},"fx":[18],"s":{"type":"d","n":"arm 1","fric":0.5,"re":-1,"de":0.01,"f_2":true,"f_3":true,"f_4":true},"id":4},{"p":[-8.266667,-2.713333],"av":10,"cf":{"y":-0.333333},"fx":[19,20,21,22,23,24,25,26,27],"s":{"type":"d","n":"gear","fric":0,"re":1,"de":0.01,"fr":true,"f_2":true,"f_3":true,"f_4":true},"id":5},{"p":[-6.933333,-2.713333],"cf":{"y":-0.666667},"fx":[28,29],"s":{"type":"d","n":"Arm 2-1","fric":0.5,"re":-1,"de":0.01,"f_2":true,"f_3":true,"f_4":true},"id":6},{"p":[6.066667,4.486667],"cf":{"x":0.166667,"y":-0.333333},"fx":[30,31,32,33],"s":{"type":"d","n":"leg 2","fric":0.5,"re":-1,"de":0.1,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":7},{"p":[5.733333,-1.713333],"cf":{"y":-0.666667},"fx":[34],"s":{"type":"d","n":"arm 2","fric":0.5,"re":-1,"de":0.01,"f_2":true,"f_3":true,"f_4":true},"id":8},{"p":[7.733333,-2.713333],"cf":{"y":-0.6},"fx":[35,36,37,38],"s":{"type":"d","n":"sword","fric":0.5,"re":5,"de":0.05,"f_2":true,"f_3":true,"f_4":true},"id":9}],"shapes":[{"type":"ci","r":0.333333,"c":[-0.8,-1.2]},{"type":"ci","r":0.333333,"c":[-0.8,0.266667]},{"type":"ci","r":0.333333,"c":[-0.8,1.733333]},{"type":"bx","w":5,"h":1.666667,"a":-1.570796},{"type":"bx","w":3.733333,"h":1.666667,"a":-0.463648},{"type":"ci","r":1,"c":[-1.666667,0.8]},{"type":"bx","w":3.733333,"h":1.666667,"a":0.463648},{"type":"ci","r":1,"c":[1.666667,0.8]},{"type":"ci","r":0.133333,"c":[0.8,0.8]},{"type":"ci","r":0.133333,"c":[-0.8,0.8]},{"type":"ci","r":0.133333,"c":[-0.8,-0.8]},{"type":"ci","r":0.133333,"c":[0.8,-0.8]},{"type":"bx","w":0.666667,"h":4.4,"c":[0,1.333333],"a":1.570796},{"type":"po","v":[[-3.333333,-1],[-2,-1.666667],[-2,1.666667],[-2.666667,1.666667]],"s":1},{"type":"po","v":[[2,-1.666667],[3.333333,-1],[2.666667,1.666667],[2,1.666667]],"s":1},{"type":"po","v":[[-2,-1.666667],[2,-1.666667],[2,-1],[-2,-1]],"s":1},{"type":"bx","w":2.333333,"h":4.666667,"c":[0.066667,-0.066667],"a":1.570796},{"type":"ci","r":1.133333},{"type":"bx","w":4.466667,"h":0.666667,"a":0.463648},{"type":"po","v":[[0,-1.066667],[0.533333,0],[0,1.066667],[-0.533333,0]],"s":0.8},{"type":"po","v":[[1.066667,0],[0,0.533333],[-1.066667,0],[0,-0.533333]],"s":0.8,"a":1.570796},{"type":"po","v":[[0.408196,-0.985471],[0.492736,0.204098],[-0.408196,0.985471],[-0.492736,-0.204098]],"s":0.8,"a":0.392699},{"type":"po","v":[[-0.408196,-0.985471],[0.492736,-0.204098],[0.408196,0.985471],[-0.492736,0.204098]],"s":0.8,"a":-0.392699},{"type":"po","v":[[-0.985471,0.408196],[-0.204098,-0.492736],[0.985471,-0.408196],[0.204098,0.492736]],"s":0.8,"a":-1.963495},{"type":"po","v":[[0.985471,0.408196],[-0.204098,0.492736],[-0.985471,-0.408196],[0.204098,-0.492736]],"s":0.8,"a":1.963495},{"type":"po","v":[[0.754247,-0.754247],[0.377124,0.377124],[-0.754247,0.754247],[-0.377124,-0.377124]],"s":0.8,"a":0.785398},{"type":"po","v":[[-0.754247,-0.754247],[0.377124,-0.377124],[0.754247,0.754247],[-0.377124,0.377124]],"s":0.8,"a":-0.785398},{"type":"ci","r":0.666667},{"type":"bx","w":2.666667,"h":0.666667},{"type":"ci","r":0.4,"c":[1.333333,0]},{"type":"ci","r":0.333333,"c":[0.8,-1.2]},{"type":"ci","r":0.333333,"c":[0.8,0.266667]},{"type":"ci","r":0.333333,"c":[0.8,1.733333]},{"type":"bx","w":5,"h":1.666667,"a":1.570796},{"type":"bx","w":4.466667,"h":0.666667,"a":-0.463648},{"type":"bx","w":0.666667,"h":0.6,"c":[0,-0.666667],"a":-1.570796},{"type":"ci","r":0.466667},{"type":"po","v":[[-0.466667,-6.2],[0,-7.133333],[0.466667,-6.2],[0.466667,-1.066667],[-0.466667,-1.066667]],"s":0.7,"c":[0,-0.133333]},{"type":"bx","w":2.666667,"h":0.333333,"c":[0,-1.066667]}],"fixtures":[{"sh":0,"f":65793,"d":true,"np":true},{"sh":1,"f":65793,"d":true,"np":true},{"sh":2,"f":65793,"d":true,"np":true},{"sh":3,"f":3559717},{"sh":4,"f":4212364},{"sh":5,"f":10987431},{"sh":6,"f":4212364},{"sh":7,"f":10987431},{"sh":8,"f":65793},{"sh":9,"f":65793},{"sh":10,"f":65793},{"sh":11,"f":65793},{"sh":12,"f":1612569},{"sh":13,"f":1612568},{"sh":14,"f":1612568},{"sh":15,"f":1612568},{"sh":16,"f":1612568,"np":true},{"sh":17,"f":9641495,"np":true},{"sh":18,"f":6104714},{"sh":19,"f":65793,"np":true},{"sh":20,"f":65793,"np":true},{"sh":21,"f":65793,"np":true},{"sh":22,"f":65793,"np":true},{"sh":23,"f":65793,"np":true},{"sh":24,"f":65793,"np":true},{"sh":25,"f":65793,"np":true},{"sh":26,"f":65793,"np":true},{"sh":27,"f":10197915,"d":true,"ng":true},{"sh":28,"f":12656192},{"sh":29,"f":6710886},{"sh":30,"f":65793,"d":true,"np":true},{"sh":31,"f":65793,"d":true,"np":true},{"sh":32,"f":65793,"d":true,"np":true},{"sh":33,"f":3559717},{"sh":34,"f":3776200},{"sh":35,"f":6052956,"np":true},{"sh":36,"f":10197915,"d":true},{"sh":37,"f":11908533,"d":true},{"sh":38,"f":13426479}],"joints":[{"type":"rv","d":{"la":0,"ua":1.570796,"el":true,"dl":true},"ba":1,"bb":0,"aa":[-1.666667,0.8],"ab":[0,-2.533333]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":3,"bb":2,"aa":[2.333333,0.333333],"ab":[-1,-0.533333]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":3,"bb":1,"aa":[-2.333333,0.333333],"ab":[1,-0.533333]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":4,"bb":3,"aa":[2,1],"ab":[-2.666667,-1]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":5,"bb":6,"ab":[-1.333333,0]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":6,"bb":4,"aa":[1.333333,0],"ab":[-2,-1]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":2,"bb":7,"aa":[1.666667,0.8],"ab":[0,-2.533333]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":false},"ba":8,"bb":3,"aa":[-2,1],"ab":[2.666667,-1]},{"type":"rv","d":{"la":0,"ua":0,"el":false,"dl":true},"ba":9,"bb":8,"ab":[2,-1]}]}`,
        },
    
        rat: {
            icon: '🐀',
            mapData: `{"bodies":[{"p":[-1.246744,-0.064346],"a":0.153588,"av":0.712022,"lv":[0.137246,1.750663],"cf":{"y":-0.666667},"fx":[0,1,2,3],"s":{"type":"d","n":"Rat Middle Body","fric":0.5,"re":-1,"de":0.025,"ld":0.25,"f_c":3,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":0},{"p":[0.73016,0.241595],"a":0.019189,"av":0.067691,"lv":[-0.073478,3.446366],"cf":{"y":-0.666667},"fx":[4,5,6,7],"s":{"type":"d","n":"Rat Back Body","fric":0.5,"re":-1,"de":0.025,"ld":5,"f_c":3,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":1},{"p":[5.729239,0.337533],"a":-0.30884,"av":-1.587096,"lv":[-0.834644,0.817897],"cf":{"y":-0.666667},"fx":[8,9,10,11],"s":{"type":"d","n":"Rat Tail","fric":0.5,"re":-1,"de":0,"ld":5,"f_c":4},"id":2},{"p":[-5.212654,-0.514782],"a":-0.008723,"av":-0.043531,"lv":[0.367115,-0.091747],"cf":{"y":-0.666667},"fx":[12,13,14,15,16,17,18,19,20,21,22],"s":{"type":"d","n":"Rat Head","fric":0.5,"re":-1,"de":0.025,"f_c":3,"f_1":true,"f_2":true,"f_3":true,"f_4":true},"id":3}],"shapes":[{"type":"po","v":[[-2.501722,1.861922],[-2.566427,1.62044],[-3.508669,1.484685],[-4.386206,1.590411],[-4.233113,1.678799],[-3.959279,1.734835],[-4.651371,2.04969],[-4.377537,2.105726],[-3.862222,2.097057],[-4.312833,2.347207],[-4.521962,2.532653],[-3.765165,2.459279],[-3.974294,2.644725],[-4.062682,2.797818],[-3.24985,2.450611]],"s":0.25,"a":-0.261799,"c":[-3.5,2]},{"type":"po","v":[[-2.566427,-1.62044],[-2.501722,-1.861922],[-3.24985,-2.450611],[-4.062682,-2.797818],[-3.974294,-2.644725],[-3.765165,-2.459279],[-4.521962,-2.532653],[-4.312833,-2.347207],[-3.862222,-2.097057],[-4.377537,-2.105726],[-4.651371,-2.04969],[-3.959279,-1.734835],[-4.233113,-1.678799],[-4.386206,-1.590411],[-3.508669,-1.484685]],"s":0.25,"a":0.261799,"c":[-3.5,-2]},{"type":"ci","r":1},{"type":"po","v":[[-4,-1],[-3,-2],[-1,-2.5],[1,-2.5],[2.5,-2],[3.5,-0.5],[3.5,0.5],[2.5,2],[1,2.5],[-1,2.5],[-3,2],[-4,1]],"s":1},{"type":"po","v":[[0.991305,2.818509],[0.916129,2.58008],[-0.031138,2.485553],[-0.903229,2.629456],[-0.746426,2.711083],[-0.470409,2.755121],[-1.148108,3.099865],[-0.872091,3.143903],[-0.357644,3.112765],[-0.796914,3.382332],[-0.997755,3.576723],[-0.244879,3.470409],[-0.44572,3.6648],[-0.527347,3.821602],[0.269568,3.43927]],"s":0.25,"a":-0.305433,"c":[0,3]},{"type":"po","v":[[0.916129,-2.58008],[0.991305,-2.818509],[0.269568,-3.43927],[-0.527347,-3.821602],[-0.44572,-3.6648],[-0.244879,-3.470409],[-0.997755,-3.576723],[-0.796914,-3.382332],[-0.357644,-3.112765],[-0.872091,-3.143903],[-1.148108,-3.099865],[-0.470409,-2.755121],[-0.746426,-2.711083],[-0.903229,-2.629456],[-0.031138,-2.485553]],"s":0.25,"a":0.305433,"c":[0,-3]},{"type":"ci","r":1},{"type":"po","v":[[-2.5,-1.5],[-1.5,-2.5],[0,-3],[3,-3],[4,-2.5],[5,-1],[5,1],[4,2.5],[3,3],[0,3],[-1.5,2.5],[-2.5,1.5],[-3,0]],"s":1},{"type":"po","v":[[0,-0.75],[1.875,-0.5625],[3.75,-0.5625],[4.875,-0.75],[5.625,-1.125],[6.75,-1.5],[8.0625,-1.6875],[9,-1.6875],[11.0625,-1.3125],[12.9375,-0.375],[13.6875,-0.1875],[15.9375,0],[13.3125,0.1875],[12.375,0],[10.875,-0.75],[8.625,-1.125],[10.875,-0.75],[9.1875,-0.9375],[8.0625,-0.9375],[6.75,-0.5625],[5.625,0.1875],[4.125,0.75],[0,0.75],[-0.375,0.375],[-0.375,-0.375]],"s":0.375},{"type":"po","v":[[-0.375,-0.375],[0,-0.75],[1.875,-0.75],[5.625,0],[1.875,0.75],[0,0.75],[-0.375,0.375]],"s":0.375},{"type":"po","v":[[13.3125,-0.1875],[15.9375,0],[13.3125,0.1875]],"s":0.375},{"type":"po","v":[[13.3125,0.1875],[15.9375,0],[13.6875,-0.1875],[12.9375,-0.375],[11.0625,-1.3125],[9,-1.6875],[8.4375,-1.6875],[8.8125,-1.5],[7.875,-1.5],[6.9375,-1.3125],[8.4375,-1.125],[8.0625,-0.9375],[9.1875,-0.9375],[10.875,-0.75],[12.375,0]],"s":0.375},{"type":"po","v":[[-3.825,-0.225],[-3.6,-0.45],[-2.025,-1.35],[-0.675,-1.8],[0.9,-1.8],[1.8,-1.35],[2.25,-0.45],[2.25,0.45],[1.8,1.35],[0.9,1.8],[-0.675,1.8],[-2.025,1.35],[-3.6,0.45],[-3.825,0.225]],"s":0.45},{"type":"ci","r":1},{"type":"po","v":[[0.893237,-1.398444],[1.618951,-1.8776],[2.052983,-1.851548],[2.49655,-1.258649],[2.434911,-0.957432],[1.106763,-0.601556],[0.73437,-0.928825]],"s":0.275,"a":-0.261799,"c":[1,-1]},{"type":"po","v":[[0.73437,0.928825],[1.106763,0.601556],[2.434911,0.957432],[2.49655,1.258649],[2.052983,1.851548],[1.618951,1.8776],[0.893237,1.398444]],"s":0.275,"a":0.261799,"c":[1,1]},{"type":"po","v":[[-4.275,-0.225],[-4.05,-0.45],[-3.375,-0.45],[-2.925,-0.225],[-2.925,0.225],[-3.375,0.45],[-4.05,0.45],[-4.275,0.225]],"s":0.45},{"type":"po","v":[[-3.25,-0.3],[-3.55,-1.2],[-3.4,-2.4],[-2.8,-3.45],[-3.25,-2.25],[-3.4,-1.2],[-3.1,-0.45],[-2.95,-0.45],[-3.1,-1.35],[-2.65,-2.4],[-1.45,-3.75],[-2.5,-2.25],[-2.8,-1.35],[-2.8,-0.6],[-2.65,-0.6],[-2.5,-1.2],[-1.9,-2.25],[-0.1,-4.05],[-1.9,-1.95],[-2.35,-1.2],[-2.5,-0.6],[-2.8,-0.6],[-2.95,-0.45],[-3.1,-0.45]],"s":0.3,"c":[-1,0]},{"type":"po","v":[[-3.25,0.3],[-3.55,1.2],[-3.4,2.4],[-2.8,3.45],[-3.25,2.25],[-3.4,1.2],[-3.1,0.45],[-2.95,0.45],[-3.1,1.35],[-2.65,2.4],[-1.45,3.75],[-2.5,2.25],[-2.8,1.35],[-2.8,0.6],[-2.65,0.6],[-2.5,1.2],[-1.9,2.25],[-0.1,4.05],[-1.9,1.95],[-2.35,1.2],[-2.5,0.6],[-2.8,0.6],[-2.95,0.45],[-3.1,0.45]],"s":0.3,"c":[-1,0]},{"type":"po","v":[[-1.3125,-1],[-1.25,-1.125],[-1.1875,-1.1875],[-1,-1.25],[-0.8125,-1.1875],[-0.75,-1.125],[-0.625,-1],[-0.8125,-0.875],[-1.125,-0.8125],[-1.25,-0.875]],"s":0.125,"c":[-1,-1]},{"type":"po","v":[[-0.9375,-1.1875],[-0.8125,-1.125],[-0.75,-1.0625],[-0.8125,-1],[-0.875,-1.0625],[-1,-1.125]],"s":0.125,"c":[-1,-1]},{"type":"po","v":[[-1.3125,1],[-1.25,0.875],[-1.125,0.8125],[-0.8125,0.875],[-0.625,1],[-0.75,1.125],[-0.8125,1.1875],[-1,1.25],[-1.1875,1.1875],[-1.25,1.125]],"s":0.125,"c":[-1,1]},{"type":"po","v":[[-0.9375,1.1875],[-0.8125,1.125],[-0.75,1.0625],[-0.8125,1],[-0.875,1.0625],[-1,1.125]],"s":0.125,"c":[-1,1]}],"fixtures":[{"sh":0,"f":9930864,"np":true,"ng":true},{"sh":1,"f":9930864,"np":true,"ng":true},{"sh":2,"f":5209260,"np":true,"ng":true},{"sh":3,"f":2564124},{"sh":4,"f":9930864,"np":true,"ng":true},{"sh":5,"f":9930864,"np":true,"ng":true},{"sh":6,"f":5209260,"np":true,"ng":true},{"sh":7,"f":3024417},{"sh":8,"f":9930864,"np":true,"ng":true},{"sh":9,"de":0.025,"f":9930864},{"sh":10,"re":100,"f":9930864,"d":true},{"sh":11,"f":9076849,"np":true},{"sh":12,"f":3024417,"d":true},{"sh":13,"f":3024417,"np":true,"ng":true},{"sh":14,"f":9930864,"np":true,"ng":true},{"sh":15,"f":9930864,"np":true,"ng":true},{"sh":16,"re":1.5,"f":8549214,"d":true},{"sh":17,"f":13748672,"np":true},{"sh":18,"f":13748672,"np":true},{"sh":19,"f":65793,"np":true,"ng":true},{"sh":20,"f":16777215,"np":true},{"sh":21,"f":65793,"np":true,"ng":true},{"sh":22,"f":16777215,"np":true}],"joints":[{"type":"rv","d":{"la":-1.53589,"ua":1.53589,"el":true,"dl":false},"ba":1,"bb":0,"ab":[2,0]},{"type":"rv","d":{"la":-2.513274,"ua":2.513274,"el":true,"dl":false},"ba":2,"bb":1,"ab":[5,0]},{"type":"rv","d":{"la":-1.53589,"ua":1.53589,"el":true,"dl":false},"ba":3,"bb":0,"aa":[1,0],"ab":[-3,0]}]}`,
        },
    };

    // -- Category 2: Player skins ---
    const SUMMONABLES_SKIN = {
        metztlin: {
            icon: '😸',
            mapData: `{"bodies":[{"p":[21.160439,15.735299],"lv":[-5.116215,13.281316],"fx":[0,1,2,3,4,5,6],"s":{"type":"d","fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-1,-0.142857],[-0.714286,-0.190476],[-0.714286,-0.380952],[-0.428571,-0.571429],[-0.238095,-0.571429],[-0.428571,-0.380952],[-0.571429,0],[-0.333333,0.142857],[-0.190476,-0.047619],[-0.190476,-0.142857],[-0.047619,0.095238],[-0.095238,0.190476],[0.285714,-0.095238],[0.428571,-0.47619],[0.666667,-0.380952],[0.333333,0.142857],[0.428571,0.428571],[0.714286,0.190476],[0.809524,0.095238],[1,0.095238],[0.952381,0.380952],[0.714286,0.761905],[0.285714,1.047619],[-0.190476,1.095238],[-0.571429,0.952381],[-0.904762,0.619048],[-1.047619,0.238095],[-1,-0.333333]],"s":1,"c":[-0.047619,0.095238]},{"type":"po","v":[[-0.809524,0.714286],[-0.619048,0.47619],[-0.714286,0.285714],[-0.666667,0.047619],[-0.47619,-0.095238],[-0.47619,-0.47619],[-0.428571,-0.52381],[-0.333333,-0.571429],[-0.238095,-0.571429],[-0.142857,-0.47619],[0,-0.238095],[0.190476,-0.238095],[0.333333,-0.428571],[0.428571,-0.47619],[0.571429,-0.52381],[0.666667,-0.47619],[0.714286,-0.333333],[0.714286,-0.142857],[0.714286,-0.047619],[0.809524,0.190476],[0.761905,0.285714],[0.714286,0.428571],[0.714286,0.52381],[0.571429,0.571429],[0.428571,0.619048],[0.380952,0.666667],[0.285714,0.666667],[0.285714,1.047619],[-0.190476,1.095238],[-0.571429,0.952381]],"s":1},{"type":"po","v":[[-0.571429,0.47619],[-0.761905,0.52381],[-0.857143,0.52381],[-0.857143,0.47619],[-0.714286,0.380952],[-0.571429,0.380952],[-0.619048,0.285714],[-0.809524,0.285714],[-0.952381,0.285714],[-0.857143,0.190476],[-0.666667,0.142857],[-0.619048,0.142857],[0.666667,0.380952],[0.714286,0.380952],[0.809524,0.428571],[0.857143,0.47619],[0.857143,0.52381],[0.666667,0.47619],[0.619048,0.47619],[0.619048,0.571429],[0.714286,0.619048],[0.761905,0.666667],[0.619048,0.714286],[0.47619,0.666667],[0.333333,0.571429],[0,0.571429]],"s":1},{"type":"po","v":[[-0.457143,0.32381],[-0.380952,0.133333],[-0.304762,0.057143],[-0.152381,0.019048],[-0.07619,0.057143],[-0.038095,0.095238],[-0.190476,0.133333],[-0.228571,0.209524],[-0.190476,0.285714],[-0.07619,0.361905],[0.038095,0.32381],[0.038095,0.438095],[-0.152381,0.438095],[-0.228571,0.4],[-0.380952,0.32381],[-0.419048,0.32381]],"s":0.8,"c":[0,0.095238]},{"type":"po","v":[[0.252381,0.395238],[0.295238,0.266667],[0.466667,0.266667],[0.466667,0.22381],[0.466667,0.138095],[0.42381,0.095238],[0.295238,0.095238],[0.380952,0.009524],[0.509524,-0.033333],[0.552381,0.009524],[0.595238,0.138095],[0.638095,0.22381],[0.638095,0.309524]],"s":0.9,"c":[-0.047619,0.095238]},{"type":"po","v":[[0.333333,0.666667],[0.190476,0.666667],[0.047619,0.666667],[-0.238095,0.619048],[-0.571429,0.52381],[-0.666667,0.52381],[-0.714286,0.619048],[-0.714286,0.666667],[-0.47619,0.761905],[-0.190476,0.809524],[0.047619,0.857143],[0.238095,0.857143],[0.333333,0.857143],[0.380952,0.761905]],"s":1}],"fixtures":[{"sh":0,"f":16777214},{"sh":1,"f":16742401,"np":true,"ng":true},{"sh":2,"f":257,"np":true,"ng":true},{"sh":3,"f":257,"np":true,"ng":true},{"sh":4,"f":16449532,"np":true,"ng":true},{"sh":5,"f":16449532,"np":true,"ng":true},{"sh":6,"f":11940918,"np":true,"ng":true}],"joints":[]}`,
        },

        mar10br0s: {
            icon: '🟦',
            mapData: `{"bodies":[{"p":[15.955499,12.564444],"lv":[-4.07173,12.215191],"fx":[0,1,2,3,4,5,6,7],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.904762,0.428571],[0.904762,0.428571],[0.952381,0.333333],[0.952381,0.238095],[1,0.142857],[1,0.095238],[1,0],[1,-0.047619],[1,-0.095238],[0.952381,-0.285714],[0.904762,-0.428571],[-0.904762,-0.428571],[-0.904762,-0.333333],[-0.952381,-0.333333],[-0.952381,-0.238095],[-1,-0.190476],[-1,-0.095238],[-1,-0.047619],[-1,0.047619]],"s":1},{"type":"po","v":[[-0.047619,0.380952],[-0.095238,0.142857],[-0.285714,0.333333],[-0.190476,0],[-0.380952,0],[-0.190476,-0.095238],[-0.238095,-0.333333],[-0.047619,-0.142857],[0.095238,-0.428571],[0.095238,-0.095238],[0.380952,-0.285714],[0.285714,-0.047619],[0.428571,0],[0.238095,0.095238],[0.380952,0.333333],[0.190476,0.142857],[0.142857,0.380952]],"s":1},{"type":"po","v":[[-0.095238,0.047619],[-0.095238,0.380952],[0.142857,0.142857],[0.238095,0.380952],[0.238095,0]],"s":1},{"type":"po","v":[[-0.842857,0.209524],[-0.633333,0.104762],[-0.47619,0.104762],[-0.371429,0.052381],[-0.214286,0.104762],[-0.109524,0.157143],[-0.057143,0.209524],[-0.057143,-0.052381],[-0.057143,-0.104762],[-0.161905,-0.209524],[-0.214286,-0.261905],[-0.371429,-0.366667],[-0.528571,-0.366667],[-0.685714,-0.314286],[-0.790476,-0.209524],[-0.895238,0]],"s":1.1,"c":[0.047619,0]},{"type":"po","v":[[0.157143,0.209524],[0.366667,0.104762],[0.52381,0.104762],[0.628571,0.052381],[0.785714,0.104762],[0.890476,0.157143],[0.942857,0.209524],[0.942857,-0.052381],[0.942857,-0.104762],[0.838095,-0.209524],[0.785714,-0.261905],[0.628571,-0.366667],[0.471429,-0.366667],[0.314286,-0.314286],[0.209524,-0.209524],[0.104762,0]],"s":1.1,"c":[1.047619,0]},{"type":"po","v":[[0.190476,0.169048],[0.380952,0.07381],[0.52381,0.07381],[0.619048,0.02619],[0.761905,0.07381],[0.857143,0.121429],[0.904762,0.169048],[0.904762,-0.069048],[0.904762,-0.116667],[0.809524,-0.211905],[0.761905,-0.259524],[0.619048,-0.354762],[0.47619,-0.354762],[0.333333,-0.307143],[0.238095,-0.211905],[0.142857,-0.021429]],"s":1,"c":[1,-0.021429]},{"type":"po","v":[[-0.809524,0.169048],[-0.619048,0.07381],[-0.47619,0.07381],[-0.380952,0.02619],[-0.238095,0.07381],[-0.142857,0.121429],[-0.095238,0.169048],[-0.095238,-0.069048],[-0.095238,-0.116667],[-0.190476,-0.211905],[-0.238095,-0.259524],[-0.380952,-0.354762],[-0.52381,-0.354762],[-0.666667,-0.307143],[-0.761905,-0.211905],[-0.857143,-0.021429]],"s":1,"c":[0,-0.021429]}],"fixtures":[{"sh":0,"f":5414320},{"sh":1,"f":16711678,"np":true,"ng":true},{"sh":2,"f":13684329,"np":true,"ng":true},{"sh":3,"f":13684329,"np":true},{"sh":4,"f":1,"np":true},{"sh":5,"f":1,"np":true},{"sh":6,"f":16711679,"np":true,"ng":true},{"sh":7,"f":16711679,"np":true,"ng":true}],"joints":[]}`,
        },

        toxix: {
            icon: '🌅',
            mapData: `{"bodies":[{"p":[20.43442,12.232996],"lv":[3.903084,11.709252],"fx":[0,1,2,3,4,5,6,7,8,9,10],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.142857,0.857143],[-0.47619,0.714286],[-0.571429,0.571429],[-0.714286,0.380952],[-0.761905,0.142857],[-0.809524,-0.047619],[-0.714286,-0.380952],[-0.52381,-0.619048],[-0.380952,-0.714286],[-0.095238,-0.761905],[0,-0.761905],[0.095238,-0.571429],[-0.095238,-0.47619],[-0.238095,-0.428571],[-0.047619,-0.333333],[0.047619,-0.190476],[-0.285714,-0.238095],[-0.380952,-0.142857],[-0.52381,0],[-0.238095,0.142857],[-0.142857,0.095238],[0,0.047619],[0.142857,0],[0.190476,0.238095],[0.47619,0.238095],[-0.095238,0.52381],[-0.047619,0.428571],[0.428571,0.619048],[0.380952,0.761905],[0.285714,0.809524]],"s":1},{"type":"po","v":[[-0.761905,0],[-0.761905,0.285714],[-0.666667,0.428571],[-0.619048,0.619048],[-0.428571,0.761905],[-0.238095,0.809524],[-0.047619,0.857143],[0.047619,0.761905],[0.190476,0.809524],[0.238095,0.714286],[0.190476,0.619048],[-0.047619,0.666667],[-0.047619,0.619048],[-0.142857,0.571429],[0,0.571429],[0.047619,0.47619],[-0.047619,0.571429],[-0.238095,0.47619],[0.142857,0.380952],[0,0.333333],[0.190476,0.333333],[0.047619,0.190476],[-0.095238,0.190476],[-0.190476,0.190476],[-0.047619,0.095238],[-0.190476,0],[0,0.095238],[-0.428571,0.190476],[-0.190476,-0.047619],[-0.238095,-0.142857],[-0.285714,-0.095238],[-0.142857,-0.238095],[-0.285714,-0.285714],[-0.047619,-0.380952],[-0.238095,-0.571429],[-0.142857,-0.666667],[-0.285714,-0.47619],[-0.714286,-0.47619],[-0.619048,-0.333333],[-0.571429,-0.190476]],"s":1},{"type":"po","v":[[0.380952,0.809524],[0.095238,0.761905],[0.285714,0.619048],[0.190476,0.571429],[0.428571,0.47619],[0.380952,0.333333],[0.285714,0.380952],[0.047619,0.333333],[0.095238,0.190476],[0.333333,0.380952],[0.285714,0.190476],[0.333333,0.095238],[0.190476,0.285714],[-0.047619,0.190476],[0.095238,0],[-0.047619,-0.047619],[0.142857,-0.095238],[0.047619,-0.190476],[0.095238,-0.190476],[0.333333,-0.380952],[0.095238,-0.428571],[0.095238,-0.619048],[0.047619,-0.714286],[0.380952,-0.619048],[0.619048,-0.571429],[0.714286,-0.47619],[0.47619,-0.285714],[0.761905,-0.238095],[0.809524,-0.142857],[0.619048,-0.047619],[0.809524,-0.047619],[0.666667,-0.285714],[0.714286,-0.285714],[0.761905,-0.238095],[0.666667,-0.619048],[0.761905,-0.380952],[0.904762,-0.047619],[0.857143,0.285714],[0.714286,0.571429],[0.666667,0.666667]],"s":1},{"type":"po","v":[[0.52381,0.761905],[0.761905,0.619048],[0.904762,0.47619],[0.857143,0.190476],[0.857143,0.047619],[0.952381,-0.142857],[0.857143,-0.333333],[0.714286,-0.571429],[0.571429,-0.666667],[0.333333,-0.761905],[0.285714,-0.714286],[0.380952,-0.571429],[0.238095,-0.52381],[0.47619,-0.428571],[0.333333,-0.238095],[0.428571,-0.142857],[0.52381,-0.095238],[0.333333,0.047619],[0.238095,-0.095238],[0.333333,-0.238095],[0.285714,-0.095238],[0.333333,0.047619],[0.238095,0.142857],[0.52381,0.238095],[0.47619,0.333333],[0.714286,0.428571]],"s":1},{"type":"po","v":[[0.109524,0.188095],[0.319048,0.083333],[0.47619,0.083333],[0.580952,0.030952],[0.738095,0.083333],[0.842857,0.135714],[0.895238,0.188095],[0.895238,-0.07381],[0.895238,-0.12619],[0.790476,-0.230952],[0.738095,-0.283333],[0.580952,-0.388095],[0.42381,-0.388095],[0.266667,-0.335714],[0.161905,-0.230952],[0.057143,-0.021429]],"s":1.1,"c":[1,-0.021429]},{"type":"po","v":[[-0.795238,0.188095],[-0.585714,0.083333],[-0.428571,0.083333],[-0.32381,0.030952],[-0.166667,0.083333],[-0.061905,0.135714],[-0.009524,0.188095],[-0.009524,-0.07381],[-0.009524,-0.12619],[-0.114286,-0.230952],[-0.166667,-0.283333],[-0.32381,-0.388095],[-0.480952,-0.388095],[-0.638095,-0.335714],[-0.742857,-0.230952],[-0.847619,-0.021429]],"s":1.1,"c":[0.095238,-0.021429]},{"type":"po","v":[[-0.809524,0.190476],[-0.857143,0.047619],[-0.857143,-0.047619],[-0.857143,-0.142857],[-0.809524,-0.238095],[-0.809524,-0.285714],[-0.761905,-0.333333],[-0.666667,-0.333333],[-0.571429,-0.380952],[-0.761905,-0.333333],[-0.619048,-0.428571],[-0.52381,-0.428571],[-0.428571,-0.428571],[-0.380952,-0.47619],[-0.285714,-0.428571],[-0.238095,-0.380952],[-0.142857,-0.333333],[-0.095238,-0.285714],[-0.047619,-0.238095],[0,-0.142857],[0,-0.047619],[0,0.047619],[0,0.142857],[-0.047619,0.190476],[-0.047619,0.142857],[-0.095238,0.095238],[-0.238095,0.095238],[-0.380952,0.047619],[-0.571429,0.047619],[-0.761905,0.095238]],"s":1},{"type":"po","v":[[0.095238,0.190476],[0.047619,0.047619],[0.047619,-0.047619],[0.047619,-0.142857],[0.095238,-0.238095],[0.095238,-0.285714],[0.142857,-0.333333],[0.238095,-0.333333],[0.333333,-0.380952],[0.142857,-0.333333],[0.285714,-0.428571],[0.380952,-0.428571],[0.47619,-0.428571],[0.52381,-0.47619],[0.619048,-0.428571],[0.666667,-0.380952],[0.761905,-0.333333],[0.809524,-0.285714],[0.857143,-0.238095],[0.904762,-0.142857],[0.904762,-0.047619],[0.904762,0.047619],[0.904762,0.142857],[0.857143,0.190476],[0.857143,0.142857],[0.809524,0.095238],[0.666667,0.095238],[0.52381,0.047619],[0.333333,0.047619],[0.142857,0.095238]],"s":1,"c":[0.904762,0]},{"type":"po","v":[[-0.619048,0.52381],[-0.47619,0.285714],[-0.380952,0.285714],[-0.380952,0.142857],[-0.238095,0.333333],[-0.142857,0.142857],[-0.095238,0.142857],[-0.047619,0.238095],[0.095238,0.380952],[0,0.52381],[-0.095238,0.619048],[0.047619,0.666667],[0.095238,0.714286],[0.190476,0.666667],[0.190476,0.761905],[0.095238,0.761905],[-0.047619,0.761905],[-0.285714,0.857143],[-0.380952,0.714286],[-0.714286,0.666667]],"s":1},{"type":"po","v":[[-0.285714,0.761905],[-0.428571,0.666667],[-0.285714,0.52381],[-0.285714,0.47619],[-0.190476,0.47619],[-0.095238,0.380952],[0.095238,0.333333],[0,0.52381],[0.047619,0.571429],[0.095238,0.619048],[0,0.714286],[0.095238,0.809524]],"s":1}],"fixtures":[{"sh":0,"f":2764892},{"sh":1,"f":11177216,"np":true},{"sh":2,"f":16690176,"np":true},{"sh":3,"f":36248,"np":true},{"sh":4,"f":53473,"np":true},{"sh":5,"f":16711679,"np":true,"ng":true},{"sh":6,"f":16711679,"np":true,"ng":true},{"sh":7,"f":16711679,"np":true},{"sh":8,"f":16711679,"np":true},{"sh":9,"f":16742145,"np":true},{"sh":10,"f":15217664,"np":true}],"joints":[]}`,
        },

        luxdll: {
            icon: '🫤',
            mapData: `{"bodies":[{"p":[24.370155,12.906931],"lv":[2.215393,13.981326],"fx":[0,1,2,3,4,5],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.809524},{"type":"po","v":[[0.761905,0.428571],[-0.238095,0.190476],[0.857143,-0.095238],[0.857143,0.142857],[0.857143,0.190476]],"s":1},{"type":"ci","r":0.190476,"c":[-0.285714,-0.095238]},{"type":"ci","r":0.190476,"c":[0.428571,-0.285714]},{"type":"po","v":[[0.842857,-0.152381],[0.790476,0.42381],[-0.466667,0.266667]],"s":1.1,"c":[-0.047619,-0.047619]}],"fixtures":[{"sh":0,"f":1},{"sh":1,"f":15516416,"np":true},{"sh":2,"f":1,"np":true},{"sh":3,"f":1,"np":true},{"sh":4,"f":1,"np":true},{"sh":5,"f":1,"np":true,"ng":true}],"joints":[]}`,
        },

        keylitaa: {
            icon: '🟦',
            mapData: `{"bodies":[{"p":[9.404101,12.179998],"lv":[8.617458,2.585237],"fx":[0,1,2,3,4,5,6,7,8,9],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.904762,0.428571],[0.904762,0.428571],[0.952381,0.333333],[0.952381,0.238095],[1,0.142857],[1,0.095238],[1,0],[1,-0.047619],[1,-0.095238],[0.952381,-0.285714],[0.904762,-0.428571],[-0.904762,-0.428571],[-0.904762,-0.333333],[-0.952381,-0.333333],[-0.952381,-0.238095],[-1,-0.190476],[-1,-0.095238],[-1,-0.047619],[-1,0.047619]],"s":1},{"type":"po","v":[[-0.904762,0.380952],[-0.809524,0.619048],[-0.666667,0.714286],[0.714286,0.714286],[0.761905,0.619048],[0.904762,0.52381],[0.904762,0.333333],[0.904762,-0.428571],[0.809524,-0.571429],[0.666667,-0.714286],[-0.761905,-0.714286],[-0.809524,-0.619048],[-0.857143,-0.47619],[-0.952381,-0.333333],[-0.952381,0.095238]],"s":1},{"type":"po","v":[[-0.952381,-0.285714],[-0.761905,-0.238095],[-0.571429,-0.238095],[-0.47619,-0.285714],[-0.428571,-0.380952],[-0.428571,-0.52381],[-0.238095,-0.428571],[-0.142857,-0.428571],[-0.095238,-0.619048],[-0.095238,-0.714286],[-0.047619,-0.666667],[0.142857,-0.761905],[0.095238,-0.857143],[0.095238,-0.904762],[0.047619,-1],[-0.190476,-1],[-0.428571,-0.904762],[-0.619048,-0.761905],[-0.809524,-0.619048],[-0.857143,-0.47619]],"s":1},{"type":"ci","r":0.333333,"c":[0,0.047619]},{"type":"ci","r":0.309524,"c":[0,0.047619]},{"type":"ci","r":0.285714,"c":[0,0.047619]},{"type":"ci","r":0.047619,"c":[0.142857,-0.095238]},{"type":"po","v":[[-0.857143,0.047619],[-0.666667,-0.095238],[-0.428571,-0.142857],[-0.190476,-0.095238],[-0.047619,0],[-0.095238,0.047619],[-0.238095,-0.047619],[-0.428571,-0.095238],[-0.666667,-0.047619]],"s":1},{"type":"po","v":[[0.047619,0.047619],[0.238095,-0.095238],[0.47619,-0.142857],[0.714286,-0.095238],[0.857143,0],[0.809524,0.047619],[0.666667,-0.047619],[0.47619,-0.095238],[0.238095,-0.047619]],"s":1,"c":[0.904762,0]}],"fixtures":[{"sh":0,"f":5414320},{"sh":1,"f":16711678,"np":true,"ng":true},{"sh":2,"f":16711678,"np":true},{"sh":3,"f":16749814,"np":true},{"sh":4,"f":13684329,"np":true,"ng":true},{"sh":5,"f":16711678,"np":true,"ng":true},{"sh":6,"f":13684329,"np":true,"ng":true},{"sh":7,"f":16777214,"np":true,"ng":true},{"sh":8,"f":65537,"np":true},{"sh":9,"f":65537,"np":true}],"joints":[]}`,
        },

        wabiwabo: {
            icon: '🧊',
            mapData: `{"bodies":[{"p":[13.819857,13.078215],"lv":[-5.181136,15.208265],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.666667,"c":[-0.142857,0.047619]},{"type":"ci","r":0.619048,"c":[-0.142857,0.047619]},{"type":"po","v":[[-0.633333,-0.414286],[-0.685714,-0.309524],[-0.790476,-0.152381],[-0.790476,0.057143],[-0.738095,0.266667],[-0.633333,0.42381],[-0.47619,0.580952],[-0.161905,0.685714],[0.1,0.633333],[0.204762,0.580952],[-0.214286,0.371429],[-0.371429,0.266667],[-0.47619,0.057143],[-0.580952,-0.047619]],"s":1.1,"c":[0.047619,-0.047619]},{"type":"ci","r":0.428571,"c":[0.428571,0.285714]},{"type":"ci","r":0.380952,"c":[0.428571,0.285714]},{"type":"ci","r":0.238095,"c":[0.428571,0.285714]},{"type":"po","v":[[-0.428571,0.619048],[-0.52381,0.666667],[-0.571429,0.714286],[-0.52381,0.761905],[-0.47619,0.809524],[-0.333333,0.809524],[-0.238095,0.809524],[-0.095238,0.857143],[-0.047619,0.761905],[-0.095238,0.714286],[-0.142857,0.666667],[-0.190476,0.666667]],"s":1},{"type":"po","v":[[-0.380952,0.772857],[-0.238095,0.725238],[-0.285714,0.963333],[-0.428571,0.915714]],"s":1,"c":[0,0.010952]},{"type":"po","v":[[-0.761905,0.238095],[-0.952381,0.285714],[-1,0.047619],[-1,-0.190476],[-0.904762,-0.428571],[-0.761905,-0.666667],[-0.571429,-0.809524],[-0.428571,-0.52381],[-0.619048,-0.380952],[-0.761905,-0.190476],[-0.761905,0]],"s":1},{"type":"po","v":[[-0.795238,0.190476],[-0.795238,0.047619],[-0.795238,-0.142857],[-0.747619,-0.333333],[-0.604762,-0.47619],[-0.509524,-0.571429],[-0.652381,-0.761905],[-0.747619,-0.666667],[-0.890476,-0.47619],[-0.985714,-0.238095],[-0.985714,-0.047619],[-0.985714,0.142857]],"s":1,"c":[0.014286,0]},{"type":"po","v":[[-1,-0.095238],[-0.761905,-0.095238],[-0.761905,0],[-1,-0.047619]],"s":1},{"type":"po","v":[[-0.857143,-0.52381],[-0.809524,-0.571429],[-0.619048,-0.380952],[-0.666667,-0.333333]],"s":1},{"type":"po","v":[[-0.047619,0.047619],[-0.142857,0.142857],[-0.190476,0.142857],[-0.238095,0.047619],[-0.238095,-0.095238],[-0.285714,-0.238095],[-0.238095,-0.333333],[-0.142857,-0.333333],[-0.095238,-0.285714],[-0.047619,-0.190476]],"s":1,"c":[0,0.095238]},{"type":"po","v":[[0.242857,-0.090476],[0.157143,-0.004762],[0.114286,-0.004762],[0.071429,-0.090476],[0.071429,-0.219048],[0.028571,-0.347619],[0.071429,-0.433333],[0.157143,-0.433333],[0.2,-0.390476],[0.242857,-0.304762]],"s":0.9,"c":[0.285714,-0.047619]},{"type":"po","v":[[-0.380952,0.690476],[-0.47619,0.738095],[-0.285714,0.738095],[-0.190476,0.785714],[-0.142857,0.833333],[-0.095238,0.785714],[-0.095238,0.738095]],"s":1,"c":[0,0.02381]},{"type":"po","v":[[-0.333333,0.809524],[-0.285714,0.809524],[-0.380952,0.904762]],"s":1},{"type":"po","v":[[-0.238095,-0.285714],[-0.142857,-0.285714],[-0.095238,-0.238095],[-0.095238,-0.190476],[-0.095238,-0.142857],[-0.095238,-0.095238],[-0.095238,-0.047619],[-0.142857,-0.047619],[-0.190476,-0.095238]],"s":1},{"type":"po","v":[[0.071429,-0.380952],[0.166667,-0.380952],[0.214286,-0.333333],[0.214286,-0.285714],[0.214286,-0.238095],[0.214286,-0.190476],[0.214286,-0.142857],[0.166667,-0.142857],[0.119048,-0.190476]],"s":1,"c":[0.309524,-0.095238]}],"fixtures":[{"sh":0,"f":6661804},{"sh":1,"f":131843,"np":true,"ng":true},{"sh":2,"f":2204881,"np":true,"ng":true},{"sh":3,"f":1737125,"np":true},{"sh":4,"f":131843,"np":true,"ng":true},{"sh":5,"f":2205137,"np":true,"ng":true},{"sh":6,"f":131843,"np":true,"ng":true},{"sh":7,"f":131843,"np":true},{"sh":8,"f":131843,"np":true,"ng":true},{"sh":9,"f":131843,"np":true},{"sh":10,"f":2205137,"np":true},{"sh":11,"f":196611,"np":true},{"sh":12,"f":196611,"np":true,"ng":true},{"sh":13,"f":196611,"np":true},{"sh":14,"f":196611,"np":true},{"sh":15,"f":19776,"np":true},{"sh":16,"f":19776,"np":true,"ng":true},{"sh":17,"f":16318450,"np":true},{"sh":18,"f":16318450,"np":true}],"joints":[]}`,
        },

        megaquasar: {
            icon: '🪐',
            mapData: `{"bodies":[{"p":[26.295571,12.969706],"lv":[-5.961668,12.425137],"fx":[0,1,2,3,4,5,6],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[0,-0.285714],[-0.190476,-0.238095],[-0.380952,-0.142857],[-0.52381,-0.095238],[-0.619048,0.047619],[-0.666667,0.142857],[-0.714286,0.238095],[-0.714286,0.47619],[-0.619048,0.52381],[-0.380952,0.52381],[-0.142857,0.47619],[0.238095,0.380952],[0.619048,0.190476],[0.809524,0],[0.857143,-0.142857],[0.857143,-0.285714],[0.809524,-0.333333],[0.714286,-0.380952],[0.47619,-0.380952]],"s":1},{"type":"po","v":[[0,-0.257143],[-0.171429,-0.214286],[-0.342857,-0.128571],[-0.471429,-0.085714],[-0.557143,0.042857],[-0.6,0.128571],[-0.642857,0.214286],[-0.642857,0.428571],[-0.557143,0.471429],[-0.342857,0.471429],[-0.128571,0.428571],[0.214286,0.342857],[0.557143,0.171429],[0.728571,0],[0.771429,-0.128571],[0.771429,-0.257143],[0.728571,-0.3],[0.642857,-0.342857],[0.428571,-0.342857]],"s":0.9},{"type":"ci","r":0.52381,"c":[0.047619,0.047619]},{"type":"ci","r":0.47619,"c":[0.047619,0.047619]},{"type":"ci","r":0.380952,"c":[0.047619,0.047619]},{"type":"po","v":[[-0.238095,0.333333],[-0.238095,-0.238095],[-0.095238,-0.238095],[0.047619,-0.095238],[0.190476,-0.238095],[0.333333,-0.238095],[0.333333,0.333333],[0.190476,0.333333],[0.190476,-0.095238],[0.047619,0.047619],[-0.095238,-0.095238],[-0.095238,0.333333]],"s":1,"c":[-0.047619,0]}],"fixtures":[{"sh":0,"f":1},{"sh":1,"f":12189883,"np":true,"ng":true},{"sh":2,"f":196611,"np":true,"ng":true},{"sh":3,"f":12189883,"np":true,"ng":true},{"sh":4,"f":13369555,"np":true,"ng":true},{"sh":5,"f":13369569,"np":true,"ng":true},{"sh":6,"f":16038656,"np":true}],"joints":[]}`,
        },

        gameok: {
            icon: '🟢',
            mapData: `{"bodies":[{"p":[27.026885,10.304428],"lv":[-7.180758,4.734575],"fx":[0,1,2,3,4,5,6,7,8,9,10],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.952381},{"type":"po","v":[[-0.171429,0.1],[-0.380952,-0.004762],[-0.380952,-0.161905],[-0.380952,-0.319048],[-0.328571,-0.47619],[-0.27619,-0.580952],[-0.171429,-0.528571],[-0.119048,-0.47619],[-0.119048,-0.371429],[-0.119048,-0.214286]],"s":1.1,"c":[0.142857,0.047619]},{"type":"po","v":[[0.352381,0.1],[0.142857,-0.004762],[0.142857,-0.161905],[0.142857,-0.319048],[0.195238,-0.47619],[0.247619,-0.580952],[0.352381,-0.528571],[0.404762,-0.47619],[0.404762,-0.371429],[0.404762,-0.214286]],"s":1.1,"c":[0.666667,0.047619]},{"type":"po","v":[[-0.047619,0.190476],[-0.142857,0.142857],[-0.238095,0.142857],[-0.380952,0.190476],[-0.52381,0.190476],[-0.52381,0.142857],[-0.52381,0.190476],[-0.47619,0.285714],[-0.333333,0.285714],[-0.190476,0.285714],[-0.142857,0.285714],[0.380952,0.285714],[0.47619,0.285714],[0.52381,0.190476],[0.47619,0.190476],[0.333333,0.238095],[0.285714,0.190476],[0.190476,0.095238],[0.047619,0.095238]],"s":1},{"type":"po","v":[[-0.680952,0.319048],[0.785714,0.319048],[0.733333,0.528571],[0.628571,0.580952],[0.471429,0.738095],[0.261905,0.790476],[0.104762,0.842857],[-0.052381,0.842857],[-0.209524,0.790476],[-0.314286,0.790476],[-0.419048,0.738095],[-0.52381,0.633333],[-0.628571,0.580952]],"s":1.1,"c":[0,-0.047619]},{"type":"po","v":[[-0.557143,0.347619],[0.642857,0.347619],[0.6,0.519048],[0.514286,0.561905],[0.385714,0.690476],[0.214286,0.733333],[0.085714,0.77619],[-0.042857,0.77619],[-0.171429,0.733333],[-0.257143,0.733333],[-0.342857,0.690476],[-0.428571,0.604762],[-0.514286,0.561905]],"s":0.9,"c":[0,0.047619]},{"type":"po","v":[[-0.285714,-0.047619],[-0.285714,-0.142857],[-0.285714,-0.238095],[-0.285714,-0.333333],[-0.190476,-0.333333],[-0.142857,-0.238095],[-0.142857,-0.190476],[-0.142857,-0.142857],[-0.142857,-0.095238],[-0.190476,-0.047619]],"s":1,"c":[-0.047619,0.047619]},{"type":"po","v":[[0.142857,-0.047619],[0.142857,-0.142857],[0.142857,-0.238095],[0.142857,-0.333333],[0.238095,-0.333333],[0.285714,-0.238095],[0.285714,-0.190476],[0.285714,-0.142857],[0.285714,-0.095238],[0.238095,-0.047619]],"s":1,"c":[0.380952,0.047619]},{"type":"po","v":[[-0.078744,-0.514799],[-0.098267,-0.619473],[-0.163564,-0.737273],[-0.255113,-0.763525],[-0.405562,-0.757127],[-0.523362,-0.69183],[-0.582262,-0.65918],[-0.595387,-0.613406],[-0.477588,-0.678704],[-0.372913,-0.698227],[-0.222464,-0.704625],[-0.17669,-0.691499],[-0.098267,-0.619473]],"s":1,"a":0.279253,"c":[-0.190476,0.047619]},{"type":"po","v":[[0.635542,-0.514799],[0.616019,-0.619473],[0.550722,-0.737273],[0.459172,-0.763525],[0.308724,-0.757127],[0.190924,-0.69183],[0.132024,-0.65918],[0.118899,-0.613406],[0.236698,-0.678704],[0.341372,-0.698227],[0.491822,-0.704625],[0.537596,-0.691499],[0.616019,-0.619473]],"s":1,"a":0.279253,"c":[0.52381,0.047619]}],"fixtures":[{"sh":0,"f":1},{"sh":1,"f":55220,"np":true,"ng":true},{"sh":2,"f":16711678,"np":true},{"sh":3,"f":16711678,"np":true},{"sh":4,"f":66050,"np":true,"ng":true},{"sh":5,"f":14524500,"np":true},{"sh":6,"f":55219,"np":true},{"sh":7,"f":66050,"np":true},{"sh":8,"f":66050,"np":true},{"sh":9,"f":66050,"np":true},{"sh":10,"f":66050,"np":true}],"joints":[]}`,
        },

        invictox: {
            icon: '🦑',
            mapData: `{"bodies":[{"p":[21.852494,9.884395],"lv":[5.268736,12.390063],"fx":[0,1,2,3,4,5,6,7,8,9],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.952381},{"type":"ci","r":0.857143},{"type":"po","v":[[-0.619048,-0.666667],[-0.285714,-0.857143],[0.047619,-0.904762],[0.380952,-0.809524],[0.809524,-0.47619],[0.904762,-0.095238],[0.904762,0.142857],[-0.857143,0.142857],[-0.904762,-0.142857],[-0.809524,-0.380952]],"s":1},{"type":"po","v":[[-0.666667,0.571429],[-0.666667,0],[-0.52381,0],[-0.52381,0.714286],[-0.380952,0.857143],[-0.380952,0],[-0.238095,0],[-0.238095,0.857143],[-0.095238,0.857143],[-0.095238,0],[0.047619,0],[0.047619,0.857143],[0.190476,0.857143],[0.190476,0],[0.333333,0],[0.333333,0.857143],[0.47619,0.714286],[0.47619,-0.142857],[0.619048,-0.142857],[0.619048,0.714286],[0.761905,0.428571],[0.761905,-0.285714],[-0.666667,-0.285714]],"s":1},{"type":"bx","w":0.714286,"h":0.095238,"c":[-0.666667,0.238095],"a":-1.570796},{"type":"ci","r":0.333333,"c":[-0.380952,-0.333333]},{"type":"ci","r":0.238095,"c":[0.380952,-0.333333]},{"type":"ci","r":0.238095,"c":[-0.380952,-0.333333]},{"type":"ci","r":0.190476,"c":[0.380952,-0.333333]}],"fixtures":[{"sh":0,"f":14352578},{"sh":1,"f":1,"np":true},{"sh":2,"f":14352577,"np":true,"ng":true},{"sh":3,"f":1,"np":true,"ng":true},{"sh":4,"f":3339,"np":true},{"sh":5,"f":65793},{"sh":6,"f":14352577,"np":true,"ng":true},{"sh":7,"f":14352577,"np":true,"ng":true},{"sh":8,"f":16449532,"np":true,"ng":true},{"sh":9,"f":16449532,"np":true,"ng":true}],"joints":[]}`,
        },

        aleaaw: {
            icon: '😿',
            mapData: `{"bodies":[{"p":[19.75587,10.196142],"lv":[3.429879,10.289638],"fx":[0,1,2,3,4,5],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.714286,0.761905],[-0.666667,0.428571],[-0.571429,0.380952],[-0.47619,0.380952],[-0.428571,0.095238],[-0.380952,0],[-0.285714,0],[-0.095238,0.285714],[0.238095,0.285714],[0.380952,-0.047619],[0.47619,-0.047619],[0.571429,0.238095],[0.571429,0.47619],[0.714286,0.47619],[0.809524,0.571429],[0.809524,0.619048],[0.571429,0.857143],[0.333333,1],[-0.047619,1.047619],[-0.380952,1]],"s":1,"c":[0,0.047619]},{"type":"po","v":[[0.52381,0.333333],[0.714286,0.47619],[0.809524,0.571429],[0.047619,0.809524],[-0.666667,0.761905],[-0.809524,0.619048],[-0.619048,0.428571],[-0.52381,0.428571],[-0.619048,0.428571],[-0.428571,0.142857],[-0.095238,0.47619]],"s":1},{"type":"ci","r":0.047619,"c":[-0.238095,0.52381]},{"type":"ci","r":0.047619,"c":[0.380952,0.52381]},{"type":"po","v":[[-0.238095,0.761905],[-0.047619,0.714286],[0.047619,0.52381],[0.095238,0.714286],[0.285714,0.761905],[0.285714,0.857143],[0.095238,0.761905],[0.047619,0.619048],[0,0.809524]],"s":1,"c":[-0.047619,0]}],"fixtures":[{"sh":0,"f":15335518},{"sh":1,"f":65793,"np":true},{"sh":2,"f":65793,"np":true},{"sh":3,"f":15335518,"np":true,"ng":true},{"sh":4,"f":15335518,"np":true,"ng":true},{"sh":5,"f":15335518,"np":true}],"joints":[]}`,
        },

        eselweas: {
            icon: '☯️',
            mapData: `{"bodies":[{"p":[16.517577,10.476877],"lv":[-4.07173,12.215191],"fx":[0,1,2,3],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1,"c":[0.095238,0.095238]},{"type":"po","v":[[-0.952381,-0.095238],[-0.761905,-0.47619],[-0.571429,-0.714286],[-0.238095,-0.857143],[0.047619,-0.904762],[0.047619,1.095238],[-0.238095,1.047619],[-0.47619,0.952381],[-0.714286,0.761905],[-0.904762,0.428571]],"s":1,"c":[-0.047619,0]},{"type":"ci","r":0.238095,"c":[0.52381,0.095238]},{"type":"ci","r":0.238095,"c":[-0.428571,0.095238]}],"fixtures":[{"sh":0,"f":261},{"sh":1,"f":16449532,"np":true,"ng":true},{"sh":2,"f":16449532,"np":true,"ng":true},{"sh":3,"f":261,"np":true,"ng":true}],"joints":[]}`,
        },

        losu3w: {
            icon: '🟣',
            mapData: `{"bodies":[{"p":[14.394509,10.669639],"lv":[-2.054911,14.640831],"fx":[0,1],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.83381,-0.588571],[-0.588571,-0.882857],[-0.19619,-1.03],[0.245238,-0.980952],[0.588571,-0.83381],[0.735714,-0.686667],[-0.686667,0.784762],[-0.931905,0.539524],[-1.03,0.098095],[-1.03,-0.19619]],"s":1.03}],"fixtures":[{"sh":0,"f":11352739},{"sh":1,"f":7940471,"np":true}],"joints":[]}`,
        },

        luna: {
            icon: '🥺',
            mapData: `{"bodies":[{"p":[12,10.722222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.904762,0.428571],[0.904762,0.428571],[0.952381,0.333333],[0.952381,0.238095],[1,0.142857],[1,0.095238],[1,0],[1,-0.047619],[1,-0.095238],[0.952381,-0.285714],[0.904762,-0.428571],[-0.904762,-0.428571],[-0.904762,-0.333333],[-0.952381,-0.333333],[-0.952381,-0.238095],[-1,-0.190476],[-1,-0.095238],[-1,-0.047619],[-1,0.047619]],"s":1},{"type":"po","v":[[-0.904762,0.380952],[-0.809524,0.619048],[-0.666667,0.714286],[0.714286,0.714286],[0.761905,0.619048],[0.904762,0.52381],[0.904762,0.333333],[0.904762,-0.428571],[0.809524,-0.571429],[0.666667,-0.714286],[-0.761905,-0.714286],[-0.809524,-0.619048],[-0.857143,-0.47619],[-0.952381,-0.333333],[-0.952381,0.095238]],"s":1},{"type":"po","v":[[-0.952381,-0.285714],[-0.761905,-0.238095],[-0.571429,-0.238095],[-0.47619,-0.285714],[-0.428571,-0.380952],[-0.428571,-0.52381],[-0.238095,-0.428571],[-0.142857,-0.428571],[-0.095238,-0.619048],[-0.095238,-0.714286],[-0.047619,-0.666667],[0.142857,-0.761905],[0.095238,-0.857143],[0.095238,-0.904762],[0.047619,-1],[-0.190476,-1],[-0.428571,-0.904762],[-0.619048,-0.761905],[-0.809524,-0.619048],[-0.857143,-0.47619]],"s":1},{"type":"ci","r":0.333333,"c":[0,0.047619]},{"type":"ci","r":0.309524,"c":[0,0.047619]},{"type":"ci","r":0.285714,"c":[0,0.047619]},{"type":"ci","r":0.047619,"c":[0.142857,-0.095238]},{"type":"ci","r":0.333333,"c":[-0.428571,0.095238]},{"type":"ci","r":0.309524,"c":[-0.428571,0.095238]},{"type":"ci","r":0.285714,"c":[-0.428571,0.095238]},{"type":"ci","r":0.095238,"c":[-0.333333,0]},{"type":"ci","r":0.333333,"c":[0.428571,0.095238]},{"type":"ci","r":0.309524,"c":[0.428571,0.095238]},{"type":"ci","r":0.285714,"c":[0.428571,0.095238]},{"type":"ci","r":0.095238,"c":[0.52381,0]}],"fixtures":[{"sh":0,"f":5414320},{"sh":1,"f":16711678,"np":true,"ng":true},{"sh":2,"f":16711678,"np":true},{"sh":3,"f":16749814,"np":true},{"sh":4,"f":13684329,"np":true,"ng":true},{"sh":5,"f":16711678,"np":true,"ng":true},{"sh":6,"f":13684329,"np":true,"ng":true},{"sh":7,"f":16777214,"np":true,"ng":true},{"sh":8,"f":257,"np":true},{"sh":9,"f":15925239,"np":true},{"sh":10,"f":256,"np":true},{"sh":11,"f":16711679,"np":true},{"sh":12,"f":257,"np":true},{"sh":13,"f":15925239,"np":true},{"sh":14,"f":256,"np":true},{"sh":15,"f":16711679,"np":true}],"joints":[]}`,
        },

        loenil: {
            icon: '☢️',
            mapData: `{"bodies":[{"p":[9.159417,10.001045],"lv":[0.282511,-2.468646],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.666667,0.761905],[-0.333333,0.571429],[-0.380952,0.238095],[-0.142857,0.333333],[0,0.238095],[0,0.095238],[0.190476,0],[0.095238,-0.238095],[0.285714,-0.285714],[0.47619,-0.333333],[0.47619,-0.52381],[0.333333,-0.571429],[0.52381,-0.619048],[0.571429,-0.666667],[0.619048,-0.714286],[0.619048,-0.761905],[0.952381,-0.380952],[1,-0.047619],[0.952381,0.285714],[0.857143,0.571429],[0.571429,0.857143],[0.095238,1],[-0.190476,1],[-0.380952,0.904762]],"s":1},{"type":"ci","r":0.047619,"c":[-0.666667,0.666667]},{"type":"ci","r":0.047619,"c":[-0.52381,0.571429]},{"type":"ci","r":0.047619,"c":[-0.47619,0.428571]},{"type":"ci","r":0.047619,"c":[-0.47619,0.238095]},{"type":"ci","r":0.047619,"c":[-0.333333,0.190476]},{"type":"ci","r":0.047619,"c":[-0.190476,0.238095]},{"type":"ci","r":0.047619,"c":[-0.095238,0.142857]},{"type":"ci","r":0.047619,"c":[-0.047619,0]},{"type":"ci","r":0.047619,"c":[0.095238,-0.047619]},{"type":"ci","r":0.047619,"c":[0.047619,-0.190476]},{"type":"ci","r":0.047619,"c":[0.142857,-0.333333]},{"type":"ci","r":0.047619,"c":[0.285714,-0.380952]},{"type":"ci","r":0.047619,"c":[0.285714,-0.52381]},{"type":"ci","r":0.047619,"c":[0.285714,-0.666667]},{"type":"ci","r":0.047619,"c":[0.428571,-0.714286]},{"type":"ci","r":0.047619,"c":[0.238095,-0.809524]},{"type":"ci","r":0.047619,"c":[0.095238,-0.666667]},{"type":"ci","r":0.047619,"c":[0.095238,-0.47619]},{"type":"ci","r":0.047619,"c":[-0.047619,-0.380952]},{"type":"ci","r":0.047619,"c":[-0.095238,-0.190476]},{"type":"ci","r":0.047619,"c":[-0.238095,0]},{"type":"ci","r":0.047619,"c":[-0.428571,0.095238]},{"type":"ci","r":0.047619,"c":[-0.619048,0.238095]},{"type":"ci","r":0.047619,"c":[-0.714286,0.47619]},{"type":"po","v":[[-0.142857,0.095238],[-0.047619,0.190476],[0.047619,0.190476],[0.095238,0.142857],[0.142857,0.142857],[0.333333,0.809524],[0.190476,0.857143],[-0.142857,0.857143],[-0.333333,0.761905]],"s":1},{"type":"po","v":[[0.210619,0.006319],[0.227033,-0.127364],[0.168399,-0.202412],[0.101557,-0.210619],[0.07224,-0.248144],[0.480312,-0.808682],[0.605788,-0.725427],[0.811009,-0.462756],[0.853229,-0.254024]],"s":1,"a":-2.234021,"c":[0.047619,-0.047619]},{"type":"po","v":[[-0.034716,-0.218827],[-0.168399,-0.202412],[-0.227033,-0.127364],[-0.218827,-0.060522],[-0.248144,-0.022998],[-0.890753,-0.283341],[-0.840326,-0.425231],[-0.635105,-0.687902],[-0.442788,-0.779365]],"s":1,"a":2.234021,"c":[-0.047619,-0.047619]},{"type":"ci","r":0.166667,"c":[0,-0.047619]},{"type":"po","v":[[0.047619,0.095238],[-0.033193,0.095238],[0.007213,-0.02598]],"s":1.2,"a":2.356194,"c":[0.047619,0.095238]},{"type":"po","v":[[0.071429,-0.166667],[0.126542,-0.107565],[0.010332,-0.054445]],"s":1.2,"a":0.034907,"c":[0.071429,-0.166667]},{"type":"po","v":[[-0.119048,-0.119048],[-0.058992,-0.173122],[-0.00791,-0.056002]],"s":1.2,"a":-1.518436,"c":[-0.119048,-0.119048]},{"type":"po","v":[[0.142857,-0.190476],[0.238095,-0.428571],[0.333333,-0.52381],[0.52381,-0.47619],[0.571429,-0.380952],[0.619048,-0.238095],[0.666667,0],[0.666667,0.142857],[0.571429,0.285714],[0.380952,0.285714],[0.190476,0.190476],[0.142857,0.047619]],"s":1},{"type":"po","v":[[0.17619,-0.171429],[0.261905,-0.385714],[0.347619,-0.471429],[0.519048,-0.428571],[0.561905,-0.342857],[0.604762,-0.214286],[0.647619,0],[0.647619,0.128571],[0.561905,0.257143],[0.390476,0.257143],[0.219048,0.171429],[0.17619,0.042857]],"s":0.9,"c":[0.047619,0]},{"type":"bx","w":0.857143,"h":0.238095,"c":[-0.380952,-0.095238],"a":-0.785398},{"type":"bx","w":0.857143,"h":0.238095,"c":[-0.380952,-0.095238],"a":0.785398},{"type":"bx","w":0.809524,"h":0.190476,"c":[-0.380952,-0.095238],"a":-0.785398},{"type":"bx","w":0.809524,"h":0.190476,"c":[-0.380952,-0.095238],"a":0.785398},{"type":"bx","w":0.714286,"h":0.095238,"c":[-0.190476,0.666667]},{"type":"bx","w":0.714286,"h":0.047619,"c":[-0.095238,0.571429]},{"type":"bx","w":0.52381,"h":0.047619,"c":[0.47619,-0.52381]}],"fixtures":[{"sh":0,"f":16776037},{"sh":1,"f":15786752,"np":true},{"sh":2,"f":15787008,"np":true},{"sh":3,"f":15787008,"np":true},{"sh":4,"f":15787008,"np":true},{"sh":5,"f":15787008,"np":true},{"sh":6,"f":15787008,"np":true},{"sh":7,"f":15787008,"np":true},{"sh":8,"f":15787008,"np":true},{"sh":9,"f":15787008,"np":true},{"sh":10,"f":15787008,"np":true},{"sh":11,"f":15787008,"np":true},{"sh":12,"f":15787008,"np":true},{"sh":13,"f":15787008,"np":true},{"sh":14,"f":15787008,"np":true},{"sh":15,"f":15787008,"np":true},{"sh":16,"f":15787008,"np":true},{"sh":17,"f":16774198,"np":true},{"sh":18,"f":16774198,"np":true},{"sh":19,"f":16774198,"np":true},{"sh":20,"f":16774198,"np":true},{"sh":21,"f":16774198,"np":true},{"sh":22,"f":16774198,"np":true},{"sh":23,"f":16774198,"np":true},{"sh":24,"f":16774198,"np":true},{"sh":25,"f":16774198,"np":true},{"sh":26,"f":256,"np":true,"ng":true},{"sh":27,"f":256,"np":true,"ng":true},{"sh":28,"f":256,"np":true,"ng":true},{"sh":29,"f":256},{"sh":30,"f":15787008,"np":true},{"sh":31,"f":15787008,"np":true},{"sh":32,"f":15787008,"np":true},{"sh":33,"f":1797,"np":true,"ng":true},{"sh":34,"f":16711679,"np":true,"ng":true},{"sh":35,"f":770,"np":true,"ng":true},{"sh":36,"f":770,"np":true,"ng":true},{"sh":37,"f":16711679,"np":true,"ng":true},{"sh":38,"f":16711679,"np":true,"ng":true},{"sh":39,"f":16646138,"np":true,"ng":true},{"sh":40,"f":16646138,"np":true,"ng":true},{"sh":41,"f":16646138,"np":true,"ng":true}],"joints":[]}`,
        },

        fantasmita: {
            icon: '🌪️',
            mapData: `{"bodies":[{"p":[10.5,8.022222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.904762},{"type":"po","v":[[0.22381,0.891429],[0.124762,0.693333],[0.421905,0.544762],[0.570476,0.346667],[0.62,0.099048],[0.669524,-0.198095],[0.570476,-0.39619],[0.520952,-0.495238],[0.372381,-0.594286],[0.124762,-0.693333],[0.025714,-0.742857],[-0.271429,-0.693333],[-0.370476,-0.891429],[-0.073333,-0.940952],[0.22381,-0.891429],[0.520952,-0.742857],[0.719048,-0.544762],[0.818095,-0.39619],[0.917143,-0.099048],[0.917143,0.198095],[0.719048,0.544762],[0.570476,0.742857],[0.421905,0.792381]],"s":1.04,"c":[-0.02381,0]},{"type":"po","v":[[-0.22381,-0.891429],[-0.124762,-0.693333],[-0.421905,-0.544762],[-0.570476,-0.346667],[-0.62,-0.099048],[-0.669524,0.198095],[-0.570476,0.39619],[-0.520952,0.495238],[-0.372381,0.594286],[-0.124762,0.693333],[-0.025714,0.742857],[0.271429,0.693333],[0.370476,0.891429],[0.073333,0.940952],[-0.22381,0.891429],[-0.520952,0.742857],[-0.719048,0.544762],[-0.818095,0.39619],[-0.917143,0.099048],[-0.917143,-0.198095],[-0.719048,-0.544762],[-0.570476,-0.742857],[-0.421905,-0.792381]],"s":1.04,"a":3.141593,"c":[0.02381,0]},{"type":"po","v":[[-0.781905,0.22381],[-0.602857,0.134286],[-0.468571,0.402857],[-0.289524,0.537143],[-0.065714,0.581905],[0.202857,0.626667],[0.381905,0.537143],[0.471429,0.492381],[0.560952,0.358095],[0.650476,0.134286],[0.695238,0.044762],[0.650476,-0.22381],[0.829524,-0.313333],[0.874286,-0.044762],[0.829524,0.22381],[0.695238,0.492381],[0.51619,0.671429],[0.381905,0.760952],[0.113333,0.850476],[-0.155238,0.850476],[-0.468571,0.671429],[-0.647619,0.537143],[-0.692381,0.402857]],"s":0.94,"a":1.570796,"c":[0.02381,0]},{"type":"po","v":[[0.74381,-0.247619],[0.58381,-0.167619],[0.46381,-0.407619],[0.30381,-0.527619],[0.10381,-0.567619],[-0.13619,-0.607619],[-0.29619,-0.527619],[-0.37619,-0.487619],[-0.45619,-0.367619],[-0.53619,-0.167619],[-0.57619,-0.087619],[-0.53619,0.152381],[-0.69619,0.232381],[-0.73619,-0.007619],[-0.69619,-0.247619],[-0.57619,-0.487619],[-0.41619,-0.647619],[-0.29619,-0.727619],[-0.05619,-0.807619],[0.18381,-0.807619],[0.46381,-0.647619],[0.62381,-0.527619],[0.66381,-0.407619]],"s":0.84,"a":-1.570796,"c":[0.02381,-0.047619]},{"type":"po","v":[[-0.2,-0.634286],[-0.129524,-0.493333],[-0.340952,-0.387619],[-0.446667,-0.246667],[-0.481905,-0.070476],[-0.517143,0.140952],[-0.446667,0.281905],[-0.411429,0.352381],[-0.305714,0.422857],[-0.129524,0.493333],[-0.059048,0.528571],[0.152381,0.493333],[0.222857,0.634286],[0.011429,0.669524],[-0.2,0.634286],[-0.411429,0.528571],[-0.552381,0.387619],[-0.622857,0.281905],[-0.693333,0.070476],[-0.693333,-0.140952],[-0.552381,-0.387619],[-0.446667,-0.528571],[-0.340952,-0.56381]],"s":0.74,"a":3.141593,"c":[-0.02381,0]},{"type":"po","v":[[0.152381,0.634286],[0.081905,0.493333],[0.293333,0.387619],[0.399048,0.246667],[0.434286,0.070476],[0.469524,-0.140952],[0.399048,-0.281905],[0.36381,-0.352381],[0.258095,-0.422857],[0.081905,-0.493333],[0.011429,-0.528571],[-0.2,-0.493333],[-0.270476,-0.634286],[-0.059048,-0.669524],[0.152381,-0.634286],[0.36381,-0.528571],[0.504762,-0.387619],[0.575238,-0.281905],[0.645714,-0.070476],[0.645714,0.140952],[0.504762,0.387619],[0.399048,0.528571],[0.293333,0.56381]],"s":0.74,"c":[-0.02381,0]},{"type":"po","v":[[0.128571,0.548571],[0.067619,0.426667],[0.250476,0.335238],[0.341905,0.213333],[0.372381,0.060952],[0.402857,-0.121905],[0.341905,-0.24381],[0.311429,-0.304762],[0.22,-0.365714],[0.067619,-0.426667],[0.006667,-0.457143],[-0.17619,-0.426667],[-0.237143,-0.548571],[-0.054286,-0.579048],[0.128571,-0.548571],[0.311429,-0.457143],[0.433333,-0.335238],[0.494286,-0.24381],[0.555238,-0.060952],[0.555238,0.121905],[0.433333,0.335238],[0.341905,0.457143],[0.250476,0.487619]],"s":0.64,"c":[-0.02381,0]},{"type":"po","v":[[-0.2,-0.415238],[-0.148571,-0.312381],[-0.302857,-0.235238],[-0.38,-0.132381],[-0.405714,-0.00381],[-0.431429,0.150476],[-0.38,0.253333],[-0.354286,0.304762],[-0.277143,0.35619],[-0.148571,0.407619],[-0.097143,0.433333],[0.057143,0.407619],[0.108571,0.510476],[-0.045714,0.53619],[-0.2,0.510476],[-0.354286,0.433333],[-0.457143,0.330476],[-0.508571,0.253333],[-0.56,0.099048],[-0.56,-0.055238],[-0.457143,-0.235238],[-0.38,-0.338095],[-0.302857,-0.36381]],"s":0.54,"a":3.141593,"c":[-0.071429,0.047619]},{"type":"po","v":[[-0.435238,0.161905],[-0.34381,0.11619],[-0.275238,0.253333],[-0.18381,0.321905],[-0.069524,0.344762],[0.067619,0.367619],[0.159048,0.321905],[0.204762,0.299048],[0.250476,0.230476],[0.29619,0.11619],[0.319048,0.070476],[0.29619,-0.066667],[0.387619,-0.112381],[0.410476,0.024762],[0.387619,0.161905],[0.319048,0.299048],[0.227619,0.390476],[0.159048,0.43619],[0.021905,0.481905],[-0.115238,0.481905],[-0.275238,0.390476],[-0.366667,0.321905],[-0.389524,0.253333]],"s":0.48,"a":1.570796,"c":[-0.02381,0.047619]},{"type":"po","v":[[0.253646,0.276963],[0.173575,0.21342],[0.26889,0.093314],[0.292397,-0.018528],[0.264813,-0.131766],[0.22757,-0.265719],[0.147499,-0.329262],[0.107464,-0.361033],[0.025997,-0.373485],[-0.096901,-0.366617],[-0.147991,-0.368013],[-0.262626,-0.289338],[-0.342696,-0.352881],[-0.228062,-0.431556],[-0.094109,-0.468799],[0.059165,-0.464611],[0.180667,-0.420389],[0.251077,-0.377561],[0.350468,-0.272588],[0.408427,-0.148293],[0.393183,0.035355],[0.369675,0.147197],[0.317189,0.196892]],"s":0.48,"a":-0.436332,"c":[-0.02381,-0.047619]},{"type":"po","v":[[-0.349524,0.138095],[-0.277143,0.101905],[-0.222857,0.210476],[-0.150476,0.264762],[-0.06,0.282857],[0.048571,0.300952],[0.120952,0.264762],[0.157143,0.246667],[0.193333,0.192381],[0.229524,0.101905],[0.247619,0.065714],[0.229524,-0.042857],[0.301905,-0.079048],[0.32,0.029524],[0.301905,0.138095],[0.247619,0.246667],[0.175238,0.319048],[0.120952,0.355238],[0.012381,0.391429],[-0.09619,0.391429],[-0.222857,0.319048],[-0.295238,0.264762],[-0.313333,0.210476]],"s":0.38,"a":1.570796,"c":[-0.02381,0.047619]},{"type":"po","v":[[0.344762,-0.102381],[0.262857,-0.061429],[0.201429,-0.184286],[0.119524,-0.245714],[0.017143,-0.26619],[-0.105714,-0.286667],[-0.187619,-0.245714],[-0.228571,-0.225238],[-0.269524,-0.16381],[-0.310476,-0.061429],[-0.330952,-0.020476],[-0.310476,0.102381],[-0.392381,0.143333],[-0.412857,0.020476],[-0.392381,-0.102381],[-0.330952,-0.225238],[-0.249048,-0.307143],[-0.187619,-0.348095],[-0.064762,-0.389048],[0.058095,-0.389048],[0.201429,-0.307143],[0.283333,-0.245714],[0.30381,-0.184286]],"s":0.43,"a":-1.570796,"c":[-0.02381,0]},{"type":"po","v":[[-0.102381,-0.282857],[-0.070952,-0.22],[-0.165238,-0.172857],[-0.212381,-0.11],[-0.228095,-0.031429],[-0.24381,0.062857],[-0.212381,0.125714],[-0.196667,0.157143],[-0.149524,0.188571],[-0.070952,0.22],[-0.039524,0.235714],[0.054762,0.22],[0.08619,0.282857],[-0.008095,0.298571],[-0.102381,0.282857],[-0.196667,0.235714],[-0.259524,0.172857],[-0.290952,0.125714],[-0.322381,0.031429],[-0.322381,-0.062857],[-0.259524,-0.172857],[-0.212381,-0.235714],[-0.165238,-0.251429]],"s":0.33,"a":3.141593,"c":[-0.02381,0]},{"type":"po","v":[[0.054762,0.282857],[0.023333,0.22],[0.117619,0.172857],[0.164762,0.11],[0.180476,0.031429],[0.19619,-0.062857],[0.164762,-0.125714],[0.149048,-0.157143],[0.101905,-0.188571],[0.023333,-0.22],[-0.008095,-0.235714],[-0.102381,-0.22],[-0.13381,-0.282857],[-0.039524,-0.298571],[0.054762,-0.282857],[0.149048,-0.235714],[0.211905,-0.172857],[0.243333,-0.125714],[0.274762,-0.031429],[0.274762,0.062857],[0.211905,0.172857],[0.164762,0.235714],[0.117619,0.251429]],"s":0.33,"c":[-0.02381,0]},{"type":"po","v":[[-0.255238,0.064286],[-0.20381,0.038571],[-0.165238,0.115714],[-0.11381,0.154286],[-0.049524,0.167143],[0.027619,0.18],[0.079048,0.154286],[0.104762,0.141429],[0.130476,0.102857],[0.15619,0.038571],[0.169048,0.012857],[0.15619,-0.064286],[0.207619,-0.09],[0.220476,-0.012857],[0.207619,0.064286],[0.169048,0.141429],[0.117619,0.192857],[0.079048,0.218571],[0.001905,0.244286],[-0.075238,0.244286],[-0.165238,0.192857],[-0.216667,0.154286],[-0.229524,0.115714]],"s":0.27,"a":1.570796,"c":[-0.02381,0]},{"type":"po","v":[[0.207619,-0.064286],[0.15619,-0.038571],[0.117619,-0.115714],[0.06619,-0.154286],[0.001905,-0.167143],[-0.075238,-0.18],[-0.126667,-0.154286],[-0.152381,-0.141429],[-0.178095,-0.102857],[-0.20381,-0.038571],[-0.216667,-0.012857],[-0.20381,0.064286],[-0.255238,0.09],[-0.268095,0.012857],[-0.255238,-0.064286],[-0.216667,-0.141429],[-0.165238,-0.192857],[-0.126667,-0.218571],[-0.049524,-0.244286],[0.027619,-0.244286],[0.117619,-0.192857],[0.169048,-0.154286],[0.181905,-0.115714]],"s":0.27,"a":-1.570796,"c":[-0.02381,0]},{"type":"ci","r":0.190476,"c":[-0.02381,0]},{"type":"ci","r":0.095238,"c":[-0.02381,0]}],"fixtures":[{"sh":0,"f":16711679},{"sh":1,"f":12105912,"np":true},{"sh":2,"f":6776679,"np":true,"ng":true},{"sh":3,"f":5855577,"np":true,"ng":true},{"sh":4,"f":197379,"np":true,"ng":true},{"sh":5,"f":14013909,"np":true,"ng":true},{"sh":6,"f":9408399,"np":true,"ng":true},{"sh":7,"f":7039851,"np":true,"ng":true},{"sh":8,"f":3750201,"np":true,"ng":true},{"sh":9,"f":197379,"np":true,"ng":true},{"sh":10,"f":15132390,"np":true,"ng":true},{"sh":11,"f":8158332,"np":true,"ng":true},{"sh":12,"f":65793,"np":true,"ng":true},{"sh":13,"f":15724527,"np":true,"ng":true},{"sh":14,"f":9145227,"np":true,"ng":true},{"sh":15,"f":6184542,"np":true,"ng":true},{"sh":16,"f":11250603,"np":true,"ng":true},{"sh":17,"f":131586,"np":true,"ng":true},{"sh":18,"f":13027014,"np":true,"ng":true},{"sh":19,"f":8947848,"np":true,"ng":true}],"joints":[]}`,
        },

        swagggman: {
            icon: '🌀',
            mapData: `{"bodies":[{"p":[13.6,8.822222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1,"c":[0,-0.904762]},{"type":"ci","r":0.952381,"c":[0,-0.904762]},{"type":"po","v":[[-0.904762,-1.285714],[-0.666667,-1.333333],[-0.47619,-1.095238],[-0.238095,-1.047619],[0.047619,-1.142857],[0.095238,-1.428571],[0,-1.714286],[-0.285714,-1.666667],[-0.333333,-1.428571],[-0.190476,-1.380952],[-0.190476,-1.47619],[-0.095238,-1.47619],[-0.095238,-1.285714],[-0.190476,-1.190476],[-0.47619,-1.285714],[-0.47619,-1.380952],[-0.52381,-1.619048],[-0.571429,-1.714286],[-0.761905,-1.52381]],"s":1},{"type":"bx","w":0.809524,"h":0.047619,"c":[0.190476,-0.047619],"a":-0.223477},{"type":"bx","w":1.142857,"h":0.047619,"c":[0.190476,-0.142857],"a":-0.223477},{"type":"bx","w":1.428571,"h":0.047619,"c":[0.142857,-0.238095],"a":-0.223477},{"type":"po","v":[[-0.571429,-0.142857],[-0.47619,-0.333333],[-0.380952,-0.47619],[-0.285714,-0.761905],[-0.047619,-0.857143],[0.238095,-0.857143],[0.571429,-0.904762],[0.666667,-1.047619],[0.714286,-1.190476],[0.761905,-1.333333],[0.857143,-1.380952],[0.904762,-1.285714],[0.857143,-1.285714],[0.761905,-1.190476],[0.761905,-1.047619],[0.714286,-0.952381],[0.619048,-0.857143],[0.238095,-0.809524],[-0.047619,-0.809524],[-0.238095,-0.714286],[-0.333333,-0.47619]],"s":1},{"type":"po","v":[[-0.857143,-0.47619],[-0.809524,-0.285714],[-0.52381,-0.47619],[-0.285714,-0.47619],[-0.095238,-0.47619],[0.142857,-0.47619],[0.333333,-0.666667],[0.428571,-0.857143],[0.47619,-1],[0.666667,-1.047619],[0.857143,-1.095238],[0.952381,-1.047619],[0.952381,-1.142857],[0.809524,-1.142857],[0.714286,-1.142857],[0.52381,-1.095238],[0.428571,-1],[0.285714,-0.714286],[0.142857,-0.52381],[-0.428571,-0.52381]],"s":1},{"type":"po","v":[[-0.095238,-0.285714],[0.142857,-0.285714],[0.238095,-0.285714],[0.285714,-0.238095],[0.238095,-0.190476],[0.285714,-0.190476],[0.238095,-0.095238],[0.380952,-0.190476],[0.333333,-0.285714],[0.380952,-0.333333],[0.52381,-0.428571],[0.571429,-0.380952],[0.619048,-0.285714],[0.619048,-0.47619],[0.619048,-0.571429],[0.47619,-0.619048],[0.285714,-0.619048],[0.190476,-0.619048],[0.238095,-0.619048],[0.190476,-0.714286],[0.095238,-0.666667],[0,-0.619048],[0.095238,-0.619048],[-0.047619,-0.714286],[-0.047619,-0.761905],[-0.142857,-0.809524],[-0.285714,-0.904762],[-0.095238,-0.952381],[-0.190476,-1.095238],[-0.380952,-1],[-0.285714,-1.095238],[-0.333333,-1.190476],[-0.52381,-1.095238],[-0.571429,-1],[-0.47619,-0.761905],[-0.571429,-0.666667],[-0.285714,-0.571429],[-0.285714,-0.52381],[-0.380952,-0.761905],[-0.333333,-0.809524],[-0.190476,-0.761905],[0,-0.666667],[0.047619,-0.619048],[0.190476,-0.52381],[0.047619,-0.380952],[0.047619,-0.428571]],"s":1}],"fixtures":[{"sh":0,"f":2915279},{"sh":1,"f":131586,"np":true,"ng":true},{"sh":2,"f":2915279,"np":true},{"sh":3,"f":2915279,"np":true,"ng":true},{"sh":4,"f":2915279,"np":true,"ng":true},{"sh":5,"f":2915279,"np":true,"ng":true},{"sh":6,"f":2915279,"np":true},{"sh":7,"f":2915279,"np":true,"ng":true},{"sh":8,"f":131586,"np":true,"ng":true}],"joints":[]}`,
        },

        Reddus: {
            icon: '☯️',
            mapData: `{"bodies":[{"p":[16.75,7.722222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.380952,-0.904762],[-0.047619,-1.047619],[0.428571,-0.952381],[0.761905,-0.714286],[0.952381,-0.238095],[1,0.095238],[0.809524,0.52381],[0.666667,0.761905],[0.333333,0.952381]],"s":1},{"type":"po","v":[[-0.428571,-0.952381],[-0.047619,-1.047619],[0.666667,-0.761905],[0.904762,-0.47619],[1,-0.285714],[1,0.095238],[0.904762,0.47619],[0.666667,0.809524],[0.333333,0.952381]],"s":1},{"type":"po","v":[[0.047619,0.238095],[0,0.333333],[-0.095238,0.428571],[-0.190476,0.47619],[-0.380952,0.52381],[-0.428571,0.52381],[-0.428571,0.571429],[-0.142857,0.52381],[0,0.428571]],"s":1},{"type":"po","v":[[-0.190476,0.47619],[-0.095238,0.666667],[-0.047619,0.666667],[0.095238,0.714286],[0.142857,0.714286],[0.333333,0.714286],[0.285714,0.666667],[0.095238,0.666667],[-0.047619,0.666667]],"s":1},{"type":"bx","w":0.666667,"h":0.047619,"c":[-0.571429,0.095238],"a":-0.071307},{"type":"bx","w":0.666667,"h":0.047619,"c":[-0.571429,0.190476],"a":-0.368013},{"type":"po","v":[[-0.904762,-0.380952],[-0.904762,-0.142857],[-0.809524,-0.238095],[-0.809524,-0.619048],[-0.619048,-0.809524],[-0.428571,-0.52381],[-0.333333,-0.619048],[-0.571429,-0.857143],[-0.904762,-0.52381]],"s":1},{"type":"po","v":[[-0.761905,-0.619048],[-0.714286,-0.333333],[-0.52381,-0.52381],[-0.666667,-0.761905],[-0.809524,-0.571429]],"s":1},{"type":"po","v":[[0.095238,0.238095],[0.285714,0.285714],[0.619048,0.190476],[0.809524,0.095238],[0.761905,0.047619],[0.571429,0.190476],[0.285714,0.238095]],"s":1},{"type":"po","v":[[0.238095,0.666667],[0.333333,0.47619],[0.333333,0.238095],[0.380952,0.238095],[0.380952,0.52381]],"s":1},{"type":"bx","w":0.666667,"h":0.047619,"c":[0.380952,-0.238095],"a":-0.588003},{"type":"bx","w":0.666667,"h":0.047619,"c":[0.428571,-0.142857],"a":-0.273843},{"type":"po","v":[[-0.142857,-1],[0,-1.047619],[0,-0.714286],[-0.190476,-0.666667]],"s":1},{"type":"po","v":[[0.345638,-0.96223],[0.446576,-0.928329],[0.48634,-0.645395],[0.378774,-0.726451]],"s":1,"a":-0.139626},{"type":"po","v":[[0.047619,-1],[0.285714,-1],[0.285714,-0.714286],[0.095238,-0.714286]],"s":1},{"type":"po","v":[[0.095238,0.095238],[0.047619,0.238095],[-0.095238,0.190476]],"s":1}],"fixtures":[{"sh":0,"f":16711679},{"sh":1,"f":131586,"np":true,"ng":true},{"sh":2,"f":131586},{"sh":3,"f":131586,"np":true},{"sh":4,"f":131586,"np":true},{"sh":5,"f":131586,"np":true,"ng":true},{"sh":6,"f":131586,"np":true,"ng":true},{"sh":7,"f":131586,"np":true},{"sh":8,"f":131586,"np":true,"ng":true},{"sh":9,"f":16711679,"np":true},{"sh":10,"f":16711679,"np":true},{"sh":11,"f":16711679,"np":true,"ng":true},{"sh":12,"f":16711679,"np":true,"ng":true},{"sh":13,"f":16711679,"np":true,"ng":true},{"sh":14,"f":16711679,"np":true},{"sh":15,"f":16711679,"np":true,"ng":true},{"sh":16,"f":11908533,"np":true,"ng":true}],"joints":[]}`,
        },

        iama_user: {
            icon: '🌌',
            mapData: `{"bodies":[{"p":[19.55,7.822222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12],"s":{"type":"d","fr":true,"fric":-0.1,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[0.190476,0.619048],[0.047619,0.428571],[-0.047619,0.333333],[-0.333333,0.238095],[-0.285714,0.238095],[-0.666667,0.047619],[-0.52381,-0.142857],[-0.809524,-0.190476],[-0.47619,-0.380952],[-0.333333,-0.571429],[-0.095238,-0.333333],[0,-0.571429],[0.142857,-0.619048],[0.095238,-0.571429],[0,-0.619048],[0,-0.52381],[0.095238,-0.52381],[0.142857,-0.714286],[0.428571,-0.428571],[0.47619,-0.52381],[0.714286,-0.47619],[0.809524,-0.238095],[1,-0.142857],[0.571429,-0.238095],[0.761905,-0.095238],[0.761905,-0.047619],[0.714286,-0.190476],[0.666667,-0.047619],[0.666667,0.142857],[0.809524,0.190476],[0.619048,0.095238],[0.571429,0.238095],[0.619048,0.333333],[0.619048,0.428571],[0.333333,0.142857],[-0.333333,0.095238],[0,0.142857],[-0.142857,0.380952],[-0.190476,0.47619]],"s":1},{"type":"po","v":[[-0.857143,-0.52381],[-0.571429,-0.666667],[-0.52381,-0.714286],[-0.47619,-0.666667],[-0.333333,-0.809524],[-0.238095,-0.761905],[-0.380952,-0.666667],[-0.47619,-0.571429],[-0.666667,-0.428571],[-0.809524,-0.285714],[-0.904762,-0.142857],[-0.857143,-0.047619],[-0.857143,0.333333],[-0.666667,0.47619],[-0.619048,0.238095],[-0.571429,0.47619],[-0.428571,0.52381],[-0.380952,0.619048],[-0.238095,0.666667],[-0.095238,0.666667],[-0.095238,0.761905],[0.190476,0.761905],[0.333333,0.714286],[0.571429,0.619048],[0.47619,0.380952],[0.571429,0.333333],[0.714286,0.285714],[0.809524,0.238095],[0.619048,-0.095238],[0.809524,-0.238095],[0.761905,-0.333333],[0.666667,-0.571429],[0.761905,-0.571429],[0.857143,-0.52381],[1,-0.047619],[0.904762,0.428571],[0.714286,0.714286],[0.238095,1],[-0.095238,1],[-0.47619,0.904762],[-0.761905,0.666667],[-0.952381,0.285714],[-1,-0.190476],[-0.952381,-0.285714]],"s":1},{"type":"po","v":[[-0.571429,0.761905],[-0.47619,0.666667],[-0.428571,0.428571],[-0.380952,0.238095],[-0.380952,0.047619],[-0.238095,-0.047619],[-0.142857,-0.190476],[-0.095238,-0.190476],[-0.190476,0],[-0.142857,0.142857],[-0.190476,0.333333],[-0.095238,0.52381],[-0.095238,0.714286],[-0.142857,0.857143],[-0.142857,1],[-0.571429,0.904762]],"s":1},{"type":"po","v":[[0.52381,0.857143],[0.285714,0.619048],[0.333333,0.428571],[0.285714,0.333333],[0.285714,0.238095],[0.333333,0.095238],[0.142857,-0.047619],[0.095238,-0.047619],[0.285714,-0.047619],[0.380952,0],[0.47619,0],[0.47619,0.190476],[0.619048,0.285714],[0.666667,0.380952],[0.714286,0.47619],[0.809524,0.619048]],"s":1},{"type":"po","v":[[0.952381,0.238095],[0.809524,0.047619],[0.666667,-0.190476],[0.619048,-0.238095],[0.428571,-0.428571],[0.333333,-0.47619],[0.285714,-0.47619],[0.47619,-0.47619],[0.714286,-0.333333],[0.857143,-0.285714],[0.904762,-0.190476],[1,-0.190476]],"s":1},{"type":"po","v":[[-0.904762,0.380952],[-0.809524,0.238095],[-0.714286,0.047619],[-0.571429,-0.095238],[-0.380952,-0.238095],[-0.285714,-0.333333],[-0.666667,-0.238095],[-0.761905,-0.142857],[-0.952381,-0.047619],[-1,-0.047619]],"s":1},{"type":"po","v":[[0.095238,1],[0.142857,0.809524],[0.095238,0.571429],[0.047619,0.238095],[0.095238,0.142857],[0.095238,0.285714],[0.190476,0.52381],[0.190476,0.761905],[0.333333,0.904762],[0.380952,1]],"s":1},{"type":"ci","r":0.047619,"c":[-0.238095,-0.619048]},{"type":"ci","r":0.047619,"c":[0.095238,-0.333333]},{"type":"ci","r":0.047619,"c":[-0.52381,0.190476]},{"type":"ci","r":0.047619,"c":[0.190476,0.285714]},{"type":"ci","r":0.047619,"c":[0.52381,-0.619048]}],"fixtures":[{"sh":0,"f":8650898},{"sh":1,"f":10092714,"np":true},{"sh":2,"f":7078003,"np":true},{"sh":3,"f":2162724,"np":true},{"sh":4,"f":2162724,"np":true},{"sh":5,"f":2162724,"np":true},{"sh":6,"f":2162724,"np":true},{"sh":7,"f":2162724,"np":true},{"sh":8,"f":16776174,"np":true,"ng":true},{"sh":9,"f":16776174,"np":true,"ng":true},{"sh":10,"f":16776174,"np":true,"ng":true},{"sh":11,"f":16776174,"np":true,"ng":true},{"sh":12,"f":16776174,"np":true,"ng":true}],"joints":[]}`,
        },

        idislikebonk: {
            icon: '⚽',
            mapData: `{"bodies":[{"p":[22.4,7.872222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1,"c":[0,-0.047619]},{"type":"po","v":[[0,-0.333333],[0.952381,0.047619],[0,0.428571],[-0.952381,0.047619]],"s":1,"c":[-0.047619,0]},{"type":"ci","r":0.285714,"c":[0,0.047619]},{"type":"po","v":[[0.238095,0.095238],[0.142857,0],[-0.142857,0],[-0.238095,0.095238],[-0.285714,0],[-0.190476,-0.095238],[0.142857,-0.095238],[0.285714,0]],"s":1,"a":3.141593},{"type":"bx","w":0.142857,"h":0.047619,"c":[-0.095238,0.142857]},{"type":"bx","w":0.142857,"h":0.047619,"c":[-0.095238,0.238095]},{"type":"bx","w":0.142857,"h":0.047619,"c":[0.095238,0.238095]},{"type":"bx","w":0.142857,"h":0.047619,"c":[0.095238,0.142857]},{"type":"po","v":[[-0.904762,-0.285714],[-0.761905,-0.380952],[-0.666667,-0.428571],[-0.47619,-0.47619],[-0.333333,-0.428571],[-0.190476,-0.333333],[-0.142857,-0.238095],[-0.190476,-0.238095],[-0.238095,-0.333333],[-0.47619,-0.428571],[-0.714286,-0.380952],[-0.952381,-0.238095],[-0.857143,-0.190476]],"s":1,"c":[-0.190476,0]},{"type":"po","v":[[0.142857,-0.285714],[0.285714,-0.380952],[0.380952,-0.428571],[0.571429,-0.47619],[0.714286,-0.428571],[0.857143,-0.333333],[0.904762,-0.238095],[0.857143,-0.238095],[0.809524,-0.333333],[0.571429,-0.428571],[0.333333,-0.380952],[0.095238,-0.238095],[0.190476,-0.190476]],"s":1,"c":[0.857143,0]}],"fixtures":[{"sh":0,"f":100864},{"sh":1,"f":14079744,"np":true,"ng":true},{"sh":2,"f":65745,"np":true,"ng":true},{"sh":3,"f":16711679,"np":true,"ng":true},{"sh":4,"f":16711679,"np":true,"ng":true},{"sh":5,"f":16711679,"np":true,"ng":true},{"sh":6,"f":16711679,"np":true,"ng":true},{"sh":7,"f":16711679,"np":true,"ng":true},{"sh":8,"f":131586,"np":true},{"sh":9,"f":131586,"np":true}],"joints":[]}`,
        },

        songojanovo: {
            icon: '2️⃣',
            mapData: `{"bodies":[{"p":[25.3,7.872222],"lv":[0,14.666667],"fx":[0,1,2,3,4],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.857143},{"type":"ci","r":0.619048},{"type":"po","v":[[0.285714,0.428571],[-0.285714,0.428571],[-0.285714,0.238095],[0.047619,-0.095238],[0.095238,-0.190476],[0.095238,-0.238095],[0,-0.285714],[-0.047619,-0.333333],[-0.142857,-0.285714],[-0.333333,-0.095238],[-0.428571,-0.190476],[-0.095238,-0.47619],[0,-0.52381],[0.142857,-0.47619],[0.285714,-0.380952],[0.333333,-0.285714],[0.285714,-0.142857],[-0.047619,0.285714],[0.285714,0.285714]],"s":1,"c":[0,0.095238]},{"type":"po","v":[[-0.333333,-0.52381],[0.047619,-0.619048],[0.380952,-0.52381],[0.571429,-0.190476],[0.619048,0.142857],[0.52381,0.380952],[0.285714,0.571429],[0.47619,0.714286],[0.666667,0.52381],[0.809524,0.238095],[0.857143,-0.095238],[0.809524,-0.285714],[0.666667,-0.571429],[0.428571,-0.761905],[0.095238,-0.857143],[-0.238095,-0.809524],[-0.428571,-0.714286]],"s":1}],"fixtures":[{"sh":0,"f":775},{"sh":1,"f":12632256,"np":true,"ng":true},{"sh":2,"f":257,"np":true,"ng":true},{"sh":3,"f":16711679,"np":true},{"sh":4,"f":9803157,"np":true}],"joints":[]}`,
        },

        withehole: {
            icon: '⚪',
            mapData: `{"bodies":[{"p":[28.05,7.872222],"lv":[0,14.666667],"fx":[0],"s":{"type":"d","fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1}],"fixtures":[{"sh":0,"f":16777214}],"joints":[]}`,
        },

        lu1g1br0ss: {
            icon: '🍄',
            mapData: `{"bodies":[{"p":[7.6,7.672222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[0,0.238095],[-0.095238,0.142857],[-0.285714,0.095238],[-0.333333,0.142857],[-0.428571,0.190476],[-0.52381,0.333333],[-0.666667,0.428571],[-0.761905,0.428571],[-0.857143,0.380952],[-0.857143,0.428571],[-0.809524,0.47619],[-0.714286,0.571429],[-0.571429,0.52381],[-0.428571,0.47619],[-0.285714,0.380952]],"s":1},{"type":"po","v":[[0,0.238095],[0.095238,0.142857],[0.238095,0.095238],[0.333333,0.142857],[0.47619,0.238095],[0.571429,0.333333],[0.714286,0.380952],[0.761905,0.428571],[0.857143,0.380952],[0.952381,0.333333],[0.809524,0.47619],[0.761905,0.52381],[0.666667,0.52381],[0.571429,0.52381],[0.52381,0.47619],[0.47619,0.428571],[0.333333,0.380952],[0.190476,0.333333]],"s":1},{"type":"po","v":[[-0.285714,0.047619],[-0.47619,0],[-0.52381,-0.095238],[-0.52381,-0.285714],[-0.52381,-0.52381],[-0.428571,-0.619048],[-0.238095,-0.666667],[-0.142857,-0.619048],[-0.095238,-0.47619],[-0.047619,-0.333333],[-0.047619,-0.142857],[-0.047619,0.047619]],"s":1,"c":[-0.047619,0]},{"type":"po","v":[[0.333333,0.047619],[0.142857,0],[0.095238,-0.095238],[0.095238,-0.285714],[0.095238,-0.52381],[0.190476,-0.619048],[0.380952,-0.666667],[0.47619,-0.619048],[0.52381,-0.47619],[0.571429,-0.333333],[0.571429,-0.142857],[0.571429,0.047619]],"s":1,"c":[0.571429,0]},{"type":"po","v":[[-0.238095,0],[-0.428571,-0.095238],[-0.428571,-0.333333],[-0.285714,-0.52381],[-0.190476,-0.52381],[-0.190476,-0.428571],[-0.095238,-0.285714],[-0.095238,-0.142857]],"s":1},{"type":"po","v":[[0.333333,0],[0.142857,-0.095238],[0.142857,-0.333333],[0.285714,-0.52381],[0.380952,-0.52381],[0.380952,-0.428571],[0.47619,-0.285714],[0.47619,-0.142857]],"s":1,"c":[0.571429,0]},{"type":"ci","r":0.238095,"c":[0,0.047619]},{"type":"po","v":[[-0.857143,-0.52381],[-0.857143,-0.333333],[-0.809524,-0.142857],[-0.761905,-0.047619],[-0.714286,0],[-0.761905,0.095238],[-0.809524,0.142857],[-0.904762,0.190476],[-1,0.190476],[-1,-0.047619],[-0.952381,-0.333333]],"s":1,"c":[0,-0.047619]},{"type":"po","v":[[1,0.190476],[0.761905,0.142857],[0.761905,0.047619],[0.857143,0],[0.952381,-0.142857],[0.952381,-0.285714],[0.952381,-0.47619],[0.904762,-0.52381],[1,-0.285714],[1.047619,-0.047619]],"s":1,"c":[0.047619,0]},{"type":"po","v":[[-0.047619,-0.714286],[-0.142857,-0.809524],[-0.190476,-0.857143],[-0.333333,-0.809524],[-0.47619,-0.761905],[-0.571429,-0.666667],[-0.285714,-0.761905]],"s":1},{"type":"po","v":[[0.619048,-0.714286],[0.52381,-0.809524],[0.47619,-0.857143],[0.333333,-0.809524],[0.190476,-0.761905],[0.095238,-0.666667],[0.380952,-0.761905]],"s":1,"c":[0.666667,0]},{"type":"po","v":[[-0.904762,-0.47619],[-0.714286,-0.666667],[-0.285714,-0.857143],[0.285714,-0.857143],[0.666667,-0.666667],[0.952381,-0.428571],[0.809524,-0.666667],[0.571429,-0.857143],[0.333333,-1],[0,-1.047619],[-0.333333,-1],[-0.619048,-0.857143],[-0.809524,-0.666667]],"s":1,"c":[0,-0.047619]}],"fixtures":[{"sh":0,"f":14860178},{"sh":1,"f":257,"np":true},{"sh":2,"f":257,"np":true},{"sh":3,"f":16711679,"np":true},{"sh":4,"f":16711679,"np":true},{"sh":5,"f":257,"np":true},{"sh":6,"f":257,"np":true},{"sh":7,"f":14860178,"np":true,"ng":true},{"sh":8,"f":257,"np":true},{"sh":9,"f":257,"np":true},{"sh":10,"f":257,"np":true},{"sh":11,"f":257,"np":true},{"sh":12,"f":902400,"np":true}],"joints":[]}`,
        },

        vexspawn: {
            icon: '🌙',
            mapData: `{"bodies":[{"p":[29.75,9.572222],"lv":[0,14.666667],"fx":[0,1,2,3],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":0.952381},{"type":"ci","r":0.428571,"c":[0.190476,0.095238]},{"type":"ci","r":0.428571,"c":[-0.047619,-0.095238]},{"type":"po","v":[[-0.952381,0.047619],[-0.666667,-0.047619],[-0.428571,0.190476],[-0.380952,0.238095],[-0.380952,0.380952],[-0.142857,0.619048],[-0.047619,0.619048],[0.095238,0.666667],[0.238095,0.666667],[0.428571,0.571429],[0.666667,0.380952],[0.714286,0.238095],[0.904762,0.238095],[0.857143,0.47619],[0.428571,0.857143],[0,0.952381],[-0.47619,0.857143],[-0.714286,0.619048],[-0.857143,0.428571]],"s":1}],"fixtures":[{"sh":0,"f":5832788},{"sh":1,"f":15921124,"np":true,"ng":true},{"sh":2,"f":5832789,"np":true,"ng":true},{"sh":3,"f":4456516,"np":true}],"joints":[]}`,
        },

        masterxxz: {
            icon: '😖',
            mapData: `{"bodies":[{"p":[30.528046,11.150099],"lv":[0.937898,-0.281369],"fx":[0,1,2,3,4,5,6,7,8,9],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.447619,0.085714],[-0.661905,0.128571],[-0.704762,0.085714],[-0.661905,0.042857],[-0.790476,0.042857],[-0.747619,0],[-0.790476,0],[-0.747619,-0.085714],[-0.833333,-0.128571],[-0.704762,-0.171429],[-0.704762,-0.257143],[-0.533333,-0.3],[-0.447619,-0.342857],[-0.233333,-0.3],[-0.104762,-0.257143],[-0.147619,-0.171429],[-0.061905,-0.128571],[-0.019048,0],[-0.061905,0.042857],[-0.104762,0.085714],[-0.190476,0.085714]],"s":0.9,"c":[-0.190476,0]},{"type":"po","v":[[0.314286,0.085714],[0.1,0.128571],[0.057143,0.085714],[0.1,0.042857],[-0.028571,0.042857],[0.014286,0],[-0.028571,0],[0.014286,-0.085714],[-0.071429,-0.128571],[0.057143,-0.171429],[0.057143,-0.257143],[0.228571,-0.3],[0.314286,-0.342857],[0.528571,-0.3],[0.657143,-0.257143],[0.614286,-0.171429],[0.7,-0.128571],[0.742857,0],[0.7,0.042857],[0.657143,0.085714],[0.571429,0.085714]],"s":0.9,"c":[0.571429,0]},{"type":"bx","w":0.714286,"h":0.047619,"c":[0.428571,-0.380952],"a":0.197396},{"type":"po","v":[[0.047619,0.714286],[0,0.47619],[0.285714,0.238095],[0.52381,0.285714],[0.666667,0.52381],[0.619048,0.571429],[0.47619,0.333333],[0.285714,0.285714],[0.095238,0.47619]],"s":1},{"type":"po","v":[[-0.380952,-0.190476],[-0.285714,-0.095238],[-0.285714,-0.047619],[-0.333333,-0.047619],[-0.571429,-0.095238]],"s":1,"c":[0,-0.047619]},{"type":"po","v":[[0.428571,-0.190476],[0.52381,-0.095238],[0.52381,-0.047619],[0.47619,-0.047619],[0.238095,-0.095238]],"s":1,"c":[0.809524,-0.047619]},{"type":"po","v":[[-0.761905,0.190476],[-0.809524,0.333333],[-0.619048,0.333333],[-0.47619,0.333333],[-0.47619,0.285714],[-0.380952,0.285714],[-0.380952,0.190476],[-0.428571,0.190476]],"s":1},{"type":"po","v":[[-0.095238,0.238095],[-0.285714,0.190476],[-0.190476,0.142857],[0,0.142857],[0.142857,0.142857],[0.142857,0.190476]],"s":1},{"type":"po","v":[[0.857143,0.190476],[0.666667,0.238095],[0.619048,0.190476],[0.666667,0.190476]],"s":1}],"fixtures":[{"sh":0,"f":16773243},{"sh":1,"f":131586,"np":true},{"sh":2,"f":131586,"np":true},{"sh":3,"f":131586,"np":true,"ng":true},{"sh":4,"f":131586,"np":true},{"sh":5,"f":16773243,"np":true,"ng":true},{"sh":6,"f":16773243,"np":true,"ng":true},{"sh":7,"f":13156451,"np":true},{"sh":8,"f":13156451,"np":true},{"sh":9,"f":13156451,"np":true,"ng":true}],"joints":[]}`,
        },

        sebas_pr0: {
            icon: '🌩️',
            mapData: `{"bodies":[{"p":[6.496684,9.330113],"lv":[0.907388,1.391774],"fx":[0,1,2,3,4,5,6],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"po","v":[[-0.761905,-0.666667],[-0.857143,-0.095238],[-0.571429,-0.142857],[-0.619048,0.47619],[-0.761905,0.333333],[-0.666667,0.714286],[-0.190476,0.47619],[-0.380952,0.47619],[-0.333333,-0.380952],[-0.52381,-0.333333],[-0.428571,-0.904762]],"s":1},{"type":"po","v":[[0.428571,-0.904762],[0.333333,-0.142857],[0.571429,-0.238095],[0.47619,0.428571],[0.285714,0.238095],[0.333333,0.714286],[0.809524,0.428571],[0.666667,0.380952],[0.761905,-0.52381],[0.619048,-0.52381],[0.666667,-0.761905]],"s":1},{"type":"po","v":[[-0.142857,0.52381],[-0.142857,0.857143],[0.142857,0.809524],[0.190476,0.571429],[0.095238,0.428571],[0.095238,-0.095238],[0.571429,-0.095238],[0.571429,-0.047619],[0.666667,-0.095238],[0.666667,-0.380952],[0.571429,-0.428571],[0.571429,-0.333333],[0.095238,-0.333333],[0.095238,-0.714286],[-0.142857,-0.714286],[-0.142857,-0.333333],[-0.571429,-0.333333],[-0.571429,-0.380952],[-0.47619,-0.380952],[-0.714286,-0.333333],[-0.666667,-0.095238],[-0.571429,0],[-0.571429,-0.142857],[-0.142857,-0.095238]],"s":1},{"type":"po","v":[[0.142857,-1],[0.095238,-0.761905],[0.142857,-0.619048],[0.047619,-0.52381],[0.095238,-0.47619],[0.095238,-0.285714],[0.190476,-0.238095],[0.238095,-0.047619],[0.333333,-0.142857],[0.428571,-0.333333],[0.47619,-0.428571],[0.428571,-0.666667],[0.428571,-0.809524],[0.428571,-0.952381]],"s":1,"c":[0,-0.047619]},{"type":"ci","r":0.238095,"c":[-0.333333,-0.047619]},{"type":"ci","r":0.238095,"c":[0.380952,-0.047619]}],"fixtures":[{"sh":0,"f":51140},{"sh":1,"f":16772122,"np":true},{"sh":2,"f":16772122,"np":true},{"sh":3,"f":11760640,"np":true},{"sh":4,"f":16770478,"np":true},{"sh":5,"f":16776952,"np":true,"ng":true},{"sh":6,"f":16776952,"np":true,"ng":true}],"joints":[]}`,
        },

        starqueen0: {
            icon: '♀️',
            mapData: `{"bodies":[{"p":[6.874819,11.422965],"lv":[6.845706,2.053712],"fx":[0,1,2,3,4,5,6,7,8,9,10,11,12],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.571429,"c":[0,-0.285714]},{"type":"po","v":[[-0.571429,-0.285714],[0.380952,-0.285714],[0.380952,0.904762],[0.52381,0.809524],[0.52381,-0.619048],[0.47619,-0.809524],[0.333333,-0.904762],[0.142857,-1],[-0.190476,-1],[-0.428571,-0.857143],[-0.571429,-0.666667],[-0.571429,0.809524],[-0.428571,0.904762]],"s":1},{"type":"po","v":[[-0.52381,-0.666667],[-0.428571,-0.333333],[-0.428571,0.904762],[-0.52381,0.809524]],"s":1},{"type":"po","v":[[-0.571429,-0.380952],[-0.333333,-0.571429],[-0.333333,0.952381],[-0.571429,0.809524]],"s":1},{"type":"po","v":[[0.333333,-0.380952],[0.571429,-0.666667],[0.571429,0.857143],[0.333333,0.952381]],"s":1,"c":[0.047619,0]},{"type":"po","v":[[-0.047619,-1],[0.333333,-0.952381],[0.47619,-0.857143],[0.571429,-0.666667],[0.095238,-0.571429]],"s":1},{"type":"po","v":[[-0.333333,0.428571],[0.333333,0.428571],[0.333333,0.904762],[0.190476,1],[0,1],[-0.190476,1],[-0.333333,0.952381]],"s":1},{"type":"po","v":[[-0.142857,0.285714],[-0.142857,0.428571],[-0.238095,0.428571],[-0.190476,0.52381],[-0.142857,0.571429],[-0.047619,0.571429],[0.095238,0.571429],[0.190476,0.571429],[0.285714,0.428571],[0.142857,0.428571],[0.142857,0.238095]],"s":1},{"type":"po","v":[[-0.380952,-0.285714],[0.333333,-0.285714],[0.333333,-0.142857],[-0.333333,-0.142857]],"s":1},{"type":"po","v":[[-0.238095,0.428571],[-0.238095,0.761905],[0.238095,0.761905],[0.190476,0.428571],[0.285714,0.428571],[0.333333,0.619048],[0.333333,0.952381],[0.190476,1],[-0.190476,1],[-0.333333,1],[-0.333333,0.619048]],"s":1},{"type":"po","v":[[-0.285714,0.428571],[-0.238095,0.428571],[-0.333333,0.619048]],"s":1},{"type":"bx","w":0.190476,"h":0.047619,"c":[0,0.666667],"a":-1.570796}],"fixtures":[{"sh":0,"f":16771978},{"sh":1,"f":16515056,"np":true,"ng":true},{"sh":2,"f":51140,"np":true},{"sh":3,"f":51140,"np":true,"ng":true},{"sh":4,"f":51140,"np":true,"ng":true},{"sh":5,"f":51140,"np":true,"ng":true},{"sh":6,"f":51140,"np":true,"ng":true},{"sh":7,"f":16515056,"np":true,"ng":true},{"sh":8,"f":16751359,"np":true},{"sh":9,"f":16751359,"np":true,"ng":true},{"sh":10,"f":14352577,"np":true},{"sh":11,"f":14352577},{"sh":12,"f":7143530}],"joints":[]}`,
        },

        CircleRadius: {
            icon: '🔵',
            mapData: `{"bodies":[{"p":[30.65,7.072222],"lv":[0,14.666667],"fx":[0,1,2,3,4],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.428571},{"type":"ci","r":0.380952},{"type":"ci","r":0.095238},{"type":"bx","w":0.380952,"h":0.047619,"c":[-0.142857,0.142857],"a":-0.785398}],"fixtures":[{"sh":0,"f":2631509},{"sh":1,"f":6413542,"np":true,"ng":true},{"sh":2,"f":2631509,"np":true,"ng":true},{"sh":3,"f":6413542,"np":true,"ng":true},{"sh":4,"f":6413286,"np":true,"ng":true}],"joints":[]}`,
        },

        billvic: {
            icon: '🎗️',
            mapData: `{"bodies":[{"p":[12.1,5.572222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.333333},{"type":"ci","r":0.285714},{"type":"po","v":[[-0.095238,0.333333],[-0.52381,0.857143],[-0.428571,0.904762],[-0.142857,0.571429],[-0.142857,1],[0.238095,0.952381],[0.238095,0.571429],[0.380952,0.809524],[0.571429,0.47619],[0.47619,0.428571],[0.380952,0.571429],[0.142857,0.285714]],"s":1},{"type":"po","v":[[-0.380952,-0.285714],[0.190476,-0.47619],[0.142857,-0.571429],[0,-0.52381],[-0.142857,-0.904762],[-0.428571,-0.809524],[-0.285714,-0.47619],[-0.52381,-0.428571]],"s":1},{"type":"po","v":[[0.52381,0.47619],[0.333333,0.380952],[0.380952,0.095238],[0.47619,0.095238],[0.52381,0],[0.619048,-0.190476],[0.666667,0],[0.809524,-0.142857],[0.809524,0.142857],[0.809524,0.285714],[0.761905,0.380952]],"s":1}],"fixtures":[{"sh":0,"f":257},{"sh":1,"f":15990596,"np":true,"ng":true},{"sh":2,"f":257,"np":true,"ng":true},{"sh":3,"f":15990596,"np":true},{"sh":4,"f":15990596,"np":true},{"sh":5,"f":51140,"np":true}],"joints":[]}`,
        },

        xi972: {
            icon: '👁️',
            mapData: `{"bodies":[{"p":[32.15,9.372222],"lv":[0,14.666667],"fx":[0,1,2,3,4,5,6,7],"s":{"type":"d","fr":true,"fric":0,"de":0.3,"re":0.8,"f_1":true,"f_2":true,"f_3":true,"f_4":true}}],"shapes":[{"type":"ci","r":1},{"type":"ci","r":0.904762},{"type":"po","v":[[-0.619048,-0.619048],[-0.52381,-0.47619],[-0.428571,-0.380952],[-0.333333,-0.333333],[-0.238095,-0.238095],[-0.047619,-0.142857],[0.190476,0],[0.52381,0.095238],[0.666667,0.190476],[0.761905,0.238095],[0.857143,0.285714],[0.952381,-0.047619],[0.904762,-0.333333],[0.714286,-0.666667],[0.428571,-0.857143],[0.047619,-0.952381],[-0.285714,-0.904762],[-0.47619,-0.761905]],"s":1,"c":[0.047619,-0.047619]},{"type":"po","v":[[-0.428571,-0.761905],[-0.095238,-0.52381],[0,-0.428571],[0.142857,-0.285714],[0.380952,-0.190476],[0.619048,-0.095238],[0.809524,0],[0.952381,0.047619],[0.904762,-0.190476],[0.904762,-0.380952],[0.714286,-0.666667],[0.428571,-0.857143],[0,-0.952381],[-0.333333,-0.904762]],"s":1},{"type":"po","v":[[-0.190476,-0.952381],[-0.047619,-0.857143],[0.095238,-0.809524],[0.238095,-0.714286],[0.238095,-0.666667],[0.190476,-0.52381],[0.380952,-0.571429],[0.428571,-0.47619],[0.619048,-0.47619],[0.666667,-0.428571],[0.809524,-0.333333],[0.857143,-0.190476],[0.904762,-0.190476],[0.904762,-0.428571],[0.904762,-0.47619],[0.714286,-0.666667],[0.52381,-0.809524],[0.285714,-0.952381],[0.142857,-0.952381]],"s":1,"c":[0,-0.047619]},{"type":"ci","r":0.333333},{"type":"ci","r":0.190476,"c":[0.190476,-0.190476]},{"type":"ci","r":0.095238,"c":[0.428571,0]}],"fixtures":[{"sh":0,"f":2302755},{"sh":1,"f":1545517,"np":true,"ng":true},{"sh":2,"f":1209889,"np":true},{"sh":3,"f":941845,"np":true},{"sh":4,"f":467974,"np":true},{"sh":5,"f":257,"np":true,"ng":true},{"sh":6,"f":16053492,"np":true,"ng":true},{"sh":7,"f":16053492,"np":true,"ng":true}],"joints":[]}`,
        },
    };

    (() => {
        'use strict';

        if (!document.getElementById('__summon_theme_vars__')) {
            const themeStyle = document.createElement('style');
            themeStyle.id = '__summon_theme_vars__';
            themeStyle.textContent = `
                .summon-theme-root {
                    --sm-panel-bg:#222222; --sm-select-bg:#2e2e2e; --sm-input-bg:#2e2e2e;
                    --sm-btn-bg:#383838;
                    --sm-text:#ffffff; --sm-text-bright:#ffffff;
                    --sm-text-dim1:rgba(255,255,255,0.55); --sm-text-dim2:rgba(255,255,255,0.68); --sm-text-dim3:rgba(255,255,255,0.8);
                    --sm-ov-1:rgba(255,255,255,0.035); --sm-ov0:rgba(255,255,255,0.05); --sm-ov1:rgba(255,255,255,0.07);
                    --sm-ov1h:rgba(255,255,255,0.09); --sm-ov2:rgba(255,255,255,0.1);
                    --sm-bd0:rgba(255,255,255,0.1); --sm-bd0-none:rgba(255,255,255,0);
                    --sm-bd1:rgba(255,255,255,0.16); --sm-bd2:rgba(89,89,89,0.9); --sm-bd3:rgba(89,89,89,1);
                    --sm-accent:#2b6351;
                    background:var(--sm-panel-bg);
                }
            `;
            document.head.appendChild(themeStyle);
        }
        const registerThemeRoot = elm => {
            elm.classList.add('summon-theme-root');
            return elm;
        };
        const accentBtn = (label, bg, bd, t, fn, extra = '') => mkBtn(label, CSS.btn(bg, bd, t) + extra, fn);

        // --- CSS TOKENS ---
        const CSS = {
            sel: 'background:var(--sm-select-bg);border:1px solid var(--sm-bd2);border-radius:5px;color:var(--sm-text);font-size:12px;font-family:monospace;padding:4px 7px;width:100%;box-sizing:border-box;outline:none;cursor:pointer;',
            inp: 'background:var(--sm-input-bg);border:1px solid var(--sm-bd2);border-radius:5px;color:var(--sm-text-bright);font-size:12px;font-family:monospace;padding:4px 7px;width:100%;box-sizing:border-box;outline:none;',
            lbl: 'font-size:10px;color:var(--sm-text-dim2);letter-spacing:.06em;text-transform:uppercase;',
            btn: (bg, bd, t) =>
                `cursor:pointer;border-radius:5px;padding:5px 10px;border:1px solid ${bd};background:${bg};color:${t};font-size:11px;font-weight:bold;white-space:nowrap;min-width:70px;`,
            tab: 'cursor:pointer;padding:3px 8px;border-radius:4px;font-size:10px;font-weight:bold;font-family:monospace;border:1px solid var(--sm-bd3);background:var(--sm-ov1);color:var(--sm-text-dim1);transition:all .15s;line-height:1;',
            inactive: { background: 'var(--sm-ov1)', borderColor: 'var(--sm-bd3)', color: 'var(--sm-text-dim1)' },
        };

        // --- THEME — accent colors used across the HUD ---
        // Edit these to retheme the mod without hunting through inline styles.
        const THEME = {
            // Player actions
            spin:     { bg: 'rgba(80,200,255,0.1)',   bd: 'rgba(80,200,255,0.35)',  t: '#80e8ff' },
            dinnerbone:{ bg: 'rgba(255,200,50,0.1)',  bd: 'rgba(255,200,50,0.35)',  t: '#ffe680' },
            balance:  { bg: 'rgba(255,120,180,0.12)', bd: 'rgba(255,150,200,0.4)',  t: '#ffb0d0' },
            implode:  { bg: 'rgba(255,100,100,0.12)', bd: 'rgba(255,100,100,0.4)',  t: '#ff9090' },
            explode:  { bg: 'rgba(255,160,60,0.12)',  bd: 'rgba(255,180,60,0.4)',   t: '#ffc060' },
            orbit:    { bg: 'rgba(160,100,255,0.12)', bd: 'rgba(180,100,255,0.4)',  t: '#c090ff' },
            attract:  { bg: 'rgba(100,220,160,0.12)', bd: 'rgba(100,220,160,0.4)',  t: '#70ffb0' },
            repulse:  { bg: 'rgba(255,80,120,0.12)',  bd: 'rgba(255,80,120,0.4)',   t: '#ff6090' },
            rain:     { bg: 'rgba(80,160,255,0.1)',   bd: 'rgba(80,180,255,0.35)',  t: '#80c8ff' },
            shotgun:  { bg: 'rgba(255,140,60,0.12)',  bd: 'rgba(255,160,60,0.4)',   t: '#ffa040' },
            fling:    { bg: 'rgba(255,220,60,0.1)',   bd: 'rgba(255,220,60,0.35)',  t: '#ffe040' },
            // Map/physics actions
            bouncy:   { bg: 'rgba(60,220,180,0.1)',   bd: 'rgba(60,220,180,0.35)',  t: '#40e8b0' },
            ice:      { bg: 'rgba(140,200,255,0.1)',  bd: 'rgba(140,220,255,0.35)', t: '#aadeff' },
            density:  { bg: 'rgba(200,140,80,0.12)',  bd: 'rgba(220,160,80,0.4)',   t: '#e0aa60' },
            death:    { bg: 'rgba(220,60,60,0.12)',   bd: 'rgba(240,60,60,0.4)',    t: '#ff6060' },
            rainbow:  { bg: 'rgba(255,120,200,0.1)',  bd: 'rgba(255,140,220,0.35)', t: '#ff90d8' },
            delete:   { bg: 'rgba(255,80,80,0.1)',    bd: 'rgba(255,80,80,0.35)',   t: '#ff7070' },
            // ForceZone affects
            affPlayers:   '#3096CC',
            affPlatforms: '#CFCF79',
            affArrows:    '#CF8A30',
            // Map Settings checkboxes
            msNoCollisions: '#ff9090',
            msRespawn:      '#80ffaa',
            msFootball:     '#ffcc60',
            msQuickPlay:    '#c080ff',
        };

        // --- DOM HELPERS ---
        const sk = el => {
            ['keydown', 'keyup', 'keypress'].forEach(ev => el.addEventListener(ev, e => e.stopPropagation(), true));
            el.addEventListener('mousedown', e => e.stopPropagation());
            return el;
        };
        const el = (tag, css, txt) => {
            const e = document.createElement(tag);
            if (css) e.style.cssText = css;
            if (txt != null) e.textContent = txt;
            return e;
        };
        const lbl = t => el('div', CSS.lbl, t);
        const mkSel = () => {
            const s = sk(el('select', CSS.sel));
            s.innerHTML =
                '<option value="-1" style="background:var(--sm-select-bg);color:var(--sm-text);">— no room —</option>';
            UI.allSelects.push(s);
            return s;
        };
        const mkInp = (ph, type = 'number') => {
            const i = sk(el('input', CSS.inp));
            i.type = type;
            i.placeholder = ph;
            i.addEventListener('click', e => e.stopPropagation());
            i.addEventListener('keydown', e => {
                if (e.key === 'Escape') i.blur();
            });
            return i;
        };
        const mkBtn = (html, css, fn) => {
            const b = el('button', css);
            b.innerHTML = html;
            b.addEventListener('click', fn);
            b.addEventListener('mouseenter', () => (b.style.filter = 'brightness(1.2)'));
            b.addEventListener('mouseleave', () => (b.style.filter = ''));
            return b;
        };
        const div = (css, ...children) => {
            const d = el('div', css);
            children.forEach(c => c && d.appendChild(c));
            return d;
        };
        const row = (...children) => div('display:flex;gap:6px;align-items:center;', ...children);

        // Stop-key wrapper for inputs
        const skInp = i => {
            sk(i);
            i.addEventListener('click', e => e.stopPropagation());
            return i;
        };

        // --- SECTION PREFS ---
        const SEC_KEY = '__SUMMON__secprefs';
        let secPrefs = {
            order: ['target', 'objects', 'actions', 'fling', 'physics', 'export', 'twoPlayers', 'fzjoints'],
            open: {
                target: true,
                objects: true,
                actions: true,
                fling: false,
                physics: false,
                export: false,
                twoPlayers: false,
                fzjoints: false,
            },
        };
        try {
            const s = JSON.parse(localStorage.getItem(SEC_KEY) || 'null');
            if (s) secPrefs = s;
        } catch (_) {}
        const saveSecPrefs = () => {
            try {
                localStorage.setItem(SEC_KEY, JSON.stringify(secPrefs));
            } catch (_) {}
        };

        // --- SHORTCUT PREFS ---
        const SC_KEY = '__SUMMON__shortcuts';
        let SHORTCUTS = {};
        try {
            const s = localStorage.getItem(SC_KEY);
            if (s) SHORTCUTS = JSON.parse(s);
        } catch (_) {}
        const saveShortcuts = () => {
            try {
                localStorage.setItem(SC_KEY, JSON.stringify(SHORTCUTS));
            } catch (_) {}
        };

        // --- CUSTOM SUMMONABLES ---
        const CUSTOM_KEY = '__SUMMON__custom';
        let SUMMONABLES_CUSTOM = {};
        try {
            const s = localStorage.getItem(CUSTOM_KEY);
            if (s) SUMMONABLES_CUSTOM = JSON.parse(s);
        } catch (_) {}
        const saveCustom = () => {
            try {
                localStorage.setItem(CUSTOM_KEY, JSON.stringify(SUMMONABLES_CUSTOM));
            } catch (_) {}
        };
        const addCustomSummonable = (name, icon, mapData) => {
            SUMMONABLES_CUSTOM[name] = { icon, mapData };
            saveCustom();
        };
        const removeCustomSummonable = name => {
            delete SUMMONABLES_CUSTOM[name];
            saveCustom();
        };

        const summonables = new Proxy(
            {},
            {
                get(_, k) {
                    return SUMMONABLES_OBJ[k] ?? SUMMONABLES_SKIN[k] ?? SUMMONABLES_CUSTOM[k];
                },
                ownKeys() {
                    return [
                        ...Object.keys(SUMMONABLES_OBJ),
                        ...Object.keys(SUMMONABLES_SKIN),
                        ...Object.keys(SUMMONABLES_CUSTOM),
                    ];
                },
                has(_, k) {
                    return (
                        k in SUMMONABLES_OBJ || k in SUMMONABLES_SKIN || k in SUMMONABLES_CUSTOM
                    );
                },
                getOwnPropertyDescriptor(_, k) {
                    return { enumerable: true, configurable: true };
                },
            }
        );
        const getSummonables = mode =>
            mode === 'skin'
                ? SUMMONABLES_SKIN
                : mode === 'custom'
                  ? SUMMONABLES_CUSTOM
                  : SUMMONABLES_OBJ;

        const BODY_DEFAULTS = {
            type: 'd',
            n: '',
            fric: 0.5,
            fricp: false,
            re: -1,
            de: 9999,
            ld: 0,
            ad: 0,
            fr: false,
            bu: false,
            f_c: 1,
            f_p: true,
            f_1: true,
            f_2: true,
            f_3: true,
            f_4: true,
        };

        // --- PLAYER TRACKING ---
        let users = [],
            myId = -1,
            hostId = -1;
        const findById = id => users.find(u => u.id === id);

        // --- MODE CHANGE HELPER ---
        function _applyModePacket(parsed) {
            if (!Array.isArray(parsed) || parsed[0] !== 26) return;
            const mo = typeof parsed[2] === 'string' ? parsed[2]
                     : typeof parsed[1] === 'object' ? parsed[1]?.mo
                     : typeof parsed[1] === 'string' ? parsed[1]
                     : null;
            if (mo) {
                window.__SUMMON__._currentMode = mo;
                if (typeof window.__SUMMON__._onModeChange === 'function')
                    window.__SUMMON__._onModeChange(mo);
            }
        }

        let _gameSocket = null;

        const _oSend = WebSocket.prototype.send;
        const _nativeAEL = WebSocket.prototype.addEventListener;
        WebSocket.prototype.send = function (args) {
            if (typeof args === 'string' && args.startsWith('42')) {
                try {
                    const d = JSON.parse(args.slice(2));

                    if (Array.isArray(d) && d[0] === 20 && typeof d[1]?.mo === 'string') {
                        window.__SUMMON__._currentMode = d[1].mo;
                        if (typeof window.__SUMMON__._onModeChange === 'function')
                            window.__SUMMON__._onModeChange(d[1].mo);
                    }
                } catch (_) {}
            }
            if (this.url?.includes('/socket.io/') && !this._smH) {
                this._smH = true;
                _gameSocket = this;

                _nativeAEL.call(this, 'message', function (ev) {
                    if (typeof ev.data !== 'string' || !ev.data.startsWith('42')) return;
                    let d;
                    try { d = JSON.parse(ev.data.slice(2)); } catch (_) { return; }
                    if (!d) return;

                    if (d[0] === 2) {
                        users = [];
                        myId = 0;
                        hostId = 0;
                        const myName = document.getElementById('pretty_top_name')?.textContent ?? 'Me';
                        const myLevel = document.getElementById('pretty_top_level')?.textContent;
                        const imGuest = !myLevel || myLevel === 'Guest';
                        users.push({ id: 0, name: myName, guest: imGuest });
                        if (!window.__SUMMON__._currentMode) {
                            window.__SUMMON__._currentMode = 'b';
                            if (typeof window.__SUMMON__._onModeChange === 'function')
                                window.__SUMMON__._onModeChange('b');
                        }
                        refreshSel();
                    }

                    if (d[0] === 3) {
                        users = [];
                        myId = d[1];
                        hostId = d[2];
                        (d[3] || []).forEach((p, i) => {
                            if (!p) return;
                            users.push({ id: i, name: p.userName, guest: !!p.guest });
                        });
                        for (let i = 4; i <= 7; i++) {
                            if (d[i] && typeof d[i] === 'object' && typeof d[i].mo === 'string') {
                                window.__SUMMON__._currentMode = d[i].mo;
                                if (typeof window.__SUMMON__._onModeChange === 'function')
                                    window.__SUMMON__._onModeChange(d[i].mo);
                                break;
                            }
                        }
                        setTimeout(() => {
                            const entries = [...document.getElementsByClassName('newbonklobby_playerentry')];
                            entries.forEach(entry => {
                                const domName = entry.childNodes[1]?.textContent;
                                if (!domName) return;
                                const u = users.find(u => domName.startsWith(u.name) && !users.find(o => o.id !== u.id && o.name === domName));
                                if (u && u.name !== domName) u.name = domName;
                            });
                            refreshSel();
                        }, 300);
                        refreshSel();
                    }

                    if (d[0] === 4 && !findById(d[1])) {
                        users = users.filter(u => u.name !== d[3]);
                        users.push({ id: d[1], name: d[3], guest: !!d[4] });
                        refreshSel();
                        if (typeof _onPlayerJoinBanCheck === "function") _onPlayerJoinBanCheck(d[1]);
                    }

                    if (d[0] === 12) {
                        const u = findById(d[1]);
                        if (u) {
                            u.name = d[2];
                            refreshSel();
                        }
                    }

                    if (d[0] === 5) {
                        if (typeof _onPlayerLeaveVote === 'function') _onPlayerLeaveVote(d[1]);
                        users = users.filter(u => u.id !== d[1]);
                        refreshSel();
                    }

                    if (d[0] === 6) {
                        hostId = d[2];
                        refreshSel();
                    }
                    if (d[0] === 41 && d[1]?.newHost !== undefined) {
                        hostId = d[1].newHost;
                        refreshSel();
                    }

                    if (d[0] === 26) _applyModePacket(d);
                });

                _nativeAEL.call(this, 'close', () => {
                    if (_gameSocket === this) {
                        users = [];
                        myId = -1;
                        hostId = -1;
                        _gameSocket = null;
                        refreshSel();
                    }
                });
            }
            return _oSend.call(this, args);
        };

        function _simulateServerMessage(eventArray) {
            if (!_gameSocket) {
                showToast('⚠️ Game socket not ready yet', 'err');
                return false;
            }
            try {
                _gameSocket.onmessage({ data: '42' + JSON.stringify(eventArray) });
                return true;
            } catch (e) {
                console.error('[SummonMod] _simulateServerMessage failed', e);
                showToast('⚠️ Could not apply map change', 'err');
                return false;
            }
        }

        // --- REPLAY ---
        const REPLAY = { slots: [], maxSlots: 30, lastHash: 0 };
        const stateHash = state => {
            if (!state?.discs) return 0;
            return state.discs.reduce((h, d) => (d ? ((h + d.x * 73856093) ^ (d.y * 19349663)) | 0 : h), 0);
        };
        setInterval(() => {
            const S = window.__SUMMON__;
            if (!S.state) return;
            const h = stateHash(S.state);
            if (h === REPLAY.lastHash) return;
            REPLAY.lastHash = h;
            try {
                REPLAY.slots.push(structuredClone(S.state));
                if (REPLAY.slots.length > REPLAY.maxSlots) REPLAY.slots.shift();
            } catch (_) {}
        }, 1000);

        function actionRewind(secs = 5) {
            if (REPLAY.slots.length < 2) {
                showToast('⚠️ No history yet', 'err');
                return;
            }
            const clamp = Math.min(Math.max(1, secs), 30);
            const idx = Math.max(0, REPLAY.slots.length - 1 - clamp);
            try {
                startGame(structuredClone(REPLAY.slots[idx]), true);
            } catch (_) {
                showToast('⚠️ Restore error', 'err');
            }
        }

        // --- COPY / PASTE POSITIONS ---
        let _copiedPositions = null;
        function actionCopyPos() {
            const S = _requireState();
            if (!S) return;
            // Save full game state: player discs + projectiles + physics (map objects)
            try {
                _copiedPositions = {
                    discs: S.state.discs.map((d, i) =>
                        d
                            ? { x: d.x, y: d.y, xv: d.xv, yv: d.yv, a: d.a, av: d.av, team: d.team }
                            : S.state.players?.[i]
                              ? { x: null, y: null, team: S.state.players[i].team, wasDead: true }
                              : null
                    ),
                    projectiles: S.state.projectiles ? structuredClone(S.state.projectiles) : null,
                    physics: S.state.physics ? structuredClone(S.state.physics) : null,
                    ms: S.state.ms != null ? S.state.ms : null,
                    capZones: S.state.capZones ? structuredClone(S.state.capZones) : null,
                    scores: S.state.scores ? structuredClone(S.state.scores) : null,
                };
            } catch (e) {
                showToast('⚠️ Copy error', 'err');
                console.error('[SummonMod] actionCopyPos error', e);
            }
        }
        function actionPastePos() {
            const S = _requireState();
            if (!S) return;
            if (!_copiedPositions) {
                showToast('⚠️ Nothing copied yet', 'err');
                return;
            }
            const template = S.state.discs.find(d => d != null);
            if (!template) {
                showToast('⚠️ No live discs as reference', 'err');
                return;
            }
            try {
                const snap = structuredClone(S.state);
                // Restore player disc positions & velocities
                let n = 0;
                const cpDiscs = _copiedPositions.discs || [];
                for (let i = 0; i < cpDiscs.length; i++) {
                    const cp = cpDiscs[i];
                    if (!cp || cp.wasDead) continue;
                    if (snap.discs[i]) {
                        Object.assign(snap.discs[i], { x: cp.x, y: cp.y, xv: cp.xv ?? 0, yv: cp.yv ?? 0, a: cp.a ?? snap.discs[i].a, av: cp.av ?? 0 });
                    } else if (snap.players?.[i]) {
                        snap.discs[i] = Object.assign({}, template, {
                            x: cp.x, y: cp.y, xv: cp.xv ?? 0, yv: cp.yv ?? 0,
                            a: cp.a ?? 0, av: cp.av ?? 0, a1: false, a2: false, team: cp.team,
                        });
                    }
                    n++;
                }
                // Restore projectiles (arrows heh)
                if (_copiedPositions.projectiles !== null) {
                    snap.projectiles = structuredClone(_copiedPositions.projectiles);
                }
                // Restore map physics objects (bodies positions)
                if (_copiedPositions.physics) {
                    snap.physics = structuredClone(_copiedPositions.physics);
                }
                // Restore cap zones, scores, match time
                if (_copiedPositions.capZones !== null) snap.capZones = structuredClone(_copiedPositions.capZones);
                if (_copiedPositions.scores !== null) snap.scores = structuredClone(_copiedPositions.scores);
                if (_copiedPositions.ms !== null) snap.ms = _copiedPositions.ms;
                startGame(snap, true, 60);
            } catch (e) {
                showToast('⚠️ Paste error', 'err');
                console.error('[SummonMod] actionPastePos error', e);
            }
        }

        // --- SCORE EDITOR ---
        function actionSetScore(pid, score) {
            const S = _requireState();
            if (!S) return;
            if (!S.state.scores?.length) {
                showToast('⚠️ No scores in this mode', 'err');
                return;
            }
            if (pid >= S.state.scores.length) {
                showToast('⚠️ ID out of range', 'err');
                return;
            }
            S.state.scores[pid] = score === '' ? 0 : isNaN(Number(score)) ? score : Number(score);
            startGame(S.state, true);
        }

        // --- HELPERS ---
        const _stateCtx = (err = '⚠️ No state') => {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast(err, 'err');
                return null;
            }
            return S;
        };
        const _discs = id => {
            const S = window.__SUMMON__;
            if (!S.state) return null;
            return id === 'all' ? S.state.discs.map((d, i) => (d ? i : null)).filter(i => i !== null) : [id];
        };

        let _applyPhysicsBusy = false;
        function _applyPhysics(mutator) {
            if (_applyPhysicsBusy) {
                showToast('⏳ Still applying, wait...', 'err');
                return null;
            }
            const S = _stateCtx();
            if (!S) return null;
            const physics = structuredClone(S.state.physics);
            const result = mutator(S, physics);
            if (result === false) return null;
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            _applyPhysicsBusy = true;
            startGame(snapshot, true);
            setTimeout(() => {
                _applyPhysicsBusy = false;
            }, 600);
            return result;
        }

        // --- SPLIT COMPOUND BODIES INTO ONE-SHAPE-PER-BODY (mutates `physics` in place) --

        function _splitBodiesByFixture(physics) {
            const { bodies, joints, bro: oldBro } = physics;
            const jointBodies = new Set();
            for (const j of joints || []) {
                if (!j) continue;
                if (j.ba != null && j.ba !== -1) jointBodies.add(j.ba);
                if (j.bb != null && j.bb !== -1) jointBodies.add(j.bb);
            }
            const splitBodies = [],
                oldToNew = bodies.map(() => []);
            let nSplit = 0;
            bodies.forEach((b, idx) => {
                if (!b) return;
                if (jointBodies.has(idx) || !Array.isArray(b.fx) || b.fx.length <= 1) {
                    const ni = splitBodies.length;
                    splitBodies.push(b);
                    oldToNew[idx] = [ni];
                    return;
                }
                for (const fxIdx of b.fx) {
                    const ni = splitBodies.length;
                    const nb = structuredClone(b);
                    nb.fx = [fxIdx];
                    splitBodies.push(nb);
                    oldToNew[idx].push(ni);
                }
                nSplit++;
            });
            splitBodies.forEach((b, i) => {
                if (b) b.id = i;
            });
            physics.bodies = splitBodies;
            // Remap joint body references to new indices
            for (const j of joints || []) {
                if (!j) continue;
                if (j.ba != null && j.ba !== -1 && oldToNew[j.ba]?.length) j.ba = oldToNew[j.ba][0];
                if (j.bb != null && j.bb !== -1 && oldToNew[j.bb]?.length) j.bb = oldToNew[j.bb][0];
            }
            const newBro = [];
            const seen = new Set();
            for (const oi of oldBro || []) {
                const m = oldToNew[oi];
                if (m) newBro.push(...m);
            }
            newBro.forEach(i => seen.add(i));
            for (let i = 0; i < splitBodies.length; i++) {
                if (!seen.has(i)) newBro.push(i);
            }
            physics.bro = newBro;
            return { nSplit };
        }

        // --- SPLIT & APPLY BODY TYPE ---
        function _splitAndApplyBodyType(percent, type) {
            const S = _stateCtx();
            if (!S) return null;
            const pct = parseFloat(percent);
            if (isNaN(pct) || pct < 0 || pct > 100) {
                showToast('⚠️ Invalid % (0-100)', 'err');
                return null;
            }
            const physics = structuredClone(S.state.physics);
            const { nSplit } = _splitBodiesByFixture(physics);
            let n = 0,
                total = 0;
            for (const b of physics.bodies) {
                if (!b?.s) continue;
                total++;
                if (Math.random() * 100 < pct) {
                    b.s.type = type;
                    n++;
                }
            }
            if (!total) {
                showToast('⚠️ No objects found in map', 'err');
                return null;
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            startGame(snapshot, true);
            return { n, total, nSplit };
        }
        const actionFreeMovingMode = pct => _splitAndApplyBodyType(pct, 'd');
        const actionStationaryMode = pct => _splitAndApplyBodyType(pct, 's');

        // --- PLAYER ACTIONS ---
        const SPIN_AV = -24.1;
        function actionSpin(id, speedDeg = 360, randomMode = false) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            let n = 0;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                const deg = randomMode ? Math.random() * 720 - 360 : speedDeg;
                d.av = (deg / 360) * SPIN_AV;
                d.a1 = false;
                d.a2 = false;
                n++;
            }
            if (!n) {
                showToast('⚠️ Disc not found', 'err');
                return;
            }
            startGame(S.state, true);
        }
        function actionDinnerbone(id) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            let n = 0;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                d.a += Math.PI;
                n++;
            }
            if (!n) {
                showToast('⚠️ Disc not found', 'err');
                return;
            }
            startGame(S.state, true);
        }

        // --- PROJECTILE RING (unified implode/explode) ---
        const IMPLODE_CFG = { count: 15, radius: 6, speed: 60, life: 250 };
        const EXPLODE_CFG = { count: 15, radius: 1.5, speed: 60, life: 250 };

        function _projectileRing(id, cfg, inward, speed, count, radius, life) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            const spd = speed ?? cfg.speed;
            const cnt = count ?? cfg.count;
            const rad = radius ?? cfg.radius;
            const lfe = life ?? cfg.life;
            // Random spin ±136–360 deg, skipping the boring ±135 or sum dead zone
            const _randSpin = () => {
                const mag = 136 + Math.random() * 224;
                return (Math.random() < 0.5 ? 1 : -1) * mag;
            };
            let n = 0;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                d.xv = 0;
                d.yv = 0;
                d.a1 = true;
                d.a2 = true;
                d.a1a = 0;
                if (inward) d.av = (_randSpin() / 360) * 9000;
                for (let k = 0; k < cnt; k++) {
                    const a = (k / cnt) * Math.PI * 2;
                    const dir = inward ? -1 : 1;
                    // implode (inward): ownerless (did:-1) - can kill the target player
                    // explode (outward): own arrows (did:i)  - won't kill the caster
                    const did = inward ? -1 : i;
                    S.state.projectiles.push({
                        x: d.x + Math.cos(a) * rad,
                        y: d.y + Math.sin(a) * rad,
                        a,
                        av: 0,
                        xv: Math.cos(a) * spd * dir,
                        yv: Math.sin(a) * spd * dir,
                        fte: lfe,
                        did,
                        type: 'arrow',
                        team: 1,
                    });
                }
                n++;
            }
            if (!n) return;
            startGame(S.state, true, 90);
        }
        const actionImplode = (id, s, c, r, dur) => _projectileRing(id, IMPLODE_CFG, true, s, c, r, dur);
        const actionExplode = (id, s, c, r, dur) => _projectileRing(id, EXPLODE_CFG, false, s, c, r, dur);

        // --- BRING / REPULSE / ATTRACT ---
        function actionBringAll(id) {
            const S = _requireState();
            if (!S) return;
            const c = S.state.discs[id];
            if (!c) {
                showToast('⚠️ Disc not found', 'err');
                return;
            }
            for (let i = 0; i < S.state.discs.length; i++) {
                const d = S.state.discs[i];
                if (!d || i === id) continue;
                d.x = c.x;
                d.y = c.y;
                d.xv = 0;
                d.yv = 0;
            }
            startGame(S.state, true);
        }
        const REPULSE_CFG = { force: 80, range: 50 };
        const ATTRACT_CFG = { force: 80, range: 50 };
        function _radialForce(id, cfg, toward) {
            const S = _requireState();
            if (!S) return;
            const c = S.state.discs[id];
            if (!c) {
                showToast('⚠️ Disc not found', 'err');
                return;
            }
            for (let i = 0; i < S.state.discs.length; i++) {
                if (i === id) continue;
                const d = S.state.discs[i];
                if (!d) continue;
                const dx = toward ? c.x - d.x : d.x - c.x;
                const dy = toward ? c.y - d.y : d.y - c.y;
                const dist = Math.sqrt(dx * dx + dy * dy) || 1;
                if (dist > cfg.range) continue;
                d.xv += (dx / dist) * cfg.force;
                d.yv += (dy / dist) * cfg.force;
            }
            startGame(S.state, true);
        }
        const actionRepulse = id => _radialForce(id, REPULSE_CFG, false);
        const actionAttract = id => _radialForce(id, ATTRACT_CFG, true);

        // --- RAIN / ORBIT ---
        const RAIN_CFG = { cols: 9, spacing: 3, height: 30, spread: 0.5, fallSpeed: 25, life: 150 };
        const ORBIT_CFG = { count: 6, radius: 8, speed: 50, life: 300 };
        const SHOTGUN_CFG = { count: 7, spread: 0.18, speed: 50, life: 200, mode: 'nearest' };

        function actionRain(id, fallSpeed, cols, height, duration) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            const spd = fallSpeed ?? RAIN_CFG.fallSpeed,
                ncols = cols ?? RAIN_CFG.cols;
            const half = Math.floor((ncols - 1) / 2);
            const { spacing, spread } = RAIN_CFG;
            const h = height ?? RAIN_CFG.height;
            const life = duration ?? RAIN_CFG.life;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                for (let col = -half; col <= half; col++)
                    S.state.projectiles.push({
                        x: d.x + col * spacing,
                        y: d.y - h,
                        a: Math.PI / 2,
                        av: 0,
                        xv: col * spread,
                        yv: spd,
                        fte: life,
                        did: -1,
                        type: 'arrow',
                        team: 1,
                    });
            }
            startGame(S.state, true);
        }
        function actionOrbit(id, speed, count, radius, life) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            const spd = speed ?? ORBIT_CFG.speed,
                cnt = count ?? ORBIT_CFG.count,
                rad = radius ?? ORBIT_CFG.radius,
                lfe = life ?? ORBIT_CFG.life;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                for (let k = 0; k < cnt; k++) {
                    const a = (k / cnt) * Math.PI * 2;
                    S.state.projectiles.push({
                        x: d.x + Math.cos(a) * rad,
                        y: d.y + Math.sin(a) * rad,
                        a,
                        av: 0,
                        xv: -Math.sin(a) * spd,
                        yv: Math.cos(a) * spd,
                        fte: lfe,
                        did: i,
                        type: 'arrow',
                        team: 1,
                    });
                }
            }
            startGame(S.state, true);
        }
        function actionShotgun(id, speed, count, spread, mode, life) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            const spd = speed ?? SHOTGUN_CFG.speed,
                cnt = count ?? SHOTGUN_CFG.count,
                spr = spread ?? SHOTGUN_CFG.spread;
            const md = mode || SHOTGUN_CFG.mode || 'nearest',
                half = Math.floor((cnt - 1) / 2);
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                let baseA;
                if (md === 'near' || md === 'nearest') {
                    let minDist = Infinity,
                        nearest = null;
                    for (let j = 0; j < S.state.discs.length; j++) {
                        if (j === i) continue;
                        const t = S.state.discs[j];
                        if (!t) continue;
                        const dx = t.x - d.x,
                            dy = t.y - d.y;
                        const dist = Math.sqrt(dx * dx + dy * dy);
                        if (dist < minDist) {
                            minDist = dist;
                            nearest = t;
                        }
                    }
                    baseA = nearest ? Math.atan2(nearest.y - d.y, nearest.x - d.x) : Math.atan2(d.yv || 0, d.xv || 0);
                } else if (md === 'ran' || md === 'random') baseA = Math.random() * Math.PI * 2;
                else baseA = Math.atan2(d.yv || 0, d.xv || 0);
                for (let k = -half; k <= half; k++) {
                    const a = baseA + k * spr;
                    S.state.projectiles.push({
                        x: d.x,
                        y: d.y,
                        a,
                        av: 0,
                        xv: Math.cos(a) * spd,
                        yv: Math.sin(a) * spd,
                        fte: life ?? SHOTGUN_CFG.life,
                        did: i,
                        type: 'arrow',
                        team: 1,
                    });
                }
            }
            startGame(S.state, true);
        }

        // --- SUPERFLING ---
        const FLING = { mode: 'random', angleValue: 0, multiplier: 1.0 };
        function _flingVec(disc, mode, mul, angleDeg) {
            const base = 100,
                m = Math.min(5, Math.max(0.1, mul ?? FLING.multiplier));
            const md = mode || FLING.mode;
            if (md === 'velocity' || md === 'vel') {
                const vx = disc.xv || 0,
                    vy = disc.yv || 0,
                    l = Math.sqrt(vx * vx + vy * vy) || 1;
                return { xv: (vx / l) * base * m, yv: (vy / l) * base * m };
            }
            if (md === 'angle') {
                const a = ((angleDeg ?? FLING.angleValue) * Math.PI) / 180;
                return { xv: Math.cos(a) * base * m, yv: -Math.sin(a) * base * m };
            }
            return { yv: Math.random() * -100 + 20, xv: Math.random() * 200 - 100 };
        }
        function actionSuperFling(id, multiplier, modeOrAngle) {
            const S = _requireState();
            if (!S) return;
            const ds = _discs(id);
            if (!ds) return;
            const KEYWORDS = ['vel', 'velocity', 'ran', 'random'];
            let rMode = null,
                rAngle = null;
            if (modeOrAngle != null) {
                const s = String(modeOrAngle).toLowerCase();
                if (KEYWORDS.includes(s)) rMode = s;
                else if (!isNaN(parseFloat(modeOrAngle))) {
                    rMode = 'angle';
                    rAngle = parseFloat(modeOrAngle);
                }
            }
            let n = 0;
            for (const i of ds) {
                const d = S.state.discs[i];
                if (!d) continue;
                const v = _flingVec(d, rMode, multiplier, rAngle);
                d.xv = v.xv;
                d.yv = v.yv;
                n++;
            }
            if (!n) return;
            startGame(S.state, true);
        }

        // --- PHYSICS ACTIONS ---

        const actionBouncyMode = (val, pct = 100) => {
            const re = parseFloat(val);
            if (isNaN(re)) {
                showToast('⚠️ Invalid value', 'err');
                return;
            }
            const p = Math.max(0, Math.min(100, parseFloat(pct)));
            if (isNaN(p)) {
                showToast('⚠️ Invalid %', 'err');
                return;
            }
            _applyPhysics((_, ph) => {
                let c = 0,
                    total = 0;
                for (const b of ph.bodies) {
                    if (!b?.s) continue;
                    total++;
                    if (Math.random() * 100 < p) {
                        b.s.re = re;
                        c++;
                    }
                }
                if (!total) {
                    showToast('⚠️ No objects found in map', 'err');
                    return false;
                }
                return { c, total };
            });
        };
        const actionFrictionMode = (val, pct = 100) => {
            const fr = parseFloat(val);
            if (isNaN(fr)) {
                showToast('⚠️ Invalid value', 'err');
                return;
            }
            const p = Math.max(0, Math.min(100, parseFloat(pct)));
            if (isNaN(p)) {
                showToast('⚠️ Invalid %', 'err');
                return;
            }
            _applyPhysics((_, ph) => {
                let c = 0,
                    total = 0;
                for (const f of ph.fixtures) {
                    if (!f) continue;
                    total++;
                    if (Math.random() * 100 < p) {
                        f.fr = fr;
                        f.frp = false;
                        c++;
                    }
                }
                if (!total) {
                    showToast('⚠️ No fixtures found in map', 'err');
                    return false;
                }
                return { c, total };
            });
        };
        const actionDensityMode = (val, pct = 100) => {
            const de = parseFloat(val);
            if (isNaN(de)) {
                showToast('⚠️ Invalid value', 'err');
                return;
            }
            const p = Math.max(0, Math.min(100, parseFloat(pct)));
            if (isNaN(p)) {
                showToast('⚠️ Invalid %', 'err');
                return;
            }
            _applyPhysics((_, ph) => {
                let c = 0,
                    total = 0;
                for (const b of ph.bodies) {
                    if (!b?.s) continue;
                    total++;
                    if (Math.random() * 100 < p) {
                        b.s.de = de;
                        c++;
                    }
                }
                if (!total) {
                    showToast('⚠️ No objects found in map', 'err');
                    return false;
                }
                return { c, total };
            });
        };
        const actionNoPhysicsMode = percent => {
            const pct = parseFloat(percent);
            if (isNaN(pct) || pct < 0 || pct > 100) {
                showToast('⚠️ Invalid % (0-100)', 'err');
                return;
            }
            _applyPhysics((_, ph) => {
                let n = 0,
                    total = 0;
                for (const f of ph.fixtures) {
                    if (!f) continue;
                    total++;
                    if (Math.random() * 100 < pct) {
                        f.np = true;
                        n++;
                    }
                }
                if (!total) {
                    showToast('⚠️ No fixtures found in map', 'err');
                    return false;
                }
                return { n, total };
            });
        };
        // -- DEATH FIXTURES (% of fixtures become instant-death on touch) --
        const actionDeathMode = percent => {
            const pct = parseFloat(percent);
            if (isNaN(pct) || pct < 0 || pct > 100) {
                showToast('⚠️ Invalid % (0-100)', 'err');
                return;
            }
            _applyPhysics((_, ph) => {
                const { nSplit } = _splitBodiesByFixture(ph);
                let n = 0,
                    total = 0;
                // After splitting, each body has exactly one fixture; apply death per-body
                for (const b of ph.bodies) {
                    if (!b || !Array.isArray(b.fx)) continue;
                    for (const fi of b.fx) {
                        const f = ph.fixtures[fi];
                        if (!f) continue;
                        total++;
                        if (Math.random() * 100 < pct) {
                            f.d = true;
                            n++;
                        }
                    }
                }
                if (!total) {
                    showToast('⚠️ No fixtures found in map', 'err');
                    return false;
                }
                return { n, total, nSplit };
            });
        };

        // -- DELETE SHAPES (% of bodies removed from the map) --
        // -- MAP SETTINGS (ms) TOGGLE — nc / re / fl / pq via command --
        function actionSetMs(key, on) {
            const S = window.__SUMMON__;
            if (!S.state?.ms) {
                showToast('⚠️ No map settings', 'err');
                return;
            }
            S.state.ms[key] = key === 'pq' ? (on ? 2 : 1) : !!on;
            startGame(S.state, true);
        }

        // --- MAP-LEVEL SETTINGS (ppm / name) --

        function _getActiveMap() {
            const physics = window.__SUMMON__?.state?.physics;
            if (!physics) {
                showToast('⚠️ No active map found', 'err');
                return null;
            }
            return window.__SUMMON__.state;
        }
        function _getActiveMapSettings() {
            const map = _getActiveMap();
            if (!map) return null;
            if (!map.s) {
                showToast('⚠️ Map has no settings block', 'err');
                return null;
            }
            return map.s;
        }

        function _reapplyActiveMap() {
            const map = _getActiveMap();
            if (!map) return false;
            const ok = _simulateServerMessage([21, { map }]);
            const S = window.__SUMMON__;
            if (ok && S?.state) startGame(S.state, true);
            return ok;
        }

        function actionSetPPM(value) {
            const S = _requireState();
            if (!S) return;
            const newPpm = parseFloat(value);
            if (isNaN(newPpm) || newPpm < 1 || newPpm > 1000) {
                showToast('⚠️ PPM must be a number between 1 and 1000', 'err');
                return;
            }
            const oldPpm = S.state.physics?.ppm ?? 30;
            if (oldPpm === newPpm) {
                return;
            }

            const oldCx = 365 / oldPpm,
                oldCy = 250 / oldPpm,
                newCx = 365 / newPpm,
                newCy = 250 / newPpm;
            const dx = newCx - oldCx,
                dy = newCy - oldCy;

            const physics = structuredClone(S.state.physics);
            physics.ppm = newPpm;
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                b.p[0] += dx;
                b.p[1] += dy;
            }
            for (const j of physics.joints || []) {
                if (!j) continue;
                if (j.type === 'lpj' && j.pax != null && j.pay != null) {
                    j.pax += dx;
                    j.pay += dy;
                }
                if (j.type === 'lsj' && j.sax != null && j.say != null) {
                    j.sax += dx;
                    j.say += dy;
                }
                if ((j.type === 'rv' || j.type === 'd') && j.bb === -1 && Array.isArray(j.ab)) {
                    j.ab[0] += dx;
                    j.ab[1] += dy;
                }
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            for (const d of snapshot.discs) {
                if (!d) continue;
                d.x += dx;
                d.y += dy;
            }

            // -- Update spawn points (capZones / rc) so Respawn on Death lands correctly --
            if (Array.isArray(snapshot.rc)) {
                for (const rc of snapshot.rc) {
                    if (!rc) continue;
                    if (rc.x != null) rc.x += dx;
                    if (rc.y != null) rc.y += dy;
                }
            }
            if (Array.isArray(snapshot.capZones)) {
                for (const cz of snapshot.capZones) {
                    if (!cz) continue;
                    if (cz.x != null) cz.x += dx;
                    if (cz.y != null) cz.y += dy;
                }
            }

            const map = _getActiveMap();
            if (map?.physics) map.physics.ppm = newPpm;

            startGame(snapshot, true);
        }

        function actionResizeViaPPM(value) {
            const S = _requireState();
            if (!S) return;
            const newPpm = parseFloat(value);
            if (isNaN(newPpm) || newPpm < 1 || newPpm > 1000) {
                showToast('⚠️ Value must be between 1 and 1000', 'err');
                return;
            }
            const oldPpm = S.state.physics?.ppm ?? 30;
            if (oldPpm === newPpm) {
                return;
            }

            const scale = oldPpm / newPpm;
            const oldCx = 365 / oldPpm,
                oldCy = 250 / oldPpm;
            const newCx = 365 / newPpm,
                newCy = 250 / newPpm;
            const ppmDx = newCx - oldCx,
                ppmDy = newCy - oldCy;

            // -- 1. Stretch all shapes in place (no startGame) ---
            const physics = structuredClone(S.state.physics);
            const sx = scale,
                sy = scale;
            const scalePoint = (x, y) => [oldCx + (x - oldCx) * sx, oldCy + (y - oldCy) * sy];
            const shapeBodyAngle = new Map();
            for (const b of physics.bodies) {
                if (!Array.isArray(b?.fx)) continue;
                for (const fi of b.fx) {
                    const f = physics.fixtures?.[fi];
                    if (f && typeof f.sh === 'number') shapeBodyAngle.set(f.sh, b.a || 0);
                }
            }
            for (let i = 0; i < physics.shapes.length; i++) {
                const sh = physics.shapes[i];
                if (!sh) continue;
                const ba = shapeBodyAngle.get(i) || 0;
                const ocx = sh.c ? sh.c[0] : 0,
                    ocy = sh.c ? sh.c[1] : 0;
                const [ncx, ncy] = stretchLocalVec(ocx, ocy, 0, ba, sx, sy);
                if (sh.type === 'bx') {
                    const hw = sh.w / 2,
                        hh = sh.h / 2,
                        A = sh.a || 0;
                    const verts = [
                        [-hw, -hh],
                        [hw, -hh],
                        [hw, hh],
                        [-hw, hh],
                    ].map(([x, y]) => rotVec(x, y, A));
                    sh.v = verts.map(([x, y]) => {
                        const [wx, wy] = stretchLocalVec(x + ocx, y + ocy, 0, ba, sx, sy);
                        return [wx, wy];
                    });
                    sh.type = 'po';
                    sh.s = 1;
                    sh.a = 0;
                    sh.c = [ncx, ncy];
                    sh.sk = sh.sk ?? false;
                    delete sh.w;
                    delete sh.h;
                } else if (sh.type === 'po' && Array.isArray(sh.v)) {
                    sh.v = sh.v.map(([x, y]) => {
                        const [wx, wy] = stretchLocalVec(x, y, 0, ba, sx, sy);
                        return [wx, wy];
                    });
                    sh.c = [ncx, ncy];
                } else if (sh.type === 'ci') {
                    sh.r = Math.abs(sh.r * sx);
                    sh.c = [ncx, ncy];
                }
            }
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                [b.p[0], b.p[1]] = scalePoint(b.p[0], b.p[1]);
                // Scale lv so dynamic bodies keep the same visual speed after ppm change.
                // lv is in physics units/step, so it must scale with the coordinate space.
                // av is in radians/step — independent of ppm, leave it alone.
                if (Array.isArray(b.lv)) {
                    b.lv[0] *= scale;
                    b.lv[1] *= scale;
                }
            }
            for (const j of physics.joints || []) {
                if (!j) continue;
                if (j.type === 'rv' || j.type === 'd') {
                    if (j.bb === -1 && Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = scalePoint(j.ab[0], j.ab[1]);
                    }
                    // aa is local to body — scale in place, no pivot/offset
                    if (Array.isArray(j.aa)) {
                        j.aa[0] *= scale;
                        j.aa[1] *= scale;
                    }
                }
                if (j.type === 'lpj' && j.pax != null) {
                    [j.pax, j.pay] = scalePoint(j.pax, j.pay);
                    // plen, pl, pu are distances in physics units → scale with coordinate space
                    if (j.plen != null) j.plen *= scale;
                    if (j.pl   != null) j.pl   *= scale;
                    if (j.pu   != null) j.pu   *= scale;
                    // pms (max speed) and pf (path force) are also in physics units/step → scale
                    if (j.pms  != null) j.pms  *= scale;
                    if (j.pf   != null) j.pf   *= scale;
                }
                if (j.type === 'lsj' && j.sax != null) {
                    [j.sax, j.say] = scalePoint(j.sax, j.say);
                    // slen (spring rest length) is a distance → scale
                    if (j.slen != null) j.slen *= scale;
                    // sf (spring force) is a force, not a distance — leave alone
                }
            }

            // -- 2. Translate bodies & joints to new screen center ---
            // scalePoint pivots around oldCx/oldCy but doesn't move to newCx/newCy.
            // Adding ppmDx/ppmDy completes the transform: newPos = newCx + (oldPos-oldCx)*scale
            physics.ppm = newPpm;
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                b.p[0] += ppmDx;
                b.p[1] += ppmDy;
            }
            for (const j of physics.joints || []) {
                if (!j) continue;
                if (j.type === 'lpj' && j.pax != null) {
                    j.pax += ppmDx;
                    j.pay += ppmDy;
                }
                if (j.type === 'lsj' && j.sax != null && j.say != null) {
                    j.sax += ppmDx;
                    j.say += ppmDy;
                }
                if ((j.type === 'rv' || j.type === 'd') && j.bb === -1 && Array.isArray(j.ab)) {
                    j.ab[0] += ppmDx;
                    j.ab[1] += ppmDy;
                }
            }

            // -- 3. Scale & offset discs once ---
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            (snapshot.discs || []).forEach(d => {
                if (!d) return;
                d.x = oldCx + (d.x - oldCx) * scale + ppmDx;
                d.y = oldCy + (d.y - oldCy) * scale + ppmDy;
            });
            const map = _getActiveMap();
            if (map?.physics) map.physics.ppm = newPpm;

            startGame(snapshot, true);
        }


        // --- ROTATE ---
        function actionRotateMode(val) {
            const S = _stateCtx();
            if (!S) return;
            const deg = parseFloat(val);
            if (isNaN(deg)) {
                showToast('⚠️ Invalid value', 'err');
                return;
            }
            const angle = (deg * Math.PI) / 180,
                ppm = S.state.physics?.ppm ?? 30;
            const cx = 365 / ppm,
                cy = 250 / ppm;
            const rotate = (x, y) => [
                cx + (x - cx) * Math.cos(angle) - (y - cy) * Math.sin(angle),
                cy + (x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle),
            ];
            const rotateVec = (x, y) => [
                x * Math.cos(angle) - y * Math.sin(angle),
                x * Math.sin(angle) + y * Math.cos(angle),
            ];
            const physics = structuredClone(S.state.physics);
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                [b.p[0], b.p[1]] = rotate(b.p[0], b.p[1]);
                b.a = (b.a || 0) + angle;
                // Rotate the force zone direction vector — fz.x/fz.y are world-space,
                // so they must rotate with the map or the force pulls in the wrong direction.
                if (b.fz?.on && (b.fz.x || b.fz.y)) {
                    [b.fz.x, b.fz.y] = rotateVec(b.fz.x, b.fz.y);
                }
            }
            for (const j of physics.joints || []) {
                if (!j) continue;
                if (j.type === 'rv' && Array.isArray(j.aa)) {
                    [j.aa[0], j.aa[1]] = rotateVec(j.aa[0], j.aa[1]);
                }
                if (j.type === 'd') {
                    if (Array.isArray(j.aa)) {
                        [j.aa[0], j.aa[1]] = rotateVec(j.aa[0], j.aa[1]);
                    }
                    if (Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = rotateVec(j.ab[0], j.ab[1]);
                    }
                }
                if (j.type === 'lpj') {
                    if (j.pax != null && j.pay != null) {
                        [j.pax, j.pay] = rotate(j.pax, j.pay);
                    }
                    j.pa = (j.pa || 0) + angle;
                }
                if (j.type === 'lsj' && j.sax != null && j.say != null) {
                    [j.sax, j.say] = rotate(j.sax, j.say);
                }
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            for (const d of snapshot.discs) {
                if (!d) continue;
                [d.x, d.y] = rotate(d.x, d.y);
                [d.xv, d.yv] = [
                    d.xv * Math.cos(angle) - d.yv * Math.sin(angle),
                    d.xv * Math.sin(angle) + d.yv * Math.cos(angle),
                ];
                d.a = (d.a || 0) + angle;
            }
            startGame(snapshot, true);
        }


        function _remapCapZones(snapshot, oldToNew, fxRemap, origPhysics) {
            if (!Array.isArray(snapshot.capZones)) return;

            if (oldToNew === null) {
                // merge mode: all fixtures end up in one body with base=0.
                // cz.i is the original fixture index → remap via fxRemap to merged index.
                // cz.l = base + i. Merged body base=0, so cz.l = newFxIdx.
                for (const cz of snapshot.capZones) {
                    if (!cz || cz.i == null) continue;
                    const newFxIdx = fxRemap?.get(cz.i);
                    if (newFxIdx == null) continue;
                    cz.i = newFxIdx;
                    cz.l = newFxIdx; // base=0 in merged body
                }
            } else {
                // bodies mode: cz.i is a fixture index, find its new index after rebuild.
                // czMap (oldToNew) maps origBodyIdx → newBodyIdx, but we need fixture remapping.
                // fxRemap maps original fixture index → new fixture index.
                for (const cz of snapshot.capZones) {
                    if (!cz || cz.i == null) continue;
                    if (fxRemap && fxRemap.has(cz.i)) {
                        const newFxIdx = fxRemap.get(cz.i);
                        // Find which new body owns this fixture to compute cz.l (base + i)
                        const newPhysics = snapshot.physics;
                        let newBase = 0;
                        for (const b of newPhysics.bodies || []) {
                            if (!Array.isArray(b?.fx)) continue;
                            if (b.fx.includes(newFxIdx)) {
                                newBase = b.fx[0]; // base = first fixture index of the body
                                break;
                            }
                        }
                        cz.i = newFxIdx;
                        cz.l = newBase + newFxIdx;
                    } else if (oldToNew && oldToNew.has(cz.i)) {
                        // fallback: remap as body index (legacy behaviour)
                        cz.i = oldToNew.get(cz.i);
                    }
                }
            }
        }

        function _fixCapZoneFixtures() { /* no-op: intentionally disabled */ }

        // --- SPIN MAP ---
        // mode: 'merge' | 'bodies' | 'shapes'
        // speedDeg: degrees/second (positive = CCW, negative = CW)
        function actionSpinMap(speedDeg, mode) {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast('No state', 'err');
                return;
            }
            const deg = parseFloat(speedDeg);
            if (isNaN(deg) || deg === 0) {
                showToast('SpinMap: speed must be non-zero deg/s', 'err');
                return;
            }
            const md = mode || 'merge';

            const physics = structuredClone(S.state.physics);
            const ppm = physics.ppm ?? 30;
            const cx = 365 / ppm,
                cy = 250 / ppm;
            const angularSpeed = (deg * Math.PI) / 180;

            let _czRemap = undefined; // undefined=skip, null=merge→0, Map=bodies remap
            let _fxRemap = undefined; // fixture index remap for merge mode
            if (md === 'merge') {
                const merged = _mergeAllBodies(physics, cx, cy);
                if (!merged) {
                    showToast('SpinMap: no map bodies found', 'err');
                    return;
                }
                _fixCapZoneFixtures(merged, S.state.physics, S.state.capZones);
                const bod = _mapBodyTemplate(cx, cy, merged.baseS, 'spinmap');
                bod.fx = merged.fxIndices;
                bod.av = angularSpeed;
                bod.id = 0;
                physics.shapes = merged.shapes;
                physics.fixtures = merged.fixtures;
                physics.bodies = [bod];
                physics.joints = [_rvJoint(0, angularSpeed, cx, cy)];
                physics.bro = [0];
                _czRemap = null;
                _fxRemap = merged.fxRemap;
            } else if (md === 'bodies') {
                const parts = _splitBodiesMode(physics, cx, cy);
                if (!parts.length) {
                    showToast('SpinMap: no bodies found', 'err');
                    return;
                }
                const newShapes = [],
                    newFixtures = [],
                    newBodies = [],
                    newJoints = [],
                    newBro = [];
                const czMap = new Map();
                const czFxRemap = new Map();
                // origFxOffset tracks the original fixture index before splitting into parts
                let origFxOffset = 0;
                for (const part of parts) {
                    const shOff = newShapes.length,
                        fiOff = newFixtures.length,
                        boIdx = newBodies.length;
                    czMap.set(part.origBi, boIdx);
                    for (const sh of part.shapes) newShapes.push(sh);
                    for (let li = 0; li < part.fixtures.length; li++) {
                        const fx = part.fixtures[li];
                        const nf = structuredClone(fx);
                        nf.sh += shOff;
                        const newFxIdx = fiOff + li;
                        newFixtures.push(nf);
                        // Map original global fixture index → new global fixture index
                        const origGlobalFx = part.origFxIndices ? part.origFxIndices[li] : (origFxOffset + li);
                        czFxRemap.set(origGlobalFx, newFxIdx);
                    }
                    origFxOffset += part.fixtures.length;
                    const bod = _mapBodyTemplate(part.bpx, part.bpy, part.baseS, 'spinbody');
                    bod.fx = part.fxIdx.map(i => i + fiOff);
                    bod.av = angularSpeed;
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_rvJoint(boIdx, angularSpeed, part.bpx, part.bpy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = czMap;
                _fxRemap = czFxRemap;
            } else if (md === 'shapes') {
                const parts = _splitShapesMode(physics);
                if (!parts.length) {
                    showToast('SpinMap: no shapes found', 'err');
                    return;
                }
                const newShapes = [], newFixtures = [], newBodies = [], newJoints = [], newBro = [];
                const czFxRemap = new Map();
                for (const part of parts) {
                    const boIdx = newBodies.length;
                    const fxList = [];
                    // For np-only shapes: prepend an invisible physics circle (bg layer)
                    // so the joint has something with mass to drive. Circle goes first (bg).
                    if (part.isNp) {
                        const ghostShIdx = newShapes.length;
                        newShapes.push({ type: 'ci', r: 0.0001, c: [0, 0] });
                        const ghostFiIdx = newFixtures.length;
                        newFixtures.push({ sh: ghostShIdx, f: 0, d: false, np: false, ng: true, ig: false });
                        fxList.push(ghostFiIdx);
                    }
                    const shIdx = newShapes.length, fiIdx = newFixtures.length;
                    newShapes.push(part.shape);
                    const nf = structuredClone(part.fixture);
                    nf.sh = shIdx;
                    newFixtures.push(nf);
                    if (part.origFxIdx != null) czFxRemap.set(part.origFxIdx, fiIdx);
                    fxList.push(fiIdx);
                    const bod = _mapBodyTemplate(part.wx, part.wy, part.baseS, 'spinshape');
                    bod.fx = fxList;
                    bod.av = angularSpeed;
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_rvJoint(boIdx, angularSpeed, part.wx, part.wy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = new Map();
                _fxRemap = czFxRemap;
            }

            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            if (_czRemap !== undefined) _remapCapZones(snapshot, _czRemap, _fxRemap, S.state.physics);
            startGame(snapshot, true);
        }

        // ===
        // --- SHARED HELPERS for SpinMap / SpringyMap / ShakeMap ---
        // ===

        // Build a "dynamic body" template for joint-driven map bodies
        function _mapBodyTemplate(px, py, baseS, name) {
            return {
                p: [px, py],
                a: 0,
                av: 0,
                lv: [0, 0],
                cf: { x: 0, y: 0, w: true, ct: 0 },
                fz: { on: false, x: 0, y: 0, d: true, p: true, a: true, t: 0, cf: 0 },
                s: {
                    type: 'd',
                    n: (baseS && baseS.n) || name,
                    fric: baseS && baseS.fric != null ? baseS.fric : 0.5,
                    fricp: false,
                    re: -99999,
                    de: 0.2,
                    ld: 0,
                    ad: 0,
                    fr: false,
                    bu: true,
                    f_c: (baseS && baseS.f_c != null) ? baseS.f_c : 2,
                    f_p: true, // always true — f_p:false on any body (e.g. "side blocker") would break all collisions with players
                    f_1: (baseS && baseS.f_1 != null) ? baseS.f_1 : false,
                    f_2: (baseS && baseS.f_2 != null) ? baseS.f_2 : false,
                    f_3: (baseS && baseS.f_3 != null) ? baseS.f_3 : true,
                    f_4: (baseS && baseS.f_4 != null) ? baseS.f_4 : false,
                },
            };
        }

        // Rotate a shape (bx/ci/po) from its body's local space into the merged
        // body's local space.  The merged body sits at [mcx, mcy] with angle 0.
        function _shapeToMergedLocal(sh, bodyPx, bodyPy, bodyA, mcx, mcy) {
            const rot = (x, y, a) => [x * Math.cos(a) - y * Math.sin(a), x * Math.sin(a) + y * Math.cos(a)];
            const s = structuredClone(sh);
            const scx = (s.c && s.c[0]) || 0,
                scy = (s.c && s.c[1]) || 0;
            const wc = rot(scx, scy, bodyA);
            const newCx = wc[0] + bodyPx - mcx,
                newCy = wc[1] + bodyPy - mcy;
            if (s.type === 'bx') {
                s.c = [newCx, newCy];
                s.a = (s.a || 0) + bodyA;
            } else if (s.type === 'ci') {
                s.c = [newCx, newCy];
            } else if (s.type === 'po' && Array.isArray(s.v)) {
                s.v = s.v.map(pt => {
                    const wv = rot(pt[0], pt[1], bodyA);
                    return [wv[0] + bodyPx - mcx, wv[1] + bodyPy - mcy];
                });
                let gcx = 0,
                    gcy = 0;
                for (const p of s.v) {
                    gcx += p[0];
                    gcy += p[1];
                }
                s.c = [gcx / s.v.length, gcy / s.v.length];
                s.a = 0;
            }
            return s;
        }

        // Get centroid in world space of a shape (after applying body rotation)
        function _shapeCentroidWorld(sh, bodyPx, bodyPy, bodyA) {
            const rot = (x, y, a) => [x * Math.cos(a) - y * Math.sin(a), x * Math.sin(a) + y * Math.cos(a)];
            const cx0 = (sh.c && sh.c[0]) || 0,
                cy0 = (sh.c && sh.c[1]) || 0;
            if (sh.type === 'bx' || sh.type === 'ci') {
                const wc = rot(cx0, cy0, bodyA);
                return [wc[0] + bodyPx, wc[1] + bodyPy];
            }
            if (sh.type === 'po' && Array.isArray(sh.v)) {
                let gcx = 0,
                    gcy = 0;
                for (const p of sh.v) {
                    const wv = rot(p[0], p[1], bodyA);
                    gcx += wv[0];
                    gcy += wv[1];
                }
                return [gcx / sh.v.length + bodyPx, gcy / sh.v.length + bodyPy];
            }
            return [bodyPx, bodyPy];
        }

        // Merge all map bodies (respecting bro order) into a single body at [mcx,mcy].
        // Returns { shapes, fixtures, fxIndices, baseS } ready to drop into physics.
        function _mergeAllBodies(physics, mcx, mcy) {
            // Build a map from body index → body for quick lookup
            const bodyMap = new Map();
            for (let i = 0; i < physics.bodies.length; i++) {
                const b = physics.bodies[i];
                if (b && Array.isArray(b.fx) && b.fx.length > 0) bodyMap.set(i, b);
            }
            if (!bodyMap.size) return null;

            // Determine bro order. bro[0] = foreground, bro[last] = background.
            // We write bg→fg so that higher fixture indices render on top (Bonk convention).
            const oldBro = physics.bro && physics.bro.length
                ? physics.bro
                : [...bodyMap.keys()];

            // Build ordered body list: bg first (bro reversed), then any bodies not in bro
            const seen = new Set();
            const orderedBodyIdx = []; // bg→fg order
            for (let i = oldBro.length - 1; i >= 0; i--) {
                const bi = oldBro[i];
                if (bodyMap.has(bi) && !seen.has(bi)) {
                    seen.add(bi);
                    orderedBodyIdx.push(bi);
                }
            }
            for (const bi of bodyMap.keys()) {
                if (!seen.has(bi)) orderedBodyIdx.push(bi);
            }

            const ordShapes = [], ordFixtures = [], ordFxIdx = [];
            // fxRemap: original physics.fixtures index → final merged fixture index
            const fxRemap = new Map();
            let baseS = null;

            for (const bi of orderedBodyIdx) {
                const b = bodyMap.get(bi);
                if (!baseS) baseS = b.s || {};
                const bpx = (b.p && b.p[0]) || 0,
                    bpy = (b.p && b.p[1]) || 0,
                    ba = b.a || 0;
                for (const fxIdx of b.fx) {
                    const fx = physics.fixtures[fxIdx];
                    if (!fx || typeof fx.sh !== 'number') continue;
                    const sh = physics.shapes[fx.sh];
                    if (!sh) continue;
                    const finalIdx = ordFixtures.length;
                    ordShapes.push(_shapeToMergedLocal(sh, bpx, bpy, ba, mcx, mcy));
                    const nf = structuredClone(fx);
                    nf.sh = finalIdx;
                    ordFixtures.push(nf);
                    ordFxIdx.push(finalIdx);
                    fxRemap.set(fxIdx, finalIdx);
                }
            }

            if (!ordShapes.length) return null;

            return {
                shapes: ordShapes,
                fixtures: ordFixtures,
                fxIndices: ordFxIdx,
                baseS: baseS || {},
                count: ordShapes.length,
                fxRemap,
            };
        }

        // Split all map bodies into individual bodies (one per original body, NO shape split).
        // Each body keeps its own shapes but is made dynamic and gets its own joint.
        // Returns array of { body, shapes, fixtures }
        function _splitBodiesMode(physics, cx, cy) {
            const bro = (physics.bro && physics.bro.length) ? physics.bro : physics.bodies.map((_, i) => i);
            const seen = new Set();
            const orderedBi = [];
            for (const bi of bro) { if (!seen.has(bi)) { seen.add(bi); orderedBi.push(bi); } }
            for (let i = 0; i < physics.bodies.length; i++) { if (!seen.has(i)) orderedBi.push(i); }
            const result = [];
            for (const bi of orderedBi) {
                const b = physics.bodies[bi];
                if (!b || !Array.isArray(b.fx) || !b.fx.length) continue;
                const bpx = (b.p && b.p[0]) || 0, bpy = (b.p && b.p[1]) || 0;
                const shapes = [], fixtures = [], fxIdx = [], origFxIndices = [];
                for (const fxI of b.fx) {
                    const fx = physics.fixtures[fxI];
                    if (!fx || typeof fx.sh !== 'number') continue;
                    const sh = physics.shapes[fx.sh];
                    if (!sh) continue;
                    const si = shapes.length;
                    shapes.push(structuredClone(sh));
                    const nfx = structuredClone(fx);
                    nfx.sh = si;
                    fixtures.push(nfx);
                    fxIdx.push(fixtures.length - 1);
                    origFxIndices.push(fxI); // global fixture index in original physics
                }
                if (!shapes.length) continue;
                result.push({ origBi: bi, bpx, bpy, baseS: b.s || {}, shapes, fixtures, fxIdx, origFxIndices });
            }
            return result;
        }

        // Split all shapes into individual bodies (one per shape — max chaos mode).
        // Each shape becomes a body at its world-space centroid position.
        // The shape itself is recentered to local [0,0] of that new body.
        function _splitShapesMode(physics) {
            const bro = (physics.bro && physics.bro.length) ? physics.bro : physics.bodies.map((_, i) => i);
            const seen = new Set();
            const orderedBi = [];
            for (const bi of bro) { if (!seen.has(bi)) { seen.add(bi); orderedBi.push(bi); } }
            for (let i = 0; i < physics.bodies.length; i++) { if (!seen.has(i)) orderedBi.push(i); }
            const result = [];
            for (const bi of orderedBi) {
                const b = physics.bodies[bi];
                if (!b || !Array.isArray(b.fx) || !b.fx.length) continue;
                const bpx = (b.p && b.p[0]) || 0, bpy = (b.p && b.p[1]) || 0, ba = b.a || 0;
                for (const fxI of b.fx) {
                    const fx = physics.fixtures[fxI];
                    if (!fx || typeof fx.sh !== 'number') continue;
                    const sh = physics.shapes[fx.sh];
                    if (!sh) continue;
                    const [wx, wy] = _shapeCentroidWorld(sh, bpx, bpy, ba);
                    const ls = _shapeToMergedLocal(sh, bpx, bpy, ba, wx, wy);
                    const nfx = structuredClone(fx);
                    nfx.sh = 0;
                    result.push({ wx, wy, baseS: b.s || {}, shape: ls, fixture: nfx, origFxIdx: fxI, isNp: !!fx.np });
                }
            }
            return result;
        }

        // --- BUILD JOINT helpers ---
        function _rvJoint(ba, angularSpeed, anchorX, anchorY) {
            return {
                type: 'rv',
                d: { la: 0, ua: 0, mmt: 9999, ms: -angularSpeed, el: false, em: true, cc: false, bf: 0, dl: false },
                ba,
                bb: -1,
                aa: [0, 0],
                ab: [anchorX, anchorY],
                ra: 0,
            };
        }
        function _lsjJoint(ba, sf, slen, anchorX, anchorY) {
            return { type: 'lsj', d: { cc: false, bf: 0, dl: true }, ba, bb: -1, sax: anchorX, say: anchorY, sf, slen };
        }
        // For lpj: anchor is moved far up (cy - HIDE_OFFSET) so the direction line is off-screen
        const _LPJ_HIDE_OFFSET = 1000; // reserved, not used
        function _lpjJoint(ba, angRad, plenPhys, mSpeed, anchorX, anchorY) {
            return {
                type: 'lpj',
                d: { cc: false, bf: 0, dl: true },
                ba,
                bb: -1,
                pax: anchorX,
                pay: anchorY,
                pa: angRad,
                pf: mSpeed,
                pl: 0,
                pu: 0,
                plen: plenPhys,
                pms: mSpeed,
            };
        }

        // --- SPRINGY MAP ---
        // mode: 'merge' | 'bodies' | 'shapes'
        function actionSpringyMap(springForce, springLen, mode) {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast('No state', 'err');
                return;
            }
            const sf = parseFloat(springForce ?? SPRINGY_CFG.springForce);
            const slen = parseFloat(springLen ?? SPRINGY_CFG.springLen);
            const md = mode || 'merge';
            if (isNaN(sf) || sf <= 0) {
                showToast('SpringyMap: force must be > 0', 'err');
                return;
            }
            if (isNaN(slen) || slen < 0) {
                showToast('SpringyMap: length must be ≥ 0', 'err');
                return;
            }

            const physics = structuredClone(S.state.physics);
            const ppm = physics.ppm ?? 30;
            const cx = 365 / ppm,
                cy = 250 / ppm;

            let _czRemap = undefined;
            let _fxRemap = undefined;
            if (md === 'merge') {
                const merged = _mergeAllBodies(physics, cx, cy);
                if (!merged) {
                    showToast('SpringyMap: no map bodies found', 'err');
                    return;
                }
                _fixCapZoneFixtures(merged, S.state.physics, S.state.capZones);
                const bod = _mapBodyTemplate(cx, cy, merged.baseS, 'springymap');
                bod.fx = merged.fxIndices;
                bod.id = 0;
                physics.shapes = merged.shapes;
                physics.fixtures = merged.fixtures;
                physics.bodies = [bod];
                physics.joints = [_lsjJoint(0, sf, slen / ppm, cx, cy)];
                physics.bro = [0];
                _czRemap = null;
                _fxRemap = merged.fxRemap;
            } else if (md === 'bodies') {
                const parts = _splitBodiesMode(physics, cx, cy);
                if (!parts.length) {
                    showToast('SpringyMap: no bodies found', 'err');
                    return;
                }
                const newShapes = [],
                    newFixtures = [],
                    newBodies = [],
                    newJoints = [],
                    newBro = [];
                const czMap = new Map();
                const czFxRemap = new Map();
                for (const part of parts) {
                    const shOff = newShapes.length,
                        fiOff = newFixtures.length,
                        boIdx = newBodies.length;
                    czMap.set(part.origBi, boIdx);
                    for (const sh of part.shapes) newShapes.push(sh);
                    for (let li = 0; li < part.fixtures.length; li++) {
                        const fx = part.fixtures[li];
                        const nf = structuredClone(fx);
                        nf.sh += shOff;
                        const newFxIdx = fiOff + li;
                        newFixtures.push(nf);
                        const origGlobalFx = part.origFxIndices ? part.origFxIndices[li] : (fiOff + li);
                        czFxRemap.set(origGlobalFx, newFxIdx);
                    }
                    const bod = _mapBodyTemplate(part.bpx, part.bpy, part.baseS, 'springybody');
                    bod.fx = part.fxIdx.map(i => i + fiOff);
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_lsjJoint(boIdx, sf, slen / ppm, part.bpx, part.bpy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = czMap;
                _fxRemap = czFxRemap;
            } else if (md === 'shapes') {
                const parts = _splitShapesMode(physics);
                if (!parts.length) {
                    showToast('SpringyMap: no shapes found', 'err');
                    return;
                }
                const newShapes = [], newFixtures = [], newBodies = [], newJoints = [], newBro = [];
                const czFxRemap = new Map();
                for (const part of parts) {
                    const boIdx = newBodies.length;
                    const fxList = [];
                    if (part.isNp) {
                        const ghostShIdx = newShapes.length;
                        newShapes.push({ type: 'ci', r: 0.0001, c: [0, 0] });
                        const ghostFiIdx = newFixtures.length;
                        newFixtures.push({ sh: ghostShIdx, f: 0, d: false, np: false, ng: true, ig: false });
                        fxList.push(ghostFiIdx);
                    }
                    const shIdx = newShapes.length, fiIdx = newFixtures.length;
                    newShapes.push(part.shape);
                    const nf = structuredClone(part.fixture);
                    nf.sh = shIdx;
                    newFixtures.push(nf);
                    if (part.origFxIdx != null) czFxRemap.set(part.origFxIdx, fiIdx);
                    fxList.push(fiIdx);
                    const bod = _mapBodyTemplate(part.wx, part.wy, part.baseS, 'springyshape');
                    bod.fx = fxList;
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_lsjJoint(boIdx, sf, slen / ppm, part.wx, part.wy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = new Map();
                _fxRemap = czFxRemap;
            }

            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            if (_czRemap !== undefined) _remapCapZones(snapshot, _czRemap, _fxRemap, S.state.physics);
            startGame(snapshot, true);
        }

        // --- SHAKE MAP ---
        // mode: 'merge' | 'bodies' | 'shapes'
        function actionFpath(angle, pathLen, moveSpeed, mode) {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast('No state', 'err');
                return;
            }
            const angDeg = parseFloat(angle ?? FPATH_CFG.angle);
            const pLen = parseFloat(pathLen ?? FPATH_CFG.pathLen);
            const mSpeed = parseFloat(moveSpeed ?? FPATH_CFG.moveSpeed);
            const md = mode || 'merge';
            if (isNaN(angDeg)) {
                showToast('ShakeMap: invalid angle', 'err');
                return;
            }
            if (isNaN(pLen) || pLen <= 0) {
                showToast('ShakeMap: path length must be > 0', 'err');
                return;
            }
            if (isNaN(mSpeed) || mSpeed <= 0) {
                showToast('ShakeMap: move speed must be > 0', 'err');
                return;
            }

            const physics = structuredClone(S.state.physics);
            const ppm = physics.ppm ?? 30;
            const cx = 365 / ppm,
                cy = 250 / ppm;
            const angRad = (angDeg * Math.PI) / 180;
            const plenPhys = pLen / ppm;

            let _czRemap = undefined;
            let _fxRemap = undefined;
            if (md === 'merge') {
                const merged = _mergeAllBodies(physics, cx, cy);
                if (!merged) {
                    showToast('ShakeMap: no map bodies found', 'err');
                    return;
                }
                _fixCapZoneFixtures(merged, S.state.physics, S.state.capZones);
                const bod = _mapBodyTemplate(cx, cy, merged.baseS, 'shakemap');
                bod.fx = merged.fxIndices;
                bod.id = 0;
                physics.shapes = merged.shapes;
                physics.fixtures = merged.fixtures;
                physics.bodies = [bod];
                physics.joints = [_lpjJoint(0, angRad, plenPhys, mSpeed, cx, cy)];
                physics.bro = [0];
                _czRemap = null;
                _fxRemap = merged.fxRemap;
            } else if (md === 'bodies') {
                const parts = _splitBodiesMode(physics, cx, cy);
                if (!parts.length) {
                    showToast('ShakeMap: no bodies found', 'err');
                    return;
                }
                const newShapes = [],
                    newFixtures = [],
                    newBodies = [],
                    newJoints = [],
                    newBro = [];
                const czMap = new Map();
                const czFxRemap = new Map();
                for (const part of parts) {
                    const shOff = newShapes.length,
                        fiOff = newFixtures.length,
                        boIdx = newBodies.length;
                    czMap.set(part.origBi, boIdx);
                    for (const sh of part.shapes) newShapes.push(sh);
                    for (let li = 0; li < part.fixtures.length; li++) {
                        const fx = part.fixtures[li];
                        const nf = structuredClone(fx);
                        nf.sh += shOff;
                        const newFxIdx = fiOff + li;
                        newFixtures.push(nf);
                        const origGlobalFx = part.origFxIndices ? part.origFxIndices[li] : (fiOff + li);
                        czFxRemap.set(origGlobalFx, newFxIdx);
                    }
                    const bod = _mapBodyTemplate(part.bpx, part.bpy, part.baseS, 'shakebody');
                    bod.fx = part.fxIdx.map(i => i + fiOff);
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_lpjJoint(boIdx, angRad, plenPhys, mSpeed, part.bpx, part.bpy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = czMap;
                _fxRemap = czFxRemap;
            } else if (md === 'shapes') {
                const parts = _splitShapesMode(physics);
                if (!parts.length) {
                    showToast('ShakeMap: no shapes found', 'err');
                    return;
                }
                const newShapes = [], newFixtures = [], newBodies = [], newJoints = [], newBro = [];
                const czFxRemap = new Map();
                for (const part of parts) {
                    const boIdx = newBodies.length;
                    const fxList = [];
                    if (part.isNp) {
                        const ghostShIdx = newShapes.length;
                        newShapes.push({ type: 'ci', r: 0.0001, c: [0, 0] });
                        const ghostFiIdx = newFixtures.length;
                        newFixtures.push({ sh: ghostShIdx, f: 0, d: false, np: false, ng: true, ig: false });
                        fxList.push(ghostFiIdx);
                    }
                    const shIdx = newShapes.length, fiIdx = newFixtures.length;
                    newShapes.push(part.shape);
                    const nf = structuredClone(part.fixture);
                    nf.sh = shIdx;
                    newFixtures.push(nf);
                    if (part.origFxIdx != null) czFxRemap.set(part.origFxIdx, fiIdx);
                    fxList.push(fiIdx);
                    const bod = _mapBodyTemplate(part.wx, part.wy, part.baseS, 'shakeshape');
                    bod.fx = fxList;
                    bod.id = boIdx;
                    newBodies.push(bod);
                    newBro.push(boIdx);
                    newJoints.push(_lpjJoint(boIdx, angRad, plenPhys, mSpeed, part.wx, part.wy));
                }
                physics.shapes = newShapes;
                physics.fixtures = newFixtures;
                physics.bodies = newBodies;
                physics.joints = newJoints;
                physics.bro = newBro;
                _czRemap = new Map();
                _fxRemap = czFxRemap;
            }

            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            if (_czRemap !== undefined) _remapCapZones(snapshot, _czRemap, _fxRemap, S.state.physics);
            startGame(snapshot, true);
        }

        // --- JELLY MAP --- (this is still buggy lol)
        // --- TRANSLATE (MAP OFFSET) ---
        function actionTranslateMap(offsetX, offsetY, includePlayers = false) {
            const S = _requireState();
            if (!S) return;
            const ox = parseFloat(offsetX),
                oy = parseFloat(offsetY);
            if (isNaN(ox) || isNaN(oy)) {
                showToast('⚠️ Invalid offset values', 'err');
                return;
            }
            const ppm = S.state.physics?.ppm ?? 30;
            // Convert editor units to physics units
            const dx = ox / ppm,
                dy = oy / ppm;
            const physics = structuredClone(S.state.physics);
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                b.p[0] += dx;
                b.p[1] += dy;
            }
            for (const j of physics.joints || []) {
                if (!j) continue;
                if ((j.type === 'rv' || j.type === 'd') && j.bb === -1 && Array.isArray(j.ab)) {
                    j.ab[0] += dx;
                    j.ab[1] += dy;
                }
                if (j.type === 'lpj' && j.pax != null) {
                    j.pax += dx;
                    j.pay += dy;
                }
                if (j.type === 'lsj' && j.sax != null) {
                    j.sax += dx;
                    j.say += dy;
                }
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            if (includePlayers) {
                for (const d of snapshot.discs) {
                    if (!d) continue;
                    d.x += dx;
                    d.y += dy;
                }
            }
            startGame(snapshot, true);
        }

        // --- STRETCH ---
        const ELLIPSE_SIDES = 40;
        const rotVec = (x, y, t) => [x * Math.cos(t) - y * Math.sin(t), x * Math.sin(t) + y * Math.cos(t)];
        function stretchLocalVec(x, y, la, ba, sx, sy) {
            const [bx, by] = rotVec(x, y, la);
            const cb = Math.cos(ba),
                sb = Math.sin(ba);
            const wx = bx * cb - by * sb,
                wy = bx * sb + by * cb;
            const swx = wx * sx,
                swy = wy * sy;
            const nbx = swx * cb + swy * sb,
                nby = -swx * sb + swy * cb;
            return rotVec(nbx, nby, -la);
        }
        function circleToEllipseVerts(r, ba, sx, sy) {
            return Array.from({ length: ELLIPSE_SIDES }, (_, i) => {
                const t = (i / ELLIPSE_SIDES) * Math.PI * 2;
                return stretchLocalVec(Math.cos(t) * r, Math.sin(t) * r, 0, ba, sx, sy);
            });
        }

        function actionStretchMode(scaleX, scaleY) {
            const S = _requireState();
            if (!S) return;
            const sx = parseFloat(scaleX),
                sy = parseFloat(scaleY);
            if (isNaN(sx) || isNaN(sy)) {
                showToast('⚠️ Invalid values', 'err');
                return;
            }
            if (sx <= 0 || sy <= 0) {
                showToast('⚠️ Values must be greater than 0', 'err');
                return;
            }
            const isUniform = sx === sy;
            const ppm = S.state.physics?.ppm ?? 30,
                cx = 365 / ppm,
                cy = 250 / ppm;
            const scalePoint = (x, y) => [cx + (x - cx) * sx, cy + (y - cy) * sy];
            const physics = structuredClone(S.state.physics);
            const shapeBodyAngle = new Map();
            for (const b of physics.bodies) {
                if (!Array.isArray(b?.fx)) continue;
                for (const fi of b.fx) {
                    const f = physics.fixtures?.[fi];
                    if (f && typeof f.sh === 'number') shapeBodyAngle.set(f.sh, b.a || 0);
                }
            }
            let nBodies = 0;
            // Unified stretch: every shape type is reduced to "a set of shape-local
            // vertices" and run through the SAME stretchLocalVec(...) transform.
            // This replaces the old bx/po/ci branches (each with their own hand-rolled
            // rotate→scale→rotate math) with a single code path.
            for (let i = 0; i < physics.shapes.length; i++) {
                const sh = physics.shapes[i];
                if (!sh) continue;
                const ba = shapeBodyAngle.get(i) || 0;
                const ocx = sh.c ? sh.c[0] : 0,
                    ocy = sh.c ? sh.c[1] : 0;
                const [ncx, ncy] = stretchLocalVec(ocx, ocy, 0, ba, sx, sy);

                let verts,
                    addCenter = true,
                    keepOwnA = false;
                if (sh.type === 'bx') {
                    const hw = sh.w / 2,
                        hh = sh.h / 2,
                        A = sh.a || 0;
                    verts = [
                        [-hw, -hh],
                        [hw, -hh],
                        [hw, hh],
                        [-hw, hh],
                    ].map(([x, y]) => rotVec(x, y, A));
                } else if (sh.type === 'po' && Array.isArray(sh.v)) {
                    // sh.v in 'po' are already ABSOLUTE FINAL coordinates (body-local).
                    // Unlike 'bx' (where 'a' is needed to build corners from w/h), in 'po'
                    // the engine does NOT re-apply 'a' over 'v' — it's an inert/editor-inherited
                    // field. Rotating 'v' by 'a' here would introduce an extra rotation that
                    // was never part of the actual render.
                    verts = sh.v;
                    addCenter = false;
                    keepOwnA = true;
                } else if (sh.type === 'ci') {
                    if (isUniform) {
                        // uniform scale keeps it a cheap circle - no need to poly-ize
                        sh.r = Math.abs(sh.r * sx);
                        sh.c = [ncx, ncy];
                        continue;
                    }
                    verts = circleToEllipseVerts(sh.r, 0, 1, 1);
                } else continue;

                const keptA = sh.a;
                sh.v = verts.map(([x, y]) => {
                    const [wx, wy] = stretchLocalVec(addCenter ? x + ocx : x, addCenter ? y + ocy : y, 0, ba, sx, sy);
                    return [wx, wy];
                });
                sh.type = 'po';
                sh.s = 1;
                sh.a = keepOwnA ? keptA : 0;
                sh.c = [ncx, ncy];
                sh.sk = sh.sk ?? false;
                delete sh.w;
                delete sh.h;
                delete sh.r;
            }
            for (const b of physics.bodies) {
                if (!b?.p) continue;
                [b.p[0], b.p[1]] = scalePoint(b.p[0], b.p[1]);
                nBodies++;
            }
            if (!nBodies) {
                showToast('⚠️ No objects found in map', 'err');
                return;
            }
            // Helper: scale a body-local anchor vector, reusing the same primitive as above
            const bodyAngleMap = new Map();
            for (let bi = 0; bi < physics.bodies.length; bi++) {
                const b = physics.bodies[bi];
                if (b?.p) bodyAngleMap.set(bi, b.a || 0);
            }
            const scaleBodyLocal = (vec, bodyIdx) =>
                stretchLocalVec(vec[0], vec[1], 0, bodyAngleMap.get(bodyIdx) || 0, sx, sy);
            for (const j of physics.joints || []) {
                if (!j) continue;
                if (j.type === 'rv') {
                    if (Array.isArray(j.aa)) {
                        [j.aa[0], j.aa[1]] = scaleBodyLocal(j.aa, j.ba);
                    }
                    if (j.bb === -1 && Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = scalePoint(j.ab[0], j.ab[1]);
                    } else if (Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = scaleBodyLocal(j.ab, j.bb);
                    }
                }
                if (j.type === 'd') {
                    if (Array.isArray(j.aa)) {
                        [j.aa[0], j.aa[1]] = scaleBodyLocal(j.aa, j.ba);
                    }
                    if (j.bb === -1 && Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = scalePoint(j.ab[0], j.ab[1]);
                    } else if (Array.isArray(j.ab)) {
                        [j.ab[0], j.ab[1]] = scaleBodyLocal(j.ab, j.bb);
                    }
                    if (j.len != null) j.len *= isUniform ? sx : Math.sqrt(sx * sy);
                }
                if (j.type === 'lpj') {
                    if (j.pax != null && j.pay != null) {
                        [j.pax, j.pay] = scalePoint(j.pax, j.pay);
                    }
                    for (const k of ['plen', 'pf', 'pms', 'pl', 'pu']) {
                        if (j[k] != null) j[k] *= isUniform ? sx : Math.sqrt(sx * sy);
                    }
                }
                if (j.type === 'lsj') {
                    if (j.sax != null && j.say != null) {
                        [j.sax, j.say] = scalePoint(j.sax, j.say);
                    }
                    if (j.slen != null) j.slen *= isUniform ? sx : Math.sqrt(sx * sy);
                }
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            startGame(snapshot, true);
        }

        // --- COLOR HELPERS ---
        const _rgbFrom = hex => ({ r: (hex >> 16) & 0xff, g: (hex >> 8) & 0xff, b: hex & 0xff });
        const _rgbTo = (r, g, b) => ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
        const _hsvToRgb = (h, s, v) => {
            const i = Math.floor(h / 60) % 6,
                f = h / 60 - Math.floor(h / 60),
                p = v * (1 - s),
                q = v * (1 - f * s),
                t = v * (1 - (1 - f) * s);
            const [r, g, b] = [
                [v, t, p],
                [q, v, p],
                [p, v, t],
                [p, q, v],
                [t, p, v],
                [v, p, q],
            ][i];
            return _rgbTo(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
        };
        const _rgbToHsv = (r, g, b) => {
            r /= 255;
            g /= 255;
            b /= 255;
            const max = Math.max(r, g, b),
                min = Math.min(r, g, b),
                d = max - min;
            let h = 0;
            if (d > 0) {
                if (max === r) h = ((g - b) / d) % 6;
                else if (max === g) h = (b - r) / d + 2;
                else h = (r - g) / d + 4;
                h = (h * 60 + 360) % 360;
            }
            return { h, s: max === 0 ? 0 : d / max, v: max };
        };
        const RAINBOW_COLORS = [
            0xe74c3c, 0xe67e22, 0xf1c40f, 0x2ecc71, 0x1abc9c, 0x3498db, 0x9b59b6, 0xe91e63, 0x00bcd4, 0x8bc34a,
            0xff5722, 0x607d8b, 0xff9800, 0x673ab7,
        ];

        function actionRainbowMap(mode = 'random', hueShift = 180, solidHue = 0, solidSat = 1) {
            _applyPhysics((_, ph) => {
                let c = 0;
                for (const f of ph.fixtures) {
                    if (!f) continue;
                    const cur = f.f ?? 0x4f7cac;
                    if (mode === 'random') f.f = RAINBOW_COLORS[Math.floor(Math.random() * RAINBOW_COLORS.length)];
                    else if (mode === 'invert') f.f = 0xffffff ^ (cur & 0xffffff);
                    else if (mode === 'gray') {
                        const { r, g, b } = _rgbFrom(cur);
                        const avg = Math.round(r * 0.299 + g * 0.587 + b * 0.114);
                        f.f = _rgbTo(avg, avg, avg);
                    } else if (mode === 'hue') {
                        const { r, g, b } = _rgbFrom(cur);
                        const { h, s, v } = _rgbToHsv(r, g, b);
                        f.f = _hsvToRgb((h + hueShift) % 360, s, v);
                    } else if (mode === 'bright') {
                        const { r, g, b } = _rgbFrom(cur);
                        f.f = _rgbTo(
                            Math.min(255, Math.round(r * hueShift)),
                            Math.min(255, Math.round(g * hueShift)),
                            Math.min(255, Math.round(b * hueShift))
                        );
                    } else if (mode === 'solid') {
                        const { r, g, b } = _rgbFrom(cur);
                        const { v } = _rgbToHsv(r, g, b);
                        f.f = _hsvToRgb(solidHue, solidSat, Math.max(0.15, v));
                    }
                    c++;
                }
                return c;
            });
        }

        function actionRandomizeMap() {
            const MAX_X = 325,
                MAX_Y = 210;
            _applyPhysics((_, ph) => {
                let c = 0;
                for (const b of ph.bodies) {
                    if (!b) continue;
                    b.a = (b.a || 0) + (Math.random() * 40 - 20) * (Math.PI / 180);
                    if (b.p) {
                        b.p[0] = Math.max(-MAX_X, Math.min(MAX_X, (b.p[0] || 0) + (Math.random() * 24 - 12)));
                        b.p[1] = Math.max(-MAX_Y, Math.min(MAX_Y, (b.p[1] || 0) + (Math.random() * 24 - 12)));
                    }
                    c++;
                }
                if (!c) {
                    showToast('⚠️ No objects found in map', 'err');
                    return false;
                }
                return c;
            });
        }

        // --- INFLATE ---
        // Inflates or deflates each shape locally from its own center.
        // Unlike stretch (which scales from the map center), this function
        // makes each individual piece grow/shrink in place.
        const INFLATE_CFG = { scale: 1.5 };
        function actionInflateMode(scale) {
            const S = _requireState();
            if (!S) return;
            const sc = parseFloat(scale || INFLATE_CFG.scale);
            if (isNaN(sc) || sc <= 0) {
                showToast('⚠️ Scale must be > 0', 'err');
                return;
            }
            const physics = structuredClone(S.state.physics);
            let c = 0;
            for (const sh of physics.shapes) {
                if (!sh) continue;
                if (sh.type === 'bx') {
                    sh.w = (sh.w || 1) * sc;
                    sh.h = (sh.h || 1) * sc;
                    c++;
                } else if (sh.type === 'ci') {
                    sh.r = (sh.r || 1) * sc;
                    c++;
                } else if (sh.type === 'po' && Array.isArray(sh.v)) {
                    // Compute local centroid
                    const n = sh.v.length;
                    if (!n) continue;
                    let gcx = 0,
                        gcy = 0;
                    for (const [x, y] of sh.v) {
                        gcx += x;
                        gcy += y;
                    }
                    gcx /= n;
                    gcy /= n;
                    sh.v = sh.v.map(([x, y]) => [gcx + (x - gcx) * sc, gcy + (y - gcy) * sc]);
                    c++;
                }
            }
            if (!c) {
                showToast('⚠️ No shapes to inflate', 'err');
                return;
            }
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            startGame(snapshot, true);
        }

        // --- SWAP SHAPES ---
        // Swaps geometry (shapes via fixtures) between random bodies.
        // Result: a map where each platform "stole" the shape of another.
        function actionSwapShapes() {
            const S = _requireState();
            if (!S) return;
            const physics = structuredClone(S.state.physics);

            // Only work with bodies that have shapes
            const validBodyIdxs = physics.bodies.map((b, i) => (b?.fx?.length ? i : null)).filter(i => i !== null);
            if (validBodyIdxs.length < 2) {
                showToast('⚠️ Need 2+ bodies with shapes', 'err');
                return;
            }

            // Helpers: rotate a 2D point by angle a
            const rot = (x, y, a) => [x * Math.cos(a) - y * Math.sin(a), x * Math.sin(a) + y * Math.cos(a)];
            const unrot = (x, y, a) => rot(x, y, -a);

            // Get body world position and angle
            const bodyPos = bi => {
                const b = physics.bodies[bi];
                return { px: b.p?.[0] ?? 0, py: b.p?.[1] ?? 0, a: b.a ?? 0 };
            };

            // Convert a shape's key points to world coords given source body transform
            const shapeToWorld = (sh, srcPos) => {
                const s = structuredClone(sh);
                const { px, py, a } = srcPos;
                const xfPt = (lx, ly) => {
                    const [wx, wy] = rot(lx, ly, a);
                    return [wx + px, wy + py];
                };
                if (s.type === 'po' && Array.isArray(s.v)) {
                    const cx = s.c?.[0] ?? 0,
                        cy = s.c?.[1] ?? 0;
                    s.v = s.v.map(([vx, vy]) => {
                        const [wx, wy] = xfPt(cx + vx, cy + vy);
                        return [wx, wy];
                    });
                    s.c = [0, 0];
                } else if (s.type === 'bx' || s.type === 'ci') {
                    const cx = s.c?.[0] ?? 0,
                        cy = s.c?.[1] ?? 0;
                    const [wx, wy] = xfPt(cx, cy);
                    s.c = [wx, wy];
                    if (s.type === 'bx') s.a = (s.a ?? 0) + a;
                }
                return s;
            };

            // Convert a world-space shape back to local coords of destination body
            const shapeToLocal = (sh, dstPos) => {
                const s = structuredClone(sh);
                const { px, py, a } = dstPos;
                const xfPt = (wx, wy) => {
                    const [lx, ly] = unrot(wx - px, wy - py, a);
                    return [lx, ly];
                };
                if (s.type === 'po' && Array.isArray(s.v)) {
                    s.v = s.v.map(([wx, wy]) => xfPt(wx, wy));
                    s.c = [0, 0];
                } else if (s.type === 'bx' || s.type === 'ci') {
                    const [cx, cy] = s.c ?? [0, 0];
                    const [lx, ly] = xfPt(cx, cy);
                    s.c = [lx, ly];
                    if (s.type === 'bx') s.a = (s.a ?? 0) - a;
                }
                return s;
            };

            // For each valid body, collect the shape objects (in world space)
            const worldShapeGroups = validBodyIdxs.map(bi => {
                const srcPos = bodyPos(bi);
                const shapeIdxs = [];
                for (const fi of physics.bodies[bi].fx) {
                    const f = physics.fixtures?.[fi];
                    if (f != null && typeof f.sh === 'number') shapeIdxs.push(f.sh);
                }
                return [...new Set(shapeIdxs)].map(si => shapeToWorld(physics.shapes[si], srcPos));
            });

            // Fisher-Yates shuffle of world shape groups
            for (let i = worldShapeGroups.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [worldShapeGroups[i], worldShapeGroups[j]] = [worldShapeGroups[j], worldShapeGroups[i]];
            }

            // Write shuffled world shapes back as local shapes for each body
            validBodyIdxs.forEach((bi, idx) => {
                const dstPos = bodyPos(bi);
                const incoming = worldShapeGroups[idx].map(ws => shapeToLocal(ws, dstPos));

                // Find which shape slots this body owns
                const ownedShapeIdxs = [
                    ...new Set(physics.bodies[bi].fx.map(fi => physics.fixtures?.[fi]?.sh).filter(si => si != null)),
                ];

                const minLen = Math.min(ownedShapeIdxs.length, incoming.length);
                for (let k = 0; k < minLen; k++) physics.shapes[ownedShapeIdxs[k]] = incoming[k];

                if (incoming.length > ownedShapeIdxs.length) {
                    // More incoming shapes — append extras
                    for (let k = minLen; k < incoming.length; k++) {
                        const newSi = physics.shapes.length;
                        physics.shapes.push(incoming[k]);
                        const lastFi = physics.bodies[bi].fx[physics.bodies[bi].fx.length - 1];
                        const newFi = physics.fixtures.length;
                        physics.fixtures.push(Object.assign(structuredClone(physics.fixtures[lastFi]), { sh: newSi }));
                        physics.bodies[bi].fx.push(newFi);
                    }
                } else if (incoming.length < ownedShapeIdxs.length) {
                    // Fewer incoming — prune extra fixtures from this body
                    physics.bodies[bi].fx.splice(-(ownedShapeIdxs.length - incoming.length));
                }
            });

            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            startGame(snapshot, true);
        }

        // --- PALETTE SHUFFLE ---
        // Collects all colors currently used in the map's fixtures
        // and redistributes them randomly across all fixtures.
        function actionPaletteShuffle() {
            _applyPhysics((_, ph) => {
                const fixtures = ph.fixtures.filter(Boolean);
                if (!fixtures.length) {
                    showToast('⚠️ No fixtures found', 'err');
                    return false;
                }
                // Collect all colors currently in use
                const palette = fixtures.map(f => f.f ?? 0x4f7cac);
                if (new Set(palette).size < 2) {
                    showToast('⚠️ Only one color in map, nothing to shuffle', 'err');
                    return false;
                }
                // Fisher-Yates shuffle of the palette copy
                const shuffled = [...palette];
                for (let i = shuffled.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * (i + 1));
                    [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
                }
                fixtures.forEach((f, i) => (f.f = shuffled[i]));
                return palette.length;
            });
        }

        // --- WOBBLY ---
        const WOBBLY_CFG = { intensity: 10 };
        function actionWobblyMode(intensity) {
            const S = _requireState();
            if (!S) return;
            const physics = structuredClone(S.state.physics);
            const amp = (intensity || WOBBLY_CFG.intensity) / 10;
            const noise = () => (Math.random() - 0.5) * 2 * amp;
            const convexHull = pts => {
                const p = [...pts].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
                const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
                const lower = [],
                    upper = [];
                for (const pt of p) {
                    while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], pt) <= 0)
                        lower.pop();
                    lower.push(pt);
                }
                for (const pt of [...p].reverse()) {
                    while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], pt) <= 0)
                        upper.pop();
                    upper.push(pt);
                }
                upper.pop();
                lower.pop();
                return [...lower, ...upper];
            };
            const wobble = verts => {
                const cx = verts.reduce((a, [x]) => a + x, 0) / verts.length;
                const cy = verts.reduce((a, [, y]) => a + y, 0) / verts.length;
                return convexHull(
                    verts.map(([x, y]) => {
                        const dx = x - cx,
                            dy = y - cy;
                        const len = Math.sqrt(dx * dx + dy * dy) || 1;
                        const push = noise();
                        return [x + (dx / len) * push, y + (dy / len) * push];
                    })
                );
            };
            physics.shapes = physics.shapes.map(sh => {
                if (!sh) return sh;
                if (sh.type === 'po') {
                    const s = structuredClone(sh);
                    s.v = wobble(s.v);
                    return s;
                }
                if (sh.type === 'bx') {
                    const hw = sh.w / 2,
                        hh = sh.h / 2,
                        a = sh.a || 0;
                    const cos = Math.cos(a),
                        sin = Math.sin(a);
                    const cx = sh.c?.[0] ?? 0,
                        cy = sh.c?.[1] ?? 0;
                    const corners = [
                        [-hw, -hh],
                        [hw, -hh],
                        [hw, hh],
                        [-hw, hh],
                    ].map(([x, y]) => [cx + x * cos - y * sin, cy + x * sin + y * cos]);
                    return { type: 'po', v: wobble(corners), s: 1, a: 0, c: [0, 0] };
                }
                if (sh.type === 'ci') {
                    const r = sh.r,
                        cx = sh.c?.[0] ?? 0,
                        cy = sh.c?.[1] ?? 0;
                    const verts = Array.from({ length: 8 }, (_, i) => {
                        const a = (i / 8) * Math.PI * 2;
                        return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
                    });
                    return { type: 'po', v: wobble(verts), s: 1, a: 0, c: [0, 0] };
                }
                return sh;
            });
            const snapshot = structuredClone(S.state);
            snapshot.physics = physics;
            startGame(snapshot, true);
        }


        // --- PLAYER SWAP / BRING ---
        function actionSwap(a, b) {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast('no state', 'err');
                return;
            }
            if (a === b) {
                showToast('same players', 'err');
                return;
            }
            if (!S.state.players[a]) {
                showToast('player A not in room', 'err');
                return;
            }
            if (!S.state.players[b]) {
                showToast('player B not in room', 'err');
                return;
            }
            const dA = S.state.discs[a],
                dB = S.state.discs[b];
            if (!dA && !dB) {
                showToast('both are dead', 'err');
                return;
            }
            if (dA && dB) {
                const t = { x: dA.x, y: dA.y, xv: dA.xv, yv: dA.yv, av: dA.av, a: dA.a };
                Object.assign(dA, { x: dB.x, y: dB.y, xv: dB.xv, yv: dB.yv, av: dB.av, a: dB.a });
                Object.assign(dB, t);
                startGame(S.state, true);
            } else {
                const aliveIdx = dA ? a : b,
                    deadIdx = dA ? b : a;
                const dAlive = S.state.discs[aliveIdx];
                S.state.discs[deadIdx] = Object.assign({}, dAlive, {
                    team: S.state.players[deadIdx].team,
                    xv: 0,
                    yv: 0,
                    av: 0,
                    a1: false,
                    a2: false,
                });
                S.state.discs[aliveIdx] = undefined;
                startGame(S.state, true, 60);
            }
        }
        function actionSwapAll() {
            const S = _requireState();
            if (!S) return;
            const alive = S.state.discs.map((d, i) => (d ? i : null)).filter(i => i !== null);
            if (alive.length < 2) {
                showToast('⚠️ Need 2+ players', 'err');
                return;
            }
            // Build a derangement: guaranteed no player stays in their own slot.
            // Sattolo cycle: swap each element with a random one strictly ahead of it,
            // which always produces a single cycle (everyone moves).
            const perm = alive.map((_, i) => i); // indices into `alive`
            for (let i = perm.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * i); 
                [perm[i], perm[j]] = [perm[j], perm[i]];
            }
            const orig = alive.map(i => ({
                x: S.state.discs[i].x,
                y: S.state.discs[i].y,
                xv: S.state.discs[i].xv,
                yv: S.state.discs[i].yv,
            }));
            for (let k = 0; k < alive.length; k++) {
                const d = S.state.discs[alive[k]];
                const src = orig[perm[k]];
                d.x = src.x;
                d.y = src.y;
                d.xv = src.xv;
                d.yv = src.yv;
            }
            startGame(S.state, true);
        }
        function actionBringTo(src, dst) {
            const S = window.__SUMMON__;
            if (!S.state) {
                showToast('no state', 'err');
                return;
            }
            if (!S.state.players[src]) {
                showToast('player A not in room', 'err');
                return;
            }
            if (!S.state.players[dst]) {
                showToast('player B not in room', 'err');
                return;
            }
            const dD = S.state.discs[dst];
            if (!dD) {
                showToast('player B is dead', 'err');
                return;
            }
            const dS = S.state.discs[src];
            if (dS) {
                Object.assign(dS, { x: dD.x, y: dD.y, xv: 0, yv: 0 });
                startGame(S.state, true);
            } else {
                S.state.discs[src] = Object.assign({}, dD, {
                    team: S.state.players[src].team,
                    xv: 0,
                    yv: 0,
                    av: 0,
                    a1: false,
                    a2: false,
                });
                startGame(S.state, true, 60);
            }
        }

        // --- MOVE PLAYER (relative offset, editor units) ---
        function actionMovePlayer(pid, offsetX, offsetY) {
            const S = _requireState();
            if (!S) return;
            const ox = parseFloat(offsetX),
                oy = parseFloat(offsetY);
            if (isNaN(ox) || isNaN(oy)) {
                showToast('⚠️ Invalid X or Y', 'err');
                return;
            }
            if (!S.state.players[pid]) {
                showToast('⚠️ Player not found', 'err');
                return;
            }
            const ppm = S.state.physics?.ppm ?? 30,
                dx = ox / ppm,
                dy = oy / ppm;
            const d = S.state.discs[pid];
            if (!d) {
                // Player is dead — respawn at given absolute coords
                const template = S.state.discs.find(Boolean) || {};
                S.state.discs[pid] = Object.assign({}, template, {
                    x: ox / ppm,
                    y: oy / ppm,
                    xv: 0,
                    yv: 0,
                    av: 0,
                    a1: false,
                    a2: false,
                    team: S.state.players[pid]?.team ?? 1,
                });
                startGame(S.state, true, 60);
                return;
            }
            d.x += dx;
            d.y += dy;
            d.xv = 0;
            d.yv = 0;
            startGame(S.state, true);
        }
        function actionMovePlayerAll(offsetX, offsetY) {
            const S = _requireState();
            if (!S) return;
            const ox = parseFloat(offsetX),
                oy = parseFloat(offsetY);
            if (isNaN(ox) || isNaN(oy)) {
                showToast('⚠️ Invalid X or Y', 'err');
                return;
            }
            const ppm = S.state.physics?.ppm ?? 30,
                dx = ox / ppm,
                dy = oy / ppm;
            let n = 0;
            for (const d of S.state.discs) {
                if (!d) continue;
                d.x += dx;
                d.y += dy;
                d.xv = 0;
                d.yv = 0;
                n++;
            }
            startGame(S.state, true);
        }

        // --- TELEPORT PLAYER (absolute map coords, editor units) ---
        // NOTE: the map editor's origin (0,0) sits at the CENTER of the map,
        // while bonk's internal disc coords are relative to the TOP-LEFT of
        // the physics world. The default map is 730x500 editor units, so the
        // center is offset by (365,250) from the internal origin. Any time we
        // convert an ABSOLUTE editor coordinate into internal x/y we must add
        // that offset back in before dividing by ppm (same fix already used
        // for "global" object summoning — see getSpawnParams / runSummonCmd).
        function actionTeleportPlayer(pid, absX, absY) {
            const S = _requireState();
            if (!S) return;
            const ox = parseFloat(absX),
                oy = parseFloat(absY);
            if (isNaN(ox) || isNaN(oy)) {
                showToast('⚠️ Invalid X or Y', 'err');
                return;
            }
            if (!S.state.players[pid]) {
                showToast('⚠️ Player not found', 'err');
                return;
            }
            const ppm = S.state.physics?.ppm ?? 30,
                ax = (ox + 365) / ppm,
                ay = (oy + 250) / ppm;
            const d = S.state.discs[pid];
            if (!d) {
                const template = S.state.discs.find(Boolean) || {};
                S.state.discs[pid] = Object.assign({}, template, {
                    x: ax,
                    y: ay,
                    xv: 0,
                    yv: 0,
                    av: 0,
                    a1: false,
                    a2: false,
                    team: S.state.players[pid]?.team ?? 1,
                });
                startGame(S.state, true, 60);
                return;
            }
            d.x = ax;
            d.y = ay;
            d.xv = 0;
            d.yv = 0;
            startGame(S.state, true);
        }
        function actionTeleportPlayerAll(absX, absY) {
            const S = _requireState();
            if (!S) return;
            const ox = parseFloat(absX),
                oy = parseFloat(absY);
            if (isNaN(ox) || isNaN(oy)) {
                showToast('⚠️ Invalid X or Y', 'err');
                return;
            }
            const ppm = S.state.physics?.ppm ?? 30,
                ax = (ox + 365) / ppm,
                ay = (oy + 250) / ppm;
            let n = 0;
            for (const d of S.state.discs) {
                if (!d) continue;
                d.x = ax;
                d.y = ay;
                d.xv = 0;
                d.yv = 0;
                n++;
            }
            startGame(S.state, true);
        }

        // --- SIZE ---
        const SIZE_CFG = { mode: 'random', value: 0 };
        function actionSize(pid, value) {
            const S = _requireState();
            if (!S) return;
            if (!S.state.players[pid]) {
                showToast('⚠️ Player not found', 'err');
                return;
            }
            // Uses the game's balance/handicap system to resize a single player's disc.
            // Sends the balance packet once to the server; the server's response (packet 36)
            // will update the game state. No need to simulate the response locally.
            const bal = value == null ? Math.floor(Math.random() * 201) - 100 : Number(value);
            if (_gameSocket?.readyState === 1) _gameSocket.send('42[29,{"sid":' + pid + ',"bal":' + bal + '}]');
            startGame(S.state, true);
        }
        function actionSizeAll(value) {
            const S = _requireState();
            if (!S) return;
            for (let i = 0; i < S.state.players.length; i++) {
                if (!S.state.players[i]) continue;
                const bal = value == null ? Math.floor(Math.random() * 201) - 100 : Number(value);
                if (_gameSocket?.readyState === 1) _gameSocket.send('42[29,{"sid":' + i + ',"bal":' + bal + '}]');
            }
            startGame(S.state, true);
        }

        // --- EXPORT / IMPORT ---
        const EXPORT_CFG = { centerMode: 'player' };
        let _lastExportedMapData = '';

        // Round a number to at most 6 decimal places, stripping trailing zeros
        function _r6(v) {
            return typeof v === 'number' ? parseFloat(v.toFixed(6)) : v;
        }

        // Deep-round all number values in an object/array to 6 decimal places
        function _roundDeep(obj) {
            if (Array.isArray(obj)) return obj.map(_roundDeep);
            if (obj !== null && typeof obj === 'object') {
                const r = {};
                for (const k in obj) {
                    r[k] = _roundDeep(obj[k]);
                }
                return r;
            }
            if (typeof obj === 'number') return _r6(obj);
            return obj;
        }


        const _STRIP_DEFAULTS = {
            // body-level
            a: 0,
            av: 0,
            lv: [0, 0],
            // shape-level
            sk: false,
            np: false,
            ng: false,
            ig: false,
            de: null,
            re: null,
            fr: null,
            fp: null,
            // fixture-level (np/ng/ig/de/re/fr/fp handled above)
            d: false,
            // fz block (force zone)
            on: false,
            x: 0,
            y: 0,
            d_fz: true,
            p: true,
            t: 0,
            cf_ct: 0,
            w: true,
        };
        function _stripObj(obj, parentKey) {
            if (Array.isArray(obj)) {
                const arr = obj.map(v => _stripObj(v, null));
                // Strip arrays that are all zeros
                if (arr.every(v => v === 0)) return undefined;
                return arr;
            }
            if (obj !== null && typeof obj === 'object') {
                const out = {};
                for (const k in obj) {
                    let v = _stripObj(obj[k], k);
                    if (v === undefined) continue;
                    // Strip known falsy defaults
                    if (v === false || v === null || v === '' || v === 0) continue;
                    if (Array.isArray(v) && v.length === 0) continue;
                    // Keep strings and true booleans
                    out[k] = v;
                }
                return Object.keys(out).length ? out : undefined;
            }
            if (typeof obj === 'number') return _r6(obj) || undefined; // strips 0
            if (obj === false || obj === null || obj === '' || obj === undefined) return undefined;
            return obj;
        }

        function exportCurrentMap(optimized = false) {
            const S = window.__SUMMON__;
            if (!S.state?.physics) {
                showToast('⚠️ No state', 'err');
                return '';
            }
            const { bodies, shapes, fixtures, joints } = S.state.physics;
            if (!bodies?.length) {
                showToast('⚠️ No objects', 'err');
                return '';
            }
            let centerX = 0,
                centerY = 0;
            if (EXPORT_CFG.centerMode === 'player') {
                const myDisc = S.state.discs?.[myId];
                if (!myDisc) {
                    showToast('⚠️ Disc not found, using origin (0,0)', 'err');
                    return '';
                }
                centerX = myDisc.x;
                centerY = myDisc.y;
            }
            const outBod = [],
                outSh = [],
                outFix = [],
                fixtureRemap = {};
            bodies.forEach((body, li) => {
                if (!body) return;
                const bCopy = structuredClone(body);
                bCopy.id = li;
                if (Array.isArray(bCopy.p)) {
                    bCopy.p[0] -= centerX;
                    bCopy.p[1] -= centerY;
                }
                const newFx = [];
                (body.fx || []).forEach(gfi => {
                    if (fixtureRemap[gfi] === undefined) {
                        const lfi = outFix.length;
                        fixtureRemap[gfi] = lfi;
                        const fCopy = structuredClone(fixtures[gfi]);
                        fCopy.sh = outSh.length;
                        outSh.push(structuredClone(shapes[fixtures[gfi].sh ?? gfi]));
                        outFix.push(fCopy);
                    }
                    newFx.push(fixtureRemap[gfi]);
                });
                bCopy.fx = newFx;
                outBod.push(bCopy);
            });
            const outJoints = [];
            (joints || []).forEach(j => {
                if (j.ba === -1 || (j.ba >= 0 && j.ba < bodies.length))
                    if (j.bb === -1 || (j.bb >= 0 && j.bb < bodies.length)) {
                        const jc = structuredClone(j);
                        if (jc.type === 'lpj') {
                            if (jc.pax != null) jc.pax -= centerX;
                            if (jc.pay != null) jc.pay -= centerY;
                        }
                        outJoints.push(jc);
                    }
            });
            let result = { bodies: outBod, shapes: outSh, fixtures: outFix, joints: outJoints };
            if (optimized) {
                // Round all numbers to 6 decimals then strip falsy defaults
                result = _roundDeep(result);
                // Custom lightweight strip: remove keys equal to 0, false, null, "" from body.s, fixtures, shapes
                function stripNode(o) {
                    if (Array.isArray(o)) return o.map(stripNode);
                    if (o !== null && typeof o === 'object') {
                        const out = {};
                        for (const k in o) {
                            const v = stripNode(o[k]);
                            if (v === false || v === null || v === '' || v === undefined) continue;
                            if (
                                typeof v === 'number' &&
                                v === 0 &&
                                k !== 'x' &&
                                k !== 'y' &&
                                k !== 'a' &&
                                k !== 'av' &&
                                k !== 'cf' &&
                                k !== 'sh' &&
                                k !== 'ba' &&
                                k !== 'bb' &&
                                k !== 'f'
                            )
                                continue;
                            if (Array.isArray(v) && v.every(x => x === 0) && (k === 'lv' || k === 'aa' || k === 'ab'))
                                continue;
                            out[k] = v;
                        }
                        return Object.keys(out).length ? out : undefined;
                    }
                    return o;
                }
                result.bodies = (result.bodies || []).map(b => (b ? stripNode(b) : b)).filter(Boolean);
                result.shapes = (result.shapes || []).map(s => (s ? stripNode(s) : s)).filter(Boolean);
                result.fixtures = (result.fixtures || []).map(f => (f ? stripNode(f) : f)).filter(Boolean);
                result.joints = (result.joints || []).map(j => (j ? stripNode(j) : j)).filter(Boolean);
            }
            _lastExportedMapData = JSON.stringify(result);
            return _lastExportedMapData;
        }

        // --- TEXT RENDERER (Times New Roman Bold glyph contours) ---
        // Each glyph: { w: advance_width, c: [[contour_pts], ...] }
        // Contours are normalized: height=1.0=cap height, y=0 top, y=1 baseline.
        // Holes (inner contours) are wound opposite to outer — bonk renders fill-rule:evenodd.
        const LETTER_GLYPHS = {"A":{"w":1.1029,"c":[[[0.3192,0.9456],[0.3192,1.0],[0.0149,1.0],[0.0149,0.9456],[0.0895,0.9254],[0.4452,-0.0082],[0.6614,-0.0082],[1.0157,0.9254],[1.0917,0.9456],[1.0917,1.0],[0.6465,1.0],[0.6465,0.9456],[0.7621,0.9254],[0.6667,0.6667],[0.2826,0.6667],[0.1909,0.9254]],[[0.478,0.1424],[0.3132,0.5846],[0.6383,0.5846]]]},"B":{"w":1.0186,"c":[[[0.3893,0.082],[0.3893,0.4303],[0.4989,0.4303],[0.6547,0.2468],[0.4929,0.082]],[[0.3893,0.5123],[0.3893,0.918],[0.5585,0.9224],[0.7301,0.7084],[0.5183,0.5123]],[[0.0254,1.0],[0.0254,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0254,0.0544],[0.0254,0.0],[0.5108,0.0],[0.8993,0.226],[0.6935,0.4653],[0.9761,0.7084],[0.5608,1.0045],[0.2207,1.0]]]},"C":{"w":1.1029,"c":[[[0.6078,1.0149],[0.0746,0.5116],[0.607,-0.0112],[0.9672,0.0388],[0.9717,0.2789],[0.9045,0.2789],[0.8837,0.1342],[0.7772,0.0839],[0.6547,0.0671],[0.3221,0.5093],[0.6488,0.9381],[0.8949,0.8628],[0.9187,0.6987],[0.9866,0.6987],[0.9821,0.9523]]]},"D":{"w":1.1029,"c":[[[0.3893,0.082],[0.3893,0.915],[0.519,0.921],[0.7808,0.5004],[0.4444,0.082]],[[0.5019,0.0],[1.0283,0.4944],[0.5332,1.003],[0.0268,1.0],[0.0268,0.9456],[0.1551,0.9254],[0.1551,0.0738],[0.0268,0.0544],[0.0268,0.0]]]},"E":{"w":1.0186,"c":[[[0.0261,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.8777,0.0],[0.8777,0.2543],[0.8098,0.2543],[0.786,0.0925],[0.3893,0.082],[0.3893,0.4489],[0.651,0.4489],[0.6741,0.3386],[0.7405,0.3386],[0.7405,0.6458],[0.6741,0.6458],[0.651,0.5324],[0.3893,0.5324],[0.3893,0.918],[0.83,0.906],[0.8725,0.7211],[0.9403,0.7211],[0.9262,1.0],[0.0261,1.0]]]},"F":{"w":0.9329,"c":[[[0.3893,0.5735],[0.3893,0.9254],[0.5556,0.9456],[0.5556,1.0],[0.0358,1.0],[0.0358,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.8598,0.0],[0.8598,0.2692],[0.7897,0.2692],[0.7658,0.0925],[0.3893,0.082],[0.3893,0.4914],[0.6331,0.4914],[0.6562,0.3647],[0.7226,0.3647],[0.7226,0.7017],[0.6562,0.7017],[0.6331,0.5735]]]},"G":{"w":1.1879,"c":[[[1.0485,0.9478],[0.6137,1.0149],[0.0746,0.5116],[0.6279,-0.0112],[1.0157,0.0522],[1.0157,0.2819],[0.9485,0.2819],[0.9306,0.1521],[0.6547,0.0671],[0.3221,0.5101],[0.6488,0.9381],[0.8136,0.912],[0.8136,0.6227],[0.6853,0.6033],[0.6853,0.5481],[1.1462,0.5481],[1.1462,0.6033],[1.0485,0.6227]]]},"H":{"w":1.1879,"c":[[[0.0261,1.0],[0.0261,0.9448],[0.1544,0.9254],[0.1544,0.0746],[0.0261,0.0544],[0.0261,0.0],[0.5175,0.0],[0.5175,0.0544],[0.3893,0.0746],[0.3893,0.4444],[0.7979,0.4444],[0.7979,0.0746],[0.6696,0.0544],[0.6696,0.0],[1.1626,0.0],[1.1626,0.0544],[1.0336,0.0746],[1.0336,0.9254],[1.1626,0.9448],[1.1626,1.0],[0.6696,1.0],[0.6696,0.9448],[0.7979,0.9254],[0.7979,0.5265],[0.3893,0.5265],[0.3893,0.9254],[0.5175,0.9448],[0.5175,1.0]]]},"I":{"w":0.5943,"c":[[[0.4146,0.9254],[0.5429,0.9448],[0.5429,1.0],[0.0515,1.0],[0.0515,0.9448],[0.1797,0.9254],[0.1797,0.0746],[0.0515,0.0544],[0.0515,0.0],[0.5429,0.0],[0.5429,0.0544],[0.4146,0.0746]]]},"J":{"w":0.7636,"c":[[[0.3736,0.0738],[0.2453,0.0544],[0.2453,0.0],[0.7218,0.0],[0.7218,0.0544],[0.6085,0.0738],[0.6085,0.6779],[0.264,1.0149],[0.0679,0.9925],[0.0679,0.7658],[0.1342,0.7658],[0.1566,0.8993],[0.2543,0.9359],[0.3736,0.7942]]]},"K":{"w":1.1879,"c":[[[1.0955,0.0],[1.0955,0.0544],[0.9732,0.0738],[0.6428,0.3654],[1.0828,0.9254],[1.176,0.9456],[1.176,1.0],[0.8494,1.0],[0.4758,0.5183],[0.3893,0.5928],[0.3893,0.9254],[0.5332,0.9456],[0.5332,1.0],[0.0261,1.0],[0.0261,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.5183,0.0],[0.5183,0.0544],[0.3893,0.0738],[0.3893,0.481],[0.8449,0.0738],[0.7532,0.0544],[0.7532,0.0]]]},"L":{"w":1.0186,"c":[[[0.5436,0.0544],[0.3893,0.0738],[0.3893,0.921],[0.8262,0.906],[0.8874,0.698],[0.9545,0.698],[0.9262,1.0],[0.0261,1.0],[0.0261,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0268,0.0544],[0.0268,0.0],[0.5436,0.0]]]},"M":{"w":1.4415,"c":[[[0.6577,1.0],[0.6167,1.0],[0.2476,0.1551],[0.2476,0.9254],[0.3818,0.9456],[0.3818,1.0],[0.0261,1.0],[0.0261,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.4191,0.0],[0.7047,0.6577],[0.9963,0.0],[1.3975,0.0],[1.3975,0.0544],[1.2692,0.0738],[1.2692,0.9254],[1.3975,0.9456],[1.3975,1.0],[0.9001,1.0],[0.9001,0.9456],[1.0343,0.9254],[1.0343,0.1551]]]},"N":{"w":1.1029,"c":[[[0.8613,0.0738],[0.7271,0.0544],[0.7271,0.0],[1.0828,0.0],[1.0828,0.0544],[0.9545,0.0738],[0.9545,1.0],[0.8673,1.0],[0.2506,0.1961],[0.2506,0.9254],[0.3848,0.9456],[0.3848,1.0],[0.0291,1.0],[0.0291,0.9456],[0.1573,0.9254],[0.1573,0.0738],[0.0291,0.0544],[0.0291,0.0],[0.3714,0.0],[0.8613,0.6391]]]},"O":{"w":1.1879,"c":[[[0.5943,0.9403],[0.8658,0.4989],[0.5943,0.0641],[0.3221,0.4989]],[[0.0746,0.4989],[0.1071,0.2757],[0.2045,0.1163],[0.3669,0.0207],[0.5943,-0.0112],[1.1133,0.4989],[0.5943,1.0149]]]},"P":{"w":0.9329,"c":[[[0.39,0.082],[0.39,0.5257],[0.4519,0.5257],[0.6495,0.296],[0.4489,0.082]],[[0.39,0.6078],[0.39,0.9254],[0.5563,0.9456],[0.5563,1.0],[0.0358,1.0],[0.0358,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.4668,0.0],[0.8896,0.2946],[0.8668,0.4316],[0.7983,0.5295],[0.6841,0.5882],[0.5242,0.6078]]]},"Q":{"w":1.1879,"c":[[[0.2514,0.0774],[0.3669,0.0207],[0.5943,-0.0112],[1.1133,0.4989],[1.0959,0.6677],[1.0436,0.8035],[0.9565,0.9063],[0.8345,0.9761],[0.989,1.135],[1.1208,1.1529],[1.1208,1.217],[0.9448,1.2476],[0.5854,1.0149],[0.0746,0.4989],[0.1323,0.2155]],[[0.5943,0.9403],[0.8658,0.4989],[0.5943,0.0641],[0.3221,0.4989]]]},"R":{"w":1.1029,"c":[[[0.39,0.5764],[0.39,0.9254],[0.5183,0.9456],[0.5183,1.0],[0.0358,1.0],[0.0358,0.9456],[0.1544,0.9254],[0.1544,0.0738],[0.0261,0.0544],[0.0261,0.0],[0.5041,0.0],[0.9426,0.2796],[0.8922,0.456],[0.7412,0.5556],[1.0082,0.9254],[1.1163,0.9456],[1.1163,1.0],[0.7927,1.0],[0.5138,0.5764]],[[0.39,0.082],[0.39,0.4944],[0.4981,0.4944],[0.7099,0.2811],[0.4944,0.082]]]},"S":{"w":0.8494,"c":[[[0.0813,0.6935],[0.1469,0.6935],[0.1805,0.8538],[0.4064,0.9396],[0.5253,0.92],[0.5967,0.8614],[0.6204,0.7636],[0.0872,0.2588],[0.44,-0.0112],[0.7174,0.0209],[0.7174,0.2588],[0.651,0.2588],[0.6174,0.1216],[0.4731,0.0679],[0.44,0.0664],[0.2498,0.2043],[0.7845,0.6965],[0.7428,0.8734],[0.6178,0.9795],[0.4094,1.0149],[0.0813,0.962]]]},"T":{"w":1.0186,"c":[[[0.2312,1.0],[0.2312,0.9456],[0.39,0.9254],[0.39,0.079],[0.1119,0.094],[0.0917,0.2796],[0.0239,0.2796],[0.0239,0.0],[0.9955,0.0],[0.9955,0.2796],[0.9269,0.2796],[0.9068,0.094],[0.6257,0.0805],[0.6257,0.9254],[0.7845,0.9456],[0.7845,1.0]]]},"U":{"w":1.1029,"c":[[[0.566,1.0142],[0.1514,0.6503],[0.1514,0.0738],[0.038,0.0544],[0.038,0.0],[0.5145,0.0],[0.5145,0.0544],[0.3863,0.0738],[0.3863,0.6607],[0.6249,0.909],[0.8591,0.6622],[0.8591,0.0738],[0.7248,0.0544],[0.7248,0.0],[1.0649,0.0],[1.0649,0.0544],[0.9515,0.0738],[0.9515,0.6562]]]},"V":{"w":1.1029,"c":[[[1.0858,0.0],[1.0858,0.0544],[0.9911,0.0746],[0.6048,1.0231],[0.5056,1.0231],[0.0992,0.0746],[0.0172,0.0544],[0.0172,0.0],[0.4519,0.0],[0.4519,0.0544],[0.3482,0.0746],[0.6294,0.7301],[0.8919,0.0746],[0.7912,0.0544],[0.7912,0.0]]]},"W":{"w":1.5272,"c":[[[1.1193,1.0231],[1.0276,1.0231],[0.7785,0.4064],[0.5317,1.0231],[0.44,1.0231],[0.1089,0.0738],[0.0216,0.0544],[0.0216,0.0],[0.4705,0.0],[0.4705,0.0544],[0.3535,0.0738],[0.5667,0.6696],[0.8009,0.085],[0.8941,0.085],[1.129,0.6682],[1.3057,0.0738],[1.1797,0.0544],[1.1797,0.0],[1.5034,0.0],[1.5034,0.0544],[1.4161,0.0738]]]},"X":{"w":1.1029,"c":[[[0.2461,0.9254],[0.3699,0.9456],[0.3699,1.0],[0.0283,1.0],[0.0283,0.9456],[0.1387,0.9254],[0.4601,0.5168],[0.1738,0.0738],[0.0611,0.0544],[0.0611,0.0],[0.5481,0.0],[0.5481,0.0544],[0.4213,0.0738],[0.5958,0.3438],[0.8084,0.0738],[0.6846,0.0544],[0.6846,0.0],[1.0268,0.0],[1.0268,0.0544],[0.9165,0.0738],[0.6443,0.4191],[0.9717,0.9254],[1.085,0.9456],[1.085,1.0],[0.5981,1.0],[0.5981,0.9456],[0.7248,0.9254],[0.5086,0.5921]]]},"Y":{"w":1.1029,"c":[[[0.6793,0.6063],[0.6793,0.9254],[0.8382,0.9456],[0.8382,1.0],[0.2849,1.0],[0.2849,0.9456],[0.4437,0.9254],[0.4437,0.6107],[0.1394,0.0738],[0.0268,0.0544],[0.0268,0.0],[0.5414,0.0],[0.5414,0.0544],[0.4072,0.0738],[0.6353,0.4929],[0.8561,0.0738],[0.7338,0.0544],[0.7338,0.0],[1.0738,0.0],[1.0738,0.0544],[0.9679,0.0738]]]},"Z":{"w":1.0186,"c":[[[0.0731,0.915],[0.6264,0.079],[0.2088,0.094],[0.1857,0.2513],[0.1193,0.2513],[0.1193,0.0],[0.8919,0.0],[0.8919,0.079],[0.3348,0.9224],[0.83,0.8978],[0.874,0.7069],[0.9411,0.7069],[0.921,1.0],[0.0731,1.0]]]},"a":{"w":0.7636,"c":[[[0.7405,1.0],[0.4825,1.0],[0.4661,0.9463],[0.2316,1.0135],[0.0869,0.9439],[0.0492,0.8061],[0.3639,0.5839],[0.4549,0.5817],[0.452,0.4408],[0.4196,0.3747],[0.3512,0.3527],[0.2611,0.37],[0.211,0.3915],[0.1827,0.4787],[0.1335,0.4787],[0.1335,0.3095],[0.4492,0.2847],[0.6247,0.3493],[0.6686,0.4463],[0.6704,0.9329],[0.7405,0.9508]],[[0.4549,0.648],[0.3922,0.6503],[0.264,0.8016],[0.3445,0.9247],[0.4549,0.8986]]]},"b":{"w":0.8494,"c":[[[0.4236,0.3632],[0.3065,0.3885],[0.3065,0.9247],[0.35,0.9326],[0.4236,0.9366],[0.569,0.6294]],[[0.091,0.006],[0.0194,-0.0112],[0.0194,-0.0597],[0.3065,-0.0597],[0.299,0.3386],[0.4952,0.2804],[0.7875,0.6301],[0.4444,1.0149],[0.091,0.9642]]]},"c":{"w":0.6779,"c":[[[0.6398,0.9575],[0.4251,1.0142],[0.0522,0.648],[0.3967,0.2804],[0.6242,0.3065],[0.6242,0.5138],[0.5705,0.5138],[0.5391,0.3908],[0.4243,0.3572],[0.2692,0.6435],[0.4638,0.9217],[0.6398,0.9001]]]},"d":{"w":0.8494,"c":[[[0.5466,0.9605],[0.3691,1.0149],[0.0619,0.6518],[0.3803,0.2804],[0.5444,0.3005],[0.5399,0.0075],[0.4683,-0.0104],[0.4683,-0.0597],[0.7554,-0.0597],[0.7554,0.9329],[0.8322,0.9508],[0.8322,1.0],[0.5623,1.0]],[[0.4161,0.9321],[0.5399,0.9001],[0.5399,0.3721],[0.4518,0.3574],[0.4221,0.3565],[0.2804,0.6458]]]},"e":{"w":0.6779,"c":[[[0.3684,0.2819],[0.6383,0.5943],[0.6383,0.6555],[0.2707,0.6555],[0.4571,0.9217],[0.6227,0.9001],[0.6227,0.9575],[0.3893,1.0142],[0.0522,0.6458]],[[0.3602,0.3572],[0.2714,0.5772],[0.434,0.5772]]]},"f":{"w":0.5086,"c":[[[0.1171,0.3766],[0.0112,0.3766],[0.0112,0.3251],[0.1171,0.296],[0.1171,0.2207],[0.3997,-0.0753],[0.5235,-0.0611],[0.5235,0.1059],[0.4758,0.1059],[0.4541,0.0246],[0.3952,0.008],[0.3512,0.0543],[0.3347,0.1388],[0.3326,0.299],[0.475,0.299],[0.475,0.3766],[0.3326,0.3766],[0.3326,0.9329],[0.4467,0.9508],[0.4467,1.0],[0.041,1.0],[0.041,0.9508],[0.1171,0.9329]]]},"g":{"w":0.7636,"c":[[[0.1916,0.7412],[0.0921,0.6587],[0.0589,0.5227],[0.3729,0.2804],[0.5287,0.299],[0.6965,0.2163],[0.7226,0.2483],[0.6376,0.3691],[0.6838,0.5227],[0.3699,0.7688],[0.2558,0.7599],[0.2379,0.8166],[0.3296,0.8762],[0.5338,0.8776],[0.6617,0.9104],[0.7203,0.9637],[0.7487,1.0731],[0.3594,1.3371],[0.0403,1.1738],[0.173,1.0321],[0.0671,0.8829]],[[0.5847,1.1125],[0.5196,1.0549],[0.2543,1.0529],[0.1887,1.1611],[0.3602,1.2565]],[[0.3684,0.6943],[0.4728,0.5227],[0.3684,0.3557],[0.2699,0.5227]]]},"h":{"w":0.8494,"c":[[[0.3251,0.2438],[0.3207,0.3602],[0.5623,0.2804],[0.67,0.3033],[0.7225,0.3507],[0.7548,0.454],[0.7562,0.9329],[0.8262,0.9508],[0.8262,1.0],[0.478,1.0],[0.478,0.9508],[0.5406,0.9329],[0.5406,0.516],[0.4196,0.384],[0.3251,0.4087],[0.3251,0.9329],[0.3893,0.9508],[0.3893,1.0],[0.041,1.0],[0.041,0.9508],[0.1096,0.9329],[0.1096,0.0075],[0.038,-0.0104],[0.038,-0.0597],[0.3251,-0.0597]]]},"i":{"w":0.4243,"c":[[[0.3251,0.9329],[0.4019,0.9508],[0.4019,1.0],[0.0336,1.0],[0.0336,0.9508],[0.1096,0.9329],[0.1096,0.3661],[0.038,0.3482],[0.038,0.299],[0.3251,0.299]],[[0.217,-0.0597],[0.3311,0.0544],[0.217,0.1693],[0.1022,0.0544]]]},"j":{"w":0.5086,"c":[[[0.2983,0.1693],[0.1834,0.0544],[0.2983,-0.0597],[0.4124,0.0544]],[[0.4064,1.0291],[0.1156,1.3251],[-0.0127,1.3117],[-0.0127,1.1439],[0.035,1.1439],[0.0671,1.2289],[0.1037,1.2431],[0.1909,1.0529],[0.1909,0.3736],[0.0671,0.3482],[0.0671,0.299],[0.4064,0.299]]]},"k":{"w":0.8494,"c":[[[0.3251,0.6637],[0.5988,0.3654],[0.5242,0.3482],[0.5242,0.299],[0.7912,0.299],[0.7912,0.3482],[0.7107,0.3647],[0.5436,0.5406],[0.7845,0.9329],[0.8471,0.9508],[0.8471,1.0],[0.5101,1.0],[0.5101,0.9508],[0.5511,0.9329],[0.4109,0.6875],[0.3251,0.7472],[0.3251,0.9329],[0.3945,0.9508],[0.3945,1.0],[0.0433,1.0],[0.0433,0.9508],[0.1096,0.9329],[0.1096,0.0075],[0.038,-0.0104],[0.038,-0.0597],[0.3251,-0.0597]]]},"l":{"w":0.4243,"c":[[[0.3214,0.9329],[0.3982,0.9508],[0.3982,1.0],[0.0298,1.0],[0.0298,0.9508],[0.1059,0.9329],[0.1059,0.0075],[0.0343,-0.0104],[0.0343,-0.0597],[0.3214,-0.0597]]]},"m":{"w":1.2722,"c":[[[0.3236,0.3602],[0.5615,0.2804],[0.6659,0.3031],[0.7286,0.3714],[0.8907,0.2962],[1.0168,0.2818],[1.1115,0.3162],[1.1707,0.4238],[1.176,0.9329],[1.2461,0.9508],[1.2461,1.0],[0.8978,1.0],[0.8978,0.9508],[0.9605,0.9329],[0.9605,0.516],[0.8628,0.3833],[0.7435,0.4146],[0.7509,0.9329],[0.821,0.9508],[0.821,1.0],[0.4728,1.0],[0.4728,0.9508],[0.5354,0.9329],[0.5354,0.516],[0.4377,0.3833],[0.3251,0.4124],[0.3251,0.9329],[0.3893,0.9508],[0.3893,1.0],[0.041,1.0],[0.041,0.9508],[0.1096,0.9329],[0.1096,0.3661],[0.041,0.3482],[0.041,0.299],[0.3132,0.299]]]},"n":{"w":0.8494,"c":[[[0.3236,0.3602],[0.4735,0.2938],[0.5932,0.2818],[0.6902,0.3162],[0.7508,0.4238],[0.7562,0.9329],[0.8262,0.9508],[0.8262,1.0],[0.478,1.0],[0.478,0.9508],[0.5406,0.9329],[0.5406,0.516],[0.4385,0.3833],[0.3251,0.4087],[0.3251,0.9329],[0.3893,0.9508],[0.3893,1.0],[0.041,1.0],[0.041,0.9508],[0.1096,0.9329],[0.1096,0.3661],[0.041,0.3482],[0.041,0.299],[0.3132,0.299]]]},"o":{"w":0.7636,"c":[[[0.3773,1.0149],[0.0582,0.6458],[0.3833,0.2804],[0.7054,0.6458]],[[0.3788,0.3557],[0.2767,0.6458],[0.3788,0.9403],[0.487,0.6458]]]},"p":{"w":0.8494,"c":[[[0.305,0.3348],[0.4989,0.2804],[0.7875,0.6368],[0.4668,1.0149],[0.3072,0.9948],[0.3117,1.258],[0.4146,1.2759],[0.4146,1.3251],[0.0201,1.3251],[0.0201,1.2759],[0.0962,1.258],[0.0962,0.3661],[0.0194,0.3482],[0.0194,0.299],[0.3035,0.299]],[[0.4599,0.3651],[0.3117,0.3915],[0.3117,0.9202],[0.4778,0.9277],[0.5544,0.8053],[0.5546,0.4871],[0.5113,0.3942]]]},"q":{"w":0.8494,"c":[[[0.4303,0.9321],[0.5436,0.9038],[0.5436,0.3699],[0.4508,0.3575],[0.4303,0.3572],[0.2811,0.6659]],[[0.7591,1.258],[0.8352,1.2759],[0.8352,1.3251],[0.4407,1.3251],[0.4407,1.2759],[0.5436,1.258],[0.5511,0.9567],[0.3684,1.0149],[0.0626,0.6465],[0.3945,0.2804],[0.6025,0.2975],[0.7591,0.2655]]]},"r":{"w":0.6779,"c":[[[0.3438,0.4444],[0.6048,0.2767],[0.645,0.2767],[0.645,0.5324],[0.6033,0.5324],[0.5593,0.4385],[0.3475,0.5071],[0.3475,0.9329],[0.4601,0.9508],[0.4601,1.0],[0.041,1.0],[0.041,0.9508],[0.132,0.9329],[0.132,0.3661],[0.041,0.3482],[0.041,0.299],[0.3356,0.299]]]},"s":{"w":0.5943,"c":[[[0.0477,0.9769],[0.0477,0.786],[0.0969,0.786],[0.1253,0.8844],[0.2804,0.9456],[0.4087,0.8464],[0.2446,0.7241],[0.0462,0.4892],[0.2998,0.2804],[0.5063,0.302],[0.5063,0.4817],[0.4571,0.4817],[0.4333,0.3997],[0.2983,0.3505],[0.1931,0.4325],[0.3572,0.5526],[0.557,0.7778],[0.2759,1.0149]]]},"t":{"w":0.5086,"c":[[[0.3266,1.0149],[0.1163,0.8382],[0.1163,0.3766],[0.0246,0.3766],[0.0246,0.3281],[0.1327,0.299],[0.22,0.1402],[0.3318,0.1402],[0.3318,0.299],[0.4795,0.299],[0.4795,0.3766],[0.3318,0.3766],[0.3318,0.8248],[0.4049,0.9224],[0.5019,0.9105],[0.5019,0.9739]]]},"u":{"w":0.8494,"c":[[[0.5257,0.9389],[0.3758,1.0052],[0.2561,1.0172],[0.1592,0.9828],[0.0986,0.8752],[0.0932,0.3661],[0.0231,0.3482],[0.0231,0.299],[0.3087,0.299],[0.3087,0.783],[0.4109,0.9157],[0.5242,0.8904],[0.5242,0.3661],[0.4601,0.3482],[0.4601,0.299],[0.7397,0.299],[0.7397,0.9329],[0.8084,0.9508],[0.8084,1.0],[0.5362,1.0]]]},"v":{"w":0.7636,"c":[[[0.4325,1.0149],[0.3423,1.0149],[0.0492,0.3661],[0.0,0.3482],[0.0,0.299],[0.3587,0.299],[0.3587,0.3482],[0.2752,0.3676],[0.4549,0.7673],[0.6167,0.3661],[0.5347,0.3482],[0.5347,0.299],[0.7636,0.299],[0.7636,0.3482],[0.7129,0.3632]]]},"w":{"w":1.1029,"c":[[[0.6518,0.3691],[0.8136,0.7681],[0.9493,0.3661],[0.8673,0.3482],[0.8673,0.299],[1.0947,0.299],[1.0947,0.3482],[1.041,0.3632],[0.8054,1.0149],[0.7151,1.0149],[0.5548,0.6219],[0.3952,1.0149],[0.305,1.0149],[0.0567,0.3661],[0.0045,0.3482],[0.0045,0.299],[0.3512,0.299],[0.3512,0.3482],[0.2677,0.3676],[0.4183,0.7681],[0.5809,0.3691]]]},"x":{"w":0.7636,"c":[[[0.0231,0.3475],[0.0231,0.299],[0.3975,0.299],[0.3975,0.3475],[0.3192,0.3676],[0.4213,0.525],[0.5563,0.3661],[0.4862,0.3475],[0.4862,0.299],[0.7248,0.299],[0.7248,0.3475],[0.6629,0.3632],[0.4661,0.5943],[0.6883,0.9359],[0.7554,0.9515],[0.7554,1.0],[0.3811,1.0],[0.3811,0.9515],[0.4594,0.9344],[0.3371,0.7457],[0.1745,0.9359],[0.2446,0.9515],[0.2446,1.0],[0.006,1.0],[0.006,0.9515],[0.0686,0.9396],[0.2923,0.6771],[0.091,0.3661]]]},"y":{"w":0.7636,"c":[[[0.7487,0.299],[0.7487,0.3482],[0.698,0.3632],[0.4213,1.044],[0.1588,1.3296],[0.0358,1.3154],[0.0358,1.135],[0.0805,1.135],[0.1126,1.2289],[0.1678,1.2461],[0.3445,1.0216],[0.0641,0.3661],[0.0149,0.3482],[0.0149,0.299],[0.3736,0.299],[0.3736,0.3482],[0.2901,0.3676],[0.4549,0.7554],[0.6018,0.3661],[0.5198,0.3482],[0.5198,0.299]]]},"z":{"w":0.6779,"c":[[[0.0298,1.0],[0.352,0.3661],[0.138,0.3922],[0.1148,0.5078],[0.0611,0.5078],[0.0611,0.299],[0.6055,0.299],[0.2834,0.9329],[0.5511,0.8978],[0.5906,0.736],[0.6443,0.736],[0.6242,1.0]]]},"0":{"w":0.7636,"c":[[[0.3773,1.0149],[0.0582,0.4959],[0.3833,-0.0157],[0.7054,0.4959],[0.6849,0.723],[0.6234,0.8852],[0.5209,0.9825]],[[0.3788,0.0597],[0.2767,0.4959],[0.3788,0.9403],[0.487,0.4959]]]},"1":{"w":0.7636,"c":[[[0.5108,0.918],[0.6846,0.9359],[0.6846,1.0],[0.1223,1.0],[0.1223,0.9359],[0.2953,0.918],[0.2953,0.1641],[0.123,0.2207],[0.123,0.1573],[0.4049,-0.0082],[0.5108,-0.0082]]]},"2":{"w":0.7636,"c":[[[0.698,1.0],[0.0641,1.0],[0.0641,0.8591],[0.1827,0.736],[0.4638,0.2461],[0.3192,0.0649],[0.1946,0.1037],[0.1626,0.2483],[0.0977,0.2483],[0.0977,0.0209],[0.3386,-0.0112],[0.6786,0.2498],[0.346,0.7129],[0.2073,0.8315],[0.698,0.8315]]]},"3":{"w":0.7636,"c":[[[0.0664,0.9851],[0.0574,0.7427],[0.126,0.7427],[0.1648,0.9031],[0.3005,0.9396],[0.4922,0.7204],[0.3199,0.528],[0.2334,0.5227],[0.2334,0.4325],[0.3169,0.4265],[0.4467,0.2438],[0.302,0.0649],[0.1872,0.1037],[0.1551,0.2483],[0.0902,0.2483],[0.0902,0.0209],[0.3304,-0.0112],[0.5522,0.0315],[0.6293,0.0982],[0.6667,0.2349],[0.4594,0.4765],[0.6484,0.5546],[0.6957,0.6293],[0.7114,0.7278],[0.3423,1.0149]]]},"4":{"w":0.7636,"c":[[[0.6353,0.8024],[0.6353,1.0],[0.4348,1.0],[0.4348,0.8024],[0.0209,0.8024],[0.0209,0.6808],[0.4713,-0.0052],[0.6353,-0.0052],[0.6353,0.6495],[0.7353,0.6495],[0.7353,0.8024]],[[0.4348,0.3535],[0.4422,0.1954],[0.1447,0.6495],[0.4348,0.6495]]]},"5":{"w":0.7636,"c":[[[0.0701,0.9851],[0.0611,0.7427],[0.1298,0.7427],[0.1685,0.9031],[0.3169,0.9396],[0.4456,0.8822],[0.4873,0.7466],[0.4884,0.7099],[0.3057,0.484],[0.1111,0.513],[0.1111,0.0],[0.6331,0.0],[0.6331,0.1663],[0.1939,0.1663],[0.1939,0.4288],[0.3579,0.4087],[0.7077,0.7025],[0.346,1.0149]]]},"6":{"w":0.7636,"c":[[[0.4064,1.0149],[0.0522,0.5063],[0.434,-0.0112],[0.5483,-0.0032],[0.6585,0.0209],[0.6585,0.2483],[0.5936,0.2483],[0.5615,0.1037],[0.4489,0.0649],[0.2729,0.4273],[0.434,0.3922],[0.7189,0.6898]],[[0.4034,0.9396],[0.4996,0.704],[0.384,0.4855],[0.2714,0.5026],[0.3044,0.8313],[0.3457,0.9125]]]},"7":{"w":0.7636,"c":[[[0.1521,0.2856],[0.0872,0.2856],[0.0872,0.0],[0.7263,0.0],[0.7263,0.0589],[0.3378,1.0],[0.1596,1.0],[0.5809,0.1663],[0.1864,0.1663]]]},"8":{"w":0.7636,"c":[[[0.3773,1.0149],[0.2344,0.9971],[0.1324,0.9437],[0.0711,0.8547],[0.0507,0.7301],[0.2252,0.4698],[0.0738,0.2438],[0.3833,-0.0157],[0.6898,0.2461],[0.5362,0.4698],[0.7129,0.7301]],[[0.3773,0.5108],[0.2625,0.7301],[0.3773,0.9396],[0.5011,0.7301]],[[0.3788,0.0597],[0.2856,0.2461],[0.3788,0.431],[0.478,0.2461]]]},"9":{"w":0.7636,"c":[[[0.3714,-0.0112],[0.7084,0.4974],[0.3117,1.0149],[0.0979,0.988],[0.0798,0.7554],[0.1447,0.7554],[0.1767,0.9001],[0.3087,0.9396],[0.487,0.5839],[0.3326,0.6115],[0.0418,0.305]],[[0.4892,0.5078],[0.3729,0.0641],[0.3387,0.0709],[0.268,0.2013],[0.2812,0.4487],[0.3418,0.5153]]]},".":{"w":0.3818,"c":[[[0.1909,1.0216],[0.0671,0.8978],[0.1909,0.774],[0.3147,0.8978]]]},",":{"w":0.3818,"c":[[[0.0201,1.2364],[0.0201,1.1678],[0.1708,1.0261],[0.0775,0.9366],[0.0582,0.8747],[0.1767,0.7755],[0.3236,0.921]]]},"!":{"w":0.5086,"c":[[[0.296,0.6868],[0.2103,0.6868],[0.1417,0.0],[0.3647,0.0]],[[0.2535,1.0216],[0.1298,0.8978],[0.2535,0.774],[0.3773,0.8978]]]},"?":{"w":0.7636,"c":[[[0.3318,1.0216],[0.2088,0.8978],[0.3318,0.774],[0.4556,0.8978]],[[0.3661,0.7092],[0.2893,0.7092],[0.2468,0.4758],[0.3177,0.4571],[0.4571,0.2588],[0.302,0.0649],[0.1909,0.0962],[0.1588,0.2409],[0.0925,0.2409],[0.0925,0.0209],[0.4375,-0.0038],[0.5574,0.0348],[0.635,0.1065],[0.6726,0.2535],[0.4512,0.5406],[0.3863,0.5585]]]},":":{"w":0.5086,"c":[[[0.2483,1.0216],[0.1253,0.8978],[0.2483,0.774],[0.3721,0.8978]],[[0.2483,0.5317],[0.1253,0.4079],[0.2483,0.2841],[0.3721,0.4079]]]},";":{"w":0.5086,"c":[[[0.2498,0.5317],[0.1268,0.4079],[0.2498,0.2841],[0.3736,0.4079]],[[0.079,1.2364],[0.079,1.1678],[0.2304,1.0261],[0.1351,0.9366],[0.1156,0.8747],[0.2364,0.7755],[0.3833,0.921]]]},"-":{"w":0.5086,"c":[[[0.0559,0.7054],[0.0559,0.5757],[0.4526,0.5757],[0.4526,0.7054]]]},"+":{"w":0.8702,"c":[[[0.4884,0.5466],[0.4884,0.8486],[0.3818,0.8486],[0.3818,0.5466],[0.0805,0.5466],[0.0805,0.44],[0.3818,0.44],[0.3818,0.1372],[0.4884,0.1372],[0.4884,0.44],[0.7905,0.44],[0.7905,0.5466]]]},"_":{"w":0.7636,"c":[[[-0.0119,1.2125],[-0.0119,1.1051],[0.7755,1.1051],[0.7755,1.2125]]]},"(":{"w":0.5086,"c":[[[0.4594,1.3251],[0.0671,0.6309],[0.4594,-0.0597],[0.4594,0.0254],[0.2707,0.6316],[0.4594,1.2401]]]},")":{"w":0.5086,"c":[[[0.0492,1.3251],[0.0492,1.2401],[0.2379,0.6316],[0.0492,0.0254],[0.0492,-0.0597],[0.4415,0.6309]]]}," ":{"w":0.45,"c":[]}};

        // --- Convex hull of a point set (for bonk's convex-only polygons) ---
        function _convexHull(pts) {
            if (pts.length <= 3) return pts;
            const sorted = [...pts].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
            const cross = (o, a, b) => (a[0]-o[0])*(b[1]-o[1])-(a[1]-o[1])*(b[0]-o[0]);
            const lower = [], upper = [];
            for (const p of sorted) {
                while (lower.length >= 2 && cross(lower[lower.length-2], lower[lower.length-1], p) <= 0) lower.pop();
                lower.push(p);
            }
            for (let i = sorted.length-1; i >= 0; i--) {
                const p = sorted[i];
                while (upper.length >= 2 && cross(upper[upper.length-2], upper[upper.length-1], p) <= 0) upper.pop();
                upper.push(p);
            }
            upper.pop(); lower.pop();
            return lower.concat(upper);
        }

        // Split a concave polygon into convex pieces (fan triangulation from centroid)
        // bonk.io accepts convex polygons up to 8 vertices.
        function _toConvexPolygons(pts, maxVerts = 7) {
            if (pts.length <= maxVerts) {
                const hull = _convexHull(pts);
                if (hull.length >= 3) return [hull];
            }
            // Fan-triangulate from centroid
            const cx = pts.reduce((s,p)=>s+p[0],0)/pts.length;
            const cy = pts.reduce((s,p)=>s+p[1],0)/pts.length;
            const tris = [];
            for (let i = 0; i < pts.length; i++) {
                const a = pts[i], b = pts[(i+1)%pts.length];
                tris.push([[cx,cy], a, b]);
            }
            return tris.filter(t => {
                const [p0,p1,p2]=t;
                return Math.abs((p1[0]-p0[0])*(p2[1]-p0[1])-(p1[1]-p0[1])*(p2[0]-p0[0])) > 1e-6;
            });
        }

        // --- SUMMON CORE ---
        function summonCore(type, targets) {
            const S = _requireState();
            if (!S) return;
            if (!targets?.length) {
                showToast('⚠️ No targets', 'err');
                return;
            }
            const obj = summonables[type];
            if (!obj) {
                showToast('⚠️ Object not found', 'err');
                return;
            }
            let bodies, shapes, fixtures, joints;
            if (obj.mapData) {
                const p = JSON.parse(expandMapData(obj.mapData));
                bodies = p.bodies;
                shapes = p.shapes;
                fixtures = p.fixtures;
                joints = p.joints ?? [];
            } else {
                bodies = JSON.parse(obj.bodies ?? '[]');
                shapes = JSON.parse(obj.shapes ?? '[]');
                fixtures = JSON.parse(obj.fixtures ?? '[]');
                joints = JSON.parse(obj.joints ?? '[]');
            }
            const validB = bodies.filter(b => b?.p);
            const centroid = validB.length
                ? {
                      x: validB.reduce((a, b) => a + b.p[0], 0) / validB.length,
                      y: validB.reduce((a, b) => a + b.p[1], 0) / validB.length,
                  }
                : { x: 0, y: 0 };
            for (const target of targets) {
                const dx = target.x - centroid.x,
                    dy = target.y - centroid.y;
                const shOff = S.state.physics.shapes.length,
                    fiOff = S.state.physics.fixtures.length,
                    boOff = S.state.physics.bodies.length;
                for (const sh of shapes) S.state.physics.shapes.push(sh ? structuredClone(sh) : sh);
                for (const fx of fixtures) {
                    if (!fx) {
                        S.state.physics.fixtures.push(fx);
                        continue;
                    }
                    const f = structuredClone(fx);
                    if (f.sh != null) f.sh += shOff;
                    S.state.physics.fixtures.push(f);
                }
                const newBodyIds = [];
                for (let i = 0; i < bodies.length; i++) {
                    const raw = bodies[i];
                    if (!raw) {
                        S.state.physics.bodies.push(raw);
                        newBodyIds.push(null);
                        continue;
                    }
                    const b = structuredClone(raw);
                    if (Array.isArray(b.fx)) b.fx = b.fx.map(fi => fi + fiOff);
                    if (Array.isArray(b.p)) {
                        b.p[0] += dx;
                        b.p[1] += dy;
                    }
                    if (!b.s) {
                        b.s = {};
                        for (const k in BODY_DEFAULTS) {
                            b.s[k] = b[k] !== undefined ? b[k] : BODY_DEFAULTS[k];
                            delete b[k];
                        }
                    }
                    b.id = S.state.physics.bodies.length;
                    newBodyIds.push(b.id);
                    S.state.physics.bodies.push(b);
                    S.state.physics.bro.unshift(b.id);
                }
                if (joints.length) {
                    if (!S.state.physics.joints) S.state.physics.joints = [];
                    for (const raw of joints) {
                        if (!raw) continue;
                        const j = structuredClone(raw);
                        if (j.ba != null && j.ba !== -1) j.ba = newBodyIds[j.ba] ?? j.ba + boOff;
                        if (j.bb != null && j.bb !== -1) j.bb = newBodyIds[j.bb] ?? j.bb + boOff;
                        if (j.type === 'rv') {
                            if (Array.isArray(j.a)) {
                                j.a[0] += dx;
                                j.a[1] += dy;
                            }
                            if (Array.isArray(j.b)) {
                                j.b[0] += dx;
                                j.b[1] += dy;
                            }
                        }
                        if (j.type === 'lpj' && j.pax != null) {
                            j.pax += dx;
                            j.pay += dy;
                        }
                        S.state.physics.joints.push(j);
                    }
                }
            }
            startGame(S.state, true);
        }

        function getSpawnTargets(forceAll = false) {
            const S = window.__SUMMON__;
            if (!S.state) return null;
            if (forceAll) {
                const alive = S.state.discs.map((d, i) => (d ? { x: d.x, y: d.y } : null)).filter(Boolean);
                if (!alive.length) {
                    showToast('⚠️ No alive players', 'err');
                    return null;
                }
                return alive;
            }
            const { ok, msg, x, y } = getSpawnParams();
            if (!ok) {
                showToast(msg, 'err');
                return null;
            }
            return [{ x, y }];
        }
        const invokeSummon = (type, forceAll = false) => {
            const t = getSpawnTargets(forceAll);
            if (!t) return;
            summonCore(type, t);
        };

        // --- TOAST ---
        let _tEl = null,
            _tTmr = null;
        function showToast(msg, type) {
            if (!_tEl) {
                _tEl = document.createElement('div');
                _tEl.style.cssText =
                    'position:fixed;bottom:22px;right:22px;z-index:2147483647;padding:8px 14px;border-radius:8px;font-family:monospace;font-size:12px;font-weight:bold;pointer-events:none;opacity:0;transition:opacity .22s,transform .22s;transform:translateY(8px);';
                document.body.appendChild(_tEl);
            }
            const c = {
                ok: { bg: 'rgba(40,180,80,.18)', b: 'rgba(80,220,120,.5)', c: '#80ffaa' },
                err: { bg: 'rgba(200,40,60,.18)', b: 'rgba(255,80,100,.5)', c: '#ff8090' },
            }[type] ?? { bg: 'rgba(40,180,80,.18)', b: 'rgba(80,220,120,.5)', c: '#80ffaa' };
            Object.assign(_tEl.style, {
                background: c.bg,
                border: `1px solid ${c.b}`,
                color: c.c,
                opacity: '1',
                transform: 'translateY(0)',
            });
            _tEl.textContent = msg;
            clearTimeout(_tTmr);
            _tTmr = setTimeout(() => {
                _tEl.style.opacity = '0';
                _tEl.style.transform = 'translateY(8px)';
            }, 2800);
        }

        // --- SELECT REFRESH ---
        const UI = {
            playerSel: null,
            playerSel2: null,
            useGlobal: false,
            offsetXInput: null,
            offsetYInput: null,
            selectedObj: null,
            selectedMode: 'obj',
            allMode: {},
            invokeAllMode: false,
            actionConfigMode: false,
            openCfgKey: null,
            allSelects: [],
        };
        function getPlayerRoleEmoji(u) {
            const n = u.name;
            if (u.id === hostId) return '👑 ';
            // roles are checked via the chat module's Sets — access via BonkSummonChat if available
            const chat = window.BonkSummonChat;
            if (chat) {
                if (chat.HAMP?.has(n)) return '🎀 ';
                if (chat.METZ?.has(n)) return '🧪 ';
                if (chat.BABY?.has(n)) return '🍼 ';
            }
            return '';
        }
        function populateSelect(sel) {
            const prev = parseInt(sel.value);
            sel.innerHTML = '';
            if (!users.length) {
                const o = document.createElement('option');
                o.value = -1;
                o.style.cssText = 'background:var(--sm-select-bg);color:var(--sm-text);';
                o.textContent = '— no room —';
                sel.appendChild(o);
                return;
            }
            for (const u of users) {
                const o = document.createElement('option');
                o.value = u.id;
                o.style.cssText = 'background:var(--sm-select-bg);color:var(--sm-text);';
                o.textContent = getPlayerRoleEmoji(u) + u.name + (u.guest ? ' (g)' : '');
                sel.appendChild(o);
            }
            if (users.some(u => u.id === prev)) sel.value = prev;
        }
        const refreshSel = () => UI.allSelects.forEach(s => s && populateSelect(s));
        // Refresh selects periodically so role emojis update when roles are assigned
        setInterval(() => refreshSel(), 2000);

        function getSpawnParams() {
            const pid = parseInt(UI.playerSel?.value ?? -1),
                ppm = window.__SUMMON__.state?.physics?.ppm ?? 30;
            const d = pid >= 0 ? window.__SUMMON__.state?.discs?.[pid] : null;
            const rawX = UI.offsetXInput?.value.trim(),
                rawY = UI.offsetYInput?.value.trim();
            const hasX = rawX !== '',
                hasY = rawY !== '',
                numX = parseFloat(rawX),
                numY = parseFloat(rawY);
            let fx, fy;
            if (UI.useGlobal) {
                fx = hasX ? (numX + 365) / ppm : (d?.x ?? null);
                fy = hasY ? (numY + 250) / ppm : (d?.y ?? null);
            } else {
                if (!d && (hasX || hasY)) return { ok: false, msg: '⚠️ Disc not found' };
                if (!d && !hasX && !hasY) return { ok: false, msg: '⚠️ Select a player' };
                fx = d ? d.x + (hasX && !isNaN(numX) ? numX / ppm : 0) : null;
                fy = d ? d.y + (hasY && !isNaN(numY) ? numY / ppm : 0) : null;
            }
            if (fx === null || fy === null) return { ok: false, msg: '⚠️ Select a player' };
            return { ok: true, x: fx, y: fy };
        }

        function buildShortcutKey(e) {
            const parts = [];
            if (e.ctrlKey) parts.push('ctrl');
            if (e.altKey) parts.push('alt');
            if (e.shiftKey) parts.push('shift');
            const rawCode = e.code || '';
            let k;
            if (rawCode.startsWith('Digit')) k = rawCode.slice(5);
            else if (rawCode.startsWith('Key')) k = rawCode.slice(3);
            else k = (e.key || '').length === 1 ? (e.key || '').toUpperCase() : (e.key || rawCode);
            parts.push(k);
            return parts.join('+');
        }
        window.addEventListener(
            'keydown',
            e => {
                const tag = document.activeElement?.tagName?.toLowerCase();
                if (tag === 'input' || tag === 'textarea' || tag === 'select') return;
                const key = buildShortcutKey(e);
                if (!SHORTCUTS[key]) return;
                const sc = SHORTCUTS[key];
                // New style: cmdText holds the raw chat command (e.g. ";expall 1c")
                const cmdText = sc.cmdText;
                if (!cmdText) return;
                e.preventDefault();
                const myName = window.BonkSummonChat?.getMyName?.();
                if (!myName) { showToast('⚠️ Not in a room', 'err'); return; }
                try {
                    window.BonkSummonChat.parseChatCmd(cmdText, myName);
                } catch (ex) {
                    showToast('⚠️ Shortcut error', 'err');
                    console.error('[SummonMod] shortcut error', ex);
                }
            },
            true
        );

        // --- SECTION BUILDER ---
        function mkSection(id, title, defaultOpen) {
            const isOpen = secPrefs.open[id] ?? defaultOpen;
            const wrap = document.createElement('div');
            wrap.dataset.secId = id;
            wrap.style.cssText =
                'background:var(--sm-ov0);border:1px solid var(--sm-bd0);border-radius:7px;flex-shrink:0;';
            const header = document.createElement('div');
            header.style.cssText =
                'display:flex;align-items:center;justify-content:space-between;padding:7px 10px;cursor:pointer;user-select:none;';
            header.innerHTML = `<span style="font-size:11px;font-weight:bold;letter-spacing:.05em;text-transform:uppercase;color:var(--sm-text-dim3);">${title}</span><span class="sec-chevron" style="font-size:10px;transition:transform .2s;">${isOpen ? '▲' : '▼'}</span>`;
            const body = document.createElement('div');
            body.style.cssText = `display:${isOpen ? 'flex' : 'none'};flex-direction:column;gap:6px;padding:8px;border-top:1px solid rgba(255,255,255,${isOpen ? '0.08' : '0'});`;
            wrap.__onToggle = null;
            const setOpen = v => {
                secPrefs.open[id] = v;
                saveSecPrefs();
                body.style.display = v ? 'flex' : 'none';
                body.style.borderTopColor = v ? 'var(--sm-bd0)' : 'var(--sm-bd0-none)';
                header.querySelector('.sec-chevron').textContent = v ? '▲' : '▼';
                wrap.__onToggle?.(v);
            };
            header.addEventListener('click', () => setOpen(!secPrefs.open[id]));
            header.addEventListener('mouseenter', () => (header.style.background = 'var(--sm-ov0)'));
            header.addEventListener('mouseleave', () => (header.style.background = ''));
            wrap.append(header, body);
            return { wrap, body, setOpen, header };
        }

        // --- CFG SLIDER ---
        function mkCfgSlider(label, min, max, step, value, color, onChange, suffix = '') {
            const wrap = div('display:flex;flex-direction:column;gap:3px;');
            const top = div('display:flex;justify-content:space-between;align-items:center;');
            const lblEl = el('span', `font-size:10px;color:${color};opacity:0.8;`, label);
            const valEl = el(
                'span',
                `font-size:11px;font-family:monospace;font-weight:bold;color:${color};`,
                value + suffix
            );
            top.append(lblEl, valEl);
            const slider = sk(el('input'));
            slider.type = 'range';
            slider.min = min;
            slider.max = max;
            slider.step = step;
            slider.value = value;
            slider.style.cssText = `width:100%;cursor:pointer;accent-color:${color};`;
            slider.addEventListener('mousedown', e => e.stopPropagation());
            slider.addEventListener('input', () => {
                const v = parseFloat(slider.value);
                onChange(v);
                valEl.textContent = v + suffix;
            });
            wrap.append(top, slider);
            return wrap;
        }

        // --- DECLARATIVE renderCfg engine ---

        function buildRenderCfg(title, titleColor, cfg, sliders, extraFn) {
            return panel => {
                panel.appendChild(el('div', `font-size:11px;font-weight:bold;color:${titleColor};`, title));
                for (const s of sliders) {
                    const rawVal = s.normalize
                        ? Math.round(((cfg[s.cfgKey] - s.min) / (s.max - s.min)) * 99 + 1)
                        : cfg[s.cfgKey];
                    panel.appendChild(
                        mkCfgSlider(
                            s.label,
                            s.normalize ? 1 : s.min,
                            s.normalize ? 100 : s.max,
                            s.step,
                            rawVal,
                            s.color ?? titleColor,
                            v => {
                                cfg[s.cfgKey] = s.normalize ? Math.round(s.min + ((v - 1) / 99) * (s.max - s.min)) : v;
                            },
                            s.suffix ?? ''
                        )
                    );
                }
                extraFn?.(panel);
            };
        }

        // --- IMPORT WINDOW ---
        let _importWin = null,
            _importNameInp = null,
            _importIconInp = null,
            _importJsonArea = null;
        function buildImportWindow() {
            if (_importWin) return;
            const win = registerThemeRoot(
                el(
                    'div',
                    'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:280px;min-width:220px;min-height:280px;z-index:2147483646;background:var(--sm-panel-bg);border:1px solid var(--sm-bd1);border-radius:10px;display:none;flex-direction:column;color:var(--sm-text);font-family:sans-serif;box-shadow:0 8px 32px rgba(0,0,0,0.6);overflow:hidden;'
                )
            );
            _importWin = win;
            win.id = '__SUMMON__importWin';
            // Header
            const header = el(
                'div',
                'display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--sm-bd0);cursor:grab;user-select:none;',
                ''
            );
            header.innerHTML = '<span style="font-size:13px;font-weight:bold;">📥 Import Summonable</span>';
            const closeBtn = el(
                'button',
                'border:none;background:rgba(255,80,80,0.15);color:#ff8090;border-radius:4px;padding:2px 8px;cursor:pointer;font-size:12px;',
                '✕'
            );
            closeBtn.addEventListener('click', () => {
                win.style.display = 'none';
            });
            header.appendChild(closeBtn);
            // Drag
            let drag = false,
                ox = 0,
                oy = 0;
            header.addEventListener('mousedown', e => {
                if (e.button !== 0) return;
                drag = true;
                ox = e.clientX - win.offsetLeft;
                oy = e.clientY - win.offsetTop;
                header.style.cursor = 'grabbing';
            });
            window.addEventListener('mousemove', e => {
                if (!drag) return;
                win.style.left = e.clientX - ox + 'px';
                win.style.top = e.clientY - oy + 'px';
                win.style.transform = 'none';
            });
            window.addEventListener('mouseup', () => {
                drag = false;
                header.style.cursor = 'grab';
            });
            // Body
            const body2 = div(
                'padding:12px;display:flex;flex-direction:column;gap:8px;flex:1 1 0;min-height:0;overflow-y:auto;'
            );
            // Name
            const nameWrap = div('display:flex;flex-direction:column;gap:3px;');
            nameWrap.append(el('span', CSS.lbl, 'Name (JS key)'));
            _importNameInp = sk(el('input', CSS.inp));
            _importNameInp.placeholder = 'my_object';
            nameWrap.appendChild(_importNameInp);
            // Icon
            const iconWrap = div('display:flex;flex-direction:column;gap:3px;');
            iconWrap.append(el('span', CSS.lbl, 'Emoji / Icon'));
            _importIconInp = sk(el('input', CSS.inp + 'width:60px;'));
            _importIconInp.placeholder = '🔷';
            iconWrap.appendChild(_importIconInp);
            // JSON
            const jsonWrap = div('display:flex;flex-direction:column;gap:3px;flex:1 1 0;min-height:0;');
            jsonWrap.append(el('span', CSS.lbl, 'mapData JSON'));
            _importJsonArea = sk(
                el(
                    'textarea',
                    'resize:none;background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.1);border-radius:5px;color:#7cffb0;font-family:monospace;font-size:9px;padding:6px;outline:none;flex:1 1 0;min-height:80px;width:100%;box-sizing:border-box;'
                )
            );
            _importJsonArea.placeholder = 'JSON will appear here after exporting...';
            jsonWrap.append(_importJsonArea);
            // Buttons
            const btnRow = div('display:flex;gap:6px;flex-shrink:0;');
            const saveBtn = mkBtn(
                '✔ Save',
                'cursor:pointer;flex:1;border-radius:5px;padding:7px;border:1px solid rgba(80,220,120,0.4);background:rgba(60,200,100,0.15);color:#80ffaa;font-size:11px;font-weight:bold;',
                () => {
                    const name = _importNameInp.value
                        .trim()
                        .replace(/\s+/g, '_')
                        .replace(/[^a-zA-Z0-9_]/g, '');
                    const icon = _importIconInp.value.trim() || '🔷';
                    const json = _importJsonArea.value.trim();
                    if (!name) {
                        showToast('⚠️ Enter a name', 'err');
                        return;
                    }
                    if (!json) {
                        showToast('⚠️ No JSON', 'err');
                        return;
                    }
                    try {
                        JSON.parse(json);
                    } catch (_) {
                        showToast('⚠️ Invalid JSON', 'err');
                        return;
                    }
                    // Block names that clash with existing chat commands or summonables
                    const _existingCmds = window.BonkSummonChat ? Object.keys(window.BonkSummonChat.R || {}) : [];
                    const _existingAliases = window.BonkSummonChat
                        ? Object.entries(window.BonkSummonChat.R || {})
                            .filter(([, d]) => d.alias)
                            .map(([, d]) => d.alias)
                        : [];
                    const _existingObjs = [
                        ...Object.keys(typeof SUMMONABLES_OBJ !== 'undefined' ? SUMMONABLES_OBJ : {}),
                        ...Object.keys(typeof SUMMONABLES_SKIN !== 'undefined' ? SUMMONABLES_SKIN : {}),
                    ];
                    const _allReserved = new Set([..._existingCmds, ..._existingAliases, ..._existingObjs]);
                    if (_allReserved.has(name)) {
                        showToast(`⚠️ "${name}" is already a command or object name`, 'err');
                        return;
                    }
                    addCustomSummonable(name, icon, json);
                    if (UI.selectedMode === 'custom') renderObjGrid?.();
                    win.style.display = 'none';
                }
            );
            const cancelBtn = mkBtn(
                '✕ Cancel',
                'cursor:pointer;padding:7px 12px;border-radius:5px;border:1px solid rgba(255,80,80,0.3);background:rgba(255,60,60,0.1);color:#ff8090;font-size:11px;font-weight:bold;',
                () => {
                    win.style.display = 'none';
                }
            );
            btnRow.append(saveBtn, cancelBtn);
            body2.append(nameWrap, iconWrap, jsonWrap, btnRow);
            // Resize handle
            const rh = el(
                'div',
                'position:absolute;right:0;bottom:0;width:14px;height:14px;cursor:nwse-resize;z-index:1;'
            );
            rh.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:block;opacity:0.35;"><line x1="4" y1="13" x2="13" y2="4" stroke="white" stroke-width="1.3"/><line x1="8" y1="13" x2="13" y2="8" stroke="white" stroke-width="1.3"/><line x1="12" y1="13" x2="13" y2="12" stroke="white" stroke-width="1.3"/></svg>`;
            let _ir = false,
                _irox = 0,
                _iroy = 0,
                _irw = 0,
                _irh = 0;
            rh.addEventListener('mousedown', e => {
                if (e.button !== 0) return;
                e.stopPropagation();
                e.preventDefault();
                _ir = true;
                _irox = e.clientX;
                _iroy = e.clientY;
                _irw = win.offsetWidth;
                _irh = win.offsetHeight;
                document.addEventListener('mousemove', _irmv);
                document.addEventListener('mouseup', _irup);
            });
            function _irmv(e) {
                if (!_ir) return;
                win.style.width = Math.max(220, _irw + (e.clientX - _irox)) + 'px';
                win.style.height = Math.max(280, _irh + (e.clientY - _iroy)) + 'px';
            }
            function _irup() {
                _ir = false;
                document.removeEventListener('mousemove', _irmv);
                document.removeEventListener('mouseup', _irup);
            }
            win.style.position = 'fixed';
            win.append(header, body2, rh);
            document.body.appendChild(win);
        }
        function openImportWindow(prefillJson = '') {
            if (!_importWin) buildImportWindow();
            if (_importJsonArea) _importJsonArea.value = prefillJson || _lastExportedMapData || '';
            if (_importNameInp) _importNameInp.value = '';
            if (_importIconInp) _importIconInp.value = '🔷';
            _importWin.style.display = 'flex';
            // Re-center if hasn't been dragged yet
            if (_importWin.style.transform !== 'none') {
                _importWin.style.top = '50%';
                _importWin.style.left = '50%';
            }
        }

        // --- SECTION DRAG-TO-REORDER ---
        function enableSectionDrag(main, sectionMap) {
            let dragInfo = null;
            function startDrag(wrap, e) {
                const rect = wrap.getBoundingClientRect();
                const ph = el(
                    'div',
                    `height:${rect.height}px;border:1px dashed rgba(120,160,255,0.5);border-radius:6px;background:rgba(120,160,255,0.08);flex:0 0 auto;`
                );
                main.insertBefore(ph, wrap);
                dragInfo = { wrap, ph, offsetY: e.clientY - rect.top };
                Object.assign(wrap.style, {
                    position: 'fixed',
                    top: rect.top + 'px',
                    left: rect.left + 'px',
                    width: rect.width + 'px',
                    zIndex: '9999',
                    opacity: '0.9',
                    boxShadow: '0 8px 24px rgba(0,0,0,0.5)',
                    pointerEvents: 'none',
                    cursor: 'grabbing',
                });
                document.addEventListener('mousemove', onMove);
                document.addEventListener('mouseup', onUp);
            }
            function onMove(e) {
                if (!dragInfo) return;
                const { wrap, ph, offsetY } = dragInfo;
                wrap.style.top = e.clientY - offsetY + 'px';
                const sibs = [...main.children].filter(c => c !== wrap && c !== ph);
                const target = sibs.find(
                    s => e.clientY < s.getBoundingClientRect().top + s.getBoundingClientRect().height / 2
                );
                target ? main.insertBefore(ph, target) : main.appendChild(ph);
            }
            function onUp() {
                if (!dragInfo) return;
                const { wrap, ph } = dragInfo;
                main.insertBefore(wrap, ph);
                ph.remove();
                Object.assign(wrap.style, {
                    position: '',
                    top: '',
                    left: '',
                    width: '',
                    zIndex: '',
                    opacity: '',
                    boxShadow: '',
                    pointerEvents: '',
                    cursor: 'grab',
                });
                document.removeEventListener('mousemove', onMove);
                document.removeEventListener('mouseup', onUp);
                secPrefs.order = [...main.children].map(c => c.dataset.secId).filter(Boolean);
                saveSecPrefs();
                dragInfo = null;
            }
            for (const wrap of Object.values(sectionMap)) {
                const header = wrap.firstChild;
                header.style.cursor = 'grab';
                let downX = 0,
                    downY = 0,
                    pending = null;
                const onM = e => {
                    if (Math.abs(e.clientX - downX) > 5 || Math.abs(e.clientY - downY) > 5) {
                        document.removeEventListener('mousemove', onM);
                        document.removeEventListener('mouseup', onU);
                        startDrag(pending, e);
                    }
                };
                const onU = () => {
                    document.removeEventListener('mousemove', onM);
                    document.removeEventListener('mouseup', onU);
                };
                header.addEventListener('mousedown', e => {
                    if (e.button !== 0) return;
                    downX = e.clientX;
                    downY = e.clientY;
                    pending = wrap;
                    document.addEventListener('mousemove', onM);
                    document.addEventListener('mouseup', onU);
                });
            }
        }

        // --- PHYSICS INPUT ROW ---
        function mkPhysicsRow(labelText, placeholder, btnLabel, bg, bd, t, fn) {
            const wrap = div('display:flex;flex-direction:column;gap:4px;');
            wrap.appendChild(lbl(labelText));
            const inp = mkInp(placeholder, 'number');
            inp.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const btn = accentBtn(btnLabel, bg, bd, t, () => fn(inp.value));
            wrap.appendChild(row(inp, btn));
            return { wrap, inp };
        }

        // --- PCT SLIDER ---
        function mkPctSlider(title, color, onApply) {
            const wrap = div('display:flex;flex-direction:column;gap:4px;');
            wrap.appendChild(lbl(title));
            const r = div('display:flex;align-items:center;gap:8px;');
            const slider = sk(el('input'));
            slider.type = 'range';
            slider.min = '0';
            slider.max = '100';
            slider.step = '1';
            slider.value = '50';
            slider.style.cssText = `flex:1;min-width:0;cursor:pointer;accent-color:${color};`;
            slider.addEventListener('mousedown', e => e.stopPropagation());
            const val = el(
                'span',
                `font-size:11px;font-family:monospace;font-weight:bold;color:${color};min-width:34px;text-align:right;`,
                '50%'
            );
            slider.addEventListener('input', () => (val.textContent = slider.value + '%'));
            const btn = accentBtn('✔ Apply', `${color}22`, color, color, () => onApply(slider.value));
            r.append(slider, val, btn);
            wrap.appendChild(r);
            return wrap;
        }

        // --- PCT + TARGET VALUE ROW ---

        function mkPctValueRow(title, defaultVal, placeholder, color, onApply) {
            const wrap = div('display:flex;flex-direction:column;gap:3px;');
            wrap.appendChild(lbl(title));
            const r = div('display:flex;align-items:center;gap:6px;');
            const slider = sk(el('input'));
            slider.type = 'range';
            slider.min = '1';
            slider.max = '100';
            slider.step = '1';
            slider.value = '100';
            slider.style.cssText = `flex:1;min-width:0;height:14px;cursor:pointer;accent-color:${color};`;
            slider.addEventListener('mousedown', e => e.stopPropagation());
            const pctLbl = el(
                'span',
                `font-size:10px;font-family:monospace;font-weight:bold;color:${color};min-width:32px;text-align:right;`,
                '100%'
            );
            slider.addEventListener('input', () => (pctLbl.textContent = slider.value + '%'));
            const valInp = skInp(el('input'));
            valInp.type = 'number';
            valInp.value = defaultVal ?? '';
            valInp.placeholder = placeholder;
            valInp.style.cssText =
                CSS.inp + 'width:56px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;';
            const btn = accentBtn('✔ Apply', `${color}22`, color, color, () => onApply(valInp.value, slider.value));
            r.append(slider, pctLbl, valInp, btn);
            wrap.appendChild(r);
            return { wrap, slider, valInp };
        }

        // ===
        //  BUILD HUD
        // ===
        function buildHUD() {
            // Inject global CSS to hide number-input spinners
            const _styleEl = document.createElement('style');
            _styleEl.textContent = `
                input[type=number]::-webkit-outer-spin-button,
                input[type=number]::-webkit-inner-spin-button {
                    -webkit-appearance: none;
                    margin: 0;
                }
                input[type=number] { -moz-appearance: textfield; }
            `;
            document.head.appendChild(_styleEl);

            const ADEFS = [
                {
                    key: 'spin',
                    icon: '🌀',
                    label: 'Spin',
                    noAll: false,
                    color: { bg: 'rgba(80,200,255,0.1)', bd: 'rgba(80,200,255,0.35)', t: '#80e8ff' },
                    cfg: SPIN_CFG,
                    defaults: { speedDeg: 360, mode: 'fixed' },
                    run: t => actionSpin(t, SPIN_CFG.speedDeg, SPIN_CFG.mode === 'random'),
                    renderCfg: panel => {
                        panel.appendChild(
                            el('div', 'font-size:11px;font-weight:bold;color:#80e8ff;', '🌀 Spin Options')
                        );
                        const modeRow = div('display:flex;gap:6px;');
                        const sliderRow = div(
                            `display:${SPIN_CFG.mode === 'fixed' ? 'flex' : 'none'};align-items:center;gap:8px;`
                        );
                        let _aSpBtn = null;
                        const _spBM = {};
                        const setSpMode = (key, btn) => {
                            SPIN_CFG.mode = key;
                            if (_aSpBtn) Object.assign(_aSpBtn.style, CSS.inactive);
                            _aSpBtn = btn;
                            Object.assign(btn.style, {
                                background: 'rgba(80,200,255,0.22)',
                                borderColor: 'rgba(80,200,255,0.6)',
                                color: '#80e8ff',
                            });
                            sliderRow.style.display = key === 'fixed' ? 'flex' : 'none';
                        };
                        for (const m of [
                            { key: 'random', icon: '🎲', l: 'Random' },
                            { key: 'fixed', icon: '🎯', l: 'Fixed' },
                        ]) {
                            const b = mkBtn(
                                `${m.icon} ${m.l}`,
                                'flex:1;text-align:center;cursor:pointer;padding:6px 4px;border-radius:5px;font-size:10px;font-weight:bold;font-family:monospace;border:1px solid var(--sm-bd2);background:var(--sm-btn-bg);color:var(--sm-text-dim2);transition:all .15s;',
                                () => setSpMode(m.key, b)
                            );
                            modeRow.appendChild(b);
                            _spBM[m.key] = b;
                        }
                        const sL = el(
                            'span',
                            'font-size:10px;color:var(--sm-text-dim2);white-space:nowrap;',
                            '🌀 Degrees'
                        );
                        const sS = sk(el('input'));
                        sS.type = 'range';
                        sS.min = '-360';
                        sS.max = '360';
                        sS.step = '1';
                        sS.value = SPIN_CFG.speedDeg;
                        sS.style.cssText = 'flex:1;cursor:pointer;accent-color:#80e8ff;';
                        sS.addEventListener('mousedown', e => e.stopPropagation());
                        const sV = el(
                            'span',
                            'font-size:11px;font-weight:bold;font-family:monospace;color:#80e8ff;min-width:40px;text-align:right;',
                            SPIN_CFG.speedDeg + '°'
                        );
                        sS.addEventListener('input', () => {
                            SPIN_CFG.speedDeg = parseInt(sS.value);
                            sV.textContent = SPIN_CFG.speedDeg + '°';
                        });
                        sliderRow.append(sL, sS, sV);
                        panel.append(modeRow, sliderRow);
                        setSpMode(SPIN_CFG.mode, _spBM[SPIN_CFG.mode] || _spBM['fixed']);
                    },
                },
                {
                    key: 'dinnerbone',
                    icon: '🙃',
                    label: 'Dinnerbone',
                    noAll: false,
                    color: { bg: 'rgba(255,200,50,0.1)', bd: 'rgba(255,200,50,0.35)', t: '#ffe680' },
                    run: t => actionDinnerbone(t),
                },
                {
                    key: 'balance',
                    icon: '⚖️',
                    label: 'Balance',
                    noAll: false,
                    color: { bg: 'rgba(255,120,180,0.12)', bd: 'rgba(255,150,200,0.4)', t: '#ffb0d0' },
                    cfg: SIZE_CFG,
                    defaults: { mode: 'random', value: 0 },
                    run: t => {
                        const val = SIZE_CFG.mode === 'fixed' ? SIZE_CFG.value : undefined;
                        if (t === 'all') actionSizeAll(val);
                        else actionSize(t, val);
                    },
                    renderCfg: panel => {
                        panel.appendChild(
                            el('div', 'font-size:11px;font-weight:bold;color:#ffb0d0;', '📏 Size Options')
                        );
                        const modeRow = div('display:flex;gap:6px;');
                        const valRow = div(
                            `display:${SIZE_CFG.mode === 'fixed' ? 'flex' : 'none'};align-items:center;gap:8px;`
                        );
                        let _aSBtn = null;
                        const _sBM = {};
                        const setMode = (key, btn) => {
                            SIZE_CFG.mode = key;
                            if (_aSBtn) Object.assign(_aSBtn.style, CSS.inactive);
                            _aSBtn = btn;
                            Object.assign(btn.style, {
                                background: 'rgba(255,150,200,0.22)',
                                borderColor: 'rgba(255,150,200,0.6)',
                                color: '#ffb0d0',
                            });
                            valRow.style.display = key === 'fixed' ? 'flex' : 'none';
                        };
                        for (const m of [
                            { key: 'random', icon: '🎲', l: 'Random' },
                            { key: 'fixed', icon: '🎯', l: 'Fixed' },
                        ]) {
                            const b = mkBtn(
                                `${m.icon} ${m.l}`,
                                'flex:1;text-align:center;cursor:pointer;padding:6px 4px;border-radius:5px;font-size:10px;font-weight:bold;font-family:monospace;border:1px solid var(--sm-bd2);background:var(--sm-btn-bg);color:var(--sm-text-dim2);transition:all .15s;',
                                () => setMode(m.key, b)
                            );
                            modeRow.appendChild(b);
                            _sBM[m.key] = b;
                        }
                        const vL = el(
                            'span',
                            'font-size:10px;color:var(--sm-text-dim2);white-space:nowrap;',
                            '📏 Value'
                        );
                        const vS = sk(el('input'));
                        vS.type = 'range';
                        vS.min = '-100';
                        vS.max = '100';
                        vS.step = '1';
                        vS.value = SIZE_CFG.value;
                        vS.style.cssText = 'flex:1;cursor:pointer;accent-color:#ffb0d0;';
                        vS.addEventListener('mousedown', e => e.stopPropagation());
                        const vN = skInp(
                            el(
                                'input',
                                'width:68px;flex-shrink:0;background:var(--sm-input-bg);border:1px solid var(--sm-bd2);border-radius:5px;color:#ffb0d0;font-size:11px;font-family:monospace;padding:3px 5px;outline:none;box-sizing:border-box;text-align:right;'
                            )
                        );
                        vN.type = 'number';
                        vN.value = SIZE_CFG.value;
                        vS.addEventListener('input', () => {
                            SIZE_CFG.value = parseFloat(vS.value);
                            vN.value = SIZE_CFG.value;
                        });
                        vN.addEventListener('input', () => {
                            const v = parseFloat(vN.value);
                            if (!isNaN(v)) {
                                SIZE_CFG.value = v;
                                vS.value = Math.max(-100, Math.min(100, v));
                            }
                        });
                        valRow.append(vL, vS, vN);
                        panel.append(modeRow, valRow);
                        setMode(SIZE_CFG.mode, _sBM[SIZE_CFG.mode] || _sBM['random']);
                    },
                },
                {
                    key: 'implode',
                    icon: '🌑',
                    label: 'Implode',
                    noAll: false,
                    color: { bg: 'rgba(80,180,255,0.1)', bd: 'rgba(80,180,255,0.35)', t: '#80c8ff' },
                    cfg: IMPLODE_CFG,
                    defaults: { count: 15, radius: 6, speed: 60, life: 250 },
                    run: t => actionImplode(t),
                    cfgTitle: '🌑 Implode Options',
                    cfgColor: '#80c8ff',
                    sliders: [
                        { label: 'Arrow count', min: 1, max: 50, step: 1, cfgKey: 'count', cfg: IMPLODE_CFG },
                        { label: 'Spawn radius', min: 1, max: 30, step: 0.5, cfgKey: 'radius', cfg: IMPLODE_CFG },
                        {
                            label: 'Force (1-100)',
                            min: 1,
                            max: 65,
                            step: 1,
                            cfgKey: 'speed',
                            cfg: IMPLODE_CFG,
                            normalize: true,
                        },
                        { label: 'Duration', min: 50, max: 1000, step: 10, cfgKey: 'life', cfg: IMPLODE_CFG },
                    ],
                },
                {
                    key: 'explode',
                    icon: '💥',
                    label: 'Explode',
                    noAll: false,
                    color: { bg: 'rgba(200,50,50,0.15)', bd: 'rgba(255,80,80,0.4)', t: '#ff9090' },
                    cfg: EXPLODE_CFG,
                    defaults: { count: 15, radius: 1.5, speed: 60, life: 250 },
                    run: t => actionExplode(t),
                    cfgTitle: '💥 Explode Options',
                    cfgColor: '#ff9090',
                    sliders: [
                        { label: 'Arrow count', min: 1, max: 50, step: 1, cfgKey: 'count', cfg: EXPLODE_CFG },
                        { label: 'Spawn radius', min: 0.5, max: 30, step: 0.5, cfgKey: 'radius', cfg: EXPLODE_CFG },
                        {
                            label: 'Force (1-100)',
                            min: 1,
                            max: 65,
                            step: 1,
                            cfgKey: 'speed',
                            cfg: EXPLODE_CFG,
                            normalize: true,
                        },
                        { label: 'Duration', min: 50, max: 1000, step: 10, cfgKey: 'life', cfg: EXPLODE_CFG },
                    ],
                },
                {
                    key: 'bringall',
                    icon: '🧲',
                    label: 'BringAll',
                    noAll: true,
                    color: { bg: 'rgba(255,160,50,0.12)', bd: 'rgba(255,200,80,0.4)', t: '#ffd080' },
                    run: t => actionBringAll(t),
                },
                {
                    key: 'repulse',
                    icon: '💨',
                    label: 'Repulse',
                    noAll: true,
                    color: { bg: 'rgba(200,255,150,0.1)', bd: 'rgba(180,255,120,0.35)', t: '#c0ff80' },
                    cfg: REPULSE_CFG,
                    defaults: { force: 80, range: 50 },
                    run: t => actionRepulse(t),
                    cfgTitle: '💨 Repulse Options',
                    cfgColor: '#c0ff80',
                    sliders: [
                        { label: 'Force', min: 10, max: 300, step: 5, cfgKey: 'force', cfg: REPULSE_CFG },
                        { label: 'Effect radius', min: 2, max: 60, step: 1, cfgKey: 'range', cfg: REPULSE_CFG },
                    ],
                },
                {
                    key: 'attract',
                    icon: '🕳️',
                    label: 'Attract',
                    noAll: true,
                    color: { bg: 'rgba(80,50,120,0.2)', bd: 'rgba(140,100,255,0.4)', t: '#b090ff' },
                    cfg: ATTRACT_CFG,
                    defaults: { force: 80, range: 50 },
                    run: t => actionAttract(t),
                    cfgTitle: '🕳️ Attract Options',
                    cfgColor: '#b090ff',
                    sliders: [
                        { label: 'Force', min: 10, max: 300, step: 5, cfgKey: 'force', cfg: ATTRACT_CFG },
                        { label: 'Effect radius', min: 2, max: 60, step: 1, cfgKey: 'range', cfg: ATTRACT_CFG },
                    ],
                },
                {
                    key: 'rain',
                    icon: '🌧️',
                    label: 'Rain',
                    noAll: false,
                    color: { bg: 'rgba(50,130,200,0.15)', bd: 'rgba(80,180,255,0.4)', t: '#80c8ff' },
                    cfg: RAIN_CFG,
                    defaults: { cols: 9, spacing: 3, height: 30, spread: 0.5, fallSpeed: 25, life: 150 },
                    run: t => actionRain(t),
                    cfgTitle: '🌧️ Rain Options',
                    cfgColor: '#80c8ff',
                    sliders: [
                        { label: 'Columns', min: 1, max: 50, step: 1, cfgKey: 'cols', cfg: RAIN_CFG },
                        { label: 'Spawn spacing', min: 0.5, max: 10, step: 0.5, cfgKey: 'spacing', cfg: RAIN_CFG },
                        { label: 'Spawn height', min: 5, max: 100, step: 5, cfgKey: 'height', cfg: RAIN_CFG },
                        { label: 'Horizontal spread', min: 0, max: 3, step: 0.1, cfgKey: 'spread', cfg: RAIN_CFG },
                        {
                            label: 'Fall speed (1-100)',
                            min: 1,
                            max: 65,
                            step: 1,
                            cfgKey: 'fallSpeed',
                            cfg: RAIN_CFG,
                            normalize: true,
                        },
                        { label: 'Duration', min: 50, max: 800, step: 10, cfgKey: 'life', cfg: RAIN_CFG },
                    ],
                },
                {
                    key: 'orbit',
                    icon: '🌐',
                    label: 'Orbit',
                    noAll: false,
                    color: { bg: 'rgba(40,200,180,0.12)', bd: 'rgba(80,220,200,0.4)', t: '#80ffe8' },
                    cfg: ORBIT_CFG,
                    defaults: { count: 6, radius: 8, speed: 50, life: 300 },
                    run: t => actionOrbit(t),
                    cfgTitle: '🌐 Orbit Options',
                    cfgColor: '#80ffe8',
                    sliders: [
                        { label: 'Arrow count', min: 1, max: 50, step: 1, cfgKey: 'count', cfg: ORBIT_CFG },
                        { label: 'Orbit radius', min: 2, max: 40, step: 1, cfgKey: 'radius', cfg: ORBIT_CFG },
                        {
                            label: 'Orbital speed (1-100)',
                            min: 1,
                            max: 65,
                            step: 1,
                            cfgKey: 'speed',
                            cfg: ORBIT_CFG,
                            normalize: true,
                        },
                        { label: 'Duration', min: 50, max: 1000, step: 10, cfgKey: 'life', cfg: ORBIT_CFG },
                    ],
                },
                {
                    key: 'shotgun',
                    icon: '🔫',
                    label: 'Shotgun',
                    noAll: false,
                    color: { bg: 'rgba(180,100,50,0.15)', bd: 'rgba(220,140,80,0.4)', t: '#ffa060' },
                    cfg: SHOTGUN_CFG,
                    defaults: { count: 7, spread: 0.18, speed: 90, life: 200, mode: 'nearest' },
                    run: t => actionShotgun(t, SHOTGUN_CFG.speed, SHOTGUN_CFG.count, SHOTGUN_CFG.spread, SHOTGUN_CFG.mode),
                    renderCfg: panel => {
                        panel.appendChild(
                            el('div', 'font-size:11px;font-weight:bold;color:#ffa060;', '🔫 Shotgun Options')
                        );
                        const modeRow = div('display:flex;gap:6px;');
                        let _aBtn = null;
                        const _bM = {};
                        const setSM = (key, btn) => {
                            SHOTGUN_CFG.mode = key;
                            if (_aBtn) Object.assign(_aBtn.style, CSS.inactive);
                            _aBtn = btn;
                            Object.assign(btn.style, {
                                background: 'rgba(255,160,80,0.22)',
                                borderColor: 'rgba(255,160,80,0.6)',
                                color: '#ffa060',
                            });
                        };
                        for (const m of [
                            { key: 'nearest', icon: '🎯', l: 'Nearest' },
                            { key: 'velocity', icon: '➡️', l: 'Velocity' },
                            { key: 'random', icon: '🎲', l: 'Random' },
                        ]) {
                            const b = mkBtn(
                                `${m.icon} ${m.l}`,
                                'flex:1;text-align:center;cursor:pointer;padding:6px 4px;border-radius:5px;font-size:10px;font-weight:bold;font-family:monospace;border:1px solid var(--sm-bd2);background:var(--sm-btn-bg);color:var(--sm-text-dim2);transition:all .15s;',
                                () => setSM(m.key, b)
                            );
                            modeRow.appendChild(b);
                            _bM[m.key] = b;
                        }
                        panel.appendChild(modeRow);
                        setSM(SHOTGUN_CFG.mode || 'nearest', _bM[SHOTGUN_CFG.mode] || _bM['nearest']);
                        panel.append(
                            mkCfgSlider(
                                'Pellet count',
                                1,
                                50,
                                1,
                                SHOTGUN_CFG.count,
                                '#ffa060',
                                v => (SHOTGUN_CFG.count = v)
                            ),
                            mkCfgSlider(
                                'Spread (1-100)',
                                1,
                                100,
                                1,
                                Math.round(((SHOTGUN_CFG.spread - 0.02) / 0.98) * 99 + 1),
                                '#ffa060',
                                v => (SHOTGUN_CFG.spread = parseFloat((0.02 + ((v - 1) / 99) * 0.98).toFixed(3)))
                            ),
                            mkCfgSlider(
                                'Force (1-100)',
                                1,
                                100,
                                1,
                                Math.round(((SHOTGUN_CFG.speed - 1) / 64) * 99 + 1),
                                '#ffa060',
                                v => (SHOTGUN_CFG.speed = Math.round(1 + ((v - 1) / 99) * 64))
                            ),
                            mkCfgSlider(
                                'Duration',
                                50,
                                800,
                                10,
                                SHOTGUN_CFG.life,
                                '#ffa060',
                                v => (SHOTGUN_CFG.life = v)
                            )
                        );
                    },
                },
                {
                    key: 'superfling',
                    icon: '🚀',
                    label: 'SuperFling',
                    noAll: false,
                    color: { bg: 'rgba(120,80,255,0.15)', bd: 'rgba(160,120,255,0.4)', t: '#c0a0ff' },
                    cfg: FLING,
                    defaults: { mode: 'random', angleValue: 0, multiplier: 1.0 },
                    run: t => actionSuperFling(t),
                    renderCfg: panel => {
                        panel.appendChild(
                            el('div', 'font-size:11px;font-weight:bold;color:#c0a0ff;', '🚀 SuperFling Options')
                        );
                        const fmRow = div('display:flex;gap:6px;');
                        let _aBtn = null;
                        const _fBM = {};
                        const _angRow = div(
                            `display:${FLING.mode === 'angle' ? 'flex' : 'none'};align-items:center;justify-content:center;gap:8px;`
                        );
                        const setFM = (key, btn) => {
                            FLING.mode = key;
                            if (_aBtn) Object.assign(_aBtn.style, CSS.inactive);
                            _aBtn = btn;
                            Object.assign(btn.style, {
                                background: 'rgba(120,160,255,0.22)',
                                borderColor: 'rgba(120,160,255,0.6)',
                                color: '#9ab8ff',
                            });
                            _angRow.style.display = key === 'angle' ? 'flex' : 'none';
                        };
                        for (const fm of [
                            { key: 'random', icon: '🎲', l: 'Random' },
                            { key: 'velocity', icon: '➡️', l: 'Velocity' },
                            { key: 'angle', icon: '📐', l: 'Angle' },
                        ]) {
                            const b = mkBtn(
                                `${fm.icon} ${fm.l}`,
                                'flex:1;text-align:center;cursor:pointer;padding:6px 4px;border-radius:5px;font-size:10px;font-weight:bold;font-family:monospace;border:1px solid var(--sm-bd2);background:var(--sm-btn-bg);color:var(--sm-text-dim2);transition:all .15s;white-space:nowrap;',
                                () => setFM(fm.key, b)
                            );
                            fmRow.appendChild(b);
                            _fBM[fm.key] = b;
                        }
                        const _aL = el(
                            'span',
                            'font-size:10px;color:var(--sm-text-dim2);white-space:nowrap;',
                            '📐 Degrees'
                        );
                        const _aS = sk(el('input'));
                        _aS.type = 'range';
                        _aS.min = '0';
                        _aS.max = '360';
                        _aS.value = FLING.angleValue;
                        _aS.style.cssText = 'flex:1;cursor:pointer;accent-color:#9ab8ff;';
                        _aS.addEventListener('mousedown', e => e.stopPropagation());
                        const _aV = el(
                            'span',
                            'font-size:11px;font-weight:bold;font-family:monospace;color:#9ab8ff;min-width:32px;text-align:right;',
                            FLING.angleValue + '°'
                        );
                        _aS.addEventListener('input', () => {
                            FLING.angleValue = parseInt(_aS.value);
                            _aV.textContent = FLING.angleValue + '°';
                        });
                        _angRow.append(_aL, _aS, _aV);
                        const _mR = div('display:flex;align-items:center;gap:6px;');
                        const _mL = el(
                            'span',
                            'font-size:10px;color:var(--sm-text-dim2);white-space:nowrap;',
                            '⚡ Force'
                        );
                        const _mS = sk(el('input'));
                        _mS.type = 'range';
                        _mS.min = '1';
                        _mS.max = '100';
                        _mS.step = '1';
                        _mS.value = Math.round(((Math.min(Math.max(FLING.multiplier, 0.1), 5.0) - 0.1) / 4.9) * 99 + 1);
                        _mS.style.cssText = 'flex:1;cursor:pointer;accent-color:#80ffaa;';
                        _mS.addEventListener('mousedown', e => e.stopPropagation());
                        const _mV = el(
                            'span',
                            'font-size:11px;font-weight:bold;font-family:monospace;color:#80ffaa;min-width:36px;text-align:right;',
                            Math.round(((Math.min(Math.max(FLING.multiplier, 0.1), 5.0) - 0.1) / 4.9) * 99 + 1) + ''
                        );
                        _mS.addEventListener('input', () => {
                            FLING.multiplier = parseFloat((0.1 + ((parseInt(_mS.value) - 1) / 99) * 4.9).toFixed(2));
                            _mV.textContent = parseInt(_mS.value);
                        });
                        _mR.append(_mL, _mS, _mV);
                        panel.append(fmRow, _angRow, _mR);
                        setFM(FLING.mode, _fBM[FLING.mode] || _fBM['random']);
                    },
                },
            ];

            // --- HOST GUARD ---
            const _iAmHost = () => myId !== -1 && myId === hostId;
            function _requireHost() {
                if (!_iAmHost()) {
                    showToast('⚠️ Only the host can do this', 'err');
                    return false;
                }
                return true;
            }

            let _frozen = false;

            function _setFrozen(force) {
                if (!_requireHost()) return null;
                const S = window.__SUMMON__;
                if (!S?.state) {
                    showToast('⚠️ No game state', 'err');
                    return null;
                }
                force = !!force;
                if (force === _frozen) {
                    return _frozen;
                }
                _frozen = force;
                // Sync with global state so startGame() can check it
                window.__SUMMON__._frozen = _frozen;
                if (_frozen) {
                    startGame(S.state, true, 1000000);
                    Object.assign(freezeBtn.style, {
                        background: 'rgba(255,80,80,0.18)',
                        borderColor: 'rgba(255,100,100,0.6)',
                        color: '#ff9090',
                    });
                    freezeBtn.textContent = '🔥 Unfreeze';
                } else {
                    // When unfreezing, temporarily clear the flag so startGame uses normal time
                    window.__SUMMON__._frozen = false;
                    startGame(S.state, true, 30);
                    Object.assign(freezeBtn.style, {
                        background: 'rgba(60,160,255,0.1)',
                        borderColor: 'rgba(100,200,255,0.4)',
                        color: '#80d8ff',
                    });
                    freezeBtn.textContent = '❄️ Freeze';
                }
                return _frozen;
            }
            function _toggleFreeze() {
                return _setFrozen(!_frozen);
            }
            window.__SUMMON__._toggleFreeze = _toggleFreeze;
            window.__SUMMON__._setFrozen = _setFrozen;
            const freezeBtn = mkBtn(
                '❄️ Freeze',
                'cursor:pointer;border-radius:6px;padding:8px 14px;border:1px solid rgba(100,200,255,0.4);background:rgba(60,160,255,0.1);color:#80d8ff;font-size:11px;font-weight:bold;transition:background .15s,border-color .15s,color .15s;width:100%;',
                () => _toggleFreeze()
            );

            // -- Register allMode map ---
            for (const def of ADEFS) {
                UI.allMode[def.key] = false;
            }

            // -- resolve renderCfg from sliders or custom fn ---
            const getRenderCfg = def => {
                if (def.renderCfg) return def.renderCfg;
                if (def.sliders)
                    return buildRenderCfg(def.cfgTitle, def.cfgColor, def.sliders[0]?.cfg ?? def.cfg, def.sliders);
                return null;
            };

            const resolveTarget = key => {
                if (UI.allMode[key]) return 'all';
                const pid = parseInt(UI.playerSel?.value ?? -1);
                if (pid < 0) {
                    showToast('⚠️ Select a player', 'err');
                    return null;
                }
                return pid;
            };

            // -- Wrapper & main scroll area ---
            const wrapper = registerThemeRoot(
                div(
                    'display:flex;flex-direction:column;height:100%;min-height:0;min-width:0;overflow:hidden;color:var(--sm-text);font-family:sans-serif;box-sizing:border-box;'
                )
            );
            const main = div(
                'flex:1 1 0;overflow-y:auto;overflow-x:hidden;min-height:0;min-width:0;padding:8px;display:flex;flex-direction:column;gap:6px;box-sizing:border-box;'
            );
            const sectionMap = {};

            // -- TARGET SECTION ---
            const { wrap: twrap, body: tbody, header: theader } = mkSection('target', '🎯 Target', true);
            const tglGlobalBtn = el('button', CSS.tab, '🌐 Global');
            UI.useGlobal = false;
            const tChevron = theader.querySelector('.sec-chevron');
            const tHeaderExtras = div('display:flex;align-items:center;gap:6px;margin-left:auto;', tglGlobalBtn);
            theader.insertBefore(tHeaderExtras, tChevron);
            twrap.__onToggle = v => (tHeaderExtras.style.display = v ? 'flex' : 'none');
            tHeaderExtras.style.display = (secPrefs.open.target ?? true) ? 'flex' : 'none';
            const pRow = div('display:flex;flex-direction:column;gap:3px;');
            pRow.appendChild(lbl('Target player'));
            const s1 = mkSel();
            UI.playerSel = s1;
            pRow.appendChild(s1);
            const xyR = div('display:grid;grid-template-columns:1fr 1fr;gap:6px;');
            const xW = div('display:flex;flex-direction:column;gap:2px;');
            const xL = el('span', 'font-size:10px;color:var(--sm-text-dim2);', 'X');
            const xI = mkInp('offset X');
            UI.offsetXInput = xI;
            xW.append(xL, xI);
            const yW = div('display:flex;flex-direction:column;gap:2px;');
            const yL = el('span', 'font-size:10px;color:var(--sm-text-dim2);', 'Y');
            const yI = mkInp('offset Y');
            UI.offsetYInput = yI;
            yW.append(yL, yI);
            xyR.append(xW, yW);
            const hintEl = el(
                'div',
                'font-size:10px;color:var(--sm-text-dim1);',
                'relative to player (empty = no offset)'
            );
            tglGlobalBtn.addEventListener('click', e => {
                e.stopPropagation();
                UI.useGlobal = !UI.useGlobal;
                if (UI.useGlobal) {
                    Object.assign(tglGlobalBtn.style, {
                        background: 'rgba(120,160,255,0.25)',
                        borderColor: 'rgba(120,160,255,0.5)',
                        color: '#9ab8ff',
                    });
                    xL.textContent = 'X (±365)';
                    yL.textContent = 'Y (±250)';
                    hintEl.textContent = 'absolute map coordinate (empty = use player pos)';
                } else {
                    Object.assign(tglGlobalBtn.style, CSS.inactive);
                    xL.textContent = 'X';
                    yL.textContent = 'Y';
                    hintEl.textContent = 'relative to player (empty = no offset)';
                }
                UI._refreshPtMode?.();
            });
            tbody.append(pRow, xyR, hintEl);
            sectionMap.target = twrap;

            // -- OBJECTS SECTION ---
            const { wrap: owrap, body: obody, header: oheader } = mkSection('objects', '📦 Objects', true);
            const OBJ_TABS = [
                {
                    key: 'obj',
                    label: 'Objects',
                    secTitle: '📦 Objects',
                    ac: 'rgba(120,160,255,0.25)',
                    bc: 'rgba(120,160,255,0.5)',
                    tc: '#9ab8ff',
                    selBg: 'rgba(120,160,255,0.2)',
                    selBd: 'rgba(120,160,255,0.6)',
                },
                {
                    key: 'skin',
                    label: 'Skins',
                    secTitle: '🎭 Skins',
                    ac: 'rgba(255,180,80,0.2)',
                    bc: 'rgba(255,180,80,0.5)',
                    tc: '#ffc060',
                    selBg: 'rgba(255,180,80,0.15)',
                    selBd: 'rgba(255,180,80,0.6)',
                },
                {
                    key: 'custom',
                    label: 'Custom',
                    secTitle: '⭐ Custom',
                    ac: 'rgba(255,200,50,0.2)',
                    bc: 'rgba(255,210,80,0.5)',
                    tc: '#ffe080',
                    selBg: 'rgba(255,200,50,0.15)',
                    selBd: 'rgba(255,200,50,0.6)',
                },
            ];
            const tabsRow = div('display:flex;gap:4px;');
            const oSecTitleEl = oheader.querySelector('span');
            const oTabBtns = {};
            const ogWrap = div('overflow-y:visible;border-radius:5px;');
            const og = div(
                'display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:5px;padding:2px;'
            );
            ogWrap.appendChild(og);
            obody.appendChild(ogWrap);
            UI.selectedObj = null;
            let oSelBtn = null;

            function renderObjGrid() {
                og.innerHTML = '';
                oSelBtn = null;
                const pool = getSummonables(UI.selectedMode);
                const entries = Object.entries(pool);
                const tabCfg = OBJ_TABS.find(t => t.key === UI.selectedMode);
                if (!entries.length) {
                    const empty = el(
                        'div',
                        'grid-column:1/-1;font-size:10px;color:var(--sm-text-dim1);text-align:center;padding:14px;',
                        'No items in this section yet'
                    );
                    og.appendChild(empty);
                    UI.selectedObj = null;
                    return;
                }
                for (const [key, obj] of entries) {
                    const b = el(
                        'button',
                        'cursor:pointer;border-radius:6px;padding:7px 4px;min-height:52px;border:1px solid var(--sm-bd1);background:var(--sm-ov0);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;transition:filter .15s;position:relative;'
                    );
                    b.append(
                        el('span', 'font-size:18px;line-height:1.2;', obj.icon),
                        el(
                            'span',
                            'font-size:9px;font-weight:bold;color:var(--sm-text-dim3);text-transform:uppercase;letter-spacing:.03em;',
                            key.replace(/_/g, ' ')
                        )
                    );
                    if (UI.selectedMode === 'custom') {
                        const delBtn = el(
                            'span',
                            'position:absolute;top:2px;right:2px;font-size:9px;font-weight:bold;line-height:1;width:14px;height:14px;display:flex;align-items:center;justify-content:center;border-radius:3px;background:rgba(255,60,60,0.25);color:#ff9090;',
                            '✕'
                        );
                        delBtn.addEventListener('click', e => {
                            e.stopPropagation();
                            removeCustomSummonable(key);
                            renderObjGrid();
                        });
                        delBtn.addEventListener('mouseenter', () => (delBtn.style.background = 'rgba(255,60,60,0.45)'));
                        delBtn.addEventListener('mouseleave', () => (delBtn.style.background = 'rgba(255,60,60,0.25)'));
                        b.appendChild(delBtn);
                    }
                    const selectBtn = (btn, k) => {
                        if (oSelBtn) {
                            oSelBtn.style.background = 'var(--sm-ov0)';
                            oSelBtn.style.borderColor = 'var(--sm-bd1)';
                        }
                        oSelBtn = btn;
                        UI.selectedObj = k;
                        btn.style.background = tabCfg.selBg;
                        btn.style.borderColor = tabCfg.selBd;
                    };
                    b.addEventListener('click', () => selectBtn(b, key));
                    b.addEventListener('mouseenter', () => {
                        if (UI.selectedObj !== key) b.style.filter = 'brightness(1.3)';
                    });
                    b.addEventListener('mouseleave', () => (b.style.filter = ''));
                    if (!oSelBtn) selectBtn(b, key);
                    og.appendChild(b);
                }
            }

            function setObjMode(key) {
                UI.selectedMode = key;
                const cfg = OBJ_TABS.find(t => t.key === key);
                oSecTitleEl.textContent = cfg.secTitle;
                for (const [k, btn] of Object.entries(oTabBtns)) {
                    const c = OBJ_TABS.find(t => t.key === k);
                    if (k === key) Object.assign(btn.style, { background: c.ac, borderColor: c.bc, color: c.tc });
                    else Object.assign(btn.style, CSS.inactive);
                }
                renderObjGrid();
            }

            const oChevron = oheader.querySelector('.sec-chevron');
            for (const tab of OBJ_TABS) {
                const tb = el('button', CSS.tab, tab.label);
                tb.addEventListener('click', e => {
                    e.stopPropagation();
                    setObjMode(tab.key);
                });
                tb.addEventListener('mouseenter', () => (tb.style.filter = 'brightness(1.2)'));
                tb.addEventListener('mouseleave', () => (tb.style.filter = ''));
                tabsRow.appendChild(tb);
                oTabBtns[tab.key] = tb;
            }
            const oHeaderRight = div('display:flex;align-items:center;gap:6px;');
            oChevron.parentNode.removeChild(oChevron);
            oHeaderRight.append(tabsRow, oChevron);
            oheader.appendChild(oHeaderRight);
            owrap.__onToggle = v => (tabsRow.style.display = v ? 'flex' : 'none');
            tabsRow.style.display = (secPrefs.open.objects ?? true) ? 'flex' : 'none';
            sectionMap.objects = owrap;
            setObjMode('obj');

            // -- ACTIONS SECTION ---
            const { wrap: awrap, body: abody, header: aheader } = mkSection('actions', '⚡ Actions', true);
            const cfgToggleBtn = el('button', CSS.tab, '⚙️');

            const aChevron = aheader.querySelector('.sec-chevron');
            const aHeaderRight = div('display:flex;align-items:center;gap:6px;', cfgToggleBtn);
            aChevron.parentNode.removeChild(aChevron);
            aHeaderRight.appendChild(aChevron);
            aheader.appendChild(aHeaderRight);
            awrap.__onToggle = v => (cfgToggleBtn.style.display = v ? '' : 'none');
            cfgToggleBtn.style.display = (secPrefs.open.actions ?? true) ? '' : 'none';

            const ag = div('display:grid;grid-template-columns:repeat(auto-fill,minmax(70px,1fr));gap:5px;');
            const cfgPanel = div(
                'display:none;flex-direction:column;gap:8px;padding:10px;border-radius:7px;background:rgba(255,200,50,0.06);border:1px dashed rgba(255,200,50,0.3);'
            );
            const closeCfgPanel = () => {
                UI.openCfgKey = null;
                cfgPanel.style.display = 'none';
                cfgPanel.innerHTML = '';
            };

            cfgToggleBtn.addEventListener('click', e => {
                e.stopPropagation();
                UI.actionConfigMode = !UI.actionConfigMode;
                if (UI.actionConfigMode)
                    Object.assign(cfgToggleBtn.style, {
                        background: 'rgba(255,200,50,0.2)',
                        borderColor: 'rgba(255,200,50,0.5)',
                        color: '#ffe080',
                    });
                else {
                    Object.assign(cfgToggleBtn.style, CSS.inactive);
                    closeCfgPanel();
                }
            });

            function renderCfgPanel(def) {
                cfgPanel.innerHTML = '';
                cfgPanel.style.display = 'flex';
                const rcfg = getRenderCfg(def);
                if (!rcfg) {
                    showToast(`⚠️ ${def.label} has no options`, 'err');
                    closeCfgPanel();
                    return;
                }
                rcfg(cfgPanel);
                if (def.cfg && def.defaults) {
                    const resetBtn = mkBtn(
                        '↺ Default',
                        'cursor:pointer;align-self:flex-start;margin-top:2px;padding:4px 10px;border-radius:5px;border:1px solid var(--sm-bd3);background:var(--sm-ov1);color:var(--sm-text-dim2);font-size:10px;font-weight:bold;',
                        () => {
                            Object.assign(def.cfg, def.defaults);
                            renderCfgPanel(def);
                        }
                    );
                    cfgPanel.appendChild(resetBtn);
                }
            }
            function openCfgPanel(def) {
                if (UI.openCfgKey === def.key) {
                    closeCfgPanel();
                    return;
                }
                UI.openCfgKey = def.key;
                renderCfgPanel(def);
            }

            const ALL_SHADOW = '0 0 6px rgba(255,200,50,0.35)',
                ALL_OUTLINE = '2px solid rgba(255,200,50,0.8)';
            for (const def of ADEFS) {
                const btn = el('button', '');
                const renderBtn = isAll => {
                    btn.style.cssText = `cursor:pointer;border-radius:6px;padding:6px 2px;min-height:52px;border:1px solid;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;transition:filter .15s,outline .1s,box-shadow .1s;position:relative;background:${def.color.bg};border-color:${def.color.bd};`;
                    btn.innerHTML = '';
                    btn.append(
                        el('span', 'font-size:16px;line-height:1.2;', def.icon),
                        el(
                            'span',
                            `font-size:9px;font-weight:bold;text-transform:uppercase;letter-spacing:.03em;color:${def.color.t};`,
                            def.label
                        )
                    );
                    if (isAll) {
                        const badge = el(
                            'span',
                            'position:absolute;top:3px;right:3px;font-size:7px;font-weight:bold;background:rgba(255,200,50,0.25);color:#ffe080;border-radius:3px;padding:1px 3px;',
                            'ALL'
                        );
                        btn.appendChild(badge);
                        btn.style.outline = ALL_OUTLINE;
                        btn.style.boxShadow = ALL_SHADOW;
                    } else {
                        btn.style.outline = '';
                        btn.style.boxShadow = '';
                    }
                };
                renderBtn(false);
                btn.addEventListener('click', () => {
                    if (UI.actionConfigMode) {
                        openCfgPanel(def);
                        return;
                    }
                    if (!_requireHost()) return;
                    const t = def.noAll ? parseInt(UI.playerSel?.value ?? -1) : resolveTarget(def.key);
                    if (t === null || (typeof t === 'number' && t < 0)) {
                        showToast('⚠️ Select a player', 'err');
                        return;
                    }
                    def.run(t);
                });
                btn.addEventListener('contextmenu', e => {
                    e.preventDefault();
                    if (UI.actionConfigMode) {
                        if (!_requireHost()) return;
                        const t = def.noAll ? parseInt(UI.playerSel?.value ?? -1) : resolveTarget(def.key);
                        if (t === null || (typeof t === 'number' && t < 0)) {
                            showToast('⚠️ Select a player', 'err');
                            return;
                        }
                        def.run(t);
                        return;
                    }
                    // Right-click → toggle ALL mode for this action
                    if (!def.noAll) {
                        UI.allMode[def.key] = !UI.allMode[def.key];
                        renderBtn(UI.allMode[def.key]);
                    }
                });
                btn.addEventListener('mouseenter', () => (btn.style.filter = 'brightness(1.2)'));
                btn.addEventListener('mouseleave', () => (btn.style.filter = ''));
                ag.appendChild(btn);
            }
            abody.append(ag, cfgPanel);
            abody.appendChild(div('border-top:1px solid var(--sm-ov2);padding-top:6px;', freezeBtn));
            sectionMap.actions = awrap;

            // -- PHYSICS & MAP SECTION ---
            const { wrap: phwrap, body: phbody } = mkSection('physics', '🔬 Physics & Map', false);
            const phBtn = (label, bg, bd, t, fn) => accentBtn(label, bg, bd, t, fn);

            // -- GAME MODE SELECTOR ---
            const gameModeWrap = div(
                'display:flex;flex-direction:column;gap:6px;padding-bottom:6px;border-bottom:1px solid var(--sm-ov2);'
            );
            gameModeWrap.appendChild(lbl('🎮 Game Mode'));

            const GAME_MODES = [
                { code: 'b',   label: '🏐 Classic' },
                { code: 'ar',  label: '🏹 Arrows' },
                { code: 'ard', label: '💀 Death Arrows' },
                { code: 'sp',  label: '🪝 Grapple' },
                { code: 'v',   label: '🚁 VTOL' },
            ];

            const gmSelect = sk(el('select',
                'width:100%;padding:5px 8px;border-radius:6px;border:1px solid var(--sm-bd0);' +
                'background:var(--sm-ov-1);color:var(--sm-text);font-size:11px;font-weight:bold;' +
                'cursor:pointer;outline:none;box-sizing:border-box;'
            ));

            for (const m of GAME_MODES) {
                const opt = document.createElement('option');
                opt.value = m.code;
                opt.textContent = m.label;
                opt.style.cssText = 'background:var(--sm-select-bg);color:var(--sm-text);font-weight:bold;';
                gmSelect.appendChild(opt);
            }

            // Read current mode from __SUMMON__ state if already known
            const _readCurrentMode = () => window.__SUMMON__._currentMode || null;

            // Sync select to current mode — uses flag to avoid triggering change event
            let _gmSyncing = false;
            const _syncGmSelect = (mo) => {
                if (!mo) return;
                for (const opt of gmSelect.options) {
                    if (opt.value === mo) {
                        if (gmSelect.value !== mo) {
                            _gmSyncing = true;
                            gmSelect.value = mo;
                            _gmSyncing = false;
                        }
                        break;
                    }
                }
            };

            // Update disabled state based on host status
            const _updateGmSelectState = () => {
                const isHost = _iAmHost();
                gmSelect.disabled = !isHost;
                gmSelect.style.opacity = isHost ? '1' : '0.5';
                gmSelect.style.cursor = isHost ? 'pointer' : 'not-allowed';
                gmSelect.title = '';
            };
            _updateGmSelectState();
            // Refresh whenever host changes (packet 6/41 triggers refreshSel → we piggyback)
            const _origRefreshSel = refreshSel;
            // Poll host state every second to update select accessibility
            setInterval(_updateGmSelectState, 1000);

            // Init on first render if mode is already known
            _syncGmSelect(_readCurrentMode());

            // Listen for mode changes broadcast from the packet hook
            window.__SUMMON__._onModeChange = (mo) => _syncGmSelect(mo);

            // Poll as fallback (covers cases where packet 26 was missed before hook set up)
            setInterval(() => {
                const mo = _readCurrentMode();
                if (mo && gmSelect.value !== mo) _syncGmSelect(mo);
            }, 1000);

            gmSelect.addEventListener('change', () => {
                if (_gmSyncing) return; // ignore programmatic value changes
                if (!_iAmHost()) {
                    showToast('⚠️ Only the host can change game mode', 'err');
                    _syncGmSelect(window.__SUMMON__._currentMode);
                    return;
                }
                const code = gmSelect.value;
                if (!code) return;
                const codeToAlias = { b: 'cl', ar: 'ar', ard: 'da', sp: 'gr', v: 'vtol' };
                const alias = codeToAlias[code] || code;
                // BonkSummonChat is set after buildHUD — access it at call time
                const chat = window.BonkSummonChat;
                if (chat?.hampMode && chat?.iAmRealHost?.()) {
                    chat.hampMode(chat.getMyName?.() || '', alias);
                } else if (!chat?.iAmRealHost?.()) {
                    showToast('⚠️ Only the host can change game mode', 'err');
                    // Revert select to current known mode
                    _syncGmSelect(window.__SUMMON__._currentMode);
                }
            });

            gameModeWrap.appendChild(gmSelect);
            phbody.appendChild(gameModeWrap);

            // -- MAP SETTINGS CHECKBOXES (ms) ---
            const msWrap = div(
                'display:flex;flex-direction:column;gap:6px;padding-bottom:6px;border-bottom:1px solid var(--sm-ov2);'
            );
            msWrap.appendChild(lbl('⚙️ Map Settings'));
            const msGrid = div('display:grid;grid-template-columns:1fr 1fr;gap:5px;');

            function mkMsCheckbox(icon, label, key, color) {
                const wrap = div(
                    'display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:6px;border:1px solid var(--sm-bd0);background:var(--sm-ov-1);cursor:pointer;transition:background .15s;'
                );
                const cb = document.createElement('input');
                cb.type = 'checkbox';
                cb.style.cssText =
                    'cursor:pointer;accent-color:' + color + ';color-scheme:dark;width:14px;height:14px;flex-shrink:0;';
                const lbEl = el(
                    'span',
                    'font-size:10px;font-weight:bold;color:' + color + ';user-select:none;',
                    icon + ' ' + label
                );
                // init from current state
                const ms = window.__SUMMON__.state?.ms;
                if (ms != null) cb.checked = key === 'pq' ? ms[key] === 2 : !!ms[key];
                cb.addEventListener('change', () => {
                    const S = window.__SUMMON__;
                    if (!_iAmHost()) {
                        showToast('⚠️ Only the host can change this', 'err');
                        // revert
                        cb.checked = !cb.checked;
                        return;
                    }
                    if (!S.state?.ms) {
                        showToast('⚠️ No map settings', 'err');
                        cb.checked = !cb.checked;
                        return;
                    }
                    S.state.ms[key] = key === 'pq' ? (cb.checked ? 2 : 1) : cb.checked;
                    startGame(S.state, true);
                });
                wrap.addEventListener('click', e => {
                    if (e.target !== cb) {
                        if (!_iAmHost()) {
                            showToast('⚠️ Only the host can change this', 'err');
                            return;
                        }
                        cb.checked = !cb.checked;
                        cb.dispatchEvent(new Event('change'));
                    }
                });
                wrap.addEventListener('mouseenter', () => (wrap.style.background = 'var(--sm-ov1h)'));
                wrap.addEventListener('mouseleave', () => (wrap.style.background = 'var(--sm-ov-1)'));
                wrap.append(cb, lbEl);
                // poll for external changes (editor, commands)
                setInterval(() => {
                    const ms = window.__SUMMON__.state?.ms;
                    if (ms == null) return;
                    const v = key === 'pq' ? ms[key] === 2 : !!ms[key];
                    if (cb.checked !== v) cb.checked = v;
                }, 500);
                return { wrap, cb };
            }

            const { wrap: ncWrap } = mkMsCheckbox('🚫', 'No Collisions', 'nc', THEME.msNoCollisions);
            const { wrap: reWrap } = mkMsCheckbox('♻️', 'Respawn on Death', 're', THEME.msRespawn);
            const { wrap: flWrap } = mkMsCheckbox('🕊️', 'Fly Mode', 'fl', '#80d8ff');
            const { wrap: pqWrap } = mkMsCheckbox('⚛️', 'Complex Physics', 'pq', THEME.msQuickPlay);
            msGrid.append(ncWrap, reWrap, flWrap, pqWrap);
            msWrap.appendChild(msGrid);

            const { wrap: bouncyWrap, slider: bouncySl, valInp: bouncyInp } = mkPctValueRow('🏀 Bounciness', '', 're', '#ffd080', (v, p) =>
                actionBouncyMode(v, p)
            );
            const { wrap: iceWrap, slider: iceSl, valInp: iceInp } = mkPctValueRow('🧊 Friction', '', 'fr', '#80c8ff', (v, p) => actionFrictionMode(v, p));
            const { wrap: denWrap, slider: denSl, valInp: denInp } = mkPctValueRow('⚖️ Density', '', 'den', '#90c8ff', (v, p) =>
                actionDensityMode(v, p)
            );
            const { wrap: rotWrap } = mkPhysicsRow(
                '🔄 Rotate (°)',
                'e.g. 15 or -90',
                '✔ Apply',
                'rgba(140,100,255,0.15)',
                'rgba(180,140,255,0.4)',
                '#c0a0ff',
                v => actionRotateMode(v)
            );

            // -- SPIN MAP ---
            const spinMapWrap = div('display:flex;flex-direction:column;gap:4px;');
            spinMapWrap.appendChild(lbl('🌀 Spin Map (deg/s)'));
            const spinMapRow = div('display:flex;align-items:center;gap:6px;');
            const spinMapSlider = sk(el('input'));
            spinMapSlider.type = 'range';
            spinMapSlider.min = '-1800';
            spinMapSlider.max = '1800';
            spinMapSlider.step = '10';
            spinMapSlider.value = SPIN_CFG.speedDeg;
            spinMapSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#c0a0ff;';
            spinMapSlider.addEventListener('mousedown', e => e.stopPropagation());
            const spinMapNumInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            spinMapNumInp.type = 'number';
            spinMapNumInp.value = SPIN_CFG.speedDeg;
            spinMapNumInp.placeholder = 'deg/s';
            spinMapSlider.addEventListener('input', () => {
                SPIN_CFG.speedDeg = parseFloat(spinMapSlider.value) || 360;
                spinMapNumInp.value = SPIN_CFG.speedDeg;
            });
            spinMapNumInp.addEventListener('change', () => {
                const v = parseFloat(spinMapNumInp.value);
                if (!isNaN(v)) {
                    SPIN_CFG.speedDeg = v;
                    const clamped = Math.max(-1800, Math.min(1800, v));
                    spinMapSlider.value = clamped;
                }
            });
            spinMapRow.append(spinMapSlider, spinMapNumInp);
            spinMapWrap.appendChild(spinMapRow);
            const spinMapBtnRow = div('display:flex;gap:4px;');
            const spinMapBtnM = accentBtn(
                '🔗 Merge',
                'rgba(140,100,255,0.15)',
                'rgba(180,140,255,0.4)',
                '#c0a0ff',
                () => actionSpinMap(SPIN_CFG.speedDeg, 'merge')
            );
            const spinMapBtnB = accentBtn(
                '📦 Bodies',
                'rgba(140,100,255,0.10)',
                'rgba(180,140,255,0.3)',
                '#c0a0ff',
                () => actionSpinMap(SPIN_CFG.speedDeg, 'bodies')
            );
            const spinMapBtnS = accentBtn(
                '🔮 Shapes',
                'rgba(140,100,255,0.10)',
                'rgba(180,140,255,0.3)',
                '#c0a0ff',
                () => actionSpinMap(SPIN_CFG.speedDeg, 'shapes')
            );
            [spinMapBtnM, spinMapBtnB, spinMapBtnS].forEach(b => {
                b.style.flex = '1';
                b.style.fontSize = '10px';
                b.style.padding = '4px 2px';
            });
            spinMapBtnRow.append(spinMapBtnM, spinMapBtnB, spinMapBtnS);
            spinMapWrap.appendChild(spinMapBtnRow);
            spinMapWrap.appendChild(
                el(
                    'div',
                    'font-size:10px;color:var(--sm-text-dim1);',
                    '+ CCW  -  CW  (merges all shapes into one spinning body)'
                )
            );

            // -- SPRINGY MAP ---
            const springyMapWrap = div('display:flex;flex-direction:column;gap:4px;');
            springyMapWrap.appendChild(lbl('🪢 Springy Map'));
            // Spring Force row
            const springyForceRow = div('display:flex;align-items:center;gap:6px;');
            springyForceRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:50px;', 'Force:')
            );
            const springyForceSlider = sk(el('input'));
            springyForceSlider.type = 'range';
            springyForceSlider.min = '100';
            springyForceSlider.max = '50000';
            springyForceSlider.step = '100';
            springyForceSlider.value = SPRINGY_CFG.springForce;
            springyForceSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#ffb060;';
            springyForceSlider.addEventListener('mousedown', e => e.stopPropagation());
            const springyForceInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:68px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            springyForceInp.type = 'number';
            springyForceInp.value = SPRINGY_CFG.springForce;
            springyForceInp.placeholder = 'force';
            springyForceSlider.addEventListener('input', () => {
                SPRINGY_CFG.springForce = parseFloat(springyForceSlider.value) || 7812.5;
                springyForceInp.value = SPRINGY_CFG.springForce;
            });
            springyForceInp.addEventListener('change', () => {
                const v = parseFloat(springyForceInp.value);
                if (!isNaN(v) && v > 0) {
                    SPRINGY_CFG.springForce = v;
                    const clamped = Math.max(100, Math.min(50000, v));
                    springyForceSlider.value = clamped;
                }
            });
            springyForceRow.append(springyForceSlider, springyForceInp);
            springyMapWrap.appendChild(springyForceRow);
            // Spring Length row
            const springyLenRow = div('display:flex;align-items:center;gap:6px;');
            springyLenRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:50px;', 'Length:')
            );
            const springyLenSlider = sk(el('input'));
            springyLenSlider.type = 'range';
            springyLenSlider.min = '0';
            springyLenSlider.max = '200';
            springyLenSlider.step = '1';
            springyLenSlider.value = SPRINGY_CFG.springLen;
            springyLenSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#ffb060;';
            springyLenSlider.addEventListener('mousedown', e => e.stopPropagation());
            const springyLenInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:68px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            springyLenInp.type = 'number';
            springyLenInp.value = SPRINGY_CFG.springLen;
            springyLenInp.placeholder = 'length';
            springyLenSlider.addEventListener('input', () => {
                SPRINGY_CFG.springLen = parseFloat(springyLenSlider.value) || 0;
                springyLenInp.value = SPRINGY_CFG.springLen;
            });
            springyLenInp.addEventListener('change', () => {
                const v = parseFloat(springyLenInp.value);
                if (!isNaN(v) && v >= 0) {
                    SPRINGY_CFG.springLen = v;
                    const clamped = Math.max(0, Math.min(200, v));
                    springyLenSlider.value = clamped;
                }
            });
            springyLenRow.append(springyLenSlider, springyLenInp);
            springyMapWrap.appendChild(springyLenRow);
            const springyBtnRow = div('display:flex;gap:4px;');
            const springyBtnM = accentBtn('🔗 Merge', 'rgba(255,160,60,0.15)', 'rgba(255,190,90,0.4)', '#ffb060', () =>
                actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'merge')
            );
            const springyBtnB = accentBtn('📦 Bodies', 'rgba(255,160,60,0.10)', 'rgba(255,190,90,0.3)', '#ffb060', () =>
                actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'bodies')
            );
            const springyBtnS = accentBtn('🔮 Shapes', 'rgba(255,160,60,0.10)', 'rgba(255,190,90,0.3)', '#ffb060', () =>
                actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'shapes')
            );
            [springyBtnM, springyBtnB, springyBtnS].forEach(b => {
                b.style.flex = '1';
                b.style.fontSize = '10px';
                b.style.padding = '4px 2px';
            });
            springyBtnRow.append(springyBtnM, springyBtnB, springyBtnS);
            springyMapWrap.appendChild(springyBtnRow);
            springyMapWrap.appendChild(
                el(
                    'div',
                    'font-size:10px;color:var(--sm-text-dim1);',
                    'merges map into one body, springs to center (length 0 = anchored)'
                )
            );

            // -- SHAKE MAP ---
            const shakeMapWrap = div('display:flex;flex-direction:column;gap:4px;');
            shakeMapWrap.appendChild(lbl('↔️ Follow Path'));
            // Angle row
            const shakeAngleRow = div('display:flex;align-items:center;gap:6px;');
            shakeAngleRow.appendChild(el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:50px;', 'Angle:'));
            const shakeAngleSlider = sk(el('input'));
            shakeAngleSlider.type = 'range';
            shakeAngleSlider.min = '-180';
            shakeAngleSlider.max = '180';
            shakeAngleSlider.step = '1';
            shakeAngleSlider.value = FPATH_CFG.angle;
            shakeAngleSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shakeAngleSlider.addEventListener('mousedown', e => e.stopPropagation());
            const shakeAngleInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shakeAngleInp.type = 'number';
            shakeAngleInp.value = FPATH_CFG.angle;
            shakeAngleInp.placeholder = 'deg';
            shakeAngleSlider.addEventListener('input', () => {
                FPATH_CFG.angle = parseFloat(shakeAngleSlider.value) || 0;
                shakeAngleInp.value = FPATH_CFG.angle;
            });
            shakeAngleInp.addEventListener('change', () => {
                const v = parseFloat(shakeAngleInp.value);
                if (!isNaN(v)) {
                    FPATH_CFG.angle = v;
                    const clamped = Math.max(-180, Math.min(180, v));
                    shakeAngleSlider.value = clamped;
                }
            });
            shakeAngleRow.append(shakeAngleSlider, shakeAngleInp);
            shakeMapWrap.appendChild(shakeAngleRow);
            // Path Length row
            const shakeLenRow = div('display:flex;align-items:center;gap:6px;');
            shakeLenRow.appendChild(el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:50px;', 'PathLen:'));
            const shakeLenSlider = sk(el('input'));
            shakeLenSlider.type = 'range';
            shakeLenSlider.min = '1';
            shakeLenSlider.max = '500';
            shakeLenSlider.step = '1';
            shakeLenSlider.value = FPATH_CFG.pathLen;
            shakeLenSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shakeLenSlider.addEventListener('mousedown', e => e.stopPropagation());
            const shakeLenInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shakeLenInp.type = 'number';
            shakeLenInp.value = FPATH_CFG.pathLen;
            shakeLenInp.placeholder = 'units';
            shakeLenSlider.addEventListener('input', () => {
                FPATH_CFG.pathLen = parseFloat(shakeLenSlider.value) || 62.5;
                shakeLenInp.value = FPATH_CFG.pathLen;
            });
            shakeLenInp.addEventListener('change', () => {
                const v = parseFloat(shakeLenInp.value);
                if (!isNaN(v) && v > 0) {
                    FPATH_CFG.pathLen = v;
                    const clamped = Math.max(1, Math.min(500, v));
                    shakeLenSlider.value = clamped;
                }
            });
            shakeLenRow.append(shakeLenSlider, shakeLenInp);
            shakeMapWrap.appendChild(shakeLenRow);
            // Move Speed row
            const shakeSpeedRow = div('display:flex;align-items:center;gap:6px;');
            shakeSpeedRow.appendChild(el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:50px;', 'Speed:'));
            const shakeSpeedSlider = sk(el('input'));
            shakeSpeedSlider.type = 'range';
            shakeSpeedSlider.min = '1';
            shakeSpeedSlider.max = '5000';
            shakeSpeedSlider.step = '1';
            shakeSpeedSlider.value = FPATH_CFG.moveSpeed;
            shakeSpeedSlider.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shakeSpeedSlider.addEventListener('mousedown', e => e.stopPropagation());
            const shakeSpeedInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shakeSpeedInp.type = 'number';
            shakeSpeedInp.value = FPATH_CFG.moveSpeed;
            shakeSpeedInp.placeholder = 'speed';
            shakeSpeedSlider.addEventListener('input', () => {
                FPATH_CFG.moveSpeed = parseFloat(shakeSpeedSlider.value) || 250;
                shakeSpeedInp.value = FPATH_CFG.moveSpeed;
            });
            shakeSpeedInp.addEventListener('change', () => {
                const v = parseFloat(shakeSpeedInp.value);
                if (!isNaN(v) && v > 0) {
                    FPATH_CFG.moveSpeed = v;
                    const clamped = Math.max(1, Math.min(5000, v));
                    shakeSpeedSlider.value = clamped;
                }
            });
            shakeSpeedRow.append(shakeSpeedSlider, shakeSpeedInp);
            shakeMapWrap.appendChild(shakeSpeedRow);
            const shakeBtnRow = div('display:flex;gap:4px;');
            const shakeBtnM = accentBtn('🔗 Merge', 'rgba(60,200,255,0.15)', 'rgba(90,220,255,0.4)', '#60d0ff', () =>
                actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'merge')
            );
            const shakeBtnB = accentBtn('📦 Bodies', 'rgba(60,200,255,0.10)', 'rgba(90,220,255,0.3)', '#60d0ff', () =>
                actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'bodies')
            );
            const shakeBtnS = accentBtn('🔮 Shapes', 'rgba(60,200,255,0.10)', 'rgba(90,220,255,0.3)', '#60d0ff', () =>
                actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'shapes')
            );
            [shakeBtnM, shakeBtnB, shakeBtnS].forEach(b => {
                b.style.flex = '1';
                b.style.fontSize = '10px';
                b.style.padding = '4px 2px';
            });
            shakeBtnRow.append(shakeBtnM, shakeBtnB, shakeBtnS);
            shakeMapWrap.appendChild(shakeBtnRow);
            shakeMapWrap.appendChild(
                el(
                    'div',
                    'font-size:10px;color:var(--sm-text-dim1);',
                    '0=right 90=down, map slides back and forth along path'
                )
            );

            // Stretch needs 2 inputs
            const stretchWrap = div('display:flex;flex-direction:column;gap:4px;');
            stretchWrap.appendChild(lbl('↔️ Stretch X/Y'));
            const sXI = mkInp('X', 'number');
            sXI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const sYI = mkInp('Y', 'number');
            sYI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const sBtn = accentBtn(
                '✔ Apply',
                'rgba(255,120,80,0.15)',
                'rgba(255,150,100,0.4)',
                '#ffa080',
                () => actionStretchMode(sXI.value, sYI.value)
            );
            stretchWrap.appendChild(row(sXI, sYI, sBtn));

            // PPM (pixels-per-meter)
            const ppmWrap = div('display:flex;flex-direction:column;gap:4px;');
            ppmWrap.appendChild(lbl('📐 PPM'));
            const ppmInp = mkInp('e.g. 12', 'number');
            ppmInp.min = '1';
            ppmInp.max = '1000';
            ppmInp.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const ppmVal = window.__SUMMON__.state?.physics?.ppm;
            if (ppmVal != null) ppmInp.value = ppmVal;
            const ppmBtn = accentBtn('✔ Apply', 'rgba(80,180,255,0.15)', 'rgba(120,200,255,0.4)', '#80c8ff', () =>
                actionSetPPM(ppmInp.value)
            );
            ppmWrap.appendChild(row(ppmInp, ppmBtn));

            // Size (grow player relative to map, via inverse-scaled PPM)
            const sizeWrap = div('display:flex;flex-direction:column;gap:4px;');
            sizeWrap.appendChild(lbl('📏 Player Size (via PPM)'));
            const sizeInp = mkInp('e.g. 40', 'number');
            sizeInp.min = '1';
            sizeInp.max = '1000';
            sizeInp.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            if (ppmVal != null) sizeInp.value = ppmVal;
            const sizeBtn = accentBtn('✔ Apply', 'rgba(255,120,180,0.15)', 'rgba(255,150,200,0.4)', '#ffb0d0', () =>
                actionResizeViaPPM(sizeInp.value)
            );
            sizeWrap.appendChild(row(sizeInp, sizeBtn));

            // Translate (Map Offset)
            const translateWrap = div('display:flex;flex-direction:column;gap:4px;');
            translateWrap.appendChild(lbl('📍 Translate Map (X Y)'));
            const tXI = mkInp('X offset', 'number');
            tXI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const tYI = mkInp('Y offset', 'number');
            tYI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const tBtn = accentBtn('✔ Apply', 'rgba(80,200,180,0.15)', 'rgba(100,220,200,0.4)', '#60e0c0', () => {
                const ox = tXI.value === '' ? 0 : parseFloat(tXI.value),
                    oy = tYI.value === '' ? 0 : parseFloat(tYI.value);
                if (isNaN(ox) || isNaN(oy)) {
                    showToast('⚠️ Invalid X or Y values', 'err');
                    return;
                }
                actionTranslateMap(ox, oy, false);
            });
            tBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                const ox = tXI.value === '' ? 0 : parseFloat(tXI.value),
                    oy = tYI.value === '' ? 0 : parseFloat(tYI.value);
                if (isNaN(ox) || isNaN(oy)) {
                    showToast('⚠️ Invalid X or Y values', 'err');
                    return;
                }
                actionTranslateMap(ox, oy, true);
            });
            translateWrap.appendChild(row(tXI, tYI, tBtn));


            const freeMovingRow = mkPctSlider('🆓 Free Moving (%)', '#80ffaa', v => actionFreeMovingMode(v));
            const stationaryRow = mkPctSlider('🔒 Stationary (%)', '#9ab8ff', v => actionStationaryMode(v));
            const noPhysicsRow = mkPctSlider('👻 No Physics (%)', '#c0a0ff', v => actionNoPhysicsMode(v));
            const deathRow = mkPctSlider('☠️ Death Zone (%)', '#ff6060', v => actionDeathMode(v));

            // Rainbow
            const rainbowWrap = div('display:flex;flex-direction:column;gap:5px;');
            rainbowWrap.appendChild(lbl('🌈 Map color'));
            const rainbowModeRow = div('display:flex;flex-wrap:wrap;gap:4px;');
            const RMODES = [
                {
                    key: 'random',
                    icon: '🌈',
                    label: 'Random',
                    color: 'rgba(255,100,200,0.1)',
                    bd: 'rgba(255,140,220,0.4)',
                    t: '#ffb0e8',
                },
                {
                    key: 'invert',
                    icon: '🔄',
                    label: 'Invert',
                    color: 'rgba(80,160,255,0.1)',
                    bd: 'rgba(120,180,255,0.4)',
                    t: '#90c8ff',
                },
                {
                    key: 'gray',
                    icon: '⬜',
                    label: 'Grayscale',
                    color: 'rgba(180,180,180,0.1)',
                    bd: 'rgba(220,220,220,0.4)',
                    t: '#d8d8d8',
                },
                {
                    key: 'solid',
                    icon: '🎯',
                    label: 'Solid Color',
                    color: 'rgba(255,90,90,0.1)',
                    bd: 'rgba(255,120,120,0.4)',
                    t: '#ff9090',
                },
                {
                    key: 'hue',
                    icon: '🎨',
                    label: 'Hue Shift',
                    color: 'rgba(255,180,50,0.1)',
                    bd: 'rgba(255,210,80,0.4)',
                    t: '#ffe080',
                },
                {
                    key: 'bright',
                    icon: '☀️',
                    label: 'Brightness',
                    color: 'rgba(255,240,100,0.1)',
                    bd: 'rgba(255,250,150,0.4)',
                    t: '#fffa80',
                },
            ];
            let _rainbowHue = 180,
                _rainbowMode = 'random',
                _activeModeBtn = null;
            const hueRow = div('display:none;align-items:center;gap:6px;');
            const hueSlider = sk(el('input'));
            hueSlider.type = 'range';
            hueSlider.min = '0';
            hueSlider.max = '360';
            hueSlider.step = '1';
            hueSlider.value = '180';
            hueSlider.style.cssText = 'flex:1;cursor:pointer;accent-color:#ffe080;';
            hueSlider.addEventListener('mousedown', e => e.stopPropagation());
            const hueVal = el(
                'span',
                'font-size:11px;font-family:monospace;color:#ffe080;min-width:36px;text-align:right;',
                '180°'
            );
            hueSlider.addEventListener('input', () => {
                if (_rainbowMode === 'bright') {
                    _rainbowHue = parseInt(hueSlider.value) / 100;
                    hueVal.textContent = _rainbowHue.toFixed(1) + '×';
                } else {
                    _rainbowHue = parseInt(hueSlider.value);
                    hueVal.textContent = _rainbowHue + '°';
                }
            });
            hueRow.append(hueSlider, hueVal);

            const solidRow = div('display:none;align-items:center;gap:8px;');
            const solidPicker = sk(el('input'));
            solidPicker.type = 'color';
            solidPicker.value = '#ff4040';
            solidPicker.style.cssText =
                'width:36px;height:28px;border:1px solid var(--sm-bd3);border-radius:5px;cursor:pointer;background:transparent;padding:0;';
            const satWrap = div('flex:1;display:flex;flex-direction:column;gap:2px;');
            const satTop = div('display:flex;justify-content:space-between;');
            satTop.append(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);', 'Saturation'),
                (() => {
                    const s = el('span', 'font-size:11px;font-family:monospace;color:#ff9090;', '100%');
                    return s;
                })()
            );
            const satVal = satTop.lastChild;
            const satSlider = sk(el('input'));
            satSlider.type = 'range';
            satSlider.min = '0';
            satSlider.max = '100';
            satSlider.step = '1';
            satSlider.value = '100';
            satSlider.style.cssText = 'width:100%;cursor:pointer;accent-color:#ff9090;';
            satSlider.addEventListener('mousedown', e => e.stopPropagation());
            satWrap.append(satTop, satSlider);
            const applySolidBtn = mkBtn(
                '✔',
                'cursor:pointer;flex-shrink:0;border-radius:5px;padding:5px 10px;border:1px solid rgba(255,120,120,0.4);background:rgba(255,90,90,0.15);color:#ff9090;font-size:12px;font-weight:bold;',
                () => _applySolid()
            );
            solidRow.append(solidPicker, satWrap, applySolidBtn);
            function _hexToHue(hex) {
                const r = parseInt(hex.slice(1, 3), 16),
                    g = parseInt(hex.slice(3, 5), 16),
                    b = parseInt(hex.slice(5, 7), 16);
                return _rgbToHsv(r, g, b).h;
            }
            function _applySolid() {
                const hue = _hexToHue(solidPicker.value);
                const sat = parseInt(satSlider.value) / 100;
                actionRainbowMap('solid', 180, hue, sat);
            }

            solidPicker.addEventListener('input', () => _applySolid());
            satSlider.addEventListener('input', () => {
                satVal.textContent = satSlider.value + '%';
                _applySolid();
            });

            for (const rm of RMODES) {
                const rb = mkBtn(
                    `${rm.icon} ${rm.l || rm.label}`,
                    `cursor:pointer;flex:1 1 70px;border-radius:5px;padding:6px 4px;border:1px solid ${rm.bd};background:${rm.color};color:${rm.t};font-size:10px;font-weight:bold;`,
                    () => {
                        const switching = _rainbowMode !== rm.key;
                        _rainbowMode = rm.key;
                        if (_activeModeBtn) _activeModeBtn.style.outline = '';
                        _activeModeBtn = rb;
                        rb.style.outline = '2px solid ' + rm.bd;
                        hueRow.style.display = 'none';
                        solidRow.style.display = 'none';
                        if (rm.key === 'hue') {
                            if (switching) {
                                hueSlider.min = '0';
                                hueSlider.max = '360';
                                hueSlider.step = '1';
                                hueSlider.value = '0';
                                _rainbowHue = 0;
                                hueVal.textContent = '0°';
                                hueRow.style.display = 'flex';
                                return;
                            }
                            hueRow.style.display = 'flex';
                            actionRainbowMap('hue', _rainbowHue);
                            return;
                        }
                        if (rm.key === 'bright') {
                            if (switching) {
                                hueSlider.min = '10';
                                hueSlider.max = '300';
                                hueSlider.step = '5';
                                hueSlider.value = '100';
                                _rainbowHue = 1.0;
                                hueVal.textContent = '1.0×';
                                hueRow.style.display = 'flex';
                                return;
                            }
                            hueRow.style.display = 'flex';
                            actionRainbowMap('bright', _rainbowHue);
                            return;
                        }
                        if (rm.key === 'solid') {
                            solidRow.style.display = 'flex';
                            _applySolid();
                            return;
                        }
                        actionRainbowMap(rm.key, _rainbowHue);
                    }
                );
                rainbowModeRow.appendChild(rb);
                if (rm.key === 'random') {
                    _activeModeBtn = rb;
                    rb.style.outline = '2px solid ' + rm.bd;
                }
            }
            rainbowWrap.append(rainbowModeRow, hueRow, solidRow);


            phwrap.__onToggle = v => {
                void v;
            }; // placeholder (map settings removed)

            // Rewind
            const rwSep = div(
                'border-top:1px solid var(--sm-ov2);padding-top:6px;display:flex;flex-direction:column;gap:5px;'
            );
            rwSep.appendChild(lbl('⏪ Rewind'));
            const rwRow = div('display:flex;gap:5px;align-items:center;');
            const rwSlider = sk(el('input'));
            rwSlider.type = 'range';
            rwSlider.min = '1';
            rwSlider.max = '30';
            rwSlider.step = '1';
            rwSlider.value = '1';
            rwSlider.style.cssText = 'flex:1;cursor:pointer;accent-color:#c0a0ff;';
            rwSlider.addEventListener('mousedown', e => e.stopPropagation());
            const rwVal = el(
                'span',
                'font-size:11px;font-family:monospace;color:#c0a0ff;min-width:32px;text-align:right;',
                '1s'
            );
            rwSlider.addEventListener('input', () => (rwVal.textContent = rwSlider.value + 's'));
            const rwBtn = mkBtn('⏪ Go', CSS.btn('rgba(140,100,255,0.15)', 'rgba(180,140,255,0.4)', '#c0a0ff'), () =>
                actionRewind(parseInt(rwSlider.value))
            );
            rwRow.append(rwSlider, rwVal, rwBtn);
            rwSep.appendChild(rwRow);
            const cpRow = div('display:flex;gap:5px;');
            cpRow.append(
                mkBtn(
                    '📋 Copy state',
                    CSS.btn('rgba(255,180,50,0.1)', 'rgba(255,200,80,0.4)', '#ffd080') + 'flex:1;',
                    () => actionCopyPos()
                ),
                mkBtn(
                    '📌 Paste state',
                    CSS.btn('rgba(60,180,100,0.1)', 'rgba(80,200,120,0.4)', '#80ffaa') + 'flex:1;',
                    () => actionPastePos()
                )
            );
            rwSep.appendChild(cpRow);

            // Score
            const scoreSep = div(
                'border-top:1px solid var(--sm-ov2);padding-top:6px;display:flex;flex-direction:column;gap:5px;'
            );
            scoreSep.appendChild(lbl('🏆 Score Editor'));
            const scoreRow = div('display:flex;gap:5px;align-items:center;');
            const scorePSel = mkSel();
            scorePSel.style.cssText += ';flex:1;width:auto;';
            const scoreInp = skInp(el('input', CSS.inp + 'width:70px;box-sizing:border-box;'));
            scoreInp.type = 'text';
            scoreInp.value = '0';
            scoreInp.placeholder = '0 or text';
            const scoreBtn = mkBtn(
                '✔',
                CSS.btn('rgba(255,180,50,0.15)', 'rgba(255,200,50,0.4)', '#ffe080') + 'font-size:12px;',
                () => {
                    const pid = parseInt(scorePSel.value ?? -1);
                    if (pid < 0) {
                        showToast('⚠️ Choose a player', 'err');
                        return;
                    }
                    actionSetScore(pid, scoreInp.value.trim());
                }
            );
            scoreRow.append(scorePSel, scoreInp, scoreBtn);
            scoreSep.appendChild(scoreRow);

            // -- ONE-SHOT MAP BUTTONS (2-col grid) ---
            const _mapBtnCss = (bg, bd, t) =>
                `cursor:pointer;border-radius:6px;padding:8px 4px;border:1px solid ${bd};background:${bg};color:${t};font-size:11px;font-weight:bold;width:100%;`;
            const miscBtnGrid = div('display:grid;grid-template-columns:1fr 1fr;gap:5px;');

            // Wobbly button with expand-slider below
            let _wobblyActive = false;
            const wobblyGridBtn = mkBtn(
                '🌀 Wobbly',
                _mapBtnCss('rgba(120,80,255,0.1)', 'rgba(160,120,255,0.4)', '#c0a0ff'),
                () => {
                    if (!_wobblyActive) {
                        _wobblyActive = true;
                        wobblySliderRow.style.display = 'flex';
                        Object.assign(wobblyGridBtn.style, {
                            outline: '2px solid rgba(160,120,255,0.7)',
                            boxShadow: '0 0 6px rgba(160,120,255,0.3)',
                            color: '#d0b0ff',
                            borderColor: 'rgba(160,120,255,0.7)',
                        });
                    } else {
                        actionWobblyMode(WOBBLY_CFG.intensity);
                    }
                }
            );
            const wobblySliderRow = div('display:none;grid-column:1/-1;align-items:center;gap:6px;');
            const wobblySlider = sk(el('input'));
            wobblySlider.type = 'range';
            wobblySlider.min = '1';
            wobblySlider.max = '100';
            wobblySlider.step = '1';
            wobblySlider.value = WOBBLY_CFG.intensity;
            wobblySlider.style.cssText = 'flex:1;cursor:pointer;accent-color:#c0a0ff;';
            wobblySlider.addEventListener('mousedown', e => e.stopPropagation());
            const wobblyVal = el(
                'span',
                'font-size:11px;font-family:monospace;color:#c0a0ff;min-width:28px;text-align:right;',
                WOBBLY_CFG.intensity + ''
            );
            wobblySlider.addEventListener('input', () => {
                WOBBLY_CFG.intensity = parseInt(wobblySlider.value);
                wobblyVal.textContent = WOBBLY_CFG.intensity;
            });
            wobblySliderRow.append(wobblySlider, wobblyVal);


            miscBtnGrid.append(
                mkBtn('😈 Rand Map', _mapBtnCss('rgba(80,200,120,0.1)', 'rgba(100,220,140,0.4)', '#80ffa0'), () =>
                    actionRandomizeMap()
                ),
                mkBtn('🎭 Swap Geo', _mapBtnCss('rgba(160,60,220,0.12)', 'rgba(200,100,255,0.4)', '#dd88ff'), () =>
                    actionSwapShapes()
                ),
                mkBtn('🎨 Palette', _mapBtnCss('rgba(220,120,30,0.12)', 'rgba(255,180,80,0.4)', '#ffcc60'), () =>
                    actionPaletteShuffle()
                ),
                wobblyGridBtn,
                wobblySliderRow,
            );

            // -- INFLATE SLIDER ---
            const inflateWrap = div('display:flex;flex-direction:column;gap:4px;');
            inflateWrap.appendChild(lbl('🫧 Inflate'));
            const inflateSliderRow = div('display:flex;align-items:center;gap:6px;');
            const inflateSlider = sk(el('input'));
            inflateSlider.type = 'range';
            inflateSlider.min = '1';
            inflateSlider.max = '300';
            inflateSlider.step = '1';
            inflateSlider.value = Math.round(INFLATE_CFG.scale * 100);
            inflateSlider.style.cssText = 'flex:1;cursor:pointer;accent-color:#80e8ff;';
            inflateSlider.addEventListener('mousedown', e => e.stopPropagation());
            const inflateVal = el(
                'span',
                'font-size:11px;font-family:monospace;color:#80e8ff;min-width:36px;text-align:right;',
                INFLATE_CFG.scale.toFixed(2) + '×'
            );
            inflateSlider.addEventListener('input', () => {
                INFLATE_CFG.scale = parseInt(inflateSlider.value) / 100;
                inflateVal.textContent = INFLATE_CFG.scale.toFixed(2) + '×';
            });
            const inflateApplyBtn = mkBtn(
                '✔',
                CSS.btn('rgba(80,200,255,0.15)', 'rgba(100,220,255,0.4)', '#80e8ff'),
                () => actionInflateMode(INFLATE_CFG.scale)
            );
            inflateSliderRow.append(inflateSlider, inflateVal, inflateApplyBtn);
            inflateWrap.appendChild(inflateSliderRow);

            // -- MISCELLANEOUS SUBSECTION ---
            const miscWrap = div(
                'display:flex;flex-direction:column;gap:6px;padding-top:6px;border-top:1px solid var(--sm-ov2);'
            );
            miscWrap.appendChild(lbl('🎲 Miscellaneous'));
            miscWrap.append(miscBtnGrid, inflateWrap, rwSep, scoreSep);

            phbody.append(
                msWrap,
                bouncyWrap,
                iceWrap,
                denWrap,
                rotWrap,
                stretchWrap,
                ppmWrap,
                sizeWrap,
                translateWrap,
                freeMovingRow,
                stationaryRow,
                noPhysicsRow,
                deathRow,
                rainbowWrap,
                miscWrap
            );

            // -- HOST GUARD: block all button clicks in Physics section ---
            phbody.addEventListener('click', e => {
                const btn = e.target.closest('button');
                if (!btn) return;
                if (!_iAmHost()) {
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    showToast('⚠️ Only the host can do this', 'err');
                }
            }, true);

            sectionMap.physics = phwrap;

            // -- EXPORT SECTION ---
            const { wrap: expwrap, body: expbody } = mkSection('export', '📤 Export', false);
            const centerRow = div('display:grid;grid-template-columns:repeat(auto-fit,minmax(95px,1fr));gap:5px;');
            let _activeCenterBtn = null;
            for (const cb of [
                { key: 'player', label: '👾 My position' },
                { key: 'origin', label: '🌐 Center (0,0)' },
            ]) {
                const btn = el(
                    'button',
                    'cursor:pointer;width:100%;border-radius:5px;padding:5px;font-size:10px;font-weight:bold;border:1px solid var(--sm-bd3);background:var(--sm-ov1);color:var(--sm-text-dim1);transition:all .15s;',
                    cb.label
                );
                btn.addEventListener('click', () => {
                    EXPORT_CFG.centerMode = cb.key;
                    if (_activeCenterBtn) Object.assign(_activeCenterBtn.style, CSS.inactive);
                    _activeCenterBtn = btn;
                    Object.assign(btn.style, {
                        background: 'rgba(120,160,255,0.22)',
                        borderColor: 'rgba(120,160,255,0.5)',
                        color: '#9ab8ff',
                    });
                });
                btn.addEventListener('mouseenter', () => (btn.style.filter = 'brightness(1.2)'));
                btn.addEventListener('mouseleave', () => (btn.style.filter = ''));
                centerRow.appendChild(btn);
                if (cb.key === 'player') {
                    _activeCenterBtn = btn;
                    Object.assign(btn.style, {
                        background: 'rgba(120,160,255,0.22)',
                        borderColor: 'rgba(120,160,255,0.5)',
                        color: '#9ab8ff',
                    });
                }
            }
            const expArea = sk(
                el(
                    'textarea',
                    'resize:none;height:70px;width:100%;background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.1);border-radius:5px;color:#7cffb0;font-family:monospace;font-size:9px;padding:6px;outline:none;box-sizing:border-box;'
                )
            );
            expArea.readOnly = true;
            expArea.placeholder = 'Exported JSON will appear here...';
            const _expBtn = mkBtn(
                '⚡ Export',
                'cursor:pointer;width:100%;border-radius:5px;padding:6px;border:1px solid rgba(120,160,255,0.4);background:rgba(80,130,255,0.15);color:#9ab8ff;font-size:11px;font-weight:bold;',
                () => {
                    const j = exportCurrentMap(false);
                    if (j) expArea.value = j;
                }
            );
            _expBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                const j = exportCurrentMap(true);
                if (j) {
                    expArea.value = j;
                }
            });
            // Button row: 3-col grid when wide enough, Import wraps to its own row if not
            const expBtnRow = div('display:grid;grid-template-columns:repeat(auto-fit,minmax(75px,1fr));gap:5px;');
            const _btnCss = (bd, bg, t) =>
                `cursor:pointer;width:100%;border-radius:5px;padding:6px 0;border:1px solid ${bd};background:${bg};color:${t};font-size:11px;font-weight:bold;`;
            expBtnRow.append(
                _expBtn,
                mkBtn(
                    '📋 Copy',
                    _btnCss('rgba(80,220,120,0.4)', 'rgba(60,200,100,0.12)', '#80ffaa'),
                    () => {
                        if (!_lastExportedMapData) {
                            showToast('⚠️ Nothing to copy', 'err');
                            return;
                        }
                        navigator.clipboard
                            .writeText(_lastExportedMapData)
                            .catch(() => showToast('⚠️ Copy failed', 'err'));
                    }
                ),
                mkBtn(
                    '📥 Import',
                    _btnCss('rgba(255,180,50,0.4)', 'rgba(255,160,30,0.12)', '#ffd080'),
                    () => openImportWindow(expArea.value)
                )
            );
            expbody.append(centerRow, expArea, expBtnRow);
            sectionMap.export = expwrap;

            // -- POSITIONS SECTION (2-player swap/bring + move/teleport) --
            const { wrap: twwrap, body: twbody } = mkSection('twoPlayers', '📍 Positions', false);
            const selPair = div('display:grid;grid-template-columns:1fr 1fr;gap:6px;');
            const sAW = div('display:flex;flex-direction:column;gap:3px;');
            sAW.appendChild(lbl('Player A'));
            const selA = mkSel();
            selA.addEventListener('change', () => {
                if (UI.playerSel) UI.playerSel.value = selA.value;
            });
            sAW.appendChild(selA);
            const sBW = div('display:flex;flex-direction:column;gap:3px;');
            sBW.appendChild(lbl('Player B'));
            const s2 = mkSel();
            UI.playerSel2 = s2;
            sBW.appendChild(s2);
            selPair.append(sAW, sBW);
            const twGrid = div('display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:2px;');
            const swBtn = mkBtn(
                '🔀 Swap',
                CSS.btn('rgba(255,200,50,0.1)', 'rgba(255,200,50,0.4)', '#ffe680') + 'font-size:12px;',
                () => {
                    const a = parseInt(selA.value ?? -1),
                        b = parseInt(UI.playerSel2?.value ?? -1);
                    if (a < 0 || b < 0) {
                        showToast('⚠️ Select both players', 'err');
                        return;
                    }
                    actionSwap(a, b);
                }
            );
            swBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                actionSwapAll();
            });
            const btBtn = mkBtn(
                '📍 A→B',
                CSS.btn('rgba(255,150,50,0.1)', 'rgba(255,150,50,0.4)', '#ffb060') + 'font-size:12px;',
                () => {
                    const a = parseInt(selA.value ?? -1),
                        b = parseInt(UI.playerSel2?.value ?? -1);
                    if (a < 0 || b < 0) {
                        showToast('⚠️ Select both players', 'err');
                        return;
                    }
                    actionBringTo(a, b);
                }
            );
            twGrid.append(swBtn, btBtn);

            twbody.append(selPair, twGrid);

            // Move / Teleport Player — a single control. Which one it actually
            // does depends on the 🌐 Global toggle up in the Target section:
            //  · Global OFF → ;move  (relative offset from current position)
            //  · Global ON  → ;tp    (absolute map coordinates)
            // Right-click the button to apply it to every player in the room.
            const ptXI = mkInp('X', 'number');
            ptXI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const ptYI = mkInp('Y', 'number');
            ptYI.style.cssText = CSS.inp + 'flex:1;min-width:0;width:auto;';
            const ptMiniHdr = el(
                'div',
                'font-size:10px;font-weight:bold;letter-spacing:.05em;color:rgba(192,224,96,0.7);padding-bottom:3px;',
                '📍 Move (;move)'
            );

            const ptBtn = mkBtn('➡ Move', CSS.btn('rgba(160,200,80,0.15)', 'rgba(180,220,100,0.4)', '#c0e060'), () => {
                const pid = parseInt(UI.playerSel?.value ?? -1);
                if (pid < 0) {
                    showToast('⚠️ Select a player', 'err');
                    return;
                }
                const x = ptXI.value === '' ? 0 : ptXI.value,
                    y = ptYI.value === '' ? 0 : ptYI.value;
                if (UI.useGlobal) actionTeleportPlayer(pid, x, y);
                else actionMovePlayer(pid, x, y);
            });
            ptBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                const x = ptXI.value === '' ? 0 : ptXI.value,
                    y = ptYI.value === '' ? 0 : ptYI.value;
                if (UI.useGlobal) actionTeleportPlayerAll(x, y);
                else actionMovePlayerAll(x, y);
            });
            function refreshPtMode() {
                if (UI.useGlobal) {
                    ptMiniHdr.textContent = '🛰 Teleport (;tp)';
                    ptMiniHdr.style.color = 'rgba(128,200,255,0.85)';
                    ptBtn.textContent = '🛰 Teleport';
                    Object.assign(ptBtn.style, {
                        borderColor: 'rgba(120,200,255,0.4)',
                        background: 'rgba(80,180,255,0.15)',
                        color: '#80c8ff',
                    });

                } else {
                    ptMiniHdr.textContent = '📍 Move (;move)';
                    ptMiniHdr.style.color = 'rgba(192,224,96,0.7)';
                    ptBtn.textContent = '➡ Move';
                    Object.assign(ptBtn.style, {
                        borderColor: 'rgba(180,220,100,0.4)',
                        background: 'rgba(160,200,80,0.15)',
                        color: '#c0e060',
                    });

                }
            }
            UI._refreshPtMode = refreshPtMode;
            refreshPtMode();
            const ptRow = div(
                'display:flex;flex-direction:column;gap:3px;border-top:1px solid var(--sm-ov2);padding-top:6px;margin-top:2px;'
            );
            ptRow.appendChild(ptMiniHdr);
            ptRow.appendChild(row(ptXI, ptYI, ptBtn));

            twbody.append(ptRow);

            // -- HOST GUARD: block all button clicks in Positions section -
            twbody.addEventListener('click', e => {
                const btn = e.target.closest('button');
                if (!btn) return;
                if (!_iAmHost()) {
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    showToast('⚠️ Only the host can do this', 'err');
                }
            }, true);

            sectionMap.twoPlayers = twwrap;

            // ===
            // -- FORCE ZONES SECTION ---
            // ===

            // -- FZ CONFIG OBJECTS ---
            const FZ_GRID_CFG = { cellSize: 5.333, force: 250, count: 10 };
            const FZ_CHECKER_CFG = { cellSize: 5.333, force: 250 };
            const FZ_NOGRAV_CFG = { playerForce: 20, arrowForce: 25, circleRadius: 10, circleCount: 5 };

            // -- HELPER: append to last layer (bro) ---
            function _fzAppendToLast(physics, newBodies) {
                const bro = physics.bro;
                if (Array.isArray(bro)) {
                    // bro[0] = top (drawn last = on top visually? bonk renders bro[0] first)
                    // We want these at the END of bro = drawn first = bottom layer
                    for (const b of newBodies) bro.push(b.id);
                } else {
                    physics.bro = physics.bodies.map((_, i) => i);
                }
            }

            // -- FZ HELPER: make a static body with a box shape + forcezone -
            function _makeFZBody(id, shIdx, fiIdx, px, py, w, h, fzX, fzY, color, fzType) {
                const shape = { type: 'bx', w, h, c: [0, 0], a: 0, sk: false };
                const fix = {
                    sh: shIdx,
                    n: 'FZ Shape',
                    fr: null,
                    fp: null,
                    re: null,
                    de: null,
                    f: color,
                    d: false,
                    np: false,
                    ng: false,
                    ig: false,
                };
                const body = {
                    p: [px, py],
                    a: 0,
                    av: 0,
                    lv: [0, 0],
                    cf: { x: 0, y: 0, w: true, ct: 0 },
                    fx: [fiIdx],
                    fz: { on: true, x: fzX, y: fzY, d: true, p: true, a: true, t: fzType || 0, cf: 0 },
                    s: {
                        type: 's',
                        n: 'FZ',
                        fric: 0.5,
                        fricp: false,
                        re: 0.8,
                        de: 0.3,
                        ld: 0,
                        ad: 0,
                        fr: false,
                        bu: false,
                        f_c: 1,
                        f_p: true,
                        f_1: true,
                        f_2: true,
                        f_3: true,
                        f_4: true,
                    },
                    id,
                };
                return { shape, fix, body };
            }

            // -- FZ HELPER: make a static body with a circle shape + forcezone -
            function _makeFZCircleBody(id, shIdx, fiIdx, px, py, radius, fzX, fzY, color, fzType, affectsArrows) {
                const shape = { type: 'ci', r: radius, c: [0, 0], a: 0, sk: false };
                const fix = {
                    sh: shIdx,
                    n: 'FZ Circle',
                    fr: null,
                    fp: null,
                    re: null,
                    de: null,
                    f: color,
                    d: false,
                    np: false,
                    ng: false,
                    ig: false,
                };
                const body = {
                    p: [px, py],
                    a: 0,
                    av: 0,
                    lv: [0, 0],
                    cf: { x: 0, y: 0, w: true, ct: 0 },
                    fx: [fiIdx],
                    fz: {
                        on: true,
                        x: fzX,
                        y: fzY,
                        d: affectsArrows ? false : true,
                        p: true,
                        a: !affectsArrows,
                        t: 0,
                        cf: 0,
                    },
                    s: {
                        type: 's',
                        n: 'FZ',
                        fric: 0.5,
                        fricp: false,
                        re: 0.8,
                        de: 0.3,
                        ld: 0,
                        ad: 0,
                        fr: false,
                        bu: false,
                        f_c: 1,
                        f_p: true,
                        f_1: true,
                        f_2: true,
                        f_3: true,
                        f_4: true,
                    },
                    id,
                };
                return { shape, fix, body };
            }

            // ---
            // ACTION 1 — FZ GRID (random forcezones as grid over map)
            // ---
            function actionFZGrid() {
                const S = _requireState();
                if (!S) return;
                const physics = structuredClone(S.state.physics);
                const ppm = physics?.ppm ?? 30;
                const mapW = 730 / ppm,
                    mapH = 500 / ppm;
                const cx = mapW / 2,
                    cy = mapH / 2;

                const cell = FZ_GRID_CFG.cellSize;
                const force = FZ_GRID_CFG.force;
                const count = Math.max(1, Math.min(200, Math.round(FZ_GRID_CFG.count)));

                // Directions pool: up/down/left/right
                const dirs = [
                    { x: 0, y: force }, // down
                    { x: 0, y: -force }, // up
                    { x: force, y: 0 }, // right
                    { x: -force, y: 0 }, // left
                ];
                const dirColors = [
                    0xcf6679, // red-ish (down)
                    0x79cf8f, // green (up)
                    0xcfb879, // orange (right)
                    0x7999cf, // blue (left)
                ];

                const shBase = physics.shapes.length;
                const fiBase = physics.fixtures.length;
                const bBase = physics.bodies.length;

                // Spread evenly across the map area
                const cols = Math.ceil(Math.sqrt(count * (mapW / mapH)));
                const rows = Math.ceil(count / cols);
                const stepX = mapW / cols;
                const stepY = mapH / rows;

                let added = 0;
                for (let r = 0; r < rows && added < count; r++) {
                    for (let c = 0; c < cols && added < count; c++) {
                        const px = (c + 0.5) * stepX + (Math.random() - 0.5) * stepX * 0.6;
                        const py = (r + 0.5) * stepY + (Math.random() - 0.5) * stepY * 0.6;
                        const di = Math.floor(Math.random() * dirs.length);
                        const d = dirs[di];
                        const col = dirColors[di];
                        const idx = added;
                        const { shape, fix, body } = _makeFZBody(
                            bBase + idx,
                            shBase + idx,
                            fiBase + idx,
                            px,
                            py,
                            cell,
                            cell,
                            d.x,
                            d.y,
                            col,
                            0
                        );
                        physics.shapes.push(shape);
                        physics.fixtures.push(fix);
                        physics.bodies.push(body);
                        added++;
                    }
                }

                // Ensure bro array exists and push new bodies to end (bottom layer)
                if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);
                for (let i = bBase; i < physics.bodies.length; i++) physics.bro.push(i);

                const snap = structuredClone(S.state);
                snap.physics = physics;
                startGame(snap, true);
            }

            // ---
            // ACTION 1b — FZ CHECKER GRID (real alternating grid pattern)
            // Pattern: FZ-NADA-FZ-NADA / NADA-FZ-NADA-FZ (checkerboard)
            // Even cells (col+row even) = one direction, odd cells = opposite
            // ---
            function actionFZCheckerGrid() {
                const S = _requireState();
                if (!S) return;
                const physics = structuredClone(S.state.physics);
                const ppm = physics?.ppm ?? 30;
                const mapW = 730 / ppm,
                    mapH = 500 / ppm;

                const cell = FZ_CHECKER_CFG.cellSize;
                const force = FZ_CHECKER_CFG.force;

                // Fit cells to map, then add +2 cols and +2 rows as padding (1 extra on each side)
                const cols = Math.floor(mapW / cell) + 2;
                const rows = Math.floor(mapH / cell) + 2;
                // Shift origin so padding cells extend just outside the normal map edge
                const startX = -cell / 2;
                const startY = -cell / 2;

                const dirs = [
                    { x: 0, y: force, col: 0xcf6679 },
                    { x: 0, y: -force, col: 0x79cf8f },
                    { x: force, y: 0, col: 0xcfb879 },
                    { x: -force, y: 0, col: 0x7999cf },
                ];

                const shBase = physics.shapes.length;
                const fiBase = physics.fixtures.length;
                const bBase = physics.bodies.length;

                let added = 0;
                for (let r = 0; r < rows; r++) {
                    for (let c = 0; c < cols; c++) {
                        if ((c + r) % 2 !== 0) continue;
                        const px = startX + c * cell + cell / 2;
                        const py = startY + r * cell + cell / 2;
                        const d = dirs[Math.floor(Math.random() * dirs.length)];
                        const idx = added;
                        const { shape, fix, body } = _makeFZBody(
                            bBase + idx,
                            shBase + idx,
                            fiBase + idx,
                            px,
                            py,
                            cell,
                            cell,
                            d.x,
                            d.y,
                            d.col,
                            0
                        );
                        physics.shapes.push(shape);
                        physics.fixtures.push(fix);
                        physics.bodies.push(body);
                        added++;
                    }
                }

                if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);
                for (let i = bBase; i < physics.bodies.length; i++) physics.bro.push(i);

                const snap = structuredClone(S.state);
                snap.physics = physics;
                startGame(snap, true);
            }


            // ---
            // ACTION 3a — NO GRAVITY full map (players)
            // ---
            function actionFZNoGravPlayers() {
                const S = _requireState();
                if (!S) return;
                const physics = structuredClone(S.state.physics);
                const ppm = physics?.ppm ?? 30;
                const mapW = 730 / ppm,
                    mapH = 500 / ppm;
                const f = FZ_NOGRAV_CFG.playerForce;

                const shBase = physics.shapes.length;
                const fiBase = physics.fixtures.length;
                const bBase = physics.bodies.length;

                // One giant box covering the map, fz upward for players only (a:false = don't affect arrows)
                const { shape, fix, body } = _makeFZCircleBody(
                    bBase,
                    shBase,
                    fiBase,
                    mapW / 2,
                    mapH / 2,
                    0,
                    0,
                    -f,
                    0x3096cc,
                    0,
                    false
                );
                // Override to box covering the map
                shape.type = 'bx';
                shape.w = mapW;
                shape.h = mapH;
                delete shape.r;
                // fz: d=true (dynamic = players), a=false (not arrows)
                body.fz = { on: true, x: 0, y: -f, d: true, p: true, a: false, t: 0, cf: 0 };
                body.s.n = 'FZ NoGrav Players';

                physics.shapes.push(shape);
                physics.fixtures.push(fix);
                physics.bodies.push(body);

                if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);
                physics.bro.push(bBase);

                const snap = structuredClone(S.state);
                snap.physics = physics;
                startGame(snap, true);
            }

            // ---
            // ACTION 3b — NO GRAVITY full map (arrows)
            // ---
            function actionFZNoGravArrows() {
                const S = _requireState();
                if (!S) return;
                const physics = structuredClone(S.state.physics);
                const ppm = physics?.ppm ?? 30;
                const mapW = 730 / ppm,
                    mapH = 500 / ppm;
                const f = FZ_NOGRAV_CFG.arrowForce;

                const shBase = physics.shapes.length;
                const fiBase = physics.fixtures.length;
                const bBase = physics.bodies.length;

                const shape = { type: 'bx', w: mapW, h: mapH, c: [0, 0], a: 0, sk: false };
                const fix = {
                    sh: shBase,
                    n: 'FZ NoGrav Arrows',
                    fr: null,
                    fp: null,
                    re: null,
                    de: null,
                    f: 0xcf8a30,
                    d: false,
                    np: false,
                    ng: false,
                    ig: false,
                };
                const body = {
                    p: [mapW / 2, mapH / 2],
                    a: 0,
                    av: 0,
                    lv: [0, 0],
                    cf: { x: 0, y: 0, w: true, ct: 0 },
                    fx: [fiBase],
                    // d:false = not dynamic (doesn't affect players), a:true = affects projectiles/arrows
                    fz: { on: true, x: 0, y: -f, d: false, p: true, a: true, t: 0, cf: 0 },
                    s: {
                        type: 's',
                        n: 'FZ NoGrav Arrows',
                        fric: 0.5,
                        fricp: false,
                        re: 0.8,
                        de: 0.3,
                        ld: 0,
                        ad: 0,
                        fr: false,
                        bu: false,
                        f_c: 1,
                        f_p: true,
                        f_1: true,
                        f_2: true,
                        f_3: true,
                        f_4: true,
                    },
                    id: bBase,
                };

                physics.shapes.push(shape);
                physics.fixtures.push(fix);
                physics.bodies.push(body);

                if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);
                physics.bro.push(bBase);

                const snap = structuredClone(S.state);
                snap.physics = physics;
                startGame(snap, true);
            }

            // ---
            // ACTION 3c — Random NO GRAVITY circles around map
            // ---
            function actionFZNoGravCircles(mode) {
                // mode: 'players' | 'arrows' | 'both'
                const S = _requireState();
                if (!S) return;
                const physics = structuredClone(S.state.physics);
                const ppm = physics?.ppm ?? 30;
                const mapW = 730 / ppm,
                    mapH = 500 / ppm;
                const radius = FZ_NOGRAV_CFG.circleRadius;
                const count = Math.max(1, Math.min(50, Math.round(FZ_NOGRAV_CFG.circleCount)));
                const fP = FZ_NOGRAV_CFG.playerForce;
                const fA = FZ_NOGRAV_CFG.arrowForce;

                const shBase = physics.shapes.length;
                const fiBase = physics.fixtures.length;
                const bBase = physics.bodies.length;

                const COLOR_P = 0x3096cc; // blue = players
                const COLOR_A = 0xcf8a30; // orange = arrows
                const COLOR_B = 0x9b59b6; // purple = both

                let added = 0;
                for (let i = 0; i < count; i++) {
                    const px = radius + Math.random() * (mapW - radius * 2);
                    const py = radius + Math.random() * (mapH - radius * 2);
                    const idx = added;

                    let affP = false,
                        affA = false,
                        col;
                    if (mode === 'players') {
                        affP = true;
                        col = COLOR_P;
                    } else if (mode === 'arrows') {
                        affA = true;
                        col = COLOR_A;
                    } else {
                        affP = true;
                        affA = true;
                        col = COLOR_B;
                    }

                    const shape = { type: 'ci', r: radius, c: [0, 0], a: 0, sk: false };
                    const fix = {
                        sh: shBase + idx,
                        n: 'FZ NoGrav Circle',
                        fr: null,
                        fp: null,
                        re: null,
                        de: null,
                        f: col,
                        d: false,
                        np: false,
                        ng: false,
                        ig: false,
                    };
                    const body = {
                        p: [px, py],
                        a: 0,
                        av: 0,
                        lv: [0, 0],
                        cf: { x: 0, y: 0, w: true, ct: 0 },
                        fx: [fiBase + idx],
                        fz: { on: true, x: 0, y: -(affP ? fP : fA), d: affP, p: true, a: affA, t: 0, cf: 0 },
                        s: {
                            type: 's',
                            n: 'FZ NoGrav Circle',
                            fric: 0.5,
                            fricp: false,
                            re: 0.8,
                            de: 0.3,
                            ld: 0,
                            ad: 0,
                            fr: false,
                            bu: false,
                            f_c: 1,
                            f_p: true,
                            f_1: true,
                            f_2: true,
                            f_3: true,
                            f_4: true,
                        },
                        id: bBase + idx,
                    };

                    physics.shapes.push(shape);
                    physics.fixtures.push(fix);
                    physics.bodies.push(body);
                    added++;
                }

                if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);
                for (let i = bBase; i < physics.bodies.length; i++) physics.bro.push(i);

                const snap = structuredClone(S.state);
                snap.physics = physics;
                startGame(snap, true);
            }

            // -- FORCE ZONES SECTION UI ---
            // ===
            // -- FORCE ZONES & JOINTS SECTION ---
            // ===
            const { wrap: fzwrap, body: fzbody } = mkSection('fzjoints', '🌀 ForceZones & Joints', false);

            // -- Shared slider helper ---
            function fzSlider(label, min, max, step, initVal, color, onChange) {
                const row = div('display:flex;align-items:center;gap:6px;');
                row.appendChild(
                    el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:72px;flex-shrink:0;', label)
                );
                const sl = sk(el('input'));
                sl.type = 'range';
                sl.min = min;
                sl.max = max;
                sl.step = step;
                sl.value = initVal;
                sl.style.cssText = 'flex:1;cursor:pointer;accent-color:' + color + ';';
                sl.addEventListener('mousedown', e => e.stopPropagation());
                const vl = el(
                    'span',
                    'font-size:11px;font-family:monospace;min-width:44px;text-align:right;color:' + color + ';',
                    initVal + ''
                );
                sl.addEventListener('input', () => {
                    const v = parseFloat(sl.value);
                    vl.textContent = v;
                    onChange(v);
                });
                row.append(sl, vl);
                return row;
            }

            // -- SUBSECTION 1: FZ BUILDER ---
            // affD=dynamic/players, affPl=platforms/static, affA=arrows
            const FZ_BUILD_CFG = {
                shape: 'square',
                type: 'centerpush',
                angle: 0,
                count: 5,
                size: 5.333,
                force: 250,
                affD: true,
                affPl: true,
                affA: true,
            };

            const fzBuildWrap = div(
                'display:flex;flex-direction:column;gap:6px;padding-bottom:10px;border-bottom:1px solid var(--sm-ov2);'
            );
            fzBuildWrap.appendChild(lbl('⚡ ForceZone Builder'));

            // -- Shape toggle: Square / Circle ---
            const fzShapeRow = div('display:flex;align-items:center;gap:6px;');
            fzShapeRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:44px;flex-shrink:0;', 'Shape')
            );
            const fzShapePills = div('display:flex;gap:4px;flex:1;');
            let _fzShapeBtn = null;
            for (const s of [
                { k: 'square', icon: '◻️ Square' },
                { k: 'circle', icon: '⭕ Circle' },
            ]) {
                const active = s.k === 'square';
                const b = mkBtn(
                    s.icon,
                    `cursor:pointer;flex:1;border-radius:5px;padding:5px 4px;font-size:10px;font-weight:bold;border:1px solid ${active ? 'rgba(100,180,255,0.6)' : 'rgba(100,180,255,0.2)'};background:${active ? 'rgba(80,160,255,0.2)' : 'rgba(80,160,255,0.05)'};color:${active ? '#9ab8ff' : 'var(--sm-text-dim2)'};transition:all .15s;`,
                    () => {
                        FZ_BUILD_CFG.shape = s.k;
                        if (_fzShapeBtn)
                            Object.assign(_fzShapeBtn.style, {
                                border: '1px solid rgba(100,180,255,0.2)',
                                background: 'rgba(80,160,255,0.05)',
                                color: 'var(--sm-text-dim2)',
                            });
                        _fzShapeBtn = b;
                        Object.assign(b.style, {
                            border: '1px solid rgba(100,180,255,0.6)',
                            background: 'rgba(80,160,255,0.2)',
                            color: '#9ab8ff',
                        });
                    }
                );
                if (active) _fzShapeBtn = b;
                fzShapePills.appendChild(b);
            }
            fzShapeRow.appendChild(fzShapePills);
            fzBuildWrap.appendChild(fzShapeRow);

            // -- Type pills: Pull / Push / Directional / Random -
            const fzTypeRow = div('display:flex;align-items:center;gap:6px;');
            fzTypeRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:44px;flex-shrink:0;', 'Type')
            );
            const fzTypePills = div('display:flex;gap:3px;flex:1;flex-wrap:wrap;');
            // Angle slider — shown only when type='directional', inserted after Affects
            const fzAngleRow = div('display:none;align-items:center;gap:6px;');
            fzAngleRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:44px;flex-shrink:0;', 'Angle')
            );
            const fzAngleSl = sk(el('input'));
            fzAngleSl.type = 'range';
            fzAngleSl.min = '-180';
            fzAngleSl.max = '180';
            fzAngleSl.step = '1';
            fzAngleSl.value = 0;
            fzAngleSl.style.cssText = 'flex:1;cursor:pointer;accent-color:#c0e060;';
            fzAngleSl.addEventListener('mousedown', e => e.stopPropagation());
            const fzAngleVl = el(
                'span',
                'font-size:11px;font-family:monospace;min-width:44px;text-align:right;color:#c0e060;',
                '0°'
            );
            fzAngleSl.addEventListener('input', () => {
                FZ_BUILD_CFG.angle = parseInt(fzAngleSl.value);
                fzAngleVl.textContent = FZ_BUILD_CFG.angle + '°';
            });
            fzAngleRow.append(fzAngleSl, fzAngleVl);

            const FZ_TYPES = [
                { k: 'centerpull', label: '🌀 Pull', col: '#d090ff' },
                { k: 'centerpush', label: '💥 Push', col: '#ff9090' },
                { k: 'directional', label: '🧭 Direction', col: '#c0e060' },
                { k: 'random', label: '🎲 Random', col: '#CFCF79' },
            ];
            let _fzTypeBtn = null;
            for (const t of FZ_TYPES) {
                const isFirst = t.k === 'centerpull';
                const b = mkBtn(
                    t.label,
                    `cursor:pointer;flex:1 1 auto;border-radius:5px;padding:4px 3px;font-size:9.5px;font-weight:bold;border:1px solid ${isFirst ? t.col + '88' : 'rgba(120,120,120,0.25)'};background:${isFirst ? t.col + '22' : 'rgba(80,80,80,0.08)'};color:${isFirst ? t.col : 'var(--sm-text-dim2)'};white-space:nowrap;transition:all .15s;`,
                    () => {
                        FZ_BUILD_CFG.type = t.k;
                        if (_fzTypeBtn)
                            Object.assign(_fzTypeBtn.style, {
                                border: '1px solid rgba(120,120,120,0.25)',
                                background: 'rgba(80,80,80,0.08)',
                                color: 'var(--sm-text-dim2)',
                            });
                        _fzTypeBtn = b;
                        Object.assign(b.style, {
                            border: '1px solid ' + t.col + '88',
                            background: t.col + '22',
                            color: t.col,
                        });
                        fzAngleRow.style.display = t.k === 'directional' ? 'flex' : 'none';
                    }
                );
                if (isFirst) {
                    _fzTypeBtn = b;
                }
                fzTypePills.appendChild(b);
            }
            fzTypeRow.appendChild(fzTypePills);
            fzBuildWrap.appendChild(fzTypeRow);

            // -- Affects checkboxes: Players / Platforms / Arrows ---
            {
                const affRow = div('display:flex;align-items:center;gap:6px;');
                affRow.appendChild(
                    el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:44px;flex-shrink:0;', 'Affects')
                );
                const checks = div('display:flex;justify-content:space-between;flex:1;');
                const AFF = [
                    { key: 'affD', label: '👤 Players', col: THEME.affPlayers, init: true },
                    { key: 'affPl', label: '🟫 Platforms', col: THEME.affPlatforms, init: true },
                    { key: 'affA', label: '🏹 Arrows', col: THEME.affArrows, init: true },
                ];
                for (const a of AFF) {
                    const wrap = div('display:flex;align-items:center;gap:3px;cursor:pointer;');
                    const box = sk(el('input'));
                    box.type = 'checkbox';
                    box.checked = a.init;
                    box.style.cssText = `cursor:pointer;accent-color:${a.col};color-scheme:dark;width:12px;height:12px;flex-shrink:0;`;
                    box.addEventListener('mousedown', e => e.stopPropagation());
                    box.addEventListener('change', () => {
                        FZ_BUILD_CFG[a.key] = box.checked;
                    });
                    const lbEl = el('span', `font-size:9.5px;color:${a.col};user-select:none;`, a.label);
                    wrap.addEventListener('click', e => {
                        if (e.target !== box) {
                            box.checked = !box.checked;
                            box.dispatchEvent(new Event('change'));
                        }
                    });
                    wrap.append(box, lbEl);
                    checks.appendChild(wrap);
                }
                affRow.appendChild(checks);
                fzBuildWrap.appendChild(affRow);
            }

            // -- Angle slider (shown only for Direction, lives here after Affects) -
            fzBuildWrap.appendChild(fzAngleRow);

            // -- Sliders: Count / Size / Force — all with same label width -
            const FZ_LBL = 'font-size:10px;color:var(--sm-text-dim2);min-width:44px;flex-shrink:0;';
            const FZ_VAL = 'font-size:11px;font-family:monospace;min-width:44px;text-align:right;';

            function fzSliderUniform(label, min, max, step, initVal, color, onChange) {
                const row = div('display:flex;align-items:center;gap:6px;');
                row.appendChild(el('span', FZ_LBL, label));
                const sl = sk(el('input'));
                sl.type = 'range';
                sl.min = min;
                sl.max = max;
                sl.step = step;
                sl.value = initVal;
                sl.style.cssText = 'flex:1;cursor:pointer;accent-color:' + color + ';';
                sl.addEventListener('mousedown', e => e.stopPropagation());
                const vl = el('span', FZ_VAL + 'color:' + color + ';', initVal + '');
                sl.addEventListener('input', () => {
                    const v = parseFloat(sl.value);
                    vl.textContent = v;
                    onChange(v);
                });
                row.append(sl, vl);
                return row;
            }

            fzBuildWrap.appendChild(
                fzSliderUniform(
                    'Count',
                    1,
                    50,
                    1,
                    FZ_BUILD_CFG.count,
                    '#7CFF9F',
                    v => (FZ_BUILD_CFG.count = Math.round(v))
                )
            );

            // -- Size slider with snap-to-full-map at max (150) ---
            {
                const FZ_SIZE_MAX = 150;
                const row = div('display:flex;align-items:center;gap:6px;');
                row.appendChild(el('span', FZ_LBL, 'Size'));
                const sl = sk(el('input'));
                sl.type = 'range';
                sl.min = 1;
                sl.max = FZ_SIZE_MAX;
                sl.step = 0.5;
                sl.value = FZ_BUILD_CFG.size;
                sl.style.cssText = 'flex:1;cursor:pointer;accent-color:#79CFCF;';
                sl.addEventListener('mousedown', e => e.stopPropagation());
                const vl = el('span', FZ_VAL + 'color:#79CFCF;', FZ_BUILD_CFG.size + '');
                sl.addEventListener('input', () => {
                    const v = parseFloat(sl.value);
                    if (v >= FZ_SIZE_MAX) {
                        FZ_BUILD_CFG.size = FZ_SIZE_MAX;
                        vl.textContent = '🗺️ MAP';
                        vl.style.color = '#ffe080';
                        sl.style.accentColor = '#ffe080';
                    } else {
                        FZ_BUILD_CFG.size = v;
                        vl.textContent = v + '';
                        vl.style.color = '#79CFCF';
                        sl.style.accentColor = '#79CFCF';
                    }
                });
                row.append(sl, vl);
                fzBuildWrap.appendChild(row);
            }

            fzBuildWrap.appendChild(
                fzSliderUniform('Force', -1000, 1000, 10, FZ_BUILD_CFG.force, '#CF7979', v => (FZ_BUILD_CFG.force = v))
            );


            // -- Apply button ---
            function _doApplyFZ(invisible) {
                    const cfg = FZ_BUILD_CFG;
                    const S = _requireState();
                    if (!S) return;
                    const physics = structuredClone(S.state.physics);
                    const ppm = physics?.ppm ?? 30;
                    const mapW = 730 / ppm,
                        mapH = 500 / ppm;
                    const fullMap = cfg.size >= 150;

                    const count = Math.max(1, Math.min(200, Math.round(cfg.count)));
                    const f = cfg.force;
                    const bBase = physics.bodies.length;
                    const shBase = physics.shapes.length;
                    const fiBase = physics.fixtures.length;

                    const RAND_DIRS = [
                        { x: 0, y: -1, col: 0x79cf8f },
                        { x: 0, y: 1, col: 0xcf6679 },
                        { x: -1, y: 0, col: 0x7999cf },
                        { x: 1, y: 0, col: 0xcfb879 },
                    ];

                    for (let i = 0; i < count; i++) {
                        let px, py, shapeObj;
                        if (fullMap) {
                            px = mapW / 2;
                            py = mapH / 2;
                            shapeObj =
                                cfg.shape === 'circle'
                                    ? { type: 'ci', r: Math.min(mapW, mapH) / 2, c: [0, 0], a: 0, sk: false }
                                    : { type: 'bx', w: mapW, h: mapH, c: [0, 0], a: 0, sk: false };
                        } else {
                            px = Math.random() * mapW;
                            py = Math.random() * mapH;
                            const sz = cfg.size;
                            shapeObj =
                                cfg.shape === 'circle'
                                    ? { type: 'ci', r: sz / 2, c: [0, 0], a: 0, sk: false }
                                    : { type: 'bx', w: sz, h: sz, c: [0, 0], a: 0, sk: false };
                        }

                        let fzX = 0, fzY = 0, fzT = 0, fzCF = 0, color = 0x60d0ff;
                        if (cfg.type === 'centerpull') {
                            fzT = 3; fzCF = Math.abs(f); color = 0xd090ff;
                        } else if (cfg.type === 'centerpush') {
                            fzT = 3; fzCF = -Math.abs(f); color = 0xff9090;
                        } else if (cfg.type === 'directional') {
                            const rad = (cfg.angle * Math.PI) / 180;
                            fzX = Math.cos(rad) * f; fzY = Math.sin(rad) * f; color = 0xc0e060;
                        } else if (cfg.type === 'random') {
                            const d = RAND_DIRS[Math.floor(Math.random() * 4)];
                            fzX = d.x * f; fzY = d.y * f; color = d.col;
                        }

                        const shIdx = shBase + i;
                        const fiIdx = fiBase + i;
                        const bIdx = bBase + i;

                        physics.shapes.push(shapeObj);
                        physics.fixtures.push({
                            sh: shIdx, n: 'FZ', fr: null, fp: null, re: null, de: null,
                            f: color, d: false, np: false, ng: false, ig: false,
                        });
                        physics.bodies.push({
                            p: [px, py], a: 0, av: 0, lv: [0, 0],
                            cf: { x: 0, y: 0, w: true, ct: 0 },
                            fx: [fiIdx],
                            fz: { on: true, x: fzX, y: fzY, d: cfg.affD, p: cfg.affPl, a: cfg.affA, t: fzT, cf: fzCF },
                            s: {
                                type: 's', n: 'FZ', fric: 0.5, fricp: false, re: 0.8, de: 0.3,
                                ld: 0, ad: 0, fr: false, bu: false, f_c: 1, f_p: true,
                                f_1: true, f_2: true, f_3: true, f_4: true,
                            },
                            id: bIdx,
                        });
                    }

                    if (!Array.isArray(physics.bro)) physics.bro = physics.bodies.slice(0, bBase).map((_, i) => i);

                    if (invisible) {
                        // Add cover body (bg color) THEN forcezones, both pushed to the back (bro.push = rendered first = behind map)
                        const cx = mapW / 2, cy = mapH / 2;
                        const r = Math.sqrt(mapW * mapW + mapH * mapH);
                        const covShIdx = physics.shapes.length;
                        physics.shapes.push({ type: 'ci', r, c: [0, 0], a: 0, sk: false });
                        const covFiIdx = physics.fixtures.length;
                        physics.fixtures.push({
                            sh: covShIdx, n: 'FZ Cover', fr: null, fp: null, re: null, de: null,
                            f: 2768720, d: false, np: true, ng: false, ig: false,
                        });
                        const covBIdx = physics.bodies.length;
                        physics.bodies.push({
                            p: [cx, cy], a: 0, av: 0, lv: [0, 0],
                            cf: { x: 0, y: 0, w: true, ct: 0 },
                            fx: [covFiIdx],
                            fz: { on: false, x: 0, y: 0, d: true, p: true, a: true, t: 0, cf: 0 },
                            s: {
                                type: 's', n: 'FZ Cover BG', fric: 0.5, fricp: false, re: 0.8, de: 0.3,
                                ld: 0, ad: 0, fr: false, bu: false, f_c: 1, f_p: true,
                                f_1: true, f_2: true, f_3: true, f_4: true,
                            },
                            id: covBIdx,
                        });
                        // Push cover first (deeper in bro = rendered behind), then fz bodies on top of cover but still behind map
                        physics.bro.push(covBIdx);
                        for (let i = bBase; i < covBIdx; i++) physics.bro.push(i);
                    } else {
                        for (let i = bBase; i < physics.bodies.length; i++) physics.bro.push(i);
                    }

                    const snap = structuredClone(S.state);
                    snap.physics = physics;
                    startGame(snap, true);
            }

            let _fzInvisibleMode = false;
            const _fzApplyBtn = accentBtn('⚡ Apply ForceZones', 'rgba(100,200,180,0.12)', 'rgba(120,220,200,0.4)', '#79CFCF', () => _doApplyFZ(_fzInvisibleMode));
            function _fzUpdateBtnStyle() {
                if (_fzInvisibleMode) {
                    _fzApplyBtn.style.background = 'rgba(255,140,40,0.18)';
                    _fzApplyBtn.style.borderColor = 'rgba(255,160,60,0.8)';
                    _fzApplyBtn.style.color = '#ffb060';
                    _fzApplyBtn.style.boxShadow = '0 0 8px 2px rgba(255,140,30,0.45)';
                    _fzApplyBtn.textContent = '👻 Apply ForceZones (Invisible)';
                } else {
                    _fzApplyBtn.style.background = 'rgba(100,200,180,0.12)';
                    _fzApplyBtn.style.borderColor = 'rgba(120,220,200,0.4)';
                    _fzApplyBtn.style.color = '#79CFCF';
                    _fzApplyBtn.style.boxShadow = '';
                    _fzApplyBtn.textContent = '⚡ Apply ForceZones';
                }
            }
            _fzApplyBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                _fzInvisibleMode = !_fzInvisibleMode;
                _fzUpdateBtnStyle();
            });
            fzBuildWrap.appendChild(_fzApplyBtn);


            // -- SUBSECTION 3: JOINTS ---
            const fzJointsWrap = div('display:flex;flex-direction:column;gap:6px;padding-top:8px;');
            fzJointsWrap.appendChild(lbl('🔗 Joints'));

            // helper: compact joint card
            function mkJointCard(color, titleText, sliders, btns, hint) {
                const card = div(
                    'display:flex;flex-direction:column;gap:4px;border:1px solid var(--sm-ov2);border-radius:6px;padding:6px;background:' +
                        color +
                        '08;'
                );
                card.appendChild(el('span', 'font-size:10px;font-weight:bold;color:' + color + ';', titleText));
                for (const s of sliders) card.appendChild(s);
                const btnRow = div('display:flex;gap:4px;');
                for (const b of btns) {
                    b.style.flex = '1';
                    b.style.fontSize = '10px';
                    b.style.padding = '4px 2px';
                    btnRow.appendChild(b);
                }
                card.appendChild(btnRow);
                if (hint) card.appendChild(el('div', 'font-size:9px;color:var(--sm-text-dim1);', hint));
                return card;
            }

            // Spin Map card
            const spinMapRow2 = div('display:flex;align-items:center;gap:6px;');
            const spinMapSlider2 = sk(el('input'));
            spinMapSlider2.type = 'range';
            spinMapSlider2.min = '-1800';
            spinMapSlider2.max = '1800';
            spinMapSlider2.step = '10';
            spinMapSlider2.value = SPIN_CFG.speedDeg;
            spinMapSlider2.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#c0a0ff;';
            spinMapSlider2.addEventListener('mousedown', e => e.stopPropagation());
            const spinMapNumInp2 = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            spinMapNumInp2.type = 'number';
            spinMapNumInp2.value = SPIN_CFG.speedDeg;
            spinMapNumInp2.placeholder = 'deg/s';
            spinMapSlider2.addEventListener('input', () => {
                SPIN_CFG.speedDeg = parseFloat(spinMapSlider2.value) || 360;
                spinMapNumInp2.value = SPIN_CFG.speedDeg;
            });
            spinMapNumInp2.addEventListener('change', () => {
                const v = parseFloat(spinMapNumInp2.value);
                if (!isNaN(v)) {
                    SPIN_CFG.speedDeg = v;
                    spinMapSlider2.value = Math.max(-1800, Math.min(1800, v));
                }
            });
            spinMapRow2.append(spinMapSlider2, spinMapNumInp2);
            fzJointsWrap.appendChild(
                mkJointCard(
                    '#c0a0ff',
                    '🌀 Spin Map (deg/s)',
                    [spinMapRow2],
                    [
                        accentBtn('🔗 Merge', 'rgba(140,100,255,0.15)', 'rgba(180,140,255,0.4)', '#c0a0ff', () =>
                            actionSpinMap(SPIN_CFG.speedDeg, 'merge')
                        ),
                        accentBtn('📦 Bodies', 'rgba(140,100,255,0.10)', 'rgba(180,140,255,0.3)', '#c0a0ff', () =>
                            actionSpinMap(SPIN_CFG.speedDeg, 'bodies')
                        ),
                        accentBtn('🔮 Shapes', 'rgba(140,100,255,0.10)', 'rgba(180,140,255,0.3)', '#c0a0ff', () =>
                            actionSpinMap(SPIN_CFG.speedDeg, 'shapes')
                        ),
                    ],
                )
            );

            // Springy Map card
            const spForceRow = div('display:flex;align-items:center;gap:6px;');
            spForceRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:48px;flex-shrink:0;', 'Force:')
            );
            const spForceSl = sk(el('input'));
            spForceSl.type = 'range';
            spForceSl.min = '100';
            spForceSl.max = '50000';
            spForceSl.step = '100';
            spForceSl.value = SPRINGY_CFG.springForce;
            spForceSl.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#ffb060;';
            spForceSl.addEventListener('mousedown', e => e.stopPropagation());
            const spForceInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:68px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            spForceInp.type = 'number';
            spForceInp.value = SPRINGY_CFG.springForce;
            spForceInp.placeholder = 'force';
            spForceSl.addEventListener('input', () => {
                SPRINGY_CFG.springForce = parseFloat(spForceSl.value) || 7812.5;
                spForceInp.value = SPRINGY_CFG.springForce;
            });
            spForceInp.addEventListener('change', () => {
                const v = parseFloat(spForceInp.value);
                if (!isNaN(v) && v > 0) {
                    SPRINGY_CFG.springForce = v;
                    spForceSl.value = Math.max(100, Math.min(50000, v));
                }
            });
            spForceRow.append(spForceSl, spForceInp);
            const spLenRow = div('display:flex;align-items:center;gap:6px;');
            spLenRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:48px;flex-shrink:0;', 'Length:')
            );
            const spLenSl = sk(el('input'));
            spLenSl.type = 'range';
            spLenSl.min = '0';
            spLenSl.max = '200';
            spLenSl.step = '1';
            spLenSl.value = SPRINGY_CFG.springLen;
            spLenSl.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#ffb060;';
            spLenSl.addEventListener('mousedown', e => e.stopPropagation());
            const spLenInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:68px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            spLenInp.type = 'number';
            spLenInp.value = SPRINGY_CFG.springLen;
            spLenInp.placeholder = 'length';
            spLenSl.addEventListener('input', () => {
                SPRINGY_CFG.springLen = parseFloat(spLenSl.value) || 0;
                spLenInp.value = SPRINGY_CFG.springLen;
            });
            spLenInp.addEventListener('change', () => {
                const v = parseFloat(spLenInp.value);
                if (!isNaN(v) && v >= 0) {
                    SPRINGY_CFG.springLen = v;
                    spLenSl.value = Math.max(0, Math.min(200, v));
                }
            });
            spLenRow.append(spLenSl, spLenInp);
            fzJointsWrap.appendChild(
                mkJointCard(
                    '#ffb060',
                    '🪢 Springy Map',
                    [spForceRow, spLenRow],
                    [
                        accentBtn('🔗 Merge', 'rgba(255,160,60,0.15)', 'rgba(255,190,90,0.4)', '#ffb060', () =>
                            actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'merge')
                        ),
                        accentBtn('📦 Bodies', 'rgba(255,160,60,0.10)', 'rgba(255,190,90,0.3)', '#ffb060', () =>
                            actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'bodies')
                        ),
                        accentBtn('🔮 Shapes', 'rgba(255,160,60,0.10)', 'rgba(255,190,90,0.3)', '#ffb060', () =>
                            actionSpringyMap(SPRINGY_CFG.springForce, SPRINGY_CFG.springLen, 'shapes')
                        ),
                    ],
                )
            );

            // Shake Map card
            const shkAngRow = div('display:flex;align-items:center;gap:6px;');
            shkAngRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:48px;flex-shrink:0;', 'Angle:')
            );
            const shkAngSl = sk(el('input'));
            shkAngSl.type = 'range';
            shkAngSl.min = '-180';
            shkAngSl.max = '180';
            shkAngSl.step = '1';
            shkAngSl.value = FPATH_CFG.angle;
            shkAngSl.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shkAngSl.addEventListener('mousedown', e => e.stopPropagation());
            const shkAngInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shkAngInp.type = 'number';
            shkAngInp.value = FPATH_CFG.angle;
            shkAngInp.placeholder = 'deg';
            shkAngSl.addEventListener('input', () => {
                FPATH_CFG.angle = parseFloat(shkAngSl.value) || 0;
                shkAngInp.value = FPATH_CFG.angle;
            });
            shkAngInp.addEventListener('change', () => {
                const v = parseFloat(shkAngInp.value);
                if (!isNaN(v)) {
                    FPATH_CFG.angle = v;
                    shkAngSl.value = Math.max(-180, Math.min(180, v));
                }
            });
            shkAngRow.append(shkAngSl, shkAngInp);
            const shkLenRow = div('display:flex;align-items:center;gap:6px;');
            shkLenRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:48px;flex-shrink:0;', 'PathLen:')
            );
            const shkLenSl = sk(el('input'));
            shkLenSl.type = 'range';
            shkLenSl.min = '1';
            shkLenSl.max = '500';
            shkLenSl.step = '1';
            shkLenSl.value = FPATH_CFG.pathLen;
            shkLenSl.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shkLenSl.addEventListener('mousedown', e => e.stopPropagation());
            const shkLenInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shkLenInp.type = 'number';
            shkLenInp.value = FPATH_CFG.pathLen;
            shkLenInp.placeholder = 'units';
            shkLenSl.addEventListener('input', () => {
                FPATH_CFG.pathLen = parseFloat(shkLenSl.value) || 62.5;
                shkLenInp.value = FPATH_CFG.pathLen;
            });
            shkLenInp.addEventListener('change', () => {
                const v = parseFloat(shkLenInp.value);
                if (!isNaN(v) && v > 0) {
                    FPATH_CFG.pathLen = v;
                    shkLenSl.value = Math.max(1, Math.min(500, v));
                }
            });
            shkLenRow.append(shkLenSl, shkLenInp);
            const shkSpdRow = div('display:flex;align-items:center;gap:6px;');
            shkSpdRow.appendChild(
                el('span', 'font-size:10px;color:var(--sm-text-dim2);min-width:48px;flex-shrink:0;', 'Speed:')
            );
            const shkSpdSl = sk(el('input'));
            shkSpdSl.type = 'range';
            shkSpdSl.min = '1';
            shkSpdSl.max = '50000';
            shkSpdSl.step = '1';
            shkSpdSl.value = FPATH_CFG.moveSpeed;
            shkSpdSl.style.cssText = 'flex:1;min-width:0;cursor:pointer;accent-color:#60d0ff;';
            shkSpdSl.addEventListener('mousedown', e => e.stopPropagation());
            const shkSpdInp = sk(
                el(
                    'input',
                    CSS.inp + 'width:62px;flex-shrink:0;padding:3px 5px;box-sizing:border-box;text-align:right;'
                )
            );
            shkSpdInp.type = 'number';
            shkSpdInp.value = FPATH_CFG.moveSpeed;
            shkSpdInp.placeholder = 'speed';
            shkSpdSl.addEventListener('input', () => {
                FPATH_CFG.moveSpeed = parseFloat(shkSpdSl.value) || 250;
                shkSpdInp.value = FPATH_CFG.moveSpeed;
            });
            shkSpdInp.addEventListener('change', () => {
                const v = parseFloat(shkSpdInp.value);
                if (!isNaN(v) && v > 0) {
                    FPATH_CFG.moveSpeed = v;
                    shkSpdSl.value = Math.max(1, Math.min(50000, v));
                }
            });
            shkSpdRow.append(shkSpdSl, shkSpdInp);
            fzJointsWrap.appendChild(
                mkJointCard(
                    '#60d0ff',
                    '↔️ Follow Path',
                    [shkAngRow, shkLenRow, shkSpdRow],
                    [
                        accentBtn('🔗 Merge', 'rgba(60,200,255,0.15)', 'rgba(90,220,255,0.4)', '#60d0ff', () =>
                            actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'merge')
                        ),
                        accentBtn('📦 Bodies', 'rgba(60,200,255,0.10)', 'rgba(90,220,255,0.3)', '#60d0ff', () =>
                            actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'bodies')
                        ),
                        accentBtn('🔮 Shapes', 'rgba(60,200,255,0.10)', 'rgba(90,220,255,0.3)', '#60d0ff', () =>
                            actionFpath(FPATH_CFG.angle, FPATH_CFG.pathLen, FPATH_CFG.moveSpeed, 'shapes')
                        ),
                    ],
                )
            );

            fzbody.append(fzBuildWrap, fzJointsWrap);

            // -- HOST GUARD: only block the Apply button and joint action buttons -
            // Shape/Type/Affects/Slider buttons are local config — always allowed.
            _fzApplyBtn.dataset.hostRequired = '1';
            // Mark all buttons inside fzJointsWrap (Spin Map, Springy, Shake) as host-required
            fzJointsWrap.querySelectorAll('button').forEach(b => { b.dataset.hostRequired = '1'; });

            fzbody.addEventListener('click', e => {
                const btn = e.target.closest('button[data-host-required]');
                if (!btn) return;
                if (!_iAmHost()) {
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    showToast('⚠️ Only the host can do this', 'err');
                }
            }, true);

            sectionMap.fzjoints = fzwrap;

            // -- RENDER SECTIONS IN SAVED ORDER ---
            for (const id of secPrefs.order) {
                if (sectionMap[id]) main.appendChild(sectionMap[id]);
            }
            for (const id of Object.keys(sectionMap)) {
                if (!secPrefs.order.includes(id)) {
                    main.appendChild(sectionMap[id]);
                    secPrefs.order.push(id);
                }
            }
            enableSectionDrag(main, sectionMap);

            // -- BOTTOM CONTROL BAR ---
            const ctrl = div(
                'flex:0 0 auto;flex-shrink:0;padding:6px;border-top:1px solid rgba(255,255,255,0.1);display:flex;gap:6px;margin-bottom:5px;'
            );
            UI.invokeAllMode = false;
            const sumBtn = el('button', '');
            const renderSumBtn = isAll => {
                sumBtn.style.cssText = `cursor:pointer;border-radius:5px;flex:1;padding:8px 0;font-size:12px;font-weight:bold;transition:outline .1s,box-shadow .1s;border:1px solid ${isAll ? 'rgba(255,200,50,0.8)' : 'rgba(120,160,255,0.4)'};background:${isAll ? 'rgba(255,200,50,0.15)' : 'rgba(80,130,255,0.18)'};color:${isAll ? '#ffe080' : '#9ab8ff'};position:relative;`;
                sumBtn.innerHTML = isAll
                    ? '⚡ Summon <span style="font-size:9px;font-weight:bold;background:rgba(255,200,50,0.25);color:#ffe080;border-radius:3px;padding:1px 4px;margin-left:4px;vertical-align:middle;">ALL</span>'
                    : '⚡ Summon';
                sumBtn.style.outline = isAll ? '2px solid rgba(255,200,50,0.8)' : '';
                sumBtn.style.boxShadow = isAll ? '0 0 6px rgba(255,200,50,0.35)' : '';
            };
            renderSumBtn(false);
            sumBtn.addEventListener('click', () => {
                if (!_requireHost()) return;
                const { ok, msg } = getSpawnParams();
                if (!ok) {
                    showToast(msg, 'err');
                    return;
                }
                invokeSummon(UI.selectedObj, UI.invokeAllMode);
            });
            sumBtn.addEventListener('contextmenu', e => {
                e.preventDefault();
                if (!UI.selectedObj) {
                    showToast('⚠️ Select an object first', 'err');
                    return;
                }
                UI.invokeAllMode = !UI.invokeAllMode;
                renderSumBtn(UI.invokeAllMode);
            });
            sumBtn.addEventListener('mouseenter', () => (sumBtn.style.filter = 'brightness(1.2)'));
            sumBtn.addEventListener('mouseleave', () => (sumBtn.style.filter = ''));

            const scBtn = mkBtn(
                '⌨️',
                'cursor:pointer;border-radius:5px;border:1px solid rgba(255,200,50,0.35);padding:8px 10px;font-size:14px;background:rgba(255,200,50,0.1);',
                () => openShortcutsWindow()
            );

            ctrl.append(sumBtn, scBtn);
            wrapper.append(main, ctrl);

            // -- Self-hosted floating panel (no bonkHUD dependency) ---
            const WIN_STORE = '__SUMMON__winPos';
            const _savedWin = (() => {
                try {
                    return JSON.parse(localStorage.getItem(WIN_STORE) || 'null');
                } catch (_) {
                    return null;
                }
            })();
            const DEFAULT_W = 260,
                DEFAULT_H = 560,
                MIN_W = 300, // ← ancho mínimo de la UI; cámbialo aquí si quieres ajustarlo
                MIN_H = 300;
            const initW = _savedWin?.w ?? DEFAULT_W;
            const initH = _savedWin?.h ?? DEFAULT_H;
            const initL = _savedWin?.l ?? window.innerWidth - initW - 20;
            const initT = _savedWin?.t ?? 60;

            const floatWin = registerThemeRoot(el('div', ''));
            floatWin.id = '__SUMMON__mainWin';
            Object.assign(floatWin.style, {
                position: 'fixed',
                zIndex: '2147483645',
                left: initL + 'px',
                top: initT + 'px',
                width: initW + 'px',
                height: initH + 'px',
                minWidth: MIN_W + 'px',
                minHeight: MIN_H + 'px',
                background: 'var(--sm-panel-bg)',
                border: '1px solid var(--sm-bd1)',
                borderRadius: '10px',
                boxShadow: '0 8px 32px rgba(0,0,0,0.6)',
                display: 'flex',
                flexDirection: 'column',
                overflow: 'hidden',
                color: 'var(--sm-text)',
                fontFamily: 'sans-serif',
                resize: 'none', // we do manual resize
            });

            // -- Title bar ---
            const titleBar = el('div', '');
            Object.assign(titleBar.style, {
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                padding: '6px 10px',
                flexShrink: '0',
                background: 'rgba(43,99,81,0.55)',
                borderBottom: '1px solid var(--sm-bd0)',
                cursor: 'grab',
                userSelect: 'none',
            });
            const titleSpan = el(
                'span',
                'font-size:12px;font-weight:bold;color:#7fffd4;letter-spacing:.04em;',
                'Bonk Toolkit v' + SM_VERSION
            );
            const winCloseBtn = el(
                'button',
                'border:none;background:rgba(255,80,80,0.15);color:#ff8090;border-radius:4px;padding:1px 7px;cursor:pointer;font-size:12px;font-weight:bold;',
                '✕'
            );

            winCloseBtn.addEventListener('click', () => {
                floatWin.style.display = floatWin.style.display === 'none' ? 'flex' : 'none';
            });
            titleBar.append(titleSpan, winCloseBtn);

            // -- Drag ---
            let _drag = false,
                _dox = 0,
                _doy = 0;
            titleBar.addEventListener('mousedown', e => {
                if (e.button !== 0) return;
                _drag = true;
                _dox = e.clientX - floatWin.offsetLeft;
                _doy = e.clientY - floatWin.offsetTop;
                titleBar.style.cursor = 'grabbing';
                document.addEventListener('mousemove', _onDragMove);
                document.addEventListener('mouseup', _onDragUp);
            });
            function _onDragMove(e) {
                if (!_drag) return;
                const l = Math.max(0, Math.min(window.innerWidth - 40, e.clientX - _dox));
                const t = Math.max(0, Math.min(window.innerHeight - 40, e.clientY - _doy));
                floatWin.style.left = l + 'px';
                floatWin.style.top = t + 'px';
            }
            function _onDragUp() {
                _drag = false;
                titleBar.style.cursor = 'grab';
                document.removeEventListener('mousemove', _onDragMove);
                document.removeEventListener('mouseup', _onDragUp);
                _saveWinPos();
            }

            // -- Resize handle (bottom-right corner) ---
            const resizeHandle = el('div', '');
            Object.assign(resizeHandle.style, {
                position: 'absolute',
                right: '0',
                bottom: '0',
                width: '14px',
                height: '14px',
                cursor: 'nwse-resize',
                zIndex: '1',
            });
            // Draw a small gripper glyph
            resizeHandle.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:block;opacity:0.35;"><line x1="4" y1="13" x2="13" y2="4" stroke="white" stroke-width="1.3"/><line x1="8" y1="13" x2="13" y2="8" stroke="white" stroke-width="1.3"/><line x1="12" y1="13" x2="13" y2="12" stroke="white" stroke-width="1.3"/></svg>`;
            let _res = false,
                _rox = 0,
                _roy = 0,
                _rw = 0,
                _rh = 0;
            resizeHandle.addEventListener('mousedown', e => {
                if (e.button !== 0) return;
                e.stopPropagation();
                e.preventDefault();
                _res = true;
                _rox = e.clientX;
                _roy = e.clientY;
                _rw = floatWin.offsetWidth;
                _rh = floatWin.offsetHeight;
                document.addEventListener('mousemove', _onResizeMove);
                document.addEventListener('mouseup', _onResizeUp);
            });
            function _onResizeMove(e) {
                if (!_res) return;
                const w = Math.max(MIN_W, _rw + (e.clientX - _rox));
                const h = Math.max(MIN_H, _rh + (e.clientY - _roy));
                floatWin.style.width = w + 'px';
                floatWin.style.height = h + 'px';
            }
            function _onResizeUp() {
                _res = false;
                document.removeEventListener('mousemove', _onResizeMove);
                document.removeEventListener('mouseup', _onResizeUp);
                _saveWinPos();
            }

            function _saveWinPos() {
                try {
                    localStorage.setItem(
                        WIN_STORE,
                        JSON.stringify({
                            l: floatWin.offsetLeft,
                            t: floatWin.offsetTop,
                            w: floatWin.offsetWidth,
                            h: floatWin.offsetHeight,
                        })
                    );
                } catch (_) {}
            }

            wrapper.style.cssText = 'display:flex;flex-direction:column;flex:1 1 0;min-height:0;overflow:hidden;';
            floatWin.append(titleBar, wrapper, resizeHandle);
            document.body.appendChild(floatWin);

            // Keyboard shortcut: Ctrl+Shift+S to toggle
            document.addEventListener('keydown', e => {
                if (e.ctrlKey && e.shiftKey && e.key === 'S') {
                    floatWin.style.display = floatWin.style.display === 'none' ? 'flex' : 'none';
                }
            });

            refreshSel();
        }

        // --- SHORTCUTS WINDOW ---
        // Shortcuts are now mapped to raw chat commands (e.g. ";expall 1c", ";fly", ";bo 999")
        function openShortcutsWindow() {
            let win = document.getElementById('__SUMMON__scWin');
            if (win) {
                win.style.display = win.style.display === 'none' ? 'flex' : 'none';
                return;
            }
            win = registerThemeRoot(
                el(
                    'div',
                    'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:320px;z-index:2147483646;background:var(--sm-panel-bg);border:1px solid var(--sm-bd1);border-radius:10px;display:flex;flex-direction:column;color:var(--sm-text);font-family:sans-serif;box-shadow:0 8px 32px rgba(0,0,0,0.6);'
                )
            );
            win.id = '__SUMMON__scWin';
            const header = el(
                'div',
                'display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--sm-bd0);cursor:move;user-select:none;',
                ''
            );
            header.innerHTML = '<span style="font-size:13px;font-weight:bold;">⌨️ Shortcuts</span>';
            const closeBtn = el(
                'button',
                'border:none;background:rgba(255,80,80,0.15);color:#ff8090;border-radius:4px;padding:2px 8px;cursor:pointer;font-size:12px;',
                '✕'
            );
            closeBtn.addEventListener('click', () => (win.style.display = 'none'));
            header.appendChild(closeBtn);
            let drag = false, ox = 0, oy = 0;
            header.addEventListener('mousedown', e => {
                drag = true;
                ox = e.clientX - win.offsetLeft;
                oy = e.clientY - win.offsetTop;
            });
            window.addEventListener('mousemove', e => {
                if (!drag) return;
                win.style.left = e.clientX - ox + 'px';
                win.style.top = e.clientY - oy + 'px';
                win.style.transform = 'none';
            });
            window.addEventListener('mouseup', () => (drag = false));

            const body2 = div('padding:12px;display:flex;flex-direction:column;gap:10px;');
            const list = el('div', 'display:flex;flex-direction:column;gap:4px;max-height:200px;overflow-y:auto;');

            function renderSCList() {
                list.innerHTML = '';
                for (const [key, sc] of Object.entries(SHORTCUTS)) {
                    const r = div('display:flex;align-items:center;gap:6px;background:var(--sm-ov0);border-radius:5px;padding:5px 8px;');
                    const displayKey = sc.displayKey || key;
                    r.append(
                        el(
                            'span',
                            'font-size:11px;font-weight:bold;font-family:monospace;color:#ffe680;background:rgba(255,200,50,0.15);border:1px solid rgba(255,200,50,0.3);border-radius:4px;padding:2px 6px;white-space:nowrap;flex-shrink:0;',
                            displayKey
                        ),
                        el(
                            'span',
                            'font-size:11px;flex:1;color:#9dffa0;font-family:monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;',
                            sc.cmdText || sc.label || '?'
                        )
                    );
                    const del = el(
                        'button',
                        'cursor:pointer;border:none;background:rgba(255,80,80,0.15);color:#ff8090;border-radius:4px;padding:2px 6px;font-size:10px;font-weight:bold;flex-shrink:0;',
                        '✕'
                    );
                    del.addEventListener('click', () => {
                        delete SHORTCUTS[key];
                        saveShortcuts();
                        renderSCList();
                    });
                    r.appendChild(del);
                    list.appendChild(r);
                }
                if (!Object.keys(SHORTCUTS).length) {
                    list.appendChild(
                        el('div', 'font-size:10px;color:var(--sm-text-dim1);text-align:center;padding:10px;', 'No shortcuts set')
                    );
                }
            }
            renderSCList();

            const sep = el('div', 'border-top:1px solid var(--sm-bd0);', '');
            const addTitle = el(
                'div',
                'font-size:10px;color:var(--sm-text-dim2);text-transform:uppercase;letter-spacing:.06em;',
                'Add shortcut'
            );

            // -- Command input ---
            const cmdInp = sk(el('input', CSS.inp + 'width:100%;font-family:monospace;font-size:12px;box-sizing:border-box;'));
            cmdInp.type = 'text';
            cmdInp.placeholder = ';expall 1c  or  ;fly  or  ;bo 999';

            cmdInp.addEventListener('click', e => e.stopPropagation());
            cmdInp.addEventListener('keydown', e => { if (e.key === 'Escape') cmdInp.blur(); e.stopPropagation(); });

            // -- Key capture display ---
            const captureDisplay = el(
                'div',
                'text-align:center;padding:10px;border-radius:6px;border:2px dashed rgba(120,160,255,0.35);background:var(--sm-ov0);font-size:12px;color:var(--sm-text-dim1);cursor:pointer;user-select:none;transition:all .15s;',
                'Click here to capture key combo...'
            );
            let _capturing = false;
            let _pendingKey = null;
            let _pendingDisplay = null;

            function startCapture() {
                _capturing = true;
                _pendingKey = null;
                _pendingDisplay = null;
                captureDisplay.style.borderColor = 'rgba(120,160,255,0.8)';
                captureDisplay.style.color = 'var(--sm-text)';
                captureDisplay.textContent = '⌨️ Hold your key combo, then release...';
                captureDisplay.style.background = 'rgba(120,160,255,0.08)';
            }
            function stopCapture() {
                _capturing = false;
                captureDisplay.style.borderColor = 'rgba(120,160,255,0.35)';
                captureDisplay.style.background = 'var(--sm-ov0)';
                if (_pendingKey) {
                    captureDisplay.style.color = '#ffe680';
                    captureDisplay.textContent = '✅ ' + _pendingDisplay + ' (click + to save)';
                } else {
                    captureDisplay.style.color = 'var(--sm-text-dim1)';
                    captureDisplay.textContent = 'Click here to capture key combo...';
                }
            }

            captureDisplay.addEventListener('click', () => { if (!_capturing) startCapture(); });

            window.addEventListener('keydown', e => {
                if (!_capturing) return;
                e.stopPropagation();
                e.preventDefault();
                if (e.key === 'Escape') { stopCapture(); return; }
                if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) return;
                const parts = [];
                if (e.ctrlKey) parts.push('ctrl');
                if (e.altKey) parts.push('alt');
                if (e.shiftKey) parts.push('shift');
                const rawCode = e.code || '';
                let rawKey;
                if (rawCode.startsWith('Digit')) rawKey = rawCode.slice(5);
                else if (rawCode.startsWith('Key')) rawKey = rawCode.slice(3);
                else rawKey = (e.key || '').length === 1 ? (e.key || '').toUpperCase() : (e.key || rawCode);
                parts.push(rawKey);
                _pendingKey = parts.join('+');
                const modLabel = parts.slice(0, -1).map(m => m[0].toUpperCase() + m.slice(1)).join('+');
                _pendingDisplay = modLabel ? modLabel + '+' + rawKey : rawKey;
                captureDisplay.textContent = '⌨️ ' + _pendingDisplay + ' release to confirm';
                captureDisplay.style.color = '#9ab8ff';
            }, true);

            window.addEventListener('keyup', e => {
                if (!_capturing || !_pendingKey) return;
                if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) return;
                stopCapture();
            }, true);

            const addBtn = mkBtn(
                '➕ Save shortcut',
                'cursor:pointer;border-radius:5px;padding:6px 14px;border:1px solid rgba(120,160,255,0.4);background:rgba(120,160,255,0.15);color:#9ab8ff;font-size:12px;font-weight:bold;width:100%;',
                () => {
                    const rawCmd = cmdInp.value.trim();
                    if (!rawCmd) {
                        showToast('⚠️ Type a command first (e.g. ;fly)', 'err');
                        return;
                    }
                    if (!_pendingKey) {
                        showToast('⚠️ Click the key capture area and press a key first', 'err');
                        return;
                    }
                    // Normalize: ensure it starts with the prefix
                    const prefix = window.BonkSummonChat?.CFG?.prefix ?? ';';
                    const cmdText = rawCmd.startsWith(prefix) ? rawCmd : prefix + rawCmd;
                    SHORTCUTS[_pendingKey] = { cmdText, displayKey: _pendingDisplay };
                    saveShortcuts();
                    renderSCList();
                    cmdInp.value = '';
                    _pendingKey = null;
                    _pendingDisplay = null;
                    captureDisplay.style.color = 'var(--sm-text-dim1)';
                    captureDisplay.textContent = 'Click here to capture key combo...';
                    showToast('✅ Shortcut saved!', 'ok');
                }
            );

            body2.append(list, sep, addTitle, cmdInp, captureDisplay, addBtn);
            win.append(header, body2);
            document.body.appendChild(win);
        }

        // ===
        //  CHAT COMMANDS (fully in English)
        // ===
        (function installChatCommands() {
            const NAME_COLOR = '#9333ea',
                  TEXT_COLOR = '#db2777',
                  HEADER_COLOR = '#0284c7',
                  EXAMPLE_COLOR = '#6b7280',
                  FONT = 'Calibri,Arial';
            const CFG = { enabled: true, prefix: ';' };
            const VOTE_CFG = {
                duration: 30,
                cooldown: 20000,
                threshold: {
                    kick: 0.51,
                    ban: 0.66,
                    host: 0.51,
                    roomname: 0.51,
                    endroom: 0.75,
                    kickall: 0.75,
                    banall: 0.75,
                },
            };
            const DEFAULT_ROLES = {};
            const HAMP = new Set(),
                METZ = new Set(),
                BABY = new Set();

            // --- VOTE: cancel/recheck on player leave ---
            // Handled via WebSocket interceptor (d[0]===5)
            const _origHandleMsg5 = _gameSocket?.onmessage;
            function _onPlayerLeaveVote(leftId) {
                if (!iAmRealHost()) return;
                if (!VOTE.active) return;
                const leftName = findById(leftId)?.name;
                if (leftName && leftName === VOTE.targetName) {
                    cancelVote(`${leftName} left the room`);
                    return;
                }
                if (leftName && leftName === VOTE.initiatorName) {
                    cancelVote(`initiator ${leftName} left the room`);
                    return;
                }
                VOTE.voters.delete(leftName);
                VOTE.noVoters.delete(leftName);
                checkNoThreshold();
                if (VOTE.voters.size >= neededVotes(VOTE.type)) {
                    endVote(true);
                }
            }

            // --- BAN LIST (persists while host is in room) ---
            const BAN_LIST = new Set();
            function _onPlayerJoinBanCheck(id) {
                if (!iAmRealHost()) return;
                const name = findById(id)?.name;
                if (name && BAN_LIST.has(name)) {
                    setTimeout(() => {
                        _gameSocket?.send('42[9,{"banshortid":' + id + ',"kickonly":false}]');
                    }, 300);
                }
            }

            const ADMIN_NAMES = new Set(['Metztlin', 'Hamptonism', 'SkieMSwoiuime', 'pekenomaton']);
            const VOTE = {
                active: false,
                type: null,
                targetName: null,
                targetArg: null,
                initiatorName: null,
                voters: new Set(),
                noVoters: new Set(),
                timer: null,
                timeLeft: 0,
                lastVoteEnd: 0,
            };
            const COOLDOWN = { perPlayer: 0, global: 0, lastGlobal: 0, perPlayerMap: new Map() };

            // -- BUILDERS ---
            const playerCmd = overrides => ({ playerAction: true, allowAll: true, params: {}, ...overrides });
            const mapCmd = overrides => ({ minLevel: 3, group: '🧪 Metz', ...overrides });
            const hampCmd = overrides => ({ minLevel: 4, group: '🎀 Hamp', ...overrides });
            const hostCmd = overrides => ({ minLevel: 5, group: '👑 Role management (host only)', ...overrides });
            const infoCmd = overrides => ({ tier: null, minLevel: 1, group: 'ℹ️ Info', ...overrides });

            const SUFFIX_LIST = ['sp', 's', 'r', 'c', 'd', 'm', 'l', 't'];
            const MODE_KEYWORDS = new Set(['vel', 'velocity', 'ran', 'random', 'near', 'nearest']);

            // -- COMMAND REGISTRY ---
            const R = {
                spin: playerCmd({
                    alias: 'sp',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Spin a player. d=degrees (-360 to 360). Use "ran" for a random spin per player.',
                    modes: ['ran', 'random'],
                    params: { d: { name: 'speedDeg', min: -360, max: 360, direct: true, step: 1 } },
                    fn: (t, d, _, mode) => {
                        const isRan = mode === 'ran' || mode === 'random';
                        actionSpin(t, d ?? 360, isRan);
                    },
                }),

                dinnerbone: playerCmd({
                    alias: 'db',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Flip a player upside-down.',
                    params: {},
                    fn: t => actionDinnerbone(t),
                }),

                implode: playerCmd({
                    alias: 'imp',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Arrows inward. s=speed c=count r=radius t=duration.',
                    params: {
                        s: { name: 'speed', min: 1, max: 65, normalize: true, step: 1 },
                        c: { name: 'count', min: 1, max: 50, direct: true, step: 1 },
                        r: { name: 'radius', min: 0, max: null, direct: true, step: 0.1 },
                        t: { name: 'duration', min: 0, max: null, direct: true, step: 1 },
                    },
                    fn: (id, s, c, r, t) => actionImplode(id, s, c, r, t),
                }),

                explode: playerCmd({
                    alias: 'exp',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Arrows outward. s=speed c=count r=radius t=duration.',
                    params: {
                        s: { name: 'speed', min: 1, max: 65, normalize: true, step: 1 },
                        c: { name: 'count', min: 1, max: 50, direct: true, step: 1 },
                        r: { name: 'radius', min: 0, max: null, direct: true, step: 0.1 },
                        t: { name: 'duration', min: 0, max: null, direct: true, step: 1 },
                    },
                    fn: (id, s, c, r, t) => actionExplode(id, s, c, r, t),
                }),

                rain: playerCmd({
                    alias: null,
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Arrow rain from above. s=speed c=columns l=height t=duration.',
                    params: {
                        s: { name: 'fallSpeed', min: 1, max: 65, normalize: true, step: 1 },
                        c: { name: 'cols', min: 1, max: 50, direct: true, step: 1 },
                        l: { name: 'height', min: 0, max: null, direct: true, step: 1 },
                        t: { name: 'duration', min: 0, max: null, direct: true, step: 1 },
                    },
                    fn: (id, s, c, l, t) => actionRain(id, s, c, l, t),
                }),

                orbit: playerCmd({
                    alias: 'orb',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Orbiting arrows. s=speed c=count r=radius t=duration.',
                    params: {
                        s: { name: 'speed', min: 1, max: 65, normalize: true, step: 1 },
                        c: { name: 'count', min: 1, max: 50, direct: true, step: 1 },
                        r: { name: 'radius', min: 0, max: null, direct: true, step: 0.1 },
                        t: { name: 'duration', min: 0, max: null, direct: true, step: 1 },
                    },
                    fn: (id, s, c, r, t) => actionOrbit(id, s, c, r, t),
                }),

                shotgun: playerCmd({
                    alias: 'shot',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Arrow shotgun. s=force c=count sp=spread t=duration [vel/near/ran].',
                    modes: ['vel', 'velocity', 'near', 'nearest', 'ran', 'random'],
                    params: {
                        s: { name: 'speed', min: 1, max: 65, normalize: true, step: 1 },
                        c: { name: 'count', min: 1, max: 50, direct: true, step: 1 },
                        sp: { name: 'spread', min: 0.02, max: 1, normalize: true, step: 0.01 },
                        t: { name: 'duration', min: 0, max: null, direct: true, step: 1 },
                    },
                    fn: (id, s, c, sp, t, mode) => actionShotgun(id, s, c, sp, mode, t),
                }),

                superfling: playerCmd({
                    alias: 'sf',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Fling a player. m=force [vel/ran] or d=angle.',
                    modes: ['vel', 'velocity', 'ran', 'random'],
                    params: {
                        m: { name: 'multiplier', min: 0.1, max: 5, normalize: true, step: 0.01 },
                        d: { name: 'angle', min: -360, max: 360, direct: true, step: 1 },
                    },
                    fn: (t, m, d, mode) => actionSuperFling(t, m, mode ?? d),
                }),

                balance: {
                    alias: 'bal',
                    tier: '🍼',
                    minLevel: 2,
                    group: '🍼 Baby',
                    desc: 'Change player balance (-100 to 100). Usage: ;balance [player] <value>',
                    handler(args, sender) {
                        const level = getRoleLevel(sender);
                        if (level < 2) { cmdError('Need role 🍼 baby to use ;bal.', sender); return; }
                        const S = window.__SUMMON__;
                        if (!S?.state) { cmdError('No active state.', sender); return; }
                        // If 2 args and second is a number or 'ran', first is player, second is value
                        // If 1 arg that is a number or 'ran', target = self
                        let targetQuery = null, rawVal = null;
                        const last = (args[args.length - 1] || '').toLowerCase();
                        const isValArg = v => v === 'ran' || !isNaN(parseFloat(v));
                        if (args.length >= 2 && isValArg(last)) {
                            rawVal = last;
                            targetQuery = args.slice(0, -1).join(' ').replace(/^"|"$/g, '');
                        } else if (args.length === 1 && isValArg(last)) {
                            rawVal = last;
                        } else if (args.length === 0) {
                            cmdError('bal: usage: ;bal [player] <value|ran>', sender); return;
                        } else {
                            cmdError('bal: usage: ;bal [player] <value|ran>', sender); return;
                        }
                        const val = rawVal === 'ran' ? undefined : parseFloat(rawVal);
                        if (rawVal !== 'ran' && (isNaN(val) || val < -100 || val > 100)) {
                            cmdError('bal: value must be -100 to 100', sender); return;
                        }
                        if (targetQuery) {
                            if (targetQuery === 'all') { actionSizeAll(val); return; }
                            const res = rp(targetQuery);
                            if (res.error) { cmdError(res.error, sender); return; }
                            actionSize(res.id, val);
                        } else {
                            const su = users.find(u => u.name === sender);
                            if (!su) { cmdError('Player not found.', sender); return; }
                            actionSize(su.id, val);
                        }
                    },
                },

                bringall: playerCmd({
                    alias: 'ba',
                    tier: '🧪',
                    minLevel: 3,
                    group: '🧪 Metz',
                    allowAll: false,
                    desc: 'Teleport all players to this one.',
                    params: {},
                    fn: t => actionBringAll(t),
                }),

                repulse: playerCmd({
                    alias: 'rep',
                    tier: '🧪',
                    minLevel: 3,
                    group: '🧪 Metz',
                    allowAll: false,
                    desc: 'Push nearby players away from target.',
                    params: {},
                    fn: t => actionRepulse(t),
                }),

                attract: playerCmd({
                    alias: 'att',
                    tier: '🧪',
                    minLevel: 3,
                    group: '🧪 Metz',
                    allowAll: false,
                    desc: 'Pull nearby players toward target.',
                    params: {},
                    fn: t => actionAttract(t),
                }),

                swap: mapCmd({
                    alias: 'sw',
                    tier: '🧪',
                    desc: 'Swap two players positions. Usage: ;swap <player1> [player2]',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('swap', 'sw', s);
                            return;
                        }
                        let a, b;
                        if (!args[1]) {
                            // 1 arg: you swap with that player
                            const me = users.find(u => u.name === s);
                            if (!me) {
                                cmdError('Could not find you in the room.', s);
                                return;
                            }
                            b = rp(args[0]);
                            if (b.error) {
                                cmdError(b.error, s);
                                return;
                            }
                            a = { id: me.id, name: me.name };
                        } else {
                            a = rp(args[0]);
                            b = rp(args[1]);
                            if (a.error) {
                                cmdError(a.error, s);
                                return;
                            }
                            if (b.error) {
                                cmdError(b.error, s);
                                return;
                            }
                        }
                        actionSwap(a.id, b.id);
                    },
                }),

                swapall: mapCmd({
                    alias: 'sa',
                    tier: '🧪',
                    desc: 'Shuffle positions of all alive players.',
                    handler(args, s) {
                        actionSwapAll();
                    },
                }),

                swapgeo: mapCmd({
                    alias: 'sg',
                    tier: '🧪',
                    desc: 'Randomly swap the shapes/geometry between all bodies in the current map.',
                    handler(args, s) {
                        actionSwapShapes();
                    },
                }),

                bringto: mapCmd({
                    alias: 'bt',
                    tier: '🧪',
                    desc: 'Teleport p1 to p2 (revives if dead). Usage: ;bringto <player1> [player2]',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('bringto', 'bt', s);
                            return;
                        }
                        let a, b;
                        if (!args[1]) {
                            // 1 arg: bring yourself to that player
                            const me = users.find(u => u.name === s);
                            if (!me) {
                                cmdError('Could not find you in the room.', s);
                                return;
                            }
                            b = rp(args[0]);
                            if (b.error) {
                                cmdError(b.error, s);
                                return;
                            }
                            a = { id: me.id, name: me.name };
                        } else {
                            a = rp(args[0]);
                            b = rp(args[1]);
                            if (a.error) {
                                cmdError(a.error, s);
                                return;
                            }
                            if (b.error) {
                                cmdError(b.error, s);
                                return;
                            }
                        }
                        actionBringTo(a.id, b.id);
                    },
                }),

                move: mapCmd({
                    alias: 'mv',
                    tier: '🧪',
                    desc: 'Move a player by an X Y offset relative to their current position (editor units). Dead players respawn at X Y. Omit the player name to move yourself. Usage: ;move [player] <x> <y>',
                    handler(args, s) {
                        if (args.length < 2) {
                            cmdUsage('move', 'mv', s);
                            return;
                        }
                        const t = resolveXYTarget(args, s);
                        if (t.error) {
                            cmdError(t.error, s);
                            return;
                        }
                        actionMovePlayer(t.id, t.x, t.y);
                    },
                }),

                teleport: mapCmd({
                    alias: 'tp',
                    tier: '🧪',
                    desc: 'Teleport a player to absolute X Y map coords. Dead players get respawned. Omit the player name to teleport yourself. Usage: ;tp [player] <x> <y>',
                    handler(args, s) {
                        if (args.length < 2) {
                            cmdUsage('teleport', 'tp', s);
                            return;
                        }
                        const t = resolveXYTarget(args, s);
                        if (t.error) {
                            cmdError(t.error, s);
                            return;
                        }
                        actionTeleportPlayer(t.id, t.x, t.y);
                    },
                }),

                bouncy: mapCmd({
                    alias: 'bo',
                    tier: '🧪',
                    desc: 'Set restitution on objects. Usage: ;bo <value> [p]',
                    handler(args, s) {
                        const r = _parsePctValueArgs(args);
                        if (r.error) {
                            cmdError(`bouncy: ${r.error}`, s);
                            return;
                        }
                        actionBouncyMode(r.value, r.pct);
                    },
                }),

                friction: mapCmd({
                    alias: 'fr',
                    tier: '🧪',
                    desc: 'Set friction on objects. Usage: ;fr <value> [p]',
                    handler(args, s) {
                        const r = _parsePctValueArgs(args);
                        if (r.error) {
                            cmdError(`friction: ${r.error}`, s);
                            return;
                        }
                        actionFrictionMode(r.value, r.pct);
                    },
                }),

                density: mapCmd({
                    alias: 'den',
                    tier: '🧪',
                    desc: 'Set density on objects. Usage: ;den <value> [p]',
                    handler(args, s) {
                        const r = _parsePctValueArgs(args);
                        if (r.error) {
                            cmdError(`density: ${r.error}`, s);
                            return;
                        }
                        actionDensityMode(r.value, r.pct);
                    },
                }),

                rotate: mapCmd({
                    alias: 'rot',
                    tier: '🧪',
                    desc: 'Rotate the whole map. Usage: ;rotate <degrees>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v)) {
                            cmdUsage('rotate', 'rot', s);
                            return;
                        }
                        actionRotateMode(v);
                    },
                }),

                spinmap: mapCmd({
                    alias: 'sm',
                    tier: '🧪',
                    desc: 'Make the whole map spin. Usage: ;spinmap <force> [merge/bodies/shapes]  (+ = CCW, - = CW)',
                    handler(args, s) {
                        let force = SPIN_CFG.speedDeg;
                        const md = ['merge', 'bodies', 'shapes'].find(m => args.includes(m)) || 'merge';
                        for (const tok of args) {
                            const tl = tok.toLowerCase();
                            if (tl.endsWith('m')) { const n = parseFloat(tok); if (!isNaN(n)) { force = n; continue; } }
                            if (!['merge','bodies','shapes'].includes(tl)) { const n = parseFloat(tok); if (!isNaN(n)) force = n; }
                        }
                        if (force === 0 || isNaN(force)) {
                            cmdError('spinmap: usage: ;spinmap <force> [merge/bodies/shapes]  e.g. ;sm 90 or ;sm -180m shapes', s);
                            return;
                        }
                        SPIN_CFG.speedDeg = force;
                        actionSpinMap(force, md);
                    },
                }),

                springymap: mapCmd({
                    alias: 'spring',
                    tier: '🧪',
                    desc: 'Spring-joint map (lsj). Usage: ;springymap [m] [l] [merge/bodies/shapes]',
                    handler(args, s) {
                        let force = SPRINGY_CFG.springForce, len = SPRINGY_CFG.springLen;
                        const md = ['merge', 'bodies', 'shapes'].find(m => args.includes(m)) || 'merge';
                        for (const tok of args) {
                            const tl = tok.toLowerCase();
                            if (['merge','bodies','shapes'].includes(tl)) continue;
                            if (tl.endsWith('m')) { const n = parseFloat(tok); if (!isNaN(n)) { force = n; continue; } }
                            if (tl.endsWith('l')) { const n = parseFloat(tok); if (!isNaN(n)) { len = n; continue; } }
                            const n = parseFloat(tok); if (!isNaN(n)) force = n;
                        }
                        if (force <= 0) { cmdError('springymap: force must be > 0', s); return; }
                        if (len < 0) { cmdError('springymap: length must be ≥ 0', s); return; }
                        SPRINGY_CFG.springForce = force;
                        SPRINGY_CFG.springLen = len;
                        actionSpringyMap(force, len, md);
                    },
                }),

                fpath: mapCmd({
                    alias: 'fp',
                    tier: '🧪',
                    desc: 'Path-joint map (lpj). Usage: ;fpath [d] [l] [m] [merge/bodies/shapes]',
                    handler(args, s) {
                        let ang = FPATH_CFG.angle, plen = FPATH_CFG.pathLen, spd = FPATH_CFG.moveSpeed;
                        const md = ['merge', 'bodies', 'shapes'].find(m => args.includes(m)) || 'merge';
                        for (const tok of args) {
                            const tl = tok.toLowerCase();
                            if (['merge','bodies','shapes'].includes(tl)) continue;
                            if (tl.endsWith('d')) { const n = parseFloat(tok); if (!isNaN(n)) { ang = n; continue; } }
                            if (tl.endsWith('l')) { const n = parseFloat(tok); if (!isNaN(n)) { plen = n; continue; } }
                            if (tl.endsWith('m')) { const n = parseFloat(tok); if (!isNaN(n)) { spd = n; continue; } }
                            const n = parseFloat(tok); if (!isNaN(n)) ang = n;
                        }
                        if (isNaN(plen) || plen <= 0) { cmdError('fpath: length must be > 0', s); return; }
                        if (isNaN(spd) || spd <= 0) { cmdError('fpath: force must be > 0', s); return; }
                        FPATH_CFG.angle = ang;
                        FPATH_CFG.pathLen = plen;
                        FPATH_CFG.moveSpeed = spd;
                        actionFpath(ang, plen, spd, md);
                    },
                }),

                setppm: mapCmd({
                    alias: 'ppm',
                    tier: '🧪',
                    desc: 'Set pixels-per-meter of the active map. Usage: ;ppm <1-1000>',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('setppm', 'ppm', s);
                            return;
                        }
                        actionSetPPM(args[0]);
                    },
                }),

                stretch: mapCmd({
                    alias: 'str',
                    tier: '🧪',
                    desc: 'Stretch the map by X Y scale factors (>0). Usage: ;stretch <x> [y]',
                    handler(args, s) {
                        const x = parseFloat(args[0]);
                        if (isNaN(x) || x <= 0) {
                            cmdError('Stretch: X must be > 0', s);
                            return;
                        }
                        const y = args[1] != null ? parseFloat(args[1]) : x;
                        if (isNaN(y) || y <= 0) {
                            cmdError('Stretch: Y must be > 0', s);
                            return;
                        }
                        actionStretchMode(x, y);
                    },
                }),

                translate: mapCmd({
                    alias: 'tr',
                    tier: '🧪',
                    desc: 'Offset the whole map in editor units. Usage: ;translate <x> <y>',
                    handler(args, s) {
                        const x = parseFloat(args[0]);
                        const y = parseFloat(args[1]);
                        if (isNaN(x) || isNaN(y)) {
                            cmdError('translate: usage: ;translate <x> <y>', s);
                            return;
                        }
                        actionTranslateMap(x, y);
                    },
                }),

                color: mapCmd({
                    alias: 'col',
                    tier: '🧪',
                    desc: 'Recolor fixtures. Usage: ;color [ran/invert/gray]',
                    handler(args, s) {
                        const MODES = ['random', 'invert', 'gray'];
                        const mode = (args[0] || 'random').toLowerCase();
                        if (!MODES.includes(mode)) {
                            cmdUsage('color', 'col', s);
                            return;
                        }
                        actionRainbowMap(mode, 180);
                    },
                }),

                hue: mapCmd({
                    alias: null,
                    tier: '🧪',
                    desc: 'Shift hue of all fixtures. Usage: ;hue <degrees>',
                    handler(args, s) {
                        const val = parseFloat(args[0]);
                        if (isNaN(val)) {
                            cmdError('hue: usage: ;hue <degrees>  e.g. ;hue 90', s);
                            return;
                        }
                        actionRainbowMap('hue', val);
                    },
                }),

                bright: mapCmd({
                    alias: null,
                    tier: '🧪',
                    desc: 'Multiply brightness of all fixtures. Usage: ;bright <multiplier>',
                    handler(args, s) {
                        const val = parseFloat(args[0]);
                        if (isNaN(val)) {
                            cmdError('bright: usage: ;bright <multiplier>  e.g. ;bright 1.5', s);
                            return;
                        }
                        actionRainbowMap('bright', val);
                    },
                }),

                randmap: mapCmd({
                    alias: 'rm',
                    tier: '🧪',
                    desc: 'Randomly shift each map body.',
                    handler: (args, s) => actionRandomizeMap(),
                }),

                wobbly: mapCmd({
                    alias: 'wb',
                    tier: '🧪',
                    desc: 'Deform map shapes. Usage: ;wobbly <value>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 1 || v > 100) {
                            cmdError('wobbly: value must be 1-100', s);
                            return;
                        }
                        actionWobblyMode(v);
                    },
                }),

                inflate: mapCmd({
                    alias: 'inf',
                    tier: '🧪',
                    desc: 'Scale all map shapes by a factor. 1.0 = no change, 2.0 = double size. Usage: ;inflate <factor>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v <= 0) {
                            cmdError('inflate: usage: ;inflate <factor> (e.g. 1.5)', s);
                            return;
                        }
                        actionInflateMode(v);
                    },
                }),

                palette: mapCmd({
                    alias: 'pal',
                    tier: '🧪',
                    desc: 'Randomly shuffle the color palette across all map fixtures.',
                    handler: (args, s) => actionPaletteShuffle(),
                }),

                nophysics: mapCmd({
                    alias: 'np',
                    tier: '🧪',
                    desc: 'Set % of fixtures to no-physics. Usage: ;nophysics <p>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 1 || v > 100) {
                            cmdError('nophysics: value must be 1-100', s);
                            return;
                        }
                        actionNoPhysicsMode(v);
                    },
                }),

                death: mapCmd({
                    alias: 'dt',
                    tier: '🧪',
                    desc: 'Set % of fixtures to instant-death. Usage: ;death <p>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 1 || v > 100) {
                            cmdError('death: usage: ;death <1-100>', s);
                            return;
                        }
                        actionDeathMode(v);
                    },
                }),

                nocollisions: mapCmd({
                    alias: 'nc',
                    tier: '🧪',
                    desc: 'Toggle No Collisions. Usage: ;nc [on/off]',
                    handler(args, s) {
                        const a = (args[0] || '').toLowerCase();
                        const S = window.__SUMMON__;
                        if (!S.state?.ms) {
                            cmdError('No map settings', s);
                            return;
                        }
                        const on = a === 'on' ? true : a === 'off' ? false : !S.state.ms['nc'];
                        actionSetMs('nc', on);
                    },
                }),

                respawn: mapCmd({
                    alias: 'rod',
                    tier: '🧪',
                    desc: 'Toggle Respawn on Death. Usage: ;respawn [on/off]',
                    handler(args, s) {
                        const a = (args[0] || '').toLowerCase();
                        const S = window.__SUMMON__;
                        if (!S.state?.ms) {
                            cmdError('No map settings', s);
                            return;
                        }
                        const on = a === 'on' ? true : a === 'off' ? false : !S.state.ms['re'];
                        actionSetMs('re', on);
                    },
                }),

                fly: mapCmd({
                    alias: null,
                    tier: '🧪',
                    desc: 'Toggle Fly Mode. Usage: ;fly [on/off]',
                    handler(args, s) {
                        const a = (args[0] || '').toLowerCase();
                        const S = window.__SUMMON__;
                        if (!S.state?.ms) {
                            cmdError('No map settings', s);
                            return;
                        }
                        const on = a === 'on' ? true : a === 'off' ? false : !S.state.ms['fl'];
                        actionSetMs('fl', on);
                    },
                }),

                complexphysics: mapCmd({
                    alias: 'cpx',
                    tier: '🧪',
                    desc: 'Toggle Complex Physics. Usage: ;cpx [on/off]',
                    handler(args, s) {
                        const a = (args[0] || '').toLowerCase();
                        const S = window.__SUMMON__;
                        if (!S.state?.ms) {
                            cmdError('No map settings', s);
                            return;
                        }
                        const on = a === 'on' ? true : a === 'off' ? false : S.state.ms['pq'] !== 2;
                        actionSetMs('pq', on);
                    },
                }),

                stationary: mapCmd({
                    alias: 'st',
                    tier: '🧪',
                    desc: 'Set % of objects to stationary. Usage: ;stationary <p>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 1 || v > 100) {
                            cmdError('stationary: value must be 1-100', s);
                            return;
                        }
                        actionStationaryMode(v);
                    },
                }),

                freemoving: mapCmd({
                    alias: 'fm',
                    tier: '🧪',
                    desc: 'Set % of objects to free-moving. Usage: ;freemoving <p>',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 1 || v > 100) {
                            cmdError('freemoving: value must be 1-100', s);
                            return;
                        }
                        actionFreeMovingMode(v);
                    },
                }),

                rewind: mapCmd({
                    alias: 'rw',
                    tier: '🧪',
                    desc: 'Rewind game state by seconds (max 20s). Usage: ;rewind [seconds]',
                    handler(args, s) {
                        const secs = parseInt(args[0]) || 5;
                        if (secs < 1 || secs > 20) {
                            cmdError('Rewind: seconds must be 1-20', s);
                            return;
                        }
                        actionRewind(secs);
                    },
                }),

                freeze: mapCmd({
                    alias: 'frz',
                    tier: '🧪',
                    desc: 'Freeze physics for everyone (pause). No args needed.',
                    handler(args, s) {
                        const setFrozen = window.__SUMMON__?._setFrozen;
                        if (!setFrozen) {
                            cmdError('Freeze: UI not ready yet', s);
                            return;
                        }
                        setFrozen(true);
                    },
                }),

                unfreeze: mapCmd({
                    alias: 'unfrz',
                    tier: '🧪',
                    desc: 'Unfreeze physics for everyone (resume). No args needed.',
                    handler(args, s) {
                        const setFrozen = window.__SUMMON__?._setFrozen;
                        if (!setFrozen) {
                            cmdError('Freeze: UI not ready yet', s);
                            return;
                        }
                        setFrozen(false);
                    },
                }),

                copypos: mapCmd({
                    alias: 'cp',
                    tier: '🧪',
                    desc: 'Copy current player positions.',
                    handler: (args, s) => actionCopyPos(),
                }),

                pastepos: mapCmd({
                    alias: 'pp',
                    tier: '🧪',
                    desc: 'Restore copied positions.',
                    handler: (args, s) => actionPastePos(),
                }),

                score: mapCmd({
                    alias: 'sc',
                    tier: '🧪',
                    desc: 'Set a player score. Usage: ;score <player> <value>  |  ;score all <value> sets everyone.',
                    handler(args, s) {
                        if (!args[0]) { cmdUsage('score', 'sc', s); return; }
                        if ((args[0] || '').toLowerCase() === 'all') {
                            if (args[1] == null) { cmdError('Usage: ;score all <value>', s); return; }
                            const S = window.__SUMMON__;
                            if (!S.state) { cmdError('⚠️ No state', s); return; }
                            if (!S.state.scores?.length) { cmdError('⚠️ No scores in this mode', s); return; }
                            const val = isNaN(Number(args[1])) ? args[1] : Number(args[1]);
                            for (let i = 0; i < S.state.scores.length; i++) S.state.scores[i] = val;
                            startGame(S.state, true);
                            if (isMe(s)) localNote(`🏆 All scores set to ${val}`);
                            return;
                        }
                        if (args[1] == null) { cmdUsage('score', 'sc', s); return; }
                        const r = rp(args[0]);
                        if (r.error) { cmdError(r.error, s); return; }
                        actionSetScore(r.id, args[1]);
                    },
                }),
                size: mapCmd({
                    alias: 'sz',
                    tier: '🧪',
                    desc: 'Resize player relative to map (via PPM, map rescaled to compensate). Usage: ;size <factor>  (e.g. 2.0 = double)',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('size', 'sz', s);
                            return;
                        }
                        actionResizeViaPPM(args[0]);
                    },
                }),

                // --- 🎀 Hamp ---
                kick: hampCmd({
                    alias: null,
                    tier: '🎀',
                    desc: 'Kick a player immediately. Usage: ;kick <player>',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('kick', null, s);
                            return;
                        }
                        const r = rp(args[0]);
                        if (r.error) {
                            cmdError(r.error, s);
                            return;
                        }
                        hampKick(s, r.name, false);
                    },
                }),

                ban: hampCmd({
                    alias: null,
                    tier: '🎀',
                    desc: 'Ban a player immediately. Usage: ;ban <player>',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('ban', null, s);
                            return;
                        }
                        const r = rp(args[0]);
                        if (r.error) {
                            cmdError(r.error, s);
                            return;
                        }
                        hampKick(s, r.name, true);
                    },
                }),

                bans: hampCmd({
                    alias: null,
                    tier: '🎀',
                    desc: 'List all banned players.',
                    handler(args, s) {
                        if (!isMe(s)) return;
                        if (!BAN_LIST.size) {
                            localNote('🚫 Ban list is empty.');
                            return;
                        }
                        localNote(`🚫 Banned: ${[...BAN_LIST].join(', ')}`);
                    },
                }),

                givehost: hostCmd({
                    alias: 'gh',
                    tier: '👑',
                    desc: 'Transfer host to a player. Usage: ;givehost <player>',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdUsage('givehost', 'gh', s);
                            return;
                        }
                        const r = rp(args[0], true);
                        if (r.error) {
                            cmdError(r.error, s);
                            return;
                        }
                        hampGiveHost(s, r.name);
                    },
                }),

                start: hampCmd({
                    alias: null,
                    tier: '🎀',
                    desc: 'Start the game.',
                    handler: (args, s) => hampStart(s),
                }),

                mode: hampCmd({
                    alias: 'mo',
                    tier: '🎀',
                    desc: 'Change game mode. Usage: ;mode [cl/ar/da/gr/vt]',
                    handler: (args, s) => hampMode(s, args[0]),
                }),

                hamp: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Give hamp role. Usage: ;hamp <player>',
                    handler: (args, s) => manageRole('hamp', false, args, s),
                }),
                unhamp: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Remove hamp role. Usage: ;unhamp <player>',
                    handler: (args, s) => manageRole('hamp', true, args, s),
                }),
                metz: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Give metz role. Usage: ;metz <player>',
                    handler: (args, s) => manageRole('metz', false, args, s),
                }),
                unmetz: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Remove metz role. Usage: ;unmetz <player>',
                    handler: (args, s) => manageRole('metz', true, args, s),
                }),
                baby: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Give baby role. Usage: ;baby <player>',
                    handler: (args, s) => manageRole('baby', false, args, s),
                }),
                unbaby: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Remove baby role. Usage: ;unbaby <player>',
                    handler: (args, s) => manageRole('baby', true, args, s),
                }),

                roles: {
                    alias: null,
                    tier: '👑',
                    minLevel: 1,
                    group: '👑 Role management (host only)',
                    desc: 'Show assigned roles (public).',
                    handler: (args, s) => showRoles(s),
                },

                cmdson: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Enable command system.',
                    handler(args, s) {
                        CFG.enabled = true;
                        if (isMe(s)) localNote('✅ Chat commands enabled.');
                    },
                }),

                cmdsoff: hostCmd({
                    alias: null,
                    tier: '👑',
                    desc: 'Disable command system.',
                    handler(args, s) {
                        CFG.enabled = false;
                        if (isMe(s)) localNote('🔴 Chat commands disabled.');
                    },
                }),

                cooldown: hostCmd({
                    alias: 'cd',
                    tier: '👑',
                    desc: 'Per-player cooldown in seconds. Usage: ;cooldown <seconds>  (0 = off)',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 0) {
                            cmdError('Usage: ;cooldown <seconds> (0 = disable)', s);
                            return;
                        }
                        COOLDOWN.perPlayer = v;
                        COOLDOWN.perPlayerMap.clear();
                        if (isMe(s))
                            localNote(v === 0 ? '✅ Per-player cooldown disabled.' : `✅ Per-player cooldown: ${v}s.`);
                    },
                }),

                gcooldown: hostCmd({
                    alias: 'gcd',
                    tier: '👑',
                    desc: 'Global cooldown in seconds. Usage: ;gcooldown <seconds>  (0 = off)',
                    handler(args, s) {
                        const v = parseFloat(args[0]);
                        if (isNaN(v) || v < 0) {
                            cmdError('Usage: ;gcooldown <seconds> (0 = disable)', s);
                            return;
                        }
                        COOLDOWN.global = v;
                        COOLDOWN.lastGlobal = 0;
                        if (isMe(s)) localNote(v === 0 ? '✅ Global cooldown disabled.' : `✅ Global cooldown: ${v}s.`);
                    },
                }),

                help: infoCmd({
                    alias: 'cmds',
                    desc: 'Show command list.',
                    localOnly: true,
                    handler: (args, s) => showHelpList(s),
                }),
                objects: infoCmd({
                    alias: null,
                    desc: 'List summonable objects.',
                    localOnly: true,
                    handler: (args, s) => showObjectsList(s),
                }),
                suffixes: infoCmd({
                    alias: null,
                    desc: 'Show all argument suffixes.',
                    localOnly: true,
                    handler: (args, s) => showSuffixes(s),
                }),

                advhelp: infoCmd({
                    alias: null,
                    desc: 'Detailed info on a command.',
                    localOnly: true,
                    handler(args, s) {
                        if (!args[0]) {
                            cmdError('Usage: ;advhelp <command>', s);
                            return;
                        }
                        showAdvHelp(args[0].toLowerCase(), s);
                    },
                }),

                zhelp: infoCmd({ alias: null, desc: 'Public command summary.', handler: (args, s) => zHelp(s) }),
                zobjects: infoCmd({ alias: null, desc: 'Public summonable list.', handler: (args, s) => zObjects(s) }),
                zsuffixes: infoCmd({ alias: null, desc: 'Public suffix reference.', handler: () => zSuffixes() }),
                zadvhelp: infoCmd({
                    alias: null,
                    desc: 'Public detailed command help.',
                    handler(args, s) {
                        if (!args[0]) {
                            cmdError('Usage: ;zadvhelp <command>', s);
                            return;
                        }
                        zAdvHelp(args[0].toLowerCase(), s);
                    },
                }),

                votekick: {
                    alias: 'vk',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to kick a player.',
                    isVote: true,
                },
                voteban: {
                    alias: 'vb',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to ban a player.',
                    isVote: true,
                },
                votehost: {
                    alias: 'vh',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to give host.',
                    isVote: true,
                },
                voteroomname: {
                    alias: 'vrn',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to rename room.',
                    isVote: true,
                },
                voteendroom: {
                    alias: 'ver',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to close room.',
                    isVote: true,
                },
                votekickall: {
                    alias: 'vka',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to kick everyone.',
                    isVote: true,
                },
                votebanall: {
                    alias: 'vba',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote to ban everyone.',
                    isVote: true,
                },
                yes: {
                    alias: 'y',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote yes on current vote.',
                    isVote: true,
                },
                no: {
                    alias: 'n',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Vote no on current vote.',
                    isVote: true,
                },
                votecancel: {
                    alias: 'vc',
                    tier: null,
                    minLevel: 1,
                    group: '🗳️ Votes (all)',
                    desc: 'Cancel current vote (initiator only).',
                    isVote: true,
                },
            };

            // Auto-build aliases
            const ALIASES = {};
            for (const [k, d] of Object.entries(R)) {
                if (d.alias) ALIASES[d.alias] = k;
            }
            // Compat: scoreall / scall both route to score
            ALIASES['scoreall'] = 'score';
            ALIASES['scall'] = 'score';
            const resolveCmd = cmd => ALIASES[cmd] || cmd;

            const VOTE_MAP = {
                votekick: 'kick',
                vk: 'kick',
                voteban: 'ban',
                vb: 'ban',
                votehost: 'host',
                vh: 'host',
                voteroomname: 'roomname',
                vrn: 'roomname',
                voteendroom: 'endroom',
                ver: 'endroom',
                votekickall: 'kickall',
                vka: 'kickall',
                votebanall: 'banall',
                vba: 'banall',
            };

            // -- PERMISSIONS ---
            const iAmRealHost = () => myId !== -1 && myId === hostId;
            const getMyName = () => findById(myId)?.name ?? null;
            const isGuest = n => /^Guest\s*\d*$/i.test(n);
            const isMe = n => n === getMyName();
            const isAdmin = n => !!n && !isGuest(n) && ADMIN_NAMES.has(n);
            const getRoleLevel = n => {
                if (isAdmin(n)) return 6;
                if (iAmRealHost() && n === getMyName()) return 5;
                if (HAMP.has(n)) return 4;
                if (METZ.has(n)) return 3;
                if (BABY.has(n)) return 2;
                return 1;
            };

            const senderIsHost = s => (iAmRealHost() && s === getMyName()) || (iAmRealHost() && isAdmin(s));
            const senderIsOwner = s => isAdmin(s); // kept name for internal refs
            const isDefaultRole = n => Object.prototype.hasOwnProperty.call(DEFAULT_ROLES, n) && !isGuest(n);

            function applyDefaultRoles() {
                if (!iAmRealHost()) return;
                for (const u of users) {
                    if (!u.name || isGuest(u.name)) continue;
                    const role = DEFAULT_ROLES[u.name];
                    if (!role) continue;
                    if (role === 'hamp' && !HAMP.has(u.name)) {
                        HAMP.add(u.name);
                        METZ.delete(u.name);
                        BABY.delete(u.name);
                    } else if (role === 'metz' && !METZ.has(u.name) && !HAMP.has(u.name)) {
                        METZ.add(u.name);
                        BABY.delete(u.name);
                    } else if (role === 'baby' && !BABY.has(u.name) && !METZ.has(u.name) && !HAMP.has(u.name)) {
                        BABY.add(u.name);
                    }
                }
            }
            setInterval(() => {
                if (iAmRealHost()) applyDefaultRoles();
            }, 3000);

            // -- PARAM RESOLUTION ---
            const normalizeParam = (v, p) => (p.direct ? v : p.min + ((v - 1) / 99) * (p.max - p.min));
            function resolveParam(params, suffix, rawValue, noLimit) {
                const p = params[suffix];
                if (!p) return { ok: false, error: `Suffix '${suffix}' not valid here` };
                // noLimit: hamp/host/owner/self — skip max cap on direct params
                if (noLimit && p.direct) {
                    if (rawValue < p.min) return { ok: false, error: `${p.name} must be >= ${p.min} (got: ${rawValue})` };
                    return { ok: true, realValue: rawValue };
                }
                if (p.max === null) {
                    if (rawValue <= 0) return { ok: false, error: `${p.name} must be > 0` };
                    return { ok: true, realValue: rawValue };
                }
                if (p.direct) {
                    if (rawValue < p.min || rawValue > p.max)
                        return {
                            ok: false,
                            error: `${p.name} must be between ${p.min} and ${p.max} (got: ${rawValue})`,
                        };
                    return { ok: true, realValue: rawValue };
                }
                if (rawValue < 1 || rawValue > 100)
                    return { ok: false, error: `${p.name} must be between 1 and 100 (got: ${rawValue})` };
                return { ok: true, realValue: normalizeParam(rawValue, p) };
            }
            function parseToken(token) {
                const t = (token || '').trim();
                if (!t) return null;
                const tl = t.toLowerCase();
                if (MODE_KEYWORDS.has(tl)) return { keyword: tl };
                for (const suf of SUFFIX_LIST) {
                    if (tl.endsWith(suf)) {
                        const v = parseFloat(t.slice(0, -suf.length));
                        if (!isNaN(v)) return { suffix: suf, value: v };
                    }
                }
                return null;
            }

            // -- USAGE STRING ---
            function buildUsage(key) {
                const d = R[key];
                if (!d) return `;${key}`;
                const base = `;${key}`;
                if (d.playerAction) {
                    const parts = Object.keys(d.params || {}).map(s => `[${s}]`);
                    const SKIP_MODES = new Set(['velocity', 'random', 'nearest']);
                    const shortModes = (d.modes || []).filter(m => !SKIP_MODES.has(m));
                    if (shortModes.length) parts.push(`[${shortModes.join('/')}]`);
                    return `${base} [player] ${parts.join(' ')}`.trim();
                }
                const m = (d.desc || '').match(/Usage:\s*(.+?)(?:\s{2,}|$)/);
                if (m) return m[1].trim();
                return base;
            }

            // -- COOLDOWN ---
            function checkCooldown(s) {
                if (getRoleLevel(s) >= 4) return true;
                const now = Date.now();
                if (COOLDOWN.global > 0) {
                    const e = (now - COOLDOWN.lastGlobal) / 1000;
                    if (e < COOLDOWN.global) {
                        cmdError(`⏳ Global cooldown: wait ${(COOLDOWN.global - e).toFixed(1)}s`, s);
                        return false;
                    }
                }
                if (COOLDOWN.perPlayer > 0) {
                    const last = COOLDOWN.perPlayerMap.get(s) || 0,
                        e = (now - last) / 1000;
                    if (e < COOLDOWN.perPlayer) {
                        cmdError(`⏳ ${s}: wait ${(COOLDOWN.perPlayer - e).toFixed(1)}s`, s);
                        return false;
                    }
                }
                return true;
            }
            function stampCooldown(s) {
                if (getRoleLevel(s) >= 4) return;
                const now = Date.now();
                COOLDOWN.lastGlobal = now;
                COOLDOWN.perPlayerMap.set(s, now);
            }

            // -- LOCAL CHAT HELPERS ---
            function appendLocalLine(buildContent) {
                for (const cid of ['newbonklobby_chat_content', 'ingamechatcontent']) {
                    const container = document.getElementById(cid);
                    if (!container) continue;
                    const atBottom = container.clientHeight + container.scrollTop >= container.scrollHeight - 1;
                    const row2 = document.createElement('div');
                    row2.style.fontFamily = FONT;
                    const span = document.createElement('span');
                    span.style.cssText = `color:${TEXT_COLOR};font-family:${FONT};`;
                    buildContent(span);
                    row2.appendChild(span);
                    row2.style.parsed = true;
                    container.appendChild(row2);
                    if (atBottom) container.scrollTop = container.scrollHeight;
                }
            }
            const localNote = text => appendLocalLine(s => (s.textContent = text));
            const appendHeaderLine = text =>
                appendLocalLine(s => {
                    s.style.cssText = `color:${HEADER_COLOR};font-weight:bold;font-family:${FONT};`;
                    s.textContent = text;
                });
            const cmdError = (msg, s) => {
                if (!isMe(s)) return;
                showToast(msg, 'err');
                localNote(`⚠️ ${msg}`);
            };
            const cmdUsage = (k, alias, s) => cmdError(`Usage: ${buildUsage(k)}`, s);

            // -- PUBLIC CHAT ---
            function sendPublicChat(text) {
                for (const id of ['newbonklobby_chat_input', 'ingamechatinputtext']) {
                    const input = document.getElementById(id);
                    if (!input) continue;
                    const s = window.getComputedStyle(input);
                    if (s.display === 'none' || s.visibility === 'hidden') continue;
                    const ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
                    ns.call(input, text);
                    input.dispatchEvent(new Event('input', { bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 13, which: 13, bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 13, which: 13, bubbles: true }));
                    return;
                }
                localNote(`[PUBLIC — chat unavailable] ${text}`);
            }

            // -- RESOLVE PLAYER ---
            const rp = (query, excludeSelf = false) => {
                if (!query?.trim()) return { error: 'No player specified.' };
                const ql = query.trim().toLowerCase(),
                    pool = excludeSelf ? users.filter(u => u.id !== myId) : users;
                const matches = pool.filter(u => u.name.toLowerCase().includes(ql));
                if (!matches.length) return { error: `Nobody matches "${query}".` };
                if (matches.length > 1) {
                    const exact = matches.find(u => u.name.toLowerCase() === ql);
                    if (exact) return { id: exact.id, name: exact.name };
                    return { error: `"${query}" is ambiguous: ${matches.map(u => u.name).join(', ')}. Try more letters.` };
                }
                return { id: matches[0].id, name: matches[0].name };
            };

            // -- RESOLVE MOVE/TELEPORT TARGET ---
            // ;move/;tp <player> <x> <y>  → move that player
            // ;move/;tp <x> <y>           → move the sender (no name needed)
            function resolveXYTarget(args, sender) {
                if (args.length >= 3) {
                    const pr = rp(args[0]);
                    if (pr.error) return { error: pr.error };
                    return { id: pr.id, x: args[1], y: args[2] };
                }
                if (args.length === 2) {
                    const su = users.find(u => u.name === sender);
                    if (!su) return { error: 'Could not resolve your own player.' };
                    return { id: su.id, x: args[0], y: args[1] };
                }
                return { error: 'Missing X/Y coordinates.' };
            }

            function _parsePctValueArgs(args) {
                let value = null,
                    pct = 100;
                for (const tok of args) {
                    const t = (tok || '').trim();
                    if (!t) continue;
                    // number with p suffix = percent
                    const mp = t.match(/^(-?[\d.]+)p$/i);
                    if (mp) {
                        const num = parseFloat(mp[1]);
                        if (isNaN(num)) return { error: `Invalid number in "${t}"` };
                        if (num < 1 || num > 100) return { error: '[p] percent must be 1-100' };
                        pct = num;
                        continue;
                    }
                    // bare number = value
                    if (/^-?[\d.]+$/.test(t)) {
                        value = parseFloat(t);
                        continue;
                    }
                    return { error: `Unrecognized token "${t}" — use e.g. ;bo 999 or ;bo 999 40p` };
                }
                if (value === null) return { error: 'Missing value (e.g. ;bo 999 or ;bo 999 40p)' };
                return { value, pct };
            }

            // -- PLAYER ACTION RUNNER ---
            function runPlayerAction(key, tokens, sender, isAll) {
                const def = R[key];
                if (!def?.playerAction) return false;
                const level = getRoleLevel(sender);
                if (level < def.minLevel) {
                    const labs = ['', '', '🍼 baby', '🧪 metz', '🎀 hamp', 'host', 'admin'];
                    cmdError(`Need role ${labs[def.minLevel] || String(def.minLevel)} to use ;${key}.`, sender);
                    return true;
                }
                const params = def.params || {};
                const isBaby = level === 2;
                let targetQuery = null,
                    startIdx = 0;
                if (tokens.length > 0 && !parseToken(tokens[0])) {
                    targetQuery = tokens[0].replace(/^"|"$/g, '');
                    startIdx = 1;
                }
                const parsedSuf = {};
                let modeKeyword = null;
                const unknown = [];
                for (let i = startIdx; i < tokens.length; i++) {
                    const p = parseToken(tokens[i]);
                    if (!p) {
                        unknown.push(tokens[i]);
                        continue;
                    }
                    if (p.keyword) {
                        if (def.modes?.includes(p.keyword)) {
                            modeKeyword = p.keyword;
                            continue;
                        }
                        unknown.push(tokens[i]);
                        continue;
                    }
                    const noLimit = level >= 4 || isMe(sender);
                    const res = resolveParam(params, p.suffix, p.value, noLimit);
                    if (!res.ok) {
                        cmdError(res.error, sender);
                        return true;
                    }
                    parsedSuf[p.suffix] = res.realValue;
                }
                if (unknown.length > 0) {
                    cmdError(`Unknown token: "${unknown[0]}", use suffixes (${SUFFIX_LIST.join(', ')})`, sender);
                    return true;
                }
                const S = window.__SUMMON__;
                if (!S?.state) {
                    cmdError('No active state.', sender);
                    return true;
                }
                let targetId;
                if (isAll) {
                    if (isBaby) {
                        cmdError('🍼 Baby cannot use the all variant.', sender);
                        return true;
                    }
                    if (!def.allowAll) {
                        cmdError(`;${key}all is not allowed.`, sender);
                        return true;
                    }
                    targetId = 'all';
                } else if (isBaby) {
                    const su = users.find(u => u.name === sender);
                    if (!su) {
                        cmdError('Player not found.', sender);
                        return true;
                    }
                    if (targetQuery && targetQuery.toLowerCase() !== sender.toLowerCase()) {
                        cmdError('🍼 Baby can only target themselves.', sender);
                        return true;
                    }
                    targetId = su.id;
                } else if (targetQuery) {
                    const res = rp(targetQuery);
                    if (res.error) {
                        cmdError(res.error, sender);
                        return true;
                    }
                    targetId = res.id;
                } else {
                    const su = users.find(u => u.name === sender);
                    if (!su) {
                        cmdError('Player not found.', sender);
                        return true;
                    }
                    targetId = su.id;
                }
                if (!checkCooldown(sender)) return true;
                const sufOrder = Object.keys(params).filter(k => k !== '_pct');
                def.fn(targetId, ...sufOrder.map(k => parsedSuf[k]), modeKeyword);
                stampCooldown(sender);
                return true;
            }

            // -- SUMMON RUNNER ---

            function runSummonCmd(cmd, tokens, sender, isAll) {
                const obj = summonables[cmd];
                if (!obj) return false;
                const level = getRoleLevel(sender);
                if (level < 2) return false;
                const S = window.__SUMMON__;
                if (!S?.state) {
                    cmdError('No active state.', sender);
                    return true;
                }
                const isBaby = level === 2;

                let count = 1;
                const rest = [];
                for (const tok of tokens) {
                    const cm = (tok || '').match(/^(\d+)c$/i);
                    if (cm) {
                        // hamp (level 4), real host (level 5), owner (level 6), or self have no count cap
                        const noLimit = level >= 4 || isMe(sender);
                        const maxCount = noLimit ? Infinity : 50;
                        count = Math.max(1, Math.min(maxCount, parseInt(cm[1], 10)));
                        continue;
                    }
                    rest.push(tok);
                }

                let coordX = null,
                    coordY = null;
                if (rest.length >= 2 && /^-?[\d.]+$/.test(rest[0]) && /^-?[\d.]+$/.test(rest[1])) {
                    const ppm = S.state.physics?.ppm ?? 30;
                    coordX = (parseFloat(rest[0]) + 365) / ppm;
                    coordY = (parseFloat(rest[1]) + 250) / ppm;
                    rest.splice(0, 2);
                }

                if (isAll) {
                    if (isBaby) {
                        cmdError('🍼 Baby cannot use the all variant.', sender);
                        return true;
                    }
                    const targets = S.state.discs.filter(Boolean).map(d => ({ x: d.x, y: d.y }));
                    if (!targets.length) {
                        cmdError('No alive players.', sender);
                        return true;
                    }
                    if (!checkCooldown(sender)) return true;
                    const rep = [];
                    for (const t of targets) for (let i = 0; i < count; i++) rep.push(t);
                    summonCore(cmd, rep);
                    stampCooldown(sender);
                    return true;
                }

                let baseX, baseY;
                if (coordX !== null && coordY !== null) {
                    if (isBaby) {
                        cmdError('🍼 Baby cannot summon at coordinates.', sender);
                        return true;
                    }
                    baseX = coordX;
                    baseY = coordY;
                } else {
                    let targetQuery = null;
                    if (rest.length > 0 && !parseToken(rest[0])) targetQuery = rest[0].replace(/^"|"$/g, '');
                    let targetDisc;
                    if (isBaby) {
                        const su = users.find(u => u.name === sender);
                        if (!su) {
                            cmdError('Player not found.', sender);
                            return true;
                        }
                        if (targetQuery && targetQuery.toLowerCase() !== sender.toLowerCase()) {
                            cmdError('🍼 Baby can only summon on themselves.', sender);
                            return true;
                        }
                        targetDisc = S.state.discs[su.id];
                    } else if (targetQuery) {
                        const res = rp(targetQuery);
                        if (res.error) {
                            cmdError(res.error, sender);
                            return true;
                        }
                        targetDisc = S.state.discs[res.id];
                    } else {
                        const su = users.find(u => u.name === sender);
                        if (!su) {
                            cmdError('Player not found.', sender);
                            return true;
                        }
                        targetDisc = S.state.discs[su.id];
                    }
                    if (!targetDisc) {
                        cmdError('Target disc not active, start a game', sender);
                        return true;
                    }
                    baseX = targetDisc.x;
                    baseY = targetDisc.y;
                }
                if (!checkCooldown(sender)) return true;
                const targets = [];
                for (let i = 0; i < count; i++) targets.push({ x: baseX, y: baseY });
                summonCore(cmd, targets);
                stampCooldown(sender);
                return true;
            }

            // -- VOTES ---
            const neededVotes = type => Math.ceil(Math.max(0, users.length - 1) * (VOTE_CFG.threshold[type] ?? 0.51));
            // No-votes: if the number of nays would make yes impossible, cancel
            function checkNoThreshold() {
                if (!VOTE.active) return;
                const eligible = Math.max(0, users.length - 1);
                const needed = neededVotes(VOTE.type);
                const remaining = eligible - VOTE.voters.size - VOTE.noVoters.size;
                // even if everyone remaining votes yes, they still can't reach needed
                if (VOTE.voters.size + remaining < needed) {
                    cancelVote('not enough yes votes possible');
                }
            }
            function cancelVote(reason) {
                if (!VOTE.active) return;
                clearInterval(VOTE.timer);
                VOTE.active = false;
                VOTE.voters.clear();
                VOTE.noVoters.clear();
                VOTE.lastVoteEnd = Date.now();
                sendPublicChat(`❌ Vote cancelled: ${reason}`);
            }
            function executeVoteAction() {
                const { type, targetName, targetArg } = VOTE;
                const getID = n => users.find(u => u.name === n)?.id ?? -1;
                const sendBan = (id, kickonly) =>
                    _gameSocket?.send('42[9,{"banshortid":' + id + ',"kickonly":' + kickonly + '}]');
                if (type === 'endroom') {
                    for (const u of users) {
                        if (u.id !== myId)
                            try {
                                sendBan(u.id, false);
                            } catch (e) {}
                    }
                    setTimeout(() => window.location.reload(), 1500);
                } else if (type === 'kick' || type === 'ban') {
                    const id = getID(targetName);
                    if (id === -1) return;
                    sendBan(id, type === 'kick');
                } else if (type === 'kickall' || type === 'banall') {
                    for (const u of users) {
                        if (u.id === myId) continue;
                        try {
                            sendBan(u.id, type === 'kickall');
                        } catch (e) {}
                    }
                } else if (type === 'host') {
                    const id = getID(targetName);
                    if (id === -1) return;
                    if (_gameSocket?.readyState === 1) {
                        // packet 34 — transfer host (packet 1 is wrong and does nothing)
                        _gameSocket.send('42[34,{"id":' + id + '}]');
                    }
                    HAMP.clear();
                    METZ.clear();
                    BABY.clear();
                } else if (type === 'roomname') sendPublicChat(`/roomname "${targetArg}"`);
            }
            function endVote(passed) {
                clearInterval(VOTE.timer);
                const { type, targetName, targetArg, voters } = VOTE;
                const eligible = Math.max(0, users.length - 1),
                    needed = neededVotes(type);
                VOTE.active = false;
                VOTE.lastVoteEnd = Date.now();
                VOTE.voters.clear();
                VOTE.noVoters.clear();
                const label =
                    {
                        kick: 'VOTEKICK',
                        ban: 'VOTEBAN',
                        host: 'VOTEHOST',
                        roomname: 'VOTEROOMNAME',
                        endroom: 'VOTEENDROOM',
                        kickall: 'VOTEKICKALL',
                        banall: 'VOTEBANALL',
                    }[type] || type.toUpperCase();
                if (passed) {
                    const tStr = ['endroom', 'kickall', 'banall'].includes(type)
                        ? 'everyone'
                        : type === 'roomname'
                          ? `"${targetArg}"`
                          : targetName;
                    sendPublicChat(`✅ ${label} PASSED (${voters.size}/${eligible}), applying to ${tStr}...`);
                    setTimeout(executeVoteAction, 800);
                } else sendPublicChat(`❌ ${label} FAILED: ${voters.size}/${eligible} votes (needed ${needed})`);
            }
            function startVote(initiator, type, targetName, targetArg) {
                if (!iAmRealHost()) return;
                const now = Date.now(),
                    eligible = Math.max(0, users.length - 1),
                    needed = neededVotes(type);
                if (VOTE.active) {
                    localNote('⚠️ A vote is already active.');
                    return;
                }
                if (users.length < 3) {
                    localNote('⚠️ Need at least 3 players to vote.');
                    return;
                }
                if (now - VOTE.lastVoteEnd < VOTE_CFG.cooldown) {
                    localNote(
                        `⚠️ Vote cooldown: wait ${Math.ceil((VOTE_CFG.cooldown - (now - VOTE.lastVoteEnd)) / 1000)}s.`
                    );
                    return;
                }
                VOTE.active = true;
                VOTE.type = type;
                VOTE.targetName = targetName || null;
                VOTE.targetArg = targetArg || null;
                VOTE.initiatorName = initiator;
                VOTE.voters = new Set([initiator]);
                VOTE.timeLeft = VOTE_CFG.duration;
                const label =
                    {
                        kick: 'VOTEKICK',
                        ban: 'VOTEBAN',
                        host: 'VOTEHOST',
                        roomname: 'VOTEROOMNAME',
                        endroom: 'VOTEENDROOM',
                        kickall: 'VOTEKICKALL',
                        banall: 'VOTEBANALL',
                    }[type] || type.toUpperCase();
                const tStr = ['endroom', 'kickall', 'banall'].includes(type)
                    ? ''
                    : type === 'roomname'
                      ? ` → "${targetArg}"`
                      : ` against ${targetName}`;
                sendPublicChat(
                    `🗳️ ${initiator} started ${label}${tStr}, type ;y to vote yes or ;n to vote no (${needed}/${eligible} needed, ${VOTE_CFG.duration}s)`
                );
                VOTE.timer = setInterval(() => {
                    VOTE.timeLeft--;
                    if (VOTE.timeLeft <= 0) {
                        clearInterval(VOTE.timer);
                        if (VOTE.active) endVote(false);
                    }
                }, 1000);
            }
            function castVote(voter) {
                if (!VOTE.active) {
                    if (isMe(voter) && iAmRealHost()) localNote('⚠️ No active vote.');
                    return;
                }
                if (!['endroom', 'kickall', 'banall'].includes(VOTE.type) && voter === VOTE.targetName) {
                    if (isMe(voter)) localNote("⚠️ Target can't vote.");
                    return;
                }
                if (VOTE.voters.has(voter) || VOTE.noVoters.has(voter)) {
                    if (isMe(voter)) localNote('⚠️ Already voted.');
                    return;
                }
                VOTE.voters.add(voter);
                const eligible = Math.max(0, users.length - 1);
                sendPublicChat(`✔️ ${voter} voted yes (${VOTE.voters.size}/${eligible})`);
                if (VOTE.voters.size >= neededVotes(VOTE.type)) endVote(true);
            }
            function castNoVote(voter) {
                if (!VOTE.active) {
                    if (isMe(voter) && iAmRealHost()) localNote('⚠️ No active vote.');
                    return;
                }
                if (voter === VOTE.initiatorName) {
                    if (isMe(voter)) localNote("⚠️ Initiator can't vote no.");
                    return;
                }
                if (VOTE.voters.has(voter) || VOTE.noVoters.has(voter)) {
                    if (isMe(voter)) localNote('⚠️ Already voted.');
                    return;
                }
                VOTE.noVoters.add(voter);
                const eligible = Math.max(0, users.length - 1);
                sendPublicChat(`✖️ ${voter} voted no (${VOTE.noVoters.size} nay / ${eligible})`);
                checkNoThreshold();
            }

            // -- ROLE MANAGEMENT ---
            function manageRole(role, revoke, args, sender) {
                if (!senderIsHost(sender)) {
                    cmdError('Only the real host can manage roles.', sender);
                    return;
                }
                if (!args[0]) {
                    cmdError(`Usage: ;${revoke ? 'un' : ''}${role} "player"`, sender);
                    return;
                }
                const res = rp(args[0]);
                if (res.error) {
                    cmdError(res.error, sender);
                    return;
                }
                const name = res.name;
                if (isGuest(name)) {
                    cmdError('Guests cannot receive roles.', sender);
                    return;
                }
                if (isDefaultRole(name)) {
                    cmdError(`${name}'s role is permanent.`, sender);
                    return;
                }
                const sets = { baby: BABY, metz: METZ, hamp: HAMP };
                const ic = role === 'hamp' ? '🎀' : role === 'metz' ? '🧪' : '🍼';
                if (revoke) {
                    if (!sets[role]?.has(name)) {
                        cmdError(`${name} does not have the ${role} role.`, sender);
                        return;
                    }
                    sets[role].delete(name);
                    sendPublicChat(`${ic} ${name}'s ${role} role has been removed.`);
                } else {
                    HAMP.delete(name);
                    METZ.delete(name);
                    BABY.delete(name);
                    sets[role].add(name);
                    const roleLabel = { baby: 'Baby', metz: 'Metz', hamp: 'Hamp' }[role];
                    const perms = {
                        baby: 'can only act on themselves',
                        metz: 'can act on anyone + change physics',
                        hamp: 'full access commands',
                    }[role];
                    sendPublicChat(`${ic} ${name} is now ${roleLabel} (${perms}).`);
                }
            }

            // -- HAMP ROOM COMMANDS ---
            function hampKick(sender, targetName, ban) {
                if (!iAmRealHost()) return;
                const id = users.find(u => u.name === targetName)?.id ?? -1;
                if (id === -1) {
                    cmdError(`${targetName} is not in the room.`, sender);
                    return;
                }
                if (ban) BAN_LIST.add(targetName);
                setTimeout(() => _gameSocket?.send('42[9,{"banshortid":' + id + ',"kickonly":' + !ban + '}]'), 400);
            }
            function hampGiveHost(sender, targetName) {
                if (!iAmRealHost()) return;
                const id = users.find(u => u.name === targetName)?.id ?? -1;
                if (id === -1) {
                    cmdError(`${targetName} is not in the room.`, sender);
                    return;
                }
                if (!_gameSocket || _gameSocket.readyState !== 1) {
                    cmdError('Socket not ready.', sender);
                    return;
                }
                // packet 34 — transfer host (packet 1 is wrong and does nothing)
                _gameSocket.send('42[34,{"id":' + id + '}]');
                HAMP.clear();
                METZ.clear();
                BABY.clear();
            }
            function hampMode(sender, modeArg) {
                if (!iAmRealHost()) return;
                const modeMap = {
                    classic: 'b',
                    cl: 'b',
                    clas: 'b',
                    arrows: 'ar',
                    ar: 'ar',
                    arr: 'ar',
                    deatharrows: 'ard',
                    da: 'ard',
                    ard: 'ard',
                    dart: 'ard',
                    grapple: 'sp',
                    gr: 'sp',
                    grap: 'sp',
                    sp: 'sp',
                    vtol: 'v',
                    vt: 'v',
                };
                const code = modeMap[(modeArg || '').toLowerCase().trim()];
                if (!code) {
                    cmdError('Valid modes: cl/ar/da/gr/vt', sender);
                    return;
                }
                if (_gameSocket?.readyState === 1) _gameSocket.send(`42[20,{"ga":"b","mo":"${code}"}]`);
                // Simulate the inbound broadcast so our own listener updates _currentMode immediately
                try { _gameSocket?.onmessage?.({ data: `42[26,"b","${code}"]` }); } catch (_) {}
                // Restart so new mode takes effect immediately
                setTimeout(() => {
                    const S = window.__SUMMON__;
                    if (S?.state) startGame(S.state, true);
                }, 200);
            }
            function hampStart(sender) {
                if (!iAmRealHost()) return;
                sendPublicChat(`▶️ ${sender} started the game`);
                const startBtn =
                    document.getElementById('newbonklobby_startbutton') || document.querySelector('[id*="start"]');
                if (startBtn) {
                    startBtn.click();
                    return;
                }
                for (const id of ['newbonklobby_chat_input', 'ingamechatinputtext']) {
                    const input = document.getElementById(id);
                    if (!input) continue;
                    const s = window.getComputedStyle(input);
                    if (s.display === 'none' || s.visibility === 'hidden') continue;
                    const ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
                    ns.call(input, '/start');
                    input.dispatchEvent(new Event('input', { bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 13, which: 13, bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 13, which: 13, bubbles: true }));
                    return;
                }
            }

            // -- INFO / HELP ---
            function showRoles(sender) {
                if (!iAmRealHost()) {
                    if (isMe(sender)) localNote('⚠️ Only the host can show roles.');
                    return;
                }
                const inRoom = new Set(users.map(u => u.name));
                const parts = [];
                if ([...HAMP].filter(n => inRoom.has(n) && !isDefaultRole(n)).length)
                    parts.push(`🎀 ${[...HAMP].filter(n => inRoom.has(n) && !isDefaultRole(n)).join(', ')}`);
                if ([...METZ].filter(n => inRoom.has(n) && !isDefaultRole(n)).length)
                    parts.push(`🧪 ${[...METZ].filter(n => inRoom.has(n) && !isDefaultRole(n)).join(', ')}`);
                if ([...BABY].filter(n => inRoom.has(n) && !isDefaultRole(n)).length)
                    parts.push(`🍼 ${[...BABY].filter(n => inRoom.has(n) && !isDefaultRole(n)).join(', ')}`);
                sendPublicChat(parts.length ? `👑 Roles: ${parts.join(' | ')}` : '👑 No roles assigned.');
            }
            function showHelpList(sender) {
                if (!isMe(sender)) return;
                const groups = {};
                for (const [k, d] of Object.entries(R)) {
                    const g = d.group || 'Other';
                    if (!groups[g]) groups[g] = [];
                    groups[g].push([k, d]);
                }
                for (const [gName, entries] of Object.entries(groups)) {
                    appendHeaderLine(`${gName}:`);
                    for (const [k, d] of entries) {
                        appendLocalLine(span => {
                            const cs = document.createElement('span');
                            cs.style.cssText = `color:${NAME_COLOR};text-decoration:underline;cursor:pointer;font-weight:bold;`;
                            cs.textContent = `;${k}`;
                            cs.addEventListener('click', () => showAdvHelp(k, sender));
                            span.appendChild(cs);
                            if (d.alias) {
                                const as = document.createElement('span');
                                as.style.cssText = `color:${EXAMPLE_COLOR};`;
                                as.textContent = ` (;${d.alias})`;
                                span.appendChild(as);
                            }
                            const us = document.createElement('span');
                            us.style.cssText = `color:${TEXT_COLOR};`;
                            const _raw = buildUsage(k);
                            const usg = _raw.replace(/^;[\w]+\s*/, '').trim();
                            us.textContent = usg ? `  ${usg}` : '';
                            span.appendChild(us);
                        });
                    }
                }
                localNote('Click a command for details. ;suffixes for suffix reference');
            }
            function showAdvHelp(key, sender) {
                if (!isMe(sender)) return;
                const rc = resolveCmd(key),
                    def = R[rc];
                if (!def) {
                    cmdError(`Unknown command: ;${key}`, sender);
                    return;
                }
                appendHeaderLine(`📖 ;${rc}${def.alias ? `  (;${def.alias})` : ''}:`);
                appendLocalLine(s => {
                    s.style.color = TEXT_COLOR;
                    s.textContent = `Usage: ${buildUsage(rc)}`;
                });
                appendLocalLine(s => {
                    s.style.cssText = `color:${EXAMPLE_COLOR};`;
                    s.textContent = def.desc || '';
                });
                const params = def.params || {};
                const entries = Object.entries(params).filter(([k]) => k !== '_pct');
                if (entries.length) {
                    appendLocalLine(s => {
                        s.style.cssText = `color:${EXAMPLE_COLOR};`;
                        s.textContent =
                            'Args: ' +
                            entries
                                .map(([suf, p]) =>
                                    p.max === null
                                        ? `[${suf}] ${p.name} (>0)`
                                        : p.direct
                                          ? `[${suf}] ${p.name} (${p.min}-${p.max})`
                                          : `[${suf}] ${p.name} (1-100)`
                                )
                                .join(' | ');
                    });
                }
                if (def.modes?.length) {
                    const SKIP_MODES = new Set(['velocity', 'random', 'nearest']);
                    const sm = def.modes.filter(m => !SKIP_MODES.has(m));
                    if (sm.length)
                        appendLocalLine(s => {
                            s.style.cssText = `color:${EXAMPLE_COLOR};`;
                            s.textContent = `Modes: ${sm.join('/')}`;
                        });
                }
            }
            function showSuffixes(sender) {
                if (!isMe(sender)) return;
                appendHeaderLine('🔤 Argument suffixes:');
                const DOCS = [
                    { suf: 's', desc: 'speed (1 to 100)' },
                    { suf: 'c', desc: 'count (1 to 100)' },
                    { suf: 'r', desc: 'radius (any value > 0)' },
                    { suf: 'd', desc: 'degrees (-360 to 360)' },
                    { suf: 'sp', desc: 'spread (1 to 100)' },
                    { suf: 'm', desc: 'force multiplier (1 to 100)' },
                    { suf: 'l', desc: 'length / height (any value > 0)' },
                    { suf: 't', desc: 'duration in frames, any value > 0 (arrows only)' },
                    { suf: 'p', desc: '% of objects (1 to 100)' },
                ];
                for (const { suf, desc } of DOCS) {
                    appendLocalLine(span => {
                        const ss = document.createElement('span');
                        ss.style.cssText = `color:${NAME_COLOR};font-family:monospace;font-weight:bold;`;
                        ss.textContent = `[${suf}]`;
                        const ds = document.createElement('span');
                        ds.style.cssText = `color:${EXAMPLE_COLOR};`;
                        ds.textContent = ` ${desc}`;
                        span.append(ss, ds);
                    });
                }
                localNote('Examples: ;rain 80s 20c 3r | ;sf -90d 50m | ;bo 1.5 40p');
            }
            function showObjectsList(sender) {
                if (!isMe(sender)) return;
                const sections = [
                    ['Objects', typeof SUMMONABLES_OBJ !== 'undefined' ? SUMMONABLES_OBJ : null],
                    ['Skins', typeof SUMMONABLES_SKIN !== 'undefined' ? SUMMONABLES_SKIN : null],
                    ['Custom', typeof SUMMONABLES_CUSTOM !== 'undefined' ? SUMMONABLES_CUSTOM : null],
                ];
                for (const [title, dict] of sections) {
                    if (!dict || !Object.keys(dict).length) continue;
                    appendHeaderLine(`${title}:`);
                    for (const [key, obj] of Object.entries(dict)) {
                        appendLocalLine(span => {
                            span.appendChild(document.createTextNode(`${obj?.icon || '✨'} `));
                            const a = document.createElement('a');
                            a.href = 'javascript:void(0)';
                            a.textContent = key;
                            a.style.cssText = `color:${NAME_COLOR};text-decoration:underline;cursor:pointer;`;
                            a.addEventListener('click', () =>
                                localNote(`;${key} [player] or ;${key}all for all (metz+)`)
                            );
                            span.appendChild(a);
                        });
                    }
                }
            }

            // -- z PUBLIC SUMMARIES ---
            function zHelp(s) {
                const baby = Object.entries(R)
                    .filter(([, d]) => d.minLevel === 2)
                    .map(([k, d]) => d.alias || k);
                const metz = Object.entries(R)
                    .filter(([, d]) => d.minLevel === 3)
                    .map(([k, d]) => d.alias || k);
                const hamp = Object.entries(R)
                    .filter(([, d]) => d.minLevel === 4)
                    .map(([k, d]) => d.alias || k);
                sendPublicChat(
                    `📋 Prefix ; | 🍼 ${baby.join(' ')} | 🧪 ${metz.join(' ')} | 🎀 ${hamp.join(' ')} | ;zsuffixes and ;zadvhelp <cmd> for details`
                );
            }
            function zObjects(s) {
                const safe = obj => (typeof obj !== 'undefined' ? obj : {});
                const allKeys = [
                    ...Object.keys(safe(typeof SUMMONABLES_OBJ !== 'undefined' ? SUMMONABLES_OBJ : {})),
                    ...Object.keys(safe(typeof SUMMONABLES_SKIN !== 'undefined' ? SUMMONABLES_SKIN : {})),
                    ...Object.keys(safe(typeof SUMMONABLES_CUSTOM !== 'undefined' ? SUMMONABLES_CUSTOM : {})),
                ];
                if (!allKeys.length) {
                    sendPublicChat('📦 No summonable objects available.');
                    return;
                }
                sendPublicChat(`📦 Summonables: ${allKeys.join(', ')} use ;name [player] to summon`);
            }
            function zSuffixes() {
                sendPublicChat(
                    '🔤 Suffixes: [s]=speed 1-100 [c]=count 1-100 [r]=radius >0 [d]=degrees -360 to 360 [sp]=spread 1-100 [m]=force 1-100 [p]=percent 1-100 | ;exp 50s 30c 5r'
                );
            }
            function zAdvHelp(cmd, sender) {
                const rc = resolveCmd(cmd),
                    def = R[rc];
                if (!def) {
                    cmdError(`Unknown command: ;${cmd}`, sender);
                    return;
                }
                const entries = Object.entries(def.params || {}).filter(([k]) => k !== '_pct');
                const paramsStr = entries.length
                    ? ' | ' +
                      entries
                          .map(([suf, p]) =>
                              p.max === null
                                  ? `${suf}=>radius>0`
                                  : p.direct
                                    ? `${suf}=>${p.min}-${p.max}`
                                    : `${suf}=>1-100`
                          )
                          .join(' ')
                    : '';
                const modesStr = def.modes?.length ? ` [${def.modes.join('|')}]` : '';
                sendPublicChat(
                    `📖 ;${rc}${def.alias ? `(;${def.alias})` : ''}: ${buildUsage(rc)}${paramsStr}${modesStr}: ${def.desc}`
                );
            }

            // -- DISPATCH ---
            function runChatCommand(cmd, args, sender) {
                const rc = resolveCmd(cmd);
                let baseCmd = rc,
                    isAll = false;

                if (!R[rc] && !summonables[rc] && rc.endsWith('all') && rc.length > 3) {
                    const mbRaw = rc.slice(0, -3);
                    const mb = resolveCmd(mbRaw);
                    if (R[mb]?.playerAction || summonables[mb] || summonables[mbRaw]) {
                        baseCmd = summonables[mbRaw] && !summonables[mb] ? mbRaw : mb;
                        isAll = true;
                    }
                }
                const entry = R[rc];
                if (entry?.localOnly) {
                    if (isMe(sender)) entry.handler(args, sender);
                    return;
                }
                if (['help', 'objects', 'advhelp', 'suffixes', 'roles'].includes(rc)) {
                    R[rc]?.handler(args, sender);
                    return;
                }

                if (['zhelp', 'zobjects', 'zsuffixes', 'zadvhelp'].includes(rc)) {
                    if (iAmRealHost()) R[rc]?.handler(args, sender);
                    return;
                }
                if (VOTE_MAP[cmd] !== undefined || VOTE_MAP[rc] !== undefined) {
                    const vtype = VOTE_MAP[cmd] ?? VOTE_MAP[rc];
                    if (['endroom', 'kickall', 'banall'].includes(vtype)) {
                        startVote(sender, vtype, null, null);
                        return;
                    }
                    if (vtype === 'roomname') {
                        if (!args[0]) {
                            cmdError('Usage: ;voteroomname "name"', sender);
                            return;
                        }
                        startVote(sender, vtype, null, args.join(' '));
                        return;
                    }
                    if (!args[0]) {
                        cmdError(`Usage: ;${cmd} "player"`, sender);
                        return;
                    }
                    const res = rp(args[0]);
                    if (res.error) {
                        cmdError(res.error, sender);
                        return;
                    }
                    startVote(sender, vtype, res.name, null);
                    return;
                }
                if (cmd === 'yes' || cmd === 'y' || cmd === 'vote') {
                    castVote(sender);
                    return;
                }
                if (cmd === 'no' || cmd === 'n') {
                    castNoVote(sender);
                    return;
                }
                if (cmd === 'votecancel' || cmd === 'vc') {
                    if (!VOTE.active) {
                        if (isMe(sender) && iAmRealHost()) localNote('⚠️ No active vote.');
                        return;
                    }
                    if (sender !== VOTE.initiatorName && !senderIsHost(sender)) {
                        if (isMe(sender)) localNote('⚠️ Only the initiator or host can cancel.');
                        return;
                    }
                    cancelVote('cancelled by ' + sender);
                    return;
                }
                if (!iAmRealHost()) return;
                if (!CFG.enabled && rc !== 'cmdson') return;
                if (entry?.minLevel === 5) {
                    if (!senderIsHost(sender)) {
                        cmdError('Only the real host can do this.', sender);
                        return;
                    }
                    entry.handler(args, sender);
                    return;
                }
                const level = getRoleLevel(sender);
                if (level < 2) return;
                if (isAll) {
                    if (runPlayerAction(baseCmd, args, sender, true)) return;
                    if (runSummonCmd(baseCmd, args, sender, true)) return;
                    if (isMe(sender)) cmdError(`Unknown command: ;${cmd}`, sender);
                    return;
                }
                if (runPlayerAction(rc, args, sender, false)) return;
                if (runSummonCmd(rc, args, sender, false)) return;
                if (entry?.handler) {
                    if (level < (entry.minLevel || 1)) {
                        const labs = ['', '', '🍼 baby', '🧪 metz', '🎀 hamp', 'host', 'admin'];
                        cmdError(`Need role ${labs[entry.minLevel] || String(entry.minLevel)} to use ;${rc}.`, sender);
                        return;
                    }
                    entry.handler(args, sender);
                    return;
                }
                if (isMe(sender)) cmdError(`Unknown command: ;${cmd} (try ;help)`, sender);
            }

            // -- INPUT HOOK ---
            const LOCAL_ONLY = new Set(
                Object.entries(R)
                    .filter(([, d]) => d.localOnly)
                    .map(([k]) => k)
                    .concat([
                        'help',
                        'cmds',
                        'objects',
                        'advhelp',
                        'suffixes',
                        'cmdson',
                        'cmdsoff',
                        'cooldown',
                        'cd',
                        'gcooldown',
                        'gcd',
                        'hamp',
                        'unhamp',
                        'metz',
                        'unmetz',
                        'baby',
                        'unbaby',
                    ])
            );
            function tokenize(str) {
                const out = [],
                    re = /"([^"]*)"|(\S+)/g;
                let m;
                while ((m = re.exec(str)) !== null) out.push(m[1] !== undefined ? m[1] : m[2]);
                return out;
            }

            function hookChatInput(input) {
                if (!input || input._summonHooked) return;
                input._summonHooked = true;
                input.addEventListener(
                    'keydown',
                    function (e) {
                        if (e.keyCode !== 13 && e.which !== 13) return;
                        const text = input.value.trim();
                        if (!text.startsWith(CFG.prefix)) return;
                        const toks = tokenize(text.slice(CFG.prefix.length).trim()),
                            cmd = (toks[0] || '').toLowerCase(),
                            args = toks.slice(1);
                        if (!cmd) return;
                        const myName = getMyName();
                        if (!myName) return;
                        const rc = resolveCmd(cmd);
                        if (LOCAL_ONLY.has(rc) || LOCAL_ONLY.has(cmd)) {
                            e.preventDefault();
                            e.stopImmediatePropagation();
                            const ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
                            ns.call(input, '');
                            input.dispatchEvent(new Event('input', { bubbles: true }));
                            if (isMe(myName))
                                try {
                                    runChatCommand(cmd, args, myName);
                                } catch (ex) {
                                    console.error(ex);
                                }
                        }
                    },
                    true
                );
            }
            setInterval(() => {
                ['newbonklobby_chat_input', 'ingamechatinputtext'].forEach(id => {
                    const e = document.getElementById(id);
                    if (e) hookChatInput(e);
                });
            }, 1000);

            function extractChatLine(node) {
                const full = node.textContent || '';
                if (!full) return null;
                let senderName = null;
                for (const span of node.querySelectorAll?.('span') || []) {
                    if (span.style?.color && span.textContent.trim()) {
                        senderName = span.textContent.trim();
                        break;
                    }
                }
                const ci = full.indexOf(': ');
                if (ci === -1) return null;
                if (!senderName) senderName = full.slice(0, ci).trim();
                return { senderName, msgText: full.slice(ci + 2).trim() };
            }
            function parseChatCmd(rawText, sender) {
                const text = (rawText || '').trim();
                if (!text.startsWith(CFG.prefix)) return;
                const toks = tokenize(text.slice(CFG.prefix.length).trim());
                const cmd = (toks.shift() || '').toLowerCase();
                if (!cmd) return;
                try {
                    runChatCommand(cmd, toks, sender);
                } catch (e) {
                    if (isMe(sender)) {
                        showToast('⚠️ Error executing command', 'err');
                        console.error('[SummonChat]', e);
                    }
                }
            }
            function makeChatObserver(container, subtree) {
                if (!container) return;
                new MutationObserver(mutations => {
                    for (const m of mutations) {
                        for (const node of m.addedNodes) {
                            if (!node || node.nodeType !== 1 || node.style?.parsed) continue;
                            const line = extractChatLine(node);
                            if (!line || !line.msgText.startsWith(CFG.prefix)) continue;
                            parseChatCmd(line.msgText, line.senderName);
                        }
                    }
                }).observe(container, { childList: true, subtree });
            }
            function hookChatDOM() {
                const l = document.getElementById('newbonklobby_chat_content');
                if (!l) {
                    setTimeout(hookChatDOM, 500);
                    return;
                }
                makeChatObserver(l, false);
            }
            function hookIngameChat() {
                const g = document.getElementById('ingamechat');
                if (!g) {
                    setTimeout(hookIngameChat, 1000);
                    return;
                }
                makeChatObserver(g.querySelector('[id*="chat"]') || g, true);
            }
            hookChatDOM();
            hookIngameChat();

            window.BonkSummonChat = { CFG, HAMP, METZ, BABY, DEFAULT_ROLES, parseChatCmd, VOTE, COOLDOWN, R, hampMode, iAmRealHost, getMyName };
            console.log(
                '[BonkSummonChat] Loaded'
            );
        })();

        // --- RECIEVE HOOK — capture packet 26 (mode change) from inside the game --
        // Summon runs inside the game iframe so window.RECIEVE is the only target.
        function _hookRecieve(obj) {
            if (!obj || obj.__smRecieveHooked) return;
            const _orig = obj.RECIEVE;
            if (typeof _orig !== 'function') return;
            obj.__smRecieveHooked = true;
            obj.RECIEVE = function (data) {
                try {
                    if (typeof data === 'string' && data.startsWith('42')) {
                        const parsed = JSON.parse(data.slice(2));
                        // packet 26: [26, ga, mo] — mo is the mode code string
                        if (Array.isArray(parsed) && parsed[0] === 26)
                            _applyModePacket(parsed);
                    }
                } catch (_) {}
                return _orig.apply(this, arguments);
            };
        }
        // Poll until the game exposes window.RECIEVE
        (function _waitForRecieve() {
            if (window.RECIEVE && !window.__smRecieveHooked) _hookRecieve(window);
            if (!window.RECIEVE || !window.__smRecieveHooked) setTimeout(_waitForRecieve, 500);
        })();

        // --- INIT ---
        const SPIN_CFG = { speedDeg: 360, mode: 'fixed' };
        const SPRINGY_CFG = { springForce: 10000, springLen: 0 };
        const FPATH_CFG = { angle: 0, pathLen: 1, moveSpeed: 50000 };
        function init() {
            buildHUD();
        }
        if (document.readyState === 'complete' || document.readyState === 'interactive') init();
        else document.addEventListener('DOMContentLoaded', init);

        window.BonkSummonMod = {
            startGame, summonables, SUMMONABLES_OBJ, SUMMONABLES_SKIN, getSummonables,
            actionSpin, actionDinnerbone, actionImplode, actionExplode, actionSwap, actionSwapAll,
            actionBringTo, actionBringAll, actionRepulse, actionAttract, actionRain, actionOrbit,
            actionShotgun, actionSuperFling, actionBouncyMode, actionFrictionMode, actionDensityMode,
            actionRainbowMap, actionRandomizeMap, actionRewind, actionCopyPos, actionPastePos,
            actionSetScore, actionSetPPM, actionResizeViaPPM, actionTranslateMap, actionSpinMap,
            actionSpringyMap, actionFpath, actionDeathMode, actionSetMs, actionMovePlayer, actionMovePlayerAll,
            actionTeleportPlayer, actionTeleportPlayerAll,
        };
    })();
})();