{"id":1175,"date":"2026-08-13T04:47:37","date_gmt":"2026-08-13T02:47:37","guid":{"rendered":"http:\/\/csp.wntest.cn\/?page_id=1175"},"modified":"2026-09-17T15:40:46","modified_gmt":"2026-09-17T13:40:46","slug":"internes-portal","status":"publish","type":"page","link":"https:\/\/cspower.de\/en\/internes-portal\/","title":{"rendered":"Internes Portal"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1175\" class=\"elementor elementor-1175\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-35265af e-flex e-con-boxed e-con e-parent\" data-id=\"35265af\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f6bf58f elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"f6bf58f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor\uff1a\u6574\u6bb5\u590d\u5236\u5230\u300cHTML\u300d\u5c0f\u5de5\u5177\uff08\u4e0d\u8981\u7528\u5b8c\u6574\u7f51\u9875\u7684 <html>\/<head>\/<body> \u5916\u58f3\uff09\u3002\n  \u811a\u672c\u987a\u5e8f\uff1a\u5148\u5b9a\u4e49 portalApp \u2192 \u518d\u52a0\u8f7d Tailwind \u2192 \u518d\u653e\u754c\u9762\u6839\u8282\u70b9 \u2192 \u6700\u540e defer Alpine\u3002\n-->\n<meta charset=\"UTF-8\">\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n<style>\n    #csp-internal-portal-root [x-cloak] { display: none !important; }\n    h1.entry-title, h1.page-title, .elementor-page-title, .page-header-title {\n        display: none !important;\n    }\n    #csp-internal-portal-root .input-minimal {\n        border: 1px solid #e2e8f0;\n        border-radius: 0.25rem;\n        padding: 0.375rem 0.75rem;\n        width: 100%;\n        font-size: 0.875rem;\n        outline: none;\n        transition: border-color 0.15s ease, box-shadow 0.15s ease;\n        background: #fff;\n        color: #1e293b;\n        box-sizing: border-box;\n    }\n    #csp-internal-portal-root .input-minimal:focus {\n        border-color: #64748b;\n    }\n    #csp-internal-portal-root .portal-module-grid { overflow-wrap: anywhere; word-wrap: break-word; }\n    #csp-internal-portal-root .portal-text-de { hyphens: auto; -webkit-hyphens: auto; }\n    #csp-internal-portal-root .portal-tile-title {\n        color: #0f172a;\n        line-height: 1.25;\n        overflow-wrap: anywhere;\n        word-break: break-word;\n    }\n    #csp-internal-portal-root .portal-tile-grid.de-lang .portal-tile-title { hyphens: auto; -webkit-hyphens: auto; }\n    #csp-internal-portal-root .portal-tile-sub {\n        position: absolute;\n        left: 1.5rem;\n        bottom: 1.25rem;\n        right: 1.5rem;\n        font-size: 0.75rem;\n        line-height: 1.35;\n        color: #64748b;\n        overflow-wrap: anywhere;\n    }\n    \/* CS Power\uff1a\u4e0e\u6a21\u5757\u9875 header-brand \u540c\u8272\u540c\u5b57\u91cd\uff0c\u5c3a\u5bf8\u4e3a\u5176 2 \u500d *\/\n    #csp-internal-portal-root .portal-header-brand {\n        font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n        font-weight: 700;\n        font-size: 2.5rem;\n        color: #0073ea;\n        letter-spacing: -0.02em;\n        line-height: 1.2;\n    }\n<\/style>\n<script>\n(function () {\n    try {\n        var p = window.location.pathname || '';\n        var m = p.match(\/^(.*\\\/internal-tools)\\\/\/i);\n        if (m) {\n            window.__CSP_API_AUTH__ = window.location.origin + m[1] + '\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = window.location.origin + m[1] + '\/';\n            return;\n        }\n        if (String(window.location.hostname || '').toLowerCase() === 'cspower.de') {\n            window.__CSP_API_AUTH__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/';\n            return;\n        }\n    } catch (e) {}\n    if (!window.__CSP_API_AUTH__) window.__CSP_API_AUTH__ = 'api_auth.php';\n    if (!window.__CSP_INTERNAL_TOOLS__) window.__CSP_INTERNAL_TOOLS__ = '';\n})();\n\n(function ensureCspApiAuth() {\n    if (typeof window.__CSP_API_AUTH__ === 'string' && window.__CSP_API_AUTH__.trim()) return;\n    try {\n        var p = window.location.pathname || '';\n        var m = p.match(\/^(.*\\\/internal-tools)\\\/\/i);\n        if (m) {\n            window.__CSP_API_AUTH__ = window.location.origin + m[1] + '\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = window.location.origin + m[1] + '\/';\n            return;\n        }\n        if (String(window.location.hostname || '').toLowerCase() === 'cspower.de') {\n            window.__CSP_API_AUTH__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/';\n            return;\n        }\n    } catch (e) {}\n    window.__CSP_API_AUTH__ = 'api_auth.php';\n})();\n\nfunction portalApp() {\n    return {\n        lang: 'de',\n        isLoggedIn: false,\n        authMode: 'login',\n        resetToken: '',\n        authData: { email: '', password: '' },\n        pwdReset: { new1: '', new2: '' },\n        pwdChange: { current: '', new1: '', new2: '' },\n        changePwdOpen: false,\n        userEmail: '',\n        userAccess: [],\n        hrEmployees: [],\n\n        authUrl(action) {\n            var base = (typeof window.__CSP_API_AUTH__ === 'string' && window.__CSP_API_AUTH__.trim()) || 'api_auth.php';\n            base = base.trim();\n            var sep = base.indexOf('?') >= 0 ? '&' : '?';\n            return base + sep + 'action=' + encodeURIComponent(action);\n        },\n\n        hrDataUrl() {\n            var b = (typeof window.__CSP_INTERNAL_TOOLS__ === 'string' && window.__CSP_INTERNAL_TOOLS__.trim()) || '';\n            return (b ? b : '') + 'hr_data.json?t=' + Date.now();\n        },\n\n        normLang(v) {\n            var s = String(v || '').trim().toLowerCase();\n            return (s === 'de' || s === 'en' || s === 'cn') ? s : '';\n        },\n\n        syncHtmlLang() {\n            try {\n                document.documentElement.lang = this.lang === 'cn' ? 'zh-Hans' : this.lang;\n            } catch (e) {}\n        },\n\n        authT() {\n            var l = this.normLang(this.lang) || 'de';\n            return this.t[l] || this.t.de;\n        },\n\n        authOverlayTitle() {\n            var m = this.authMode;\n            var t = this.authT();\n            if (m === 'register') return t.authRegisterTitle;\n            if (m === 'forgot') return t.authForgotTitle;\n            if (m === 'reset') return t.authResetTitle;\n            return t.authLoginTitle;\n        },\n\n        authOverlayHint() {\n            var m = this.authMode;\n            var t = this.authT();\n            if (m === 'register') return t.authRegisterHint;\n            if (m === 'forgot') return t.authForgotHint;\n            if (m === 'reset') return t.authResetHint;\n            return t.authLoginHint;\n        },\n\n        pwdErrMsg(code) {\n            var c = String(code || '');\n            var t = this.t[this.lang] || this.t.de;\n            if (c === 'password_too_short' || c === 'invalid_input') return t.pwdTooShort8;\n            if (c === 'wrong_current_password') return t.wrongCurrentPwd;\n            if (c === 'invalid_or_expired_token') return t.invalidToken;\n            return c || t.authServerError;\n        },\n\n        t: {\n            de: {\n                title: \"Internes Portal\",\n                subtitle: \"Willkommen\",\n                authLoginTitle: \"Anmeldung\",\n                authRegisterTitle: \"Registrierung\",\n                authLoginHint: \"Bitte melden Sie sich an\",\n                authRegisterHint: \"Registrieren Sie sich f\u00fcr den Zugriff\",\n                authEmail: \"E-Mail\",\n                authPassword: \"Passwort\",\n                authSignIn: \"Anmelden\",\n                authSubmitReg: \"Registrierung absenden\",\n                authNoAccount: \"Noch kein Konto? Hier registrieren\",\n                authBackLogin: \"Zur\u00fcck zur Anmeldung\",\n                forgotPasswordLink: \"Passwort vergessen?\",\n                authForgotTitle: \"Passwort zur\u00fccksetzen\",\n                authForgotHint: \"Wir senden einen Link per E-Mail (falls die Adresse registriert ist).\",\n                authForgotSubmit: \"Link anfordern\",\n                authResetTitle: \"Neues Passwort setzen\",\n                authResetHint: \"Neues Passwort (min. 8 Zeichen).\",\n                authForgotBack: \"Zur Anmeldung\",\n                authResetNewPwd: \"Neues Passwort\",\n                authResetConfirm: \"Passwort best\u00e4tigen\",\n                authResetSubmit: \"Passwort speichern\",\n                forgotSentGeneric: \"Wenn die E-Mail registriert ist, erhalten Sie einen Link.\",\n                pwdMismatch: \"Passw\u00f6rter stimmen nicht \u00fcberein.\",\n                pwdTooShort8: \"Mindestens 8 Zeichen.\",\n                resetOkLogin: \"Passwort gespeichert. Sie k\u00f6nnen sich jetzt anmelden.\",\n                invalidToken: \"Link ung\u00fcltig oder abgelaufen.\",\n                changePasswordBtn: \"Passwort \u00e4ndern\",\n                changePasswordTitle: \"Passwort \u00e4ndern\",\n                currentPasswordLabel: \"Aktuelles Passwort\",\n                newPasswordLabel: \"Neues Passwort\",\n                confirmPasswordLabel: \"Best\u00e4tigen\",\n                pwdSave: \"Speichern\",\n                pwdCancel: \"Abbrechen\",\n                pwdChangedOk: \"Passwort wurde ge\u00e4ndert.\",\n                wrongCurrentPwd: \"Aktuelles Passwort ist falsch.\",\n                authFillAll: \"Bitte f\u00fcllen Sie alle Felder aus.\",\n                authServerError: \"Serverfehler.\",\n                restricted: \"GESCH\u00dcTZT\",\n                crmTitle: \"CRM-\\nProjekt\u00fcberblick\",\n                crmDesc: \"Vertrieb & Projektmanagement\",\n                tasksTitle: \"Meine Aufgaben\",\n                tasksDesc: \"Aufgaben & Freigaben\",\n                projTitle: \"Projektplaner\",\n                projDesc: \"Ausf\u00fchrung: Tasks, Zeitplan\",\n                offlineBadge: \"\",\n                hrTitle: \"Personal-\\nwesen\",\n                hrDesc: \"Mitarbeiter & Organisation\",\n                finTitle: \"Finanz-\\nmanagement\",\n                finDesc: \"Budget & Ein-\/Ausgaben\",\n                salesTitle: \"Vertriebs-\\nmanagement\",\n                salesDesc: \"Kunden, Leads & Angebote\",\n                engTitle: \"Engineering-\\nManager\",\n                engDesc: \"Design, Lieferpakete, Netzcompliance\",\n                supplyTitle: \"Supply-\\nChain\",\n                supplyDesc: \"Beschaffung & Logistik\",\n                supplySoon: \"Demn\u00e4chst\",\n                adminTitle: \"Benutzer-\\nverwaltung\",\n                adminDesc: \"Freigaben & Modulzugriff (users.json)\",\n                footerMsg: \"\ud83d\udd12 Nur f\u00fcr internen Zugriff.\",\n                logout: \"Abmelden\"\n            },\n            en: {\n                title: \"Internal Portal\",\n                subtitle: \"Welcome\",\n                authLoginTitle: \"System Login\",\n                authRegisterTitle: \"Staff Registration\",\n                authLoginHint: \"Please sign in\",\n                authRegisterHint: \"Register to request access\",\n                authEmail: \"Email\",\n                authPassword: \"Password\",\n                authSignIn: \"Sign In\",\n                authSubmitReg: \"Submit Registration\",\n                authNoAccount: \"No account? Register here\",\n                authBackLogin: \"Back to Login\",\n                forgotPasswordLink: \"Forgot password?\",\n                authForgotTitle: \"Reset password\",\n                authForgotHint: \"We will email a reset link if the address is registered.\",\n                authForgotSubmit: \"Send link\",\n                authResetTitle: \"Set new password\",\n                authResetHint: \"Enter a new password (min. 8 characters).\",\n                authForgotBack: \"Back to login\",\n                authResetNewPwd: \"New password\",\n                authResetConfirm: \"Confirm password\",\n                authResetSubmit: \"Save password\",\n                forgotSentGeneric: \"If the email is registered, you will receive a link shortly.\",\n                pwdMismatch: \"Passwords do not match.\",\n                pwdTooShort8: \"At least 8 characters.\",\n                resetOkLogin: \"Password saved. You can sign in now.\",\n                invalidToken: \"Invalid or expired link.\",\n                changePasswordBtn: \"Change password\",\n                changePasswordTitle: \"Change password\",\n                currentPasswordLabel: \"Current password\",\n                newPasswordLabel: \"New password\",\n                confirmPasswordLabel: \"Confirm\",\n                pwdSave: \"Save\",\n                pwdCancel: \"Cancel\",\n                pwdChangedOk: \"Password has been changed.\",\n                wrongCurrentPwd: \"Current password is incorrect.\",\n                authFillAll: \"Please fill in all fields.\",\n                authServerError: \"Server error.\",\n                restricted: \"RESTRICTED\",\n                crmTitle: \"CRM project overview\",\n                crmDesc: \"Sales & project management\",\n                tasksTitle: \"My tasks\",\n                tasksDesc: \"Tasks & approvals\",\n                projTitle: \"Project planner\",\n                projDesc: \"Execution tasks & timeline\",\n                offlineBadge: \"\",\n                hrTitle: \"HR management\",\n                hrDesc: \"People & organization\",\n                finTitle: \"Finance management\",\n                finDesc: \"Budget & income \/ expenses\",\n                salesTitle: \"Sales management\",\n                salesDesc: \"Clients, leads & quotes\",\n                engTitle: \"Engineering manager\",\n                engDesc: \"Design, deliverables, grid compliance\",\n                supplyTitle: \"Supply chain\",\n                supplyDesc: \"Procurement & logistics\",\n                supplySoon: \"Coming soon\",\n                adminTitle: \"User admin\",\n                adminDesc: \"Approvals & module access (users.json)\",\n                footerMsg: \"\ud83d\udd12 Secure internal access only.\",\n                logout: \"Log out\"\n            },\n            cn: {\n                title: \"\u5185\u90e8\u603b\u63a7\u53f0\",\n                subtitle: \"\u6b22\u8fce\u767b\u5f55\",\n                authLoginTitle: \"\u7cfb\u7edf\u767b\u5f55\",\n                authRegisterTitle: \"\u5458\u5de5\u6ce8\u518c\",\n                authLoginHint: \"\u8bf7\u767b\u5f55\",\n                authRegisterHint: \"\u6ce8\u518c\u4ee5\u7533\u8bf7\u8bbf\u95ee\u6743\u9650\",\n                authEmail: \"\u90ae\u7bb1\",\n                authPassword: \"\u5bc6\u7801\",\n                authSignIn: \"\u767b\u5f55\",\n                authSubmitReg: \"\u63d0\u4ea4\u6ce8\u518c\",\n                authNoAccount: \"\u6ca1\u6709\u8d26\u53f7\uff1f\u7acb\u5373\u6ce8\u518c\",\n                authBackLogin: \"\u8fd4\u56de\u767b\u5f55\",\n                forgotPasswordLink: \"\u5fd8\u8bb0\u5bc6\u7801\uff1f\",\n                authForgotTitle: \"\u627e\u56de\u5bc6\u7801\",\n                authForgotHint: \"\u82e5\u8be5\u90ae\u7bb1\u5df2\u6ce8\u518c\uff0c\u6211\u4eec\u5c06\u53d1\u9001\u91cd\u7f6e\u94fe\u63a5\u90ae\u4ef6\u3002\",\n                authForgotSubmit: \"\u53d1\u9001\u91cd\u7f6e\u94fe\u63a5\",\n                authResetTitle: \"\u8bbe\u7f6e\u65b0\u5bc6\u7801\",\n                authResetHint: \"\u8bf7\u8f93\u5165\u65b0\u5bc6\u7801\uff08\u81f3\u5c11 8 \u4f4d\uff09\u3002\",\n                authForgotBack: \"\u8fd4\u56de\u767b\u5f55\",\n                authResetNewPwd: \"\u65b0\u5bc6\u7801\",\n                authResetConfirm: \"\u786e\u8ba4\u5bc6\u7801\",\n                authResetSubmit: \"\u4fdd\u5b58\u5bc6\u7801\",\n                forgotSentGeneric: \"\u82e5\u90ae\u7bb1\u5df2\u6ce8\u518c\uff0c\u60a8\u5c06\u5f88\u5feb\u6536\u5230\u90ae\u4ef6\u3002\",\n                pwdMismatch: \"\u4e24\u6b21\u8f93\u5165\u7684\u5bc6\u7801\u4e0d\u4e00\u81f4\u3002\",\n                pwdTooShort8: \"\u81f3\u5c11 8 \u4e2a\u5b57\u7b26\u3002\",\n                resetOkLogin: \"\u5bc6\u7801\u5df2\u4fdd\u5b58\uff0c\u8bf7\u767b\u5f55\u3002\",\n                invalidToken: \"\u94fe\u63a5\u65e0\u6548\u6216\u5df2\u8fc7\u671f\u3002\",\n                changePasswordBtn: \"\u4fee\u6539\u5bc6\u7801\",\n                changePasswordTitle: \"\u4fee\u6539\u5bc6\u7801\",\n                currentPasswordLabel: \"\u5f53\u524d\u5bc6\u7801\",\n                newPasswordLabel: \"\u65b0\u5bc6\u7801\",\n                confirmPasswordLabel: \"\u786e\u8ba4\u65b0\u5bc6\u7801\",\n                pwdSave: \"\u4fdd\u5b58\",\n                pwdCancel: \"\u53d6\u6d88\",\n                pwdChangedOk: \"\u5bc6\u7801\u5df2\u4fee\u6539\u3002\",\n                wrongCurrentPwd: \"\u5f53\u524d\u5bc6\u7801\u4e0d\u6b63\u786e\u3002\",\n                authFillAll: \"\u8bf7\u586b\u5199\u6240\u6709\u5b57\u6bb5\u3002\",\n                authServerError: \"\u670d\u52a1\u5668\u9519\u8bef\u3002\",\n                restricted: \"\u53d7\u9650\",\n                crmTitle: \"CRM \u9879\u76ee\u603b\u89c8\",\n                crmDesc: \"\u9500\u552e\/\u9879\u76ee\u7ba1\u7406\",\n                tasksTitle: \"\u6211\u7684\u4efb\u52a1\",\n                tasksDesc: \"\u4efb\u52a1\u4e0e\u5ba1\u6279\",\n                projTitle: \"\u9879\u76ee\u89c4\u5212\",\n                projDesc: \"\u6267\u884c\u4efb\u52a1\u4e0e\u65f6\u95f4\u7ebf\",\n                offlineBadge: \"\",\n                hrTitle: \"\u4eba\u4e8b\u7ba1\u7406\",\n                hrDesc: \"\u4eba\u5458\u4e0e\u67b6\u6784\",\n                finTitle: \"\u8d22\u52a1\u7ba1\u7406\",\n                finDesc: \"\u9884\u7b97\u4e0e\u6536\u652f\",\n                salesTitle: \"\u9500\u552e\u7ba1\u7406\",\n                salesDesc: \"\u5ba2\u6237\u3001\u7ebf\u7d22\u4e0e\u62a5\u4ef7\",\n                engTitle: \"\u5de5\u7a0b\u8bbe\u8ba1\u7ba1\u7406\",\n                engDesc: \"\u8bbe\u8ba1\u4ea4\u4ed8\u3001\u56fe\u7eb8\u4e0e\u7535\u7f51\u5408\u89c4\",\n                supplyTitle: \"\u4f9b\u5e94\u94fe\",\n                supplyDesc: \"\u91c7\u8d2d\u4e0e\u7269\u6d41\",\n                supplySoon: \"\u5373\u5c06\u4e0a\u7ebf\",\n                adminTitle: \"\u7528\u6237\u7ba1\u7406\",\n                adminDesc: \"\u5ba1\u6838\u8d26\u53f7\u4e0e\u6a21\u5757\u6743\u9650\uff08users.json\uff09\",\n                footerMsg: \"\ud83d\udd12 \u4ec5\u9650\u4f01\u4e1a\u5185\u90e8\u8bbf\u95ee\u3002\",\n                logout: \"\u9000\u51fa\u767b\u5f55\"\n            }\n        },\n\n        async initApp() {\n            try {\n                try {\n                    const hr = await fetch(this.hrDataUrl(), { credentials: 'include' });\n                    if (hr.ok) {\n                        const d = await hr.json().catch(() => null);\n                        if (Array.isArray(d)) this.hrEmployees = d;\n                    }\n                } catch (e) {}\n\n                let res = await fetch(this.authUrl('check'), { credentials: 'include' });\n                const raw = await res.text();\n                let data = null;\n                try {\n                    data = JSON.parse(raw);\n                } catch (e) {}\n                if (data && data.status === 'logged_in') {\n                    this.isLoggedIn = true;\n                    this.userEmail = data.email;\n                    this.userAccess = data.access;\n                    this.lang = this.normLang(data.preferredLang) || 'de';\n                } else {\n                    try {\n                        var q = new URLSearchParams(window.location.search);\n                        var rt = q.get('csp_reset');\n                        if (rt) {\n                            this.resetToken = rt;\n                            this.authMode = 'reset';\n                            this.pwdReset = { new1: '', new2: '' };\n                        }\n                    } catch (e) {}\n                }\n            } catch (e) { console.log(\"Not logged in\"); }\n            this.syncHtmlLang();\n        },\n\n        getEmpNameByEmail(email) {\n            const e = String(email || '').trim().toLowerCase();\n            if (!e || e === 'unknown') return '';\n            const emp = (this.hrEmployees || []).find(x => String(x?.email || '').trim().toLowerCase() === e);\n            return emp?.name ? String(emp.name) : '';\n        },\n\n        getUserDisplayName(email) {\n            const raw = String(email || '').trim();\n            if (!raw) return '-';\n            const n = this.getEmpNameByEmail(raw);\n            return n || raw;\n        },\n\n        async persistPortalLang() {\n            if (!this.isLoggedIn) return;\n            var l = this.normLang(this.lang);\n            if (!l) return;\n            try {\n                if (window.cspUserLang && typeof window.cspUserLang.save === 'function') {\n                    await window.cspUserLang.save(l);\n                } else {\n                    await fetch(this.authUrl('set_lang'), {\n                        method: 'POST',\n                        headers: { 'Content-Type': 'application\/json' },\n                        credentials: 'include',\n                        body: JSON.stringify({ lang: l })\n                    });\n                }\n            } catch (e) {}\n            this.syncHtmlLang();\n        },\n\n        async handleAuth() {\n            var AT = this.authT();\n            if (this.authMode !== 'login' && this.authMode !== 'register') return;\n            if (!this.authData.email || !this.authData.password) { alert(AT.authFillAll); return; }\n            try {\n                let res = await fetch(this.authUrl(this.authMode), {\n                    method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify(this.authData), credentials: 'include'\n                });\n                const loginRaw = await res.text();\n                let result = null;\n                try {\n                    result = JSON.parse(loginRaw);\n                } catch (e) {}\n                if (!result) {\n                    alert(AT.authServerError);\n                    return;\n                }\n                if (result.status === 'success') {\n                    if (this.authMode === 'login') {\n                        this.isLoggedIn = true;\n                        this.userAccess = result.access;\n                        this.userEmail = this.authData.email;\n                        this.lang = this.normLang(result.preferredLang) || 'de';\n                        this.syncHtmlLang();\n                    } else {\n                        alert(result.message);\n                        this.authMode = 'login';\n                    }\n                } else { alert(result.message); }\n            } catch (e) { alert(this.authT().authServerError); }\n        },\n\n        async handleForgotSubmit() {\n            var AT = this.authT();\n            var em = String(this.authData.email || '').trim();\n            if (!em) { alert(AT.authFillAll); return; }\n            try {\n                var res = await fetch(this.authUrl('request_password_reset'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ email: em }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    alert(AT.forgotSentGeneric);\n                    this.authMode = 'login';\n                } else {\n                    alert(AT.authServerError);\n                }\n            } catch (e) {\n                alert(AT.authServerError);\n            }\n        },\n\n        async handleResetSubmit() {\n            var AT = this.authT();\n            var n1 = String(this.pwdReset.new1 || '');\n            var n2 = String(this.pwdReset.new2 || '');\n            if (!this.resetToken) { alert(AT.invalidToken); return; }\n            if (n1.length < 8) { alert(AT.pwdTooShort8); return; }\n            if (n1 !== n2) { alert(AT.pwdMismatch); return; }\n            try {\n                var res = await fetch(this.authUrl('reset_password'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ token: this.resetToken, new_password: n1 }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    try {\n                        var u = new URL(window.location.href);\n                        u.searchParams.delete('csp_reset');\n                        window.history.replaceState({}, '', u.pathname + (u.search || '') + (u.hash || ''));\n                    } catch (e) {}\n                    this.resetToken = '';\n                    this.authMode = 'login';\n                    this.pwdReset = { new1: '', new2: '' };\n                    alert(AT.resetOkLogin);\n                } else {\n                    alert(this.pwdErrMsg(result && result.message));\n                }\n            } catch (e) {\n                alert(AT.authServerError);\n            }\n        },\n\n        async submitChangePassword() {\n            var t = this.t[this.lang] || this.t.de;\n            var cur = String(this.pwdChange.current || '');\n            var n1 = String(this.pwdChange.new1 || '');\n            var n2 = String(this.pwdChange.new2 || '');\n            if (!cur || !n1 || !n2) { alert(t.authFillAll); return; }\n            if (n1.length < 8) { alert(t.pwdTooShort8); return; }\n            if (n1 !== n2) { alert(t.pwdMismatch); return; }\n            try {\n                var res = await fetch(this.authUrl('change_password'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ current_password: cur, new_password: n1 }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    this.changePwdOpen = false;\n                    this.pwdChange = { current: '', new1: '', new2: '' };\n                    alert(t.pwdChangedOk);\n                } else {\n                    alert(this.pwdErrMsg(result && result.message));\n                }\n            } catch (e) {\n                alert(t.authServerError);\n            }\n        },\n\n        async logout() {\n            await fetch(this.authUrl('logout'), { credentials: 'include' });\n            this.isLoggedIn = false;\n            this.userAccess = [];\n            this.authData.password = '';\n            this.changePwdOpen = false;\n            this.pwdChange = { current: '', new1: '', new2: '' };\n        }\n    };\n}\n<\/script>\n<script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n<div id=\"csp-internal-portal-root\" class=\"bg-transparent pt-2 pb-6 font-sans\" x-data=\"portalApp()\" x-init=\"(async () => { await initApp(); })()\" x-cloak>\n\n    <div x-show=\"!isLoggedIn\" class=\"fixed inset-0 z-[99999] flex items-center justify-center bg-slate-900\">\n        <div class=\"bg-white p-8 rounded shadow-2xl w-full max-w-sm border border-slate-200\">\n            <div class=\"flex justify-end mb-3\">\n                <select x-model=\"lang\" @change=\"syncHtmlLang()\" class=\"bg-white text-slate-700 border border-slate-200 rounded px-2 py-1 text-sm outline-none cursor-pointer\">\n                    <option value=\"de\">Deutsch<\/option>\n                    <option value=\"en\">English<\/option>\n                    <option value=\"cn\">\u4e2d\u6587<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"mb-8 text-center\">\n                <div class=\"portal-header-brand mb-3\">CS Power<\/div>\n                <h2 class=\"text-xl font-bold text-slate-800\" x-text=\"authOverlayTitle()\"><\/h2>\n                <p class=\"text-xs text-slate-500 mt-1\" x-text=\"authOverlayHint()\"><\/p>\n            <\/div>\n\n            <div class=\"space-y-4\">\n                <div x-show=\"authMode === 'login'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authPassword\"><\/label>\n                        <input type=\"password\" x-model=\"authData.password\" @keydown.enter=\"handleAuth()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleAuth()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authSignIn\"><\/button>\n                    <div class=\"text-center mt-3 space-y-2\">\n                        <div>\n                            <span @click=\"authMode = 'register'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authNoAccount\"><\/span>\n                        <\/div>\n                        <div>\n                            <span @click=\"authMode = 'forgot'; authData.password = ''\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().forgotPasswordLink\"><\/span>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'register'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authPassword\"><\/label>\n                        <input type=\"password\" x-model=\"authData.password\" @keydown.enter=\"handleAuth()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleAuth()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authSubmitReg\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authBackLogin\"><\/span>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'forgot'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" @keydown.enter=\"handleForgotSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleForgotSubmit()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authForgotSubmit\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authForgotBack\"><\/span>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'reset'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authResetNewPwd\"><\/label>\n                        <input type=\"password\" x-model=\"pwdReset.new1\" @keydown.enter=\"handleResetSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authResetConfirm\"><\/label>\n                        <input type=\"password\" x-model=\"pwdReset.new2\" @keydown.enter=\"handleResetSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleResetSubmit()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authResetSubmit\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'; resetToken = ''; pwdReset = { new1: '', new2: '' }\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authForgotBack\"><\/span>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div x-show=\"isLoggedIn\" class=\"w-full px-2 lg:px-4\">\n\n        <div class=\"portal-header-brand mb-1\">CS Power<\/div>\n\n        <div class=\"flex flex-row flex-wrap justify-between items-center gap-x-4 gap-y-2 border-b border-slate-200 pb-3 mb-4\">\n            <p class=\"text-sm text-slate-500 min-w-0 flex-1 pr-2\" :class=\"{ 'portal-text-de': lang === 'de' }\" :lang=\"lang === 'de' ? 'de' : lang\" style=\"overflow-wrap: anywhere;\">\n                <span x-text=\"t[lang].subtitle\"><\/span>\n                <span class=\"font-semibold text-blue-600 ml-1\" x-text=\"getUserDisplayName(userEmail)\"><\/span>\n            <\/p>\n            <div class=\"flex items-center gap-4 shrink-0\">\n                <select x-model=\"lang\" @change=\"persistPortalLang()\" class=\"bg-white text-slate-700 border border-slate-200 rounded px-2 py-1 text-sm outline-none cursor-pointer\">\n                    <option value=\"de\">Deutsch<\/option>\n                    <option value=\"en\">English<\/option>\n                    <option value=\"cn\">\u4e2d\u6587<\/option>\n                <\/select>\n                <button type=\"button\" @click=\"changePwdOpen = true\" class=\"text-sm font-semibold text-slate-700 border border-slate-300 bg-white hover:bg-slate-50 px-4 py-1.5 rounded-lg shadow-sm transition-colors\" x-text=\"t[lang].changePasswordBtn\"><\/button>\n                <button type=\"button\" @click=\"logout()\" class=\"text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 px-4 py-1.5 rounded-lg shadow-sm transition-colors\" x-text=\"t[lang].logout\"><\/button>\n            <\/div>\n        <\/div>\n\n        <h1 class=\"text-2xl lg:text-3xl font-bold text-slate-800 tracking-tight mb-8\" x-text=\"t[lang].title\"><\/h1>\n\n        <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 portal-module-grid portal-tile-grid max-w-6xl\"\n             :class=\"{ 'de-lang': lang === 'de' }\"\n             :lang=\"lang\">\n\n            <a href=\"https:\/\/cspower.de\/client-intern\" x-show=\"userAccess.includes('CRM')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-blue-100 bg-blue-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].crmTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].crmDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/engineering-intern\"\n               x-show=\"userAccess.includes('CRM') || userAccess.includes('engineering')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-indigo-100 bg-indigo-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].engTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].engDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/task-intern\" x-show=\"userAccess.includes('tasks')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-violet-100 bg-violet-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].tasksTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].tasksDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/project-intern\" x-show=\"userAccess.includes('project') || userAccess.includes('CRM')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-emerald-100 bg-emerald-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].projTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].projDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/hr-intern\" x-show=\"userAccess.includes('hr')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-purple-100 bg-purple-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-purple-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].hrTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].hrDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/finance-intern\" x-show=\"userAccess.includes('finance')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-amber-100 bg-amber-50\/80 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-amber-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].finTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].finDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/sales-intern\" x-show=\"userAccess.includes('sales')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-sky-100 bg-sky-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-sky-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].salesTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].salesDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/supply-intern\" x-show=\"isLoggedIn\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-teal-100 bg-teal-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-teal-600\" x-text=\"t[lang].supplySoon\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].supplyTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].supplyDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/admin-intern\"\n               x-show=\"(userAccess || []).some(function (a) { return String(a || '').toLowerCase() === 'admin'; })\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-slate-200 bg-slate-50\/95 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-slate-700\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].adminTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].adminDesc\"><\/p>\n            <\/a>\n\n        <\/div>\n\n        <div class=\"mt-8 text-left text-xs text-slate-400 font-medium max-w-3xl\" :class=\"{ 'portal-text-de': lang === 'de' }\" :lang=\"lang === 'de' ? 'de' : lang\" style=\"overflow-wrap: anywhere;\" x-text=\"t[lang].footerMsg\"><\/div>\n\n        <div x-show=\"changePwdOpen\" x-cloak class=\"fixed inset-0 z-[100000] flex items-center justify-center bg-slate-900\/60 p-4\" @keydown.escape.window=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\">\n            <div class=\"bg-white rounded-xl shadow-xl w-full max-w-sm border border-slate-200 p-6\" @click.outside=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\">\n                <h3 class=\"text-lg font-bold text-slate-800 mb-4\" x-text=\"t[lang].changePasswordTitle\"><\/h3>\n                <div class=\"space-y-3\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].currentPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.current\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].newPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.new1\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].confirmPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.new2\" @keydown.enter=\"submitChangePassword()\" class=\"input-minimal py-2\">\n                    <\/div>\n                <\/div>\n                <div class=\"flex justify-end gap-2 mt-6\">\n                    <button type=\"button\" class=\"px-4 py-2 text-sm font-semibold border border-slate-300 rounded-lg hover:bg-slate-50\" @click=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\" x-text=\"t[lang].pwdCancel\"><\/button>\n                    <button type=\"button\" class=\"px-4 py-2 text-sm font-semibold bg-slate-900 text-white rounded-lg hover:bg-slate-800\" @click=\"submitChangePassword()\" x-text=\"t[lang].pwdSave\"><\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n<script defer src=\"https:\/\/cdn.jsdelivr.net\/npm\/alpinejs@3.14.3\/dist\/cdn.min.js\"><\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-ba57df5 e-flex e-con-boxed e-con e-parent\" data-id=\"ba57df5\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d4c9a5d elementor-widget elementor-widget-html\" data-id=\"d4c9a5d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\n  Elementor\uff1a\u6574\u6bb5\u590d\u5236\u5230\u300cHTML\u300d\u5c0f\u5de5\u5177\uff08\u4e0d\u8981\u7528\u5b8c\u6574\u7f51\u9875\u7684 <html>\/<head>\/<body> \u5916\u58f3\uff09\u3002\n  \u811a\u672c\u987a\u5e8f\uff1a\u5148\u5b9a\u4e49 portalApp \u2192 \u518d\u52a0\u8f7d Tailwind \u2192 \u518d\u653e\u754c\u9762\u6839\u8282\u70b9 \u2192 \u6700\u540e defer Alpine\u3002\n-->\n<meta charset=\"UTF-8\">\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n<style>\n    #csp-internal-portal-root [x-cloak] { display: none !important; }\n    h1.entry-title, h1.page-title, .elementor-page-title, .page-header-title {\n        display: none !important;\n    }\n    #csp-internal-portal-root .input-minimal {\n        border: 1px solid #e2e8f0;\n        border-radius: 0.25rem;\n        padding: 0.375rem 0.75rem;\n        width: 100%;\n        font-size: 0.875rem;\n        outline: none;\n        transition: border-color 0.15s ease, box-shadow 0.15s ease;\n        background: #fff;\n        color: #1e293b;\n        box-sizing: border-box;\n    }\n    #csp-internal-portal-root .input-minimal:focus {\n        border-color: #64748b;\n    }\n    #csp-internal-portal-root .portal-module-grid { overflow-wrap: anywhere; word-wrap: break-word; }\n    #csp-internal-portal-root .portal-text-de { hyphens: auto; -webkit-hyphens: auto; }\n    #csp-internal-portal-root .portal-tile-title {\n        color: #0f172a;\n        line-height: 1.25;\n        overflow-wrap: anywhere;\n        word-break: break-word;\n    }\n    #csp-internal-portal-root .portal-tile-grid.de-lang .portal-tile-title { hyphens: auto; -webkit-hyphens: auto; }\n    #csp-internal-portal-root .portal-tile-sub {\n        position: absolute;\n        left: 1.5rem;\n        bottom: 1.25rem;\n        right: 1.5rem;\n        font-size: 0.75rem;\n        line-height: 1.35;\n        color: #64748b;\n        overflow-wrap: anywhere;\n    }\n    \/* CS Power\uff1a\u4e0e\u6a21\u5757\u9875 header-brand \u540c\u8272\u540c\u5b57\u91cd\uff0c\u5c3a\u5bf8\u4e3a\u5176 2 \u500d *\/\n    #csp-internal-portal-root .portal-header-brand {\n        font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n        font-weight: 700;\n        font-size: 2.5rem;\n        color: #0073ea;\n        letter-spacing: -0.02em;\n        line-height: 1.2;\n    }\n<\/style>\n<script>\n(function () {\n    try {\n        var p = window.location.pathname || '';\n        var m = p.match(\/^(.*\\\/internal-tools)\\\/\/i);\n        if (m) {\n            window.__CSP_API_AUTH__ = window.location.origin + m[1] + '\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = window.location.origin + m[1] + '\/';\n            return;\n        }\n        if (String(window.location.hostname || '').toLowerCase() === 'cspower.de') {\n            window.__CSP_API_AUTH__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/';\n            return;\n        }\n    } catch (e) {}\n    if (!window.__CSP_API_AUTH__) window.__CSP_API_AUTH__ = 'api_auth.php';\n    if (!window.__CSP_INTERNAL_TOOLS__) window.__CSP_INTERNAL_TOOLS__ = '';\n})();\n\n(function ensureCspApiAuth() {\n    if (typeof window.__CSP_API_AUTH__ === 'string' && window.__CSP_API_AUTH__.trim()) return;\n    try {\n        var p = window.location.pathname || '';\n        var m = p.match(\/^(.*\\\/internal-tools)\\\/\/i);\n        if (m) {\n            window.__CSP_API_AUTH__ = window.location.origin + m[1] + '\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = window.location.origin + m[1] + '\/';\n            return;\n        }\n        if (String(window.location.hostname || '').toLowerCase() === 'cspower.de') {\n            window.__CSP_API_AUTH__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/api_auth.php';\n            window.__CSP_INTERNAL_TOOLS__ = 'https:\/\/cspower.de\/wp-content\/internal-tools\/';\n            return;\n        }\n    } catch (e) {}\n    window.__CSP_API_AUTH__ = 'api_auth.php';\n})();\n\nfunction portalApp() {\n    return {\n        lang: 'de',\n        isLoggedIn: false,\n        authMode: 'login',\n        resetToken: '',\n        authData: { email: '', password: '' },\n        pwdReset: { new1: '', new2: '' },\n        pwdChange: { current: '', new1: '', new2: '' },\n        changePwdOpen: false,\n        userEmail: '',\n        userAccess: [],\n        hrEmployees: [],\n\n        authUrl(action) {\n            var base = (typeof window.__CSP_API_AUTH__ === 'string' && window.__CSP_API_AUTH__.trim()) || 'api_auth.php';\n            base = base.trim();\n            var sep = base.indexOf('?') >= 0 ? '&' : '?';\n            return base + sep + 'action=' + encodeURIComponent(action);\n        },\n\n        hrDataUrl() {\n            var b = (typeof window.__CSP_INTERNAL_TOOLS__ === 'string' && window.__CSP_INTERNAL_TOOLS__.trim()) || '';\n            return (b ? b : '') + 'hr_data.json?t=' + Date.now();\n        },\n\n        normLang(v) {\n            var s = String(v || '').trim().toLowerCase();\n            return (s === 'de' || s === 'en' || s === 'cn') ? s : '';\n        },\n\n        syncHtmlLang() {\n            try {\n                document.documentElement.lang = this.lang === 'cn' ? 'zh-Hans' : this.lang;\n            } catch (e) {}\n        },\n\n        authT() {\n            var l = this.normLang(this.lang) || 'de';\n            return this.t[l] || this.t.de;\n        },\n\n        authOverlayTitle() {\n            var m = this.authMode;\n            var t = this.authT();\n            if (m === 'register') return t.authRegisterTitle;\n            if (m === 'forgot') return t.authForgotTitle;\n            if (m === 'reset') return t.authResetTitle;\n            return t.authLoginTitle;\n        },\n\n        authOverlayHint() {\n            var m = this.authMode;\n            var t = this.authT();\n            if (m === 'register') return t.authRegisterHint;\n            if (m === 'forgot') return t.authForgotHint;\n            if (m === 'reset') return t.authResetHint;\n            return t.authLoginHint;\n        },\n\n        pwdErrMsg(code) {\n            var c = String(code || '');\n            var t = this.t[this.lang] || this.t.de;\n            if (c === 'password_too_short' || c === 'invalid_input') return t.pwdTooShort8;\n            if (c === 'wrong_current_password') return t.wrongCurrentPwd;\n            if (c === 'invalid_or_expired_token') return t.invalidToken;\n            return c || t.authServerError;\n        },\n\n        t: {\n            de: {\n                title: \"Internes Portal\",\n                subtitle: \"Willkommen\",\n                authLoginTitle: \"Anmeldung\",\n                authRegisterTitle: \"Registrierung\",\n                authLoginHint: \"Bitte melden Sie sich an\",\n                authRegisterHint: \"Registrieren Sie sich f\u00fcr den Zugriff\",\n                authEmail: \"E-Mail\",\n                authPassword: \"Passwort\",\n                authSignIn: \"Anmelden\",\n                authSubmitReg: \"Registrierung absenden\",\n                authNoAccount: \"Noch kein Konto? Hier registrieren\",\n                authBackLogin: \"Zur\u00fcck zur Anmeldung\",\n                forgotPasswordLink: \"Passwort vergessen?\",\n                authForgotTitle: \"Passwort zur\u00fccksetzen\",\n                authForgotHint: \"Wir senden einen Link per E-Mail (falls die Adresse registriert ist).\",\n                authForgotSubmit: \"Link anfordern\",\n                authResetTitle: \"Neues Passwort setzen\",\n                authResetHint: \"Neues Passwort (min. 8 Zeichen).\",\n                authForgotBack: \"Zur Anmeldung\",\n                authResetNewPwd: \"Neues Passwort\",\n                authResetConfirm: \"Passwort best\u00e4tigen\",\n                authResetSubmit: \"Passwort speichern\",\n                forgotSentGeneric: \"Wenn die E-Mail registriert ist, erhalten Sie einen Link.\",\n                pwdMismatch: \"Passw\u00f6rter stimmen nicht \u00fcberein.\",\n                pwdTooShort8: \"Mindestens 8 Zeichen.\",\n                resetOkLogin: \"Passwort gespeichert. Sie k\u00f6nnen sich jetzt anmelden.\",\n                invalidToken: \"Link ung\u00fcltig oder abgelaufen.\",\n                changePasswordBtn: \"Passwort \u00e4ndern\",\n                changePasswordTitle: \"Passwort \u00e4ndern\",\n                currentPasswordLabel: \"Aktuelles Passwort\",\n                newPasswordLabel: \"Neues Passwort\",\n                confirmPasswordLabel: \"Best\u00e4tigen\",\n                pwdSave: \"Speichern\",\n                pwdCancel: \"Abbrechen\",\n                pwdChangedOk: \"Passwort wurde ge\u00e4ndert.\",\n                wrongCurrentPwd: \"Aktuelles Passwort ist falsch.\",\n                authFillAll: \"Bitte f\u00fcllen Sie alle Felder aus.\",\n                authServerError: \"Serverfehler.\",\n                restricted: \"GESCH\u00dcTZT\",\n                crmTitle: \"CRM-\\nProjekt\u00fcberblick\",\n                crmDesc: \"Vertrieb & Projektmanagement\",\n                tasksTitle: \"Meine Aufgaben\",\n                tasksDesc: \"Aufgaben & Freigaben\",\n                projTitle: \"Projektplaner\",\n                projDesc: \"Ausf\u00fchrung: Tasks, Zeitplan\",\n                offlineBadge: \"\",\n                hrTitle: \"Personal-\\nwesen\",\n                hrDesc: \"Mitarbeiter & Organisation\",\n                finTitle: \"Finanz-\\nmanagement\",\n                finDesc: \"Budget & Ein-\/Ausgaben\",\n                salesTitle: \"Vertriebs-\\nmanagement\",\n                salesDesc: \"Kunden, Leads & Angebote\",\n                engTitle: \"Engineering-\\nManager\",\n                engDesc: \"Design, Lieferpakete, Netzcompliance\",\n                supplyTitle: \"Supply-\\nChain\",\n                supplyDesc: \"Beschaffung & Logistik\",\n                supplySoon: \"Demn\u00e4chst\",\n                adminTitle: \"Benutzer-\\nverwaltung\",\n                adminDesc: \"Freigaben & Modulzugriff (users.json)\",\n                footerMsg: \"\ud83d\udd12 Nur f\u00fcr internen Zugriff.\",\n                logout: \"Abmelden\"\n            },\n            en: {\n                title: \"Internal Portal\",\n                subtitle: \"Welcome\",\n                authLoginTitle: \"System Login\",\n                authRegisterTitle: \"Staff Registration\",\n                authLoginHint: \"Please sign in\",\n                authRegisterHint: \"Register to request access\",\n                authEmail: \"Email\",\n                authPassword: \"Password\",\n                authSignIn: \"Sign In\",\n                authSubmitReg: \"Submit Registration\",\n                authNoAccount: \"No account? Register here\",\n                authBackLogin: \"Back to Login\",\n                forgotPasswordLink: \"Forgot password?\",\n                authForgotTitle: \"Reset password\",\n                authForgotHint: \"We will email a reset link if the address is registered.\",\n                authForgotSubmit: \"Send link\",\n                authResetTitle: \"Set new password\",\n                authResetHint: \"Enter a new password (min. 8 characters).\",\n                authForgotBack: \"Back to login\",\n                authResetNewPwd: \"New password\",\n                authResetConfirm: \"Confirm password\",\n                authResetSubmit: \"Save password\",\n                forgotSentGeneric: \"If the email is registered, you will receive a link shortly.\",\n                pwdMismatch: \"Passwords do not match.\",\n                pwdTooShort8: \"At least 8 characters.\",\n                resetOkLogin: \"Password saved. You can sign in now.\",\n                invalidToken: \"Invalid or expired link.\",\n                changePasswordBtn: \"Change password\",\n                changePasswordTitle: \"Change password\",\n                currentPasswordLabel: \"Current password\",\n                newPasswordLabel: \"New password\",\n                confirmPasswordLabel: \"Confirm\",\n                pwdSave: \"Save\",\n                pwdCancel: \"Cancel\",\n                pwdChangedOk: \"Password has been changed.\",\n                wrongCurrentPwd: \"Current password is incorrect.\",\n                authFillAll: \"Please fill in all fields.\",\n                authServerError: \"Server error.\",\n                restricted: \"RESTRICTED\",\n                crmTitle: \"CRM project overview\",\n                crmDesc: \"Sales & project management\",\n                tasksTitle: \"My tasks\",\n                tasksDesc: \"Tasks & approvals\",\n                projTitle: \"Project planner\",\n                projDesc: \"Execution tasks & timeline\",\n                offlineBadge: \"\",\n                hrTitle: \"HR management\",\n                hrDesc: \"People & organization\",\n                finTitle: \"Finance management\",\n                finDesc: \"Budget & income \/ expenses\",\n                salesTitle: \"Sales management\",\n                salesDesc: \"Clients, leads & quotes\",\n                engTitle: \"Engineering manager\",\n                engDesc: \"Design, deliverables, grid compliance\",\n                supplyTitle: \"Supply chain\",\n                supplyDesc: \"Procurement & logistics\",\n                supplySoon: \"Coming soon\",\n                adminTitle: \"User admin\",\n                adminDesc: \"Approvals & module access (users.json)\",\n                footerMsg: \"\ud83d\udd12 Secure internal access only.\",\n                logout: \"Log out\"\n            },\n            cn: {\n                title: \"\u5185\u90e8\u603b\u63a7\u53f0\",\n                subtitle: \"\u6b22\u8fce\u767b\u5f55\",\n                authLoginTitle: \"\u7cfb\u7edf\u767b\u5f55\",\n                authRegisterTitle: \"\u5458\u5de5\u6ce8\u518c\",\n                authLoginHint: \"\u8bf7\u767b\u5f55\",\n                authRegisterHint: \"\u6ce8\u518c\u4ee5\u7533\u8bf7\u8bbf\u95ee\u6743\u9650\",\n                authEmail: \"\u90ae\u7bb1\",\n                authPassword: \"\u5bc6\u7801\",\n                authSignIn: \"\u767b\u5f55\",\n                authSubmitReg: \"\u63d0\u4ea4\u6ce8\u518c\",\n                authNoAccount: \"\u6ca1\u6709\u8d26\u53f7\uff1f\u7acb\u5373\u6ce8\u518c\",\n                authBackLogin: \"\u8fd4\u56de\u767b\u5f55\",\n                forgotPasswordLink: \"\u5fd8\u8bb0\u5bc6\u7801\uff1f\",\n                authForgotTitle: \"\u627e\u56de\u5bc6\u7801\",\n                authForgotHint: \"\u82e5\u8be5\u90ae\u7bb1\u5df2\u6ce8\u518c\uff0c\u6211\u4eec\u5c06\u53d1\u9001\u91cd\u7f6e\u94fe\u63a5\u90ae\u4ef6\u3002\",\n                authForgotSubmit: \"\u53d1\u9001\u91cd\u7f6e\u94fe\u63a5\",\n                authResetTitle: \"\u8bbe\u7f6e\u65b0\u5bc6\u7801\",\n                authResetHint: \"\u8bf7\u8f93\u5165\u65b0\u5bc6\u7801\uff08\u81f3\u5c11 8 \u4f4d\uff09\u3002\",\n                authForgotBack: \"\u8fd4\u56de\u767b\u5f55\",\n                authResetNewPwd: \"\u65b0\u5bc6\u7801\",\n                authResetConfirm: \"\u786e\u8ba4\u5bc6\u7801\",\n                authResetSubmit: \"\u4fdd\u5b58\u5bc6\u7801\",\n                forgotSentGeneric: \"\u82e5\u90ae\u7bb1\u5df2\u6ce8\u518c\uff0c\u60a8\u5c06\u5f88\u5feb\u6536\u5230\u90ae\u4ef6\u3002\",\n                pwdMismatch: \"\u4e24\u6b21\u8f93\u5165\u7684\u5bc6\u7801\u4e0d\u4e00\u81f4\u3002\",\n                pwdTooShort8: \"\u81f3\u5c11 8 \u4e2a\u5b57\u7b26\u3002\",\n                resetOkLogin: \"\u5bc6\u7801\u5df2\u4fdd\u5b58\uff0c\u8bf7\u767b\u5f55\u3002\",\n                invalidToken: \"\u94fe\u63a5\u65e0\u6548\u6216\u5df2\u8fc7\u671f\u3002\",\n                changePasswordBtn: \"\u4fee\u6539\u5bc6\u7801\",\n                changePasswordTitle: \"\u4fee\u6539\u5bc6\u7801\",\n                currentPasswordLabel: \"\u5f53\u524d\u5bc6\u7801\",\n                newPasswordLabel: \"\u65b0\u5bc6\u7801\",\n                confirmPasswordLabel: \"\u786e\u8ba4\u65b0\u5bc6\u7801\",\n                pwdSave: \"\u4fdd\u5b58\",\n                pwdCancel: \"\u53d6\u6d88\",\n                pwdChangedOk: \"\u5bc6\u7801\u5df2\u4fee\u6539\u3002\",\n                wrongCurrentPwd: \"\u5f53\u524d\u5bc6\u7801\u4e0d\u6b63\u786e\u3002\",\n                authFillAll: \"\u8bf7\u586b\u5199\u6240\u6709\u5b57\u6bb5\u3002\",\n                authServerError: \"\u670d\u52a1\u5668\u9519\u8bef\u3002\",\n                restricted: \"\u53d7\u9650\",\n                crmTitle: \"CRM \u9879\u76ee\u603b\u89c8\",\n                crmDesc: \"\u9500\u552e\/\u9879\u76ee\u7ba1\u7406\",\n                tasksTitle: \"\u6211\u7684\u4efb\u52a1\",\n                tasksDesc: \"\u4efb\u52a1\u4e0e\u5ba1\u6279\",\n                projTitle: \"\u9879\u76ee\u89c4\u5212\",\n                projDesc: \"\u6267\u884c\u4efb\u52a1\u4e0e\u65f6\u95f4\u7ebf\",\n                offlineBadge: \"\",\n                hrTitle: \"\u4eba\u4e8b\u7ba1\u7406\",\n                hrDesc: \"\u4eba\u5458\u4e0e\u67b6\u6784\",\n                finTitle: \"\u8d22\u52a1\u7ba1\u7406\",\n                finDesc: \"\u9884\u7b97\u4e0e\u6536\u652f\",\n                salesTitle: \"\u9500\u552e\u7ba1\u7406\",\n                salesDesc: \"\u5ba2\u6237\u3001\u7ebf\u7d22\u4e0e\u62a5\u4ef7\",\n                engTitle: \"\u5de5\u7a0b\u8bbe\u8ba1\u7ba1\u7406\",\n                engDesc: \"\u8bbe\u8ba1\u4ea4\u4ed8\u3001\u56fe\u7eb8\u4e0e\u7535\u7f51\u5408\u89c4\",\n                supplyTitle: \"\u4f9b\u5e94\u94fe\",\n                supplyDesc: \"\u91c7\u8d2d\u4e0e\u7269\u6d41\",\n                supplySoon: \"\u5373\u5c06\u4e0a\u7ebf\",\n                adminTitle: \"\u7528\u6237\u7ba1\u7406\",\n                adminDesc: \"\u5ba1\u6838\u8d26\u53f7\u4e0e\u6a21\u5757\u6743\u9650\uff08users.json\uff09\",\n                footerMsg: \"\ud83d\udd12 \u4ec5\u9650\u4f01\u4e1a\u5185\u90e8\u8bbf\u95ee\u3002\",\n                logout: \"\u9000\u51fa\u767b\u5f55\"\n            }\n        },\n\n        async initApp() {\n            try {\n                try {\n                    const hr = await fetch(this.hrDataUrl(), { credentials: 'include' });\n                    if (hr.ok) {\n                        const d = await hr.json().catch(() => null);\n                        if (Array.isArray(d)) this.hrEmployees = d;\n                    }\n                } catch (e) {}\n\n                let res = await fetch(this.authUrl('check'), { credentials: 'include' });\n                const raw = await res.text();\n                let data = null;\n                try {\n                    data = JSON.parse(raw);\n                } catch (e) {}\n                if (data && data.status === 'logged_in') {\n                    this.isLoggedIn = true;\n                    this.userEmail = data.email;\n                    this.userAccess = data.access;\n                    this.lang = this.normLang(data.preferredLang) || 'de';\n                } else {\n                    try {\n                        var q = new URLSearchParams(window.location.search);\n                        var rt = q.get('csp_reset');\n                        if (rt) {\n                            this.resetToken = rt;\n                            this.authMode = 'reset';\n                            this.pwdReset = { new1: '', new2: '' };\n                        }\n                    } catch (e) {}\n                }\n            } catch (e) { console.log(\"Not logged in\"); }\n            this.syncHtmlLang();\n        },\n\n        getEmpNameByEmail(email) {\n            const e = String(email || '').trim().toLowerCase();\n            if (!e || e === 'unknown') return '';\n            const emp = (this.hrEmployees || []).find(x => String(x?.email || '').trim().toLowerCase() === e);\n            return emp?.name ? String(emp.name) : '';\n        },\n\n        getUserDisplayName(email) {\n            const raw = String(email || '').trim();\n            if (!raw) return '-';\n            const n = this.getEmpNameByEmail(raw);\n            return n || raw;\n        },\n\n        async persistPortalLang() {\n            if (!this.isLoggedIn) return;\n            var l = this.normLang(this.lang);\n            if (!l) return;\n            try {\n                if (window.cspUserLang && typeof window.cspUserLang.save === 'function') {\n                    await window.cspUserLang.save(l);\n                } else {\n                    await fetch(this.authUrl('set_lang'), {\n                        method: 'POST',\n                        headers: { 'Content-Type': 'application\/json' },\n                        credentials: 'include',\n                        body: JSON.stringify({ lang: l })\n                    });\n                }\n            } catch (e) {}\n            this.syncHtmlLang();\n        },\n\n        async handleAuth() {\n            var AT = this.authT();\n            if (this.authMode !== 'login' && this.authMode !== 'register') return;\n            if (!this.authData.email || !this.authData.password) { alert(AT.authFillAll); return; }\n            try {\n                let res = await fetch(this.authUrl(this.authMode), {\n                    method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify(this.authData), credentials: 'include'\n                });\n                const loginRaw = await res.text();\n                let result = null;\n                try {\n                    result = JSON.parse(loginRaw);\n                } catch (e) {}\n                if (!result) {\n                    alert(AT.authServerError);\n                    return;\n                }\n                if (result.status === 'success') {\n                    if (this.authMode === 'login') {\n                        this.isLoggedIn = true;\n                        this.userAccess = result.access;\n                        this.userEmail = this.authData.email;\n                        this.lang = this.normLang(result.preferredLang) || 'de';\n                        this.syncHtmlLang();\n                    } else {\n                        alert(result.message);\n                        this.authMode = 'login';\n                    }\n                } else { alert(result.message); }\n            } catch (e) { alert(this.authT().authServerError); }\n        },\n\n        async handleForgotSubmit() {\n            var AT = this.authT();\n            var em = String(this.authData.email || '').trim();\n            if (!em) { alert(AT.authFillAll); return; }\n            try {\n                var res = await fetch(this.authUrl('request_password_reset'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ email: em }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    alert(AT.forgotSentGeneric);\n                    this.authMode = 'login';\n                } else {\n                    alert(AT.authServerError);\n                }\n            } catch (e) {\n                alert(AT.authServerError);\n            }\n        },\n\n        async handleResetSubmit() {\n            var AT = this.authT();\n            var n1 = String(this.pwdReset.new1 || '');\n            var n2 = String(this.pwdReset.new2 || '');\n            if (!this.resetToken) { alert(AT.invalidToken); return; }\n            if (n1.length < 8) { alert(AT.pwdTooShort8); return; }\n            if (n1 !== n2) { alert(AT.pwdMismatch); return; }\n            try {\n                var res = await fetch(this.authUrl('reset_password'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ token: this.resetToken, new_password: n1 }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    try {\n                        var u = new URL(window.location.href);\n                        u.searchParams.delete('csp_reset');\n                        window.history.replaceState({}, '', u.pathname + (u.search || '') + (u.hash || ''));\n                    } catch (e) {}\n                    this.resetToken = '';\n                    this.authMode = 'login';\n                    this.pwdReset = { new1: '', new2: '' };\n                    alert(AT.resetOkLogin);\n                } else {\n                    alert(this.pwdErrMsg(result && result.message));\n                }\n            } catch (e) {\n                alert(AT.authServerError);\n            }\n        },\n\n        async submitChangePassword() {\n            var t = this.t[this.lang] || this.t.de;\n            var cur = String(this.pwdChange.current || '');\n            var n1 = String(this.pwdChange.new1 || '');\n            var n2 = String(this.pwdChange.new2 || '');\n            if (!cur || !n1 || !n2) { alert(t.authFillAll); return; }\n            if (n1.length < 8) { alert(t.pwdTooShort8); return; }\n            if (n1 !== n2) { alert(t.pwdMismatch); return; }\n            try {\n                var res = await fetch(this.authUrl('change_password'), {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/json' },\n                    body: JSON.stringify({ current_password: cur, new_password: n1 }),\n                    credentials: 'include'\n                });\n                var result = await res.json().catch(() => null);\n                if (result && result.status === 'success') {\n                    this.changePwdOpen = false;\n                    this.pwdChange = { current: '', new1: '', new2: '' };\n                    alert(t.pwdChangedOk);\n                } else {\n                    alert(this.pwdErrMsg(result && result.message));\n                }\n            } catch (e) {\n                alert(t.authServerError);\n            }\n        },\n\n        async logout() {\n            await fetch(this.authUrl('logout'), { credentials: 'include' });\n            this.isLoggedIn = false;\n            this.userAccess = [];\n            this.authData.password = '';\n            this.changePwdOpen = false;\n            this.pwdChange = { current: '', new1: '', new2: '' };\n        }\n    };\n}\n<\/script>\n<script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n<div id=\"csp-internal-portal-root\" class=\"bg-transparent pt-2 pb-6 font-sans\" x-data=\"portalApp()\" x-init=\"(async () => { await initApp(); })()\" x-cloak>\n\n    <div x-show=\"!isLoggedIn\" class=\"fixed inset-0 z-[99999] flex items-center justify-center bg-slate-900\">\n        <div class=\"bg-white p-8 rounded shadow-2xl w-full max-w-sm border border-slate-200\">\n            <div class=\"flex justify-end mb-3\">\n                <select x-model=\"lang\" @change=\"syncHtmlLang()\" class=\"bg-white text-slate-700 border border-slate-200 rounded px-2 py-1 text-sm outline-none cursor-pointer\">\n                    <option value=\"de\">Deutsch<\/option>\n                    <option value=\"en\">English<\/option>\n                    <option value=\"cn\">\u4e2d\u6587<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"mb-8 text-center\">\n                <div class=\"portal-header-brand mb-3\">CS Power<\/div>\n                <h2 class=\"text-xl font-bold text-slate-800\" x-text=\"authOverlayTitle()\"><\/h2>\n                <p class=\"text-xs text-slate-500 mt-1\" x-text=\"authOverlayHint()\"><\/p>\n            <\/div>\n\n            <div class=\"space-y-4\">\n                <div x-show=\"authMode === 'login'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authPassword\"><\/label>\n                        <input type=\"password\" x-model=\"authData.password\" @keydown.enter=\"handleAuth()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleAuth()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authSignIn\"><\/button>\n                    <div class=\"text-center mt-3 space-y-2\">\n                        <div>\n                            <span @click=\"authMode = 'register'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authNoAccount\"><\/span>\n                        <\/div>\n                        <div>\n                            <span @click=\"authMode = 'forgot'; authData.password = ''\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().forgotPasswordLink\"><\/span>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'register'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authPassword\"><\/label>\n                        <input type=\"password\" x-model=\"authData.password\" @keydown.enter=\"handleAuth()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleAuth()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authSubmitReg\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authBackLogin\"><\/span>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'forgot'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authEmail\"><\/label>\n                        <input type=\"email\" x-model=\"authData.email\" @keydown.enter=\"handleForgotSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleForgotSubmit()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authForgotSubmit\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authForgotBack\"><\/span>\n                    <\/div>\n                <\/div>\n\n                <div x-show=\"authMode === 'reset'\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authResetNewPwd\"><\/label>\n                        <input type=\"password\" x-model=\"pwdReset.new1\" @keydown.enter=\"handleResetSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"authT().authResetConfirm\"><\/label>\n                        <input type=\"password\" x-model=\"pwdReset.new2\" @keydown.enter=\"handleResetSubmit()\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <button type=\"button\" @click=\"handleResetSubmit()\" class=\"w-full bg-slate-900 hover:bg-slate-800 text-white py-2.5 rounded text-sm font-bold transition-colors mt-2\" x-text=\"authT().authResetSubmit\"><\/button>\n                    <div class=\"text-center mt-4\">\n                        <span @click=\"authMode = 'login'; resetToken = ''; pwdReset = { new1: '', new2: '' }\" class=\"text-xs text-slate-500 hover:text-slate-800 cursor-pointer font-medium underline underline-offset-2\" x-text=\"authT().authForgotBack\"><\/span>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div x-show=\"isLoggedIn\" class=\"w-full px-2 lg:px-4\">\n\n        <div class=\"portal-header-brand mb-1\">CS Power<\/div>\n\n        <div class=\"flex flex-row flex-wrap justify-between items-center gap-x-4 gap-y-2 border-b border-slate-200 pb-3 mb-4\">\n            <p class=\"text-sm text-slate-500 min-w-0 flex-1 pr-2\" :class=\"{ 'portal-text-de': lang === 'de' }\" :lang=\"lang === 'de' ? 'de' : lang\" style=\"overflow-wrap: anywhere;\">\n                <span x-text=\"t[lang].subtitle\"><\/span>\n                <span class=\"font-semibold text-blue-600 ml-1\" x-text=\"getUserDisplayName(userEmail)\"><\/span>\n            <\/p>\n            <div class=\"flex items-center gap-4 shrink-0\">\n                <select x-model=\"lang\" @change=\"persistPortalLang()\" class=\"bg-white text-slate-700 border border-slate-200 rounded px-2 py-1 text-sm outline-none cursor-pointer\">\n                    <option value=\"de\">Deutsch<\/option>\n                    <option value=\"en\">English<\/option>\n                    <option value=\"cn\">\u4e2d\u6587<\/option>\n                <\/select>\n                <button type=\"button\" @click=\"changePwdOpen = true\" class=\"text-sm font-semibold text-slate-700 border border-slate-300 bg-white hover:bg-slate-50 px-4 py-1.5 rounded-lg shadow-sm transition-colors\" x-text=\"t[lang].changePasswordBtn\"><\/button>\n                <button type=\"button\" @click=\"logout()\" class=\"text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 px-4 py-1.5 rounded-lg shadow-sm transition-colors\" x-text=\"t[lang].logout\"><\/button>\n            <\/div>\n        <\/div>\n\n        <h1 class=\"text-2xl lg:text-3xl font-bold text-slate-800 tracking-tight mb-8\" x-text=\"t[lang].title\"><\/h1>\n\n        <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 portal-module-grid portal-tile-grid max-w-6xl\"\n             :class=\"{ 'de-lang': lang === 'de' }\"\n             :lang=\"lang\">\n\n            <a href=\"https:\/\/cspower.de\/client-intern\" x-show=\"userAccess.includes('CRM')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-blue-100 bg-blue-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].crmTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].crmDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/engineering-intern\"\n               x-show=\"userAccess.includes('CRM') || userAccess.includes('engineering')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-indigo-100 bg-indigo-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].engTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].engDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/task-intern\" x-show=\"userAccess.includes('tasks')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-violet-100 bg-violet-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].tasksTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].tasksDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/project-intern\" x-show=\"userAccess.includes('project') || userAccess.includes('CRM')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-emerald-100 bg-emerald-50\/90 p-6 pb-14 shadow-sm transition-all hover:shadow-md\">\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].projTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].projDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/hr-intern\" x-show=\"userAccess.includes('hr')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-purple-100 bg-purple-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-purple-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].hrTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].hrDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/finance-intern\" x-show=\"userAccess.includes('finance')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-amber-100 bg-amber-50\/80 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-amber-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].finTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].finDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/sales-intern\" x-show=\"userAccess.includes('sales')\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-sky-100 bg-sky-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-sky-600\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].salesTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].salesDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/supply-intern\" x-show=\"isLoggedIn\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-teal-100 bg-teal-50\/90 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-teal-600\" x-text=\"t[lang].supplySoon\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].supplyTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].supplyDesc\"><\/p>\n            <\/a>\n\n            <a href=\"https:\/\/cspower.de\/admin-intern\"\n               x-show=\"(userAccess || []).some(function (a) { return String(a || '').toLowerCase() === 'admin'; })\"\n               class=\"relative block min-h-[158px] rounded-2xl border border-slate-200 bg-slate-50\/95 p-6 pb-14 pr-24 shadow-sm transition-all hover:shadow-md\">\n                <span class=\"absolute top-4 right-4 rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-white bg-slate-700\" x-text=\"t[lang].restricted\"><\/span>\n                <h3 class=\"portal-tile-title text-xl font-bold tracking-tight\" style=\"white-space: pre-line;\" x-text=\"t[lang].adminTitle\"><\/h3>\n                <p class=\"portal-tile-sub\" x-text=\"t[lang].adminDesc\"><\/p>\n            <\/a>\n\n        <\/div>\n\n        <div class=\"mt-8 text-left text-xs text-slate-400 font-medium max-w-3xl\" :class=\"{ 'portal-text-de': lang === 'de' }\" :lang=\"lang === 'de' ? 'de' : lang\" style=\"overflow-wrap: anywhere;\" x-text=\"t[lang].footerMsg\"><\/div>\n\n        <div x-show=\"changePwdOpen\" x-cloak class=\"fixed inset-0 z-[100000] flex items-center justify-center bg-slate-900\/60 p-4\" @keydown.escape.window=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\">\n            <div class=\"bg-white rounded-xl shadow-xl w-full max-w-sm border border-slate-200 p-6\" @click.outside=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\">\n                <h3 class=\"text-lg font-bold text-slate-800 mb-4\" x-text=\"t[lang].changePasswordTitle\"><\/h3>\n                <div class=\"space-y-3\">\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].currentPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.current\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].newPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.new1\" class=\"input-minimal py-2\">\n                    <\/div>\n                    <div>\n                        <label class=\"block text-xs font-bold text-slate-500 uppercase tracking-widest mb-1\" x-text=\"t[lang].confirmPasswordLabel\"><\/label>\n                        <input type=\"password\" x-model=\"pwdChange.new2\" @keydown.enter=\"submitChangePassword()\" class=\"input-minimal py-2\">\n                    <\/div>\n                <\/div>\n                <div class=\"flex justify-end gap-2 mt-6\">\n                    <button type=\"button\" class=\"px-4 py-2 text-sm font-semibold border border-slate-300 rounded-lg hover:bg-slate-50\" @click=\"changePwdOpen = false; pwdChange = { current: '', new1: '', new2: '' }\" x-text=\"t[lang].pwdCancel\"><\/button>\n                    <button type=\"button\" class=\"px-4 py-2 text-sm font-semibold bg-slate-900 text-white rounded-lg hover:bg-slate-800\" @click=\"submitChangePassword()\" x-text=\"t[lang].pwdSave\"><\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n<script defer src=\"https:\/\/cdn.jsdelivr.net\/npm\/alpinejs@3.14.3\/dist\/cdn.min.js\"><\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>{ await initApp(); })()&#8220; x-cloak> DeutschEnglish\u4e2d\u6587 CS Power CS Power DeutschEnglish\u4e2d\u6587 { await initApp(); })()&#8220; x-cloak> DeutschEnglish\u4e2d\u6587 CS Power CS Power DeutschEnglish\u4e2d\u6587<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"class_list":["post-1175","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/pages\/1175","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/comments?post=1175"}],"version-history":[{"count":8,"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/pages\/1175\/revisions"}],"predecessor-version":[{"id":1343,"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/pages\/1175\/revisions\/1343"}],"wp:attachment":[{"href":"https:\/\/cspower.de\/en\/wp-json\/wp\/v2\/media?parent=1175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}