{"id":6020,"date":"2025-07-17T13:41:55","date_gmt":"2025-07-17T13:41:55","guid":{"rendered":"https:\/\/visionbridge.eu\/?page_id=6020"},"modified":"2026-02-18T11:19:16","modified_gmt":"2026-02-18T11:19:16","slug":"playground","status":"publish","type":"page","link":"https:\/\/visionbridge.eu\/?page_id=6020","title":{"rendered":"playground"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"6020\" class=\"elementor elementor-6020\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0a2651d elementor-hidden-mobile e-flex e-con-boxed e-con e-parent\" data-id=\"0a2651d\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-c3985f9 e-con-full e-flex e-con e-child\" data-id=\"c3985f9\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f9316e6 elementor-invisible elementor-widget elementor-widget-image\" data-id=\"f9316e6\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;fadeInDown&quot;,&quot;_animation_delay&quot;:1000}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/visionbridge.eu\">\n\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1523\" height=\"211\" src=\"https:\/\/visionbridge.eu\/wp-content\/uploads\/2024\/09\/white_version2_150.png\" class=\"attachment-large size-large wp-image-4830\" alt=\"\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-97816d5 e-con-full e-grid e-con e-child\" data-id=\"97816d5\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-caf10d4 animated-fast desk-links elementor-invisible elementor-widget elementor-widget-heading\" data-id=\"caf10d4\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;fadeInDown&quot;,&quot;_animation_delay&quot;:1500}\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\"><a href=\"\/services\">services<\/a><\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-aaf531e animated-fast desk-links elementor-invisible elementor-widget elementor-widget-heading\" data-id=\"aaf531e\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;fadeInDown&quot;,&quot;_animation_delay&quot;:1500}\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\"><a href=\"\/work\">work<\/a><\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e3a74f0 animated-fast desk-links elementor-invisible elementor-widget elementor-widget-heading\" data-id=\"e3a74f0\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;fadeInDown&quot;,&quot;_animation_delay&quot;:1500}\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\"><a href=\"\/apps\">apps<\/a><\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cb6b910 animated-fast desk-links elementor-invisible elementor-widget elementor-widget-heading\" data-id=\"cb6b910\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;fadeInDown&quot;,&quot;_animation_delay&quot;:1500}\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\"><a href=\"\/contact\">contact<\/a><\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\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-3d45d2d e-con-full elementor-hidden-desktop elementor-hidden-tablet e-flex e-con e-parent\" data-id=\"3d45d2d\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2247a10 elementor-widget elementor-widget-html\" data-id=\"2247a10\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<head>\n    <script>\n        if ('scrollRestoration' in history) {\n            history.scrollRestoration = 'manual';\n        }\n\n        window.addEventListener('beforeunload', function () {\n            window.scrollTo(0, 0);\n        });\n    <\/script>\n<\/head>\n\n<div class=\"selector-top\"><\/div> <!-- Dieser Abschnitt schrumpft von oben nach unten -->\n<header class=\"header\" id=\"header\" class=\"visible\"> <!-- Added visible class initially -->\n    <div class=\"nav-content\" id=\"nav-content\">\n        <div class=\"logo\">\n            <img decoding=\"async\" src=\"https:\/\/visionbridge.eu\/wp-content\/uploads\/2024\/09\/logo_browser.png\" alt=\"Visionbridge Logo\" class=\"logo-img\">\n            <a href=\"\/\" class='vb_text'>\n                visionbridge\n            <\/a>\n        <\/div>\n        <div class=\"menu\">\n            <a href=\"#\" id=\"menu-toggle\" class=\"heading-menu\">menu<\/a>\n        <\/div>\n    <\/div>\n    <div class=\"menu-items\" id=\"menu-items\">\n        <ul>\n            <li><a href=\"\/services\">services<\/a><\/li>\n            <li><a href=\"\/work\">work<\/a><\/li>\n            <li><a href=\"\/apps\">apps<\/a><\/li>\n            <li><a href=\"\/contact\">contact<\/a><\/li>\n        <\/ul>\n        <div class=\"close\">\n            <a href=\"#\" id=\"close-toggle\">close<\/a>\n        <\/div>\n    <\/div>\n<\/header>\n<div class=\"selector\"><\/div> <!-- Dieser Abschnitt schrumpft von unten nach oben -->\n\n<script>\n    document.addEventListener('DOMContentLoaded', () => {\n        const header      = document.getElementById('header');\n        const menuToggle  = document.getElementById('menu-toggle');\n        const closeToggle = document.getElementById('close-toggle');\n        const navContent  = document.getElementById('nav-content');\n        const menuItems   = document.getElementById('menu-items');\n\n        \/\/ Header is already visible by default\n        header.classList.add('visible');\n\n        \/\/ Simulate menu open\/close instantly on load to prime the animation\n        menuToggle.click();\n        setTimeout(() => {\n            closeToggle.click();\n        }, 0); \/\/ Minimal delay to allow open animation to start before closing\n\n        \/\/ Men\u00fc-Open\/Close Logik\n        function simulateMenuClicks() {\n            menuToggle.addEventListener('click', (e) => {\n                e.preventDefault();\n                header.classList.add('is-open');\n                setTimeout(() => {\n                    navContent.style.display = 'none';\n                    menuItems.style.display  = 'flex';\n                }, 500);\n            });\n            closeToggle.addEventListener('click', (e) => {\n                e.preventDefault();\n                menuItems.style.display  = 'none';\n                navContent.style.display = 'flex';\n                setTimeout(() => {\n                    header.classList.remove('is-open');\n                }, 50);\n            });\n        }\n        simulateMenuClicks();\n\n        \/\/ Scroll-Handler (only hide when scrolling down, keep visible at top)\n        let lastY = window.pageYOffset;\n        window.addEventListener('scroll', () => {\n            const currentY = window.pageYOffset;\n            if (currentY < lastY) {\n                header.classList.add('visible');\n            } else if (currentY > 0) {\n                header.classList.remove('visible');\n            }\n            lastY = currentY;\n        });\n\n        \/\/ Ensure header stays visible at top on load\n        if (window.pageYOffset === 0) {\n            header.classList.add('visible');\n        }\n    });\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-f2216cf e-con-full e-flex e-con e-parent\" data-id=\"f2216cf\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-719b2e3 elementor-widget elementor-widget-html\" data-id=\"719b2e3\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"de\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>VisionBridge Wallet Playground<\/title>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r134\/three.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.12.5\/gsap.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.12.5\/ScrollTrigger.min.js\"><\/script>\n  <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\" integrity=\"sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" \/>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    body {\n      font-family: 'Montserrat', sans-serif;\n      background: linear-gradient(135deg, #390879, #000000);\n      color: #ffffff;\n      overflow-x: hidden;\n    }\n    h1, h2, h3 {\n      font-family: 'Playfair Display', sans-serif;\n    }\n    \/* Hero Section *\/\n    #hero {\n      height: 100vh;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      position: relative;\n      overflow: hidden;\n    }\n    \n    #hero .note {\n      margin-top: 1rem;\n      font-size: 0.9rem;\n      opacity: 0.8;\n      color: white;\n    }\n    \n    #hero h1 {\n      font-size: 4rem;\n      color: #B8DF10;\n      text-shadow: 0 0 20px rgba(184, 223, 16, 0.7);\n      z-index: 2;\n      animation: glow 2s ease-in-out infinite alternate;\n    }\n    #hero p {\n      font-size: 1.5rem;\n      color: #ffffff;\n      max-width: 600px;\n      text-align: center;\n      margin-top: 1rem;\n      z-index: 2;\n    }\n    #canvas-container {\n      position: absolute;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      z-index: 1;\n    }\n    @keyframes glow {\n      0% { text-shadow: 0 0 10px rgba(184, 223, 16, 0.7); }\n      100% { text-shadow: 0 0 30px rgba(184, 223, 16, 1); }\n    }\n    \/* Form Section *\/\n    #wallet-form {\n      padding: 5rem 2rem;\n                  box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);\n      background: url('data:image\/svg+xml;utf8,<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"100\" height=\"100\" viewBox=\"0 0 100 100\"><g stroke=\"%23B8DF10\" stroke-width=\"0.5\" opacity=\"0.2\"><line x1=\"0\" y1=\"0\" x2=\"100\" y2=\"0\" \/><line x1=\"0\" y1=\"20\" x2=\"100\" y2=\"20\" \/><line x1=\"0\" y1=\"40\" x2=\"100\" y2=\"40\" \/><line x1=\"0\" y1=\"60\" x2=\"100\" y2=\"60\" \/><line x1=\"0\" y1=\"80\" x2=\"100\" y2=\"80\" \/><line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"100\" \/><line x1=\"20\" y1=\"0\" x2=\"20\" y2=\"100\" \/><line x1=\"40\" y1=\"0\" x2=\"40\" y2=\"100\" \/><line x1=\"60\" y1=\"0\" x2=\"60\" y2=\"100\" \/><line x1=\"80\" y1=\"0\" x2=\"80\" y2=\"100\" \/><\/g><\/svg>') repeat;\n      background-color: rgba(57, 8, 121, 0.8);\n      backdrop-filter: blur(10px);\n      display: flex;\n      justify-content: center;\n      position: relative;\n    }\n    \/* Selector & Buttons *\/\n    .selector {\n      font-family: 'Montserrat', sans-serif;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      color: #390879;\n      margin-bottom: 2rem;\n    }\n    .button-container {\n      display: flex;\n      gap: 20px;\n      margin-bottom: 2rem;\n    }\n    .button {\n      background-color: transparent;\n      border: 2px solid #390879;\n      color: #390879;\n      cursor: pointer;\n      padding: 10px 28px;\n      font-size: 1rem;\n      text-align: center;\n      border-radius: 8px;\n      transition: background-color .3s, color .3s;\n    }\n    .button.active {\n      background-color: #B8DF10;\n      color: #390879 !important;\n    }\n    .button.inactive {\n      background-color: transparent;\n      border: 2px solid white;\n      color: white !important;\n      cursor: pointer;\n      padding: 10px 28px;\n      font-size: 1rem;\n      text-align: center;\n      border-radius: 8px;\n      transition: background-color .3s, color .3s;\n    }\n    \/* Form-Container *\/\n    .form-container {\n      max-width: 600px;\n      width: 100%;\n      display: none;\n      opacity: 0;\n      transform: translateY(50px);\n      transition: all .6s ease-out;\n    }\n    .form-container.active {\n      display: block;\n      opacity: 1;\n      transform: translateY(0);\n    }\n    .form-container h2 {\n      font-size: 2.5rem;\n      color: #B8DF10;\n      margin-bottom: 2rem;\n      text-align: center;\n      text-shadow: 0 0 15px rgba(184, 223, 16, 0.7);\n    }\n    .form-group {\n      margin-bottom: 1.5rem;\n      position: relative;\n      opacity: 0;\n      transform: translateX(-50px);\n    }\n    .form-group select,\n    .form-group input,\n    .form-group textarea {\n      width: 100%;\n      padding: 1rem;\n      background: rgba(255, 255, 255, 0.1);\n      border: 2px solid #B8DF10;\n      border-radius: 8px;\n      color: #ffffff;\n      font-family: 'Montserrat', sans-serif;\n      font-size: 1rem;\n      transition: all 0.3s ease;\n      animation: pulse 3s ease-in-out infinite;\n    }\n    @keyframes pulse {\n      0% { box-shadow: 0 0 5px rgba(184, 223, 16, 0.3); }\n      50% { box-shadow: 0 0 15px rgba(184, 223, 16, 0.7); }\n      100% { box-shadow: 0 0 5px rgba(184, 223, 16, 0.3); }\n    }\n    .form-group select:focus,\n    .form-group input:focus,\n    .form-group textarea:focus {\n      outline: none;\n      box-shadow: 0 0 20px rgba(184, 223, 16, 0.8);\n      background: rgba(255, 255, 255, 0.2);\n      transform: scale(1.02);\n    }\n    .form-group label {\n      position: absolute;\n      top: -0.75rem;\n      left: 1rem;\n      background: #390879;\n      padding: 0 0.5rem;\n      color: #B8DF10;\n      font-size: 0.9rem;\n      transition: all 0.3s ease;\n    }\n    .form-group select:valid + label,\n    .form-group input:valid + label,\n    .form-group textarea:valid + label {\n      color: #ffffff;\n    }\n    .submit-btn {\n      display: block;\n      width: 100%;\n      padding: 1rem;\n      background: linear-gradient(45deg, #B8DF10, #ffffff) !important;\n      color: #390879 !important;\n      font-family: 'Montserrat', sans-serif;\n      font-size: 1.2rem;\n      border: none;\n      border-radius: 8px;\n      cursor: pointer;\n      position: relative;\n      overflow: hidden;\n      transition: all 0.3s ease;\n    }\n    .submit-btn::before {\n      content: '';\n      position: absolute;\n      top: 50%;\n      left: 50%;\n      width: 0;\n      height: 0;\n      background: rgba(255, 255, 255, 0.3);\n      border-radius: 50%;\n      transform: translate(-50%, -50%);\n      transition: width 0.6s ease, height 0.6s ease;\n    }\n    .submit-btn:hover::before {\n      width: 300px;\n      height: 300px;\n    }\n    .submit-btn:hover {\n      transform: translateY(-2px);\n      box-shadow: 0 0 20px rgba(184, 223, 16, 0.7);\n    }\n    \/* Anmerkung *\/\n    .note {\n      font-size: 0.7rem;\n      color: #ffffff;\n      text-align: center;\n      margin-top: 1rem;\n      opacity: 0.7;\n    }\n    \/* Overlay mit Spinner *\/\n    #loadingOverlay {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      background: rgba(0,0,0,0.7);\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      z-index: 9999;\n      visibility: hidden;\n      opacity: 0;\n      transition: opacity .3s ease;\n    }\n    #loadingOverlay.visible {\n      visibility: visible;\n      opacity: 1;\n    }\n    .spinner {\n      margin: 0 auto;\n      width: 80px;\n      height: 80px;\n      border: 8px solid rgba(184,223,16,0.3);\n      border-top: 8px solid #B8DF10;\n      border-radius: 50%;\n      animation: spin 1s linear infinite;\n      box-shadow: 0 0 20px rgba(184,223,16,0.7);\n    }\n    @keyframes spin {\n      to { transform: rotate(360deg); }\n    }\n    .loading-text {\n      margin-top: 1rem;\n      color: #B8DF10;\n      font-size: 1.2rem;\n      text-shadow: 0 0 10px rgba(184,223,16,0.7);\n    }\n    \n      #language-switcher {\n  position: fixed;\n  bottom: 20px;\n  right: 20px;\n  background: linear-gradient(135deg, #B8DF10, #ffffff);\n  color: #390879;\n  font-size: 1.5rem;\n  padding: 12px 16px;\n  border-radius: 50px;\n  box-shadow: 0 0 15px rgba(184, 223, 16, 0.5);\n  cursor: pointer;\n  z-index: 99999;\n  transition: all 0.3s ease-in-out;\n  animation: pulseLangBtn 3s infinite ease-in-out;\n}\n#language-switcher:hover {\n  transform: scale(1.1);\n  box-shadow: 0 0 25px rgba(255, 255, 255, 0.8);\n}\n\n@keyframes pulseLangBtn {\n  0% { box-shadow: 0 0 5px rgba(184,223,16,0.3); }\n  50% { box-shadow: 0 0 20px rgba(184,223,16,0.9); }\n  100% { box-shadow: 0 0 5px rgba(184,223,16,0.3); }\n}\n    \n    \/* Responsive Design *\/\n    @media (max-width: 1024px) {\n      #hero h1 {\n        font-size: 2.5rem;\n        text-align: center;\n        padding: 0 1rem;\n      }\n      #hero p {\n        font-size: 1.2rem;\n        max-width: 90%;\n        padding: 0 1rem;\n      }\n      .form-container h2 {\n        font-size: 2rem;\n      }\n      \n          #hero .note {\n      margin-top: 1rem;\n      font-size: 0.9rem;\n      opacity: 0.8;\n      color: red;\n    }\n    }\n    \n    \n    \n    \n              @media (max-width: 768px) {\n    #language-switcher {\n        top: auto;          \/* oben zur\u00fccksetzen *\/\n        right: auto;        \/* rechts zur\u00fccksetzen *\/\n        bottom: 20px;       \/* 20px vom unteren Rand *\/\n        left: 50%;          \/* genau in der Mitte *\/\n        transform: translateX(-50%);  \/* zentrieren *\/\n        font-size: 1.2rem;  \/* evtl. etwas kleiner *\/\n        padding: 8px 12px;  \/* kompakter *\/\n        opacity: 0.85;      \/* leicht transparent, damit es weniger aufdringlich wirkt *\/\n        transition: transform 0.3s ease, opacity 0.3s ease;\n  }\n  \n    #language-switcher.hidden {\n    transform: translate(-50%, 100%);\n    opacity: 0;\n  }\n  \n    \/* und null Hover *\/\n  #language-switcher:hover {\n    transform: translateX(-50%); \/* nur zentriert, keine Skalierung *\/\n    box-shadow: 0 0 15px rgba(184,223,16,0.5);\n  }\n              }\n\n  <\/style>\n<\/head>\n<body>\n  <!-- Sprache wechseln -->\n  <div id=\"language-switcher\" onclick=\"toggleLanguage()\">\n    <i class=\"fas fa-globe\"><\/i>\n  <\/div>\n\n  <!-- Hero Section -->\n  <section id=\"hero\">\n    <div id=\"canvas-container\"><\/div>\n    <h1 data-i18n-key=\"title\">Willkommen im VisionBridge Wallet Playground<\/h1>\n    <p data-i18n-key=\"subtitle\">\n      Erstelle und teste eigene Wallet-Cards! Experimentiere mit verschiedenen Designs und Funktionen \u2013 ein sicherer Ort zum Ausprobieren.\n    <\/p>\n    <p class=\"note\" data-i18n-key=\"hint\">\n      Hinweis: Aktuell werden hier nur iOS-Wallet-P\u00e4sse unterst\u00fctzt.\n    <\/p>\n  <\/section>\n\n  <!-- Loading Overlay -->\n  <div id=\"loadingOverlay\">\n    <div class=\"spinner\"><\/div>\n    <div class=\"loading-text\" data-i18n-key=\"loading\">Pass wird erstellt\u2026<\/div>\n  <\/div>\n\n  <!-- Wallet Form Section -->\n  <section id=\"wallet-form\">\n    <div class=\"selector\">\n      <div class=\"button-container\">\n        <!-- Wichtig: hier die IDs generic\/event verwenden -->\n        <a class=\"button\" id=\"generic\" data-i18n-key=\"btnGeneric\">Generic<\/a>\n        <a class=\"button inactive\" id=\"event\" data-i18n-key=\"btnEvent\">Event<\/a>\n      <\/div>\n\n      <!-- Generic Form -->\n      <div class=\"form-container\" id=\"genericForm\">\n        <h2 data-i18n-key=\"headingGeneric\">Generic Pass erstellen<\/h2>\n        <form id=\"genericPassForm\" novalidate>\n          <input type=\"hidden\" id=\"layout\" value=\"standard\" \/>\n\n          <div class=\"form-group\">\n            <input type=\"text\" id=\"membershipId\" required \/>\n            <label for=\"membershipId\" data-i18n-key=\"labelMembership\">Mitgliedsnummer<\/label>\n            <div id=\"membershipIdError\" class=\"error\">Mitgliedsnummer ist erforderlich.<\/div>\n          <\/div>\n\n          <div id=\"genericFields\">\n            <div class=\"form-group\">\n              <input type=\"text\" id=\"name\" required \/>\n              <label for=\"name\" data-i18n-key=\"labelName\">Name<\/label>\n              <div id=\"nameError\" class=\"error\">Name ist erforderlich.<\/div>\n            <\/div>\n            <div class=\"form-group\">\n              <input type=\"date\" id=\"birthDate\" required \/>\n              <label for=\"birthDate\" data-i18n-key=\"labelBirthDate\">Geburtsdatum (YYYY-MM-DD)<\/label>\n              <div id=\"birthDateError\" class=\"error\">Geburtsdatum ist erforderlich.<\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"form-group\">\n            <input type=\"text\" id=\"organizationName\" \/>\n            <label for=\"organizationName\" data-i18n-key=\"labelOrganization\">\n              Organisation \/ Firma (optional)\n            <\/label>\n          <\/div>\n\n          <div class=\"form-group\">\n            <input type=\"email\" id=\"email\" \/>\n            <label for=\"email\" data-i18n-key=\"labelEmail\">E-Mail (Pass Empf\u00e4nger)<\/label>\n            <div id=\"emailError\" class=\"error\">Ung\u00fcltige E-Mail-Adresse.<\/div>\n          <\/div>\n\n          <button type=\"submit\" class=\"submit-btn\" data-i18n-key=\"submitGeneric\">\n            Erstellen\n          <\/button>\n        <\/form>\n      <\/div>\n\n      <!-- Event Form -->\n      <div class=\"form-container\" id=\"eventForm\">\n        <h2 data-i18n-key=\"headingEvent\">Event Pass erstellen<\/h2>\n        <form id=\"eventPassForm\" novalidate>\n          <input type=\"hidden\" id=\"layoutEvent\" value=\"event\" \/>\n\n          <div class=\"form-group\">\n            <input type=\"text\" id=\"membershipIdE\" required \/>\n            <label for=\"membershipIdE\" data-i18n-key=\"labelMembership\">\n              Mitgliedsnummer\n            <\/label>\n            <div id=\"membershipIdErrorE\" class=\"error\">Mitgliedsnummer ist erforderlich.<\/div>\n          <\/div>\n\n          <div id=\"eventFields\">\n            <div class=\"form-group\">\n              <input type=\"text\" id=\"eventName\" required \/>\n              <label for=\"eventName\" data-i18n-key=\"labelEventName\">\n                Event-Name\n              <\/label>\n              <div id=\"eventNameError\" class=\"error\">Event-Name ist erforderlich.<\/div>\n            <\/div>\n            <div class=\"form-group\">\n              <input type=\"date\" id=\"eventDate\" required \/>\n              <label for=\"eventDate\" data-i18n-key=\"labelEventDate\">\n                Event-Datum (YYYY-MM-DD)\n              <\/label>\n              <div id=\"eventDateError\" class=\"error\">Event-Datum ist erforderlich.<\/div>\n            <\/div>\n            <div class=\"form-group\">\n              <input type=\"text\" id=\"eventLocation\" required \/>\n              <label for=\"eventLocation\" data-i18n-key=\"labelEventLocation\">\n                Ort\n              <\/label>\n              <div id=\"eventLocationError\" class=\"error\">Ort ist erforderlich.<\/div>\n            <\/div>\n            <div class=\"form-group\">\n              <textarea id=\"eventDescription\" rows=\"3\" required><\/textarea>\n              <label for=\"eventDescription\" data-i18n-key=\"labelEventDesc\">\n                Beschreibung\n              <\/label>\n              <div id=\"eventDescriptionError\" class=\"error\">\n                Beschreibung ist erforderlich.\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"form-group\">\n            <input type=\"text\" id=\"organizationNameE\" \/>\n            <label for=\"organizationNameE\" data-i18n-key=\"labelOrganization\">\n              Organisation \/ Firma (optional)\n            <\/label>\n          <\/div>\n\n          <div class=\"form-group\">\n            <input type=\"email\" id=\"emailE\" \/>\n            <label for=\"emailE\" data-i18n-key=\"labelEmail\">\n              E-Mail (Pass Empf\u00e4nger)\n            <\/label>\n            <div id=\"emailErrorE\" class=\"error\">Ung\u00fcltige E-Mail-Adresse.<\/div>\n          <\/div>\n\n          <button type=\"submit\" class=\"submit-btn\" data-i18n-key=\"submitEvent\">\n            Erstellen\n          <\/button>\n        <\/form>\n      <\/div>\n\n      <p class=\"note\" data-i18n-key=\"testNote\">\n        Hinweis: Dies ist ein Test-Playground f\u00fcr Wallet-Cards. Erstellte P\u00e4sse sind nur zum Experimentieren gedacht.\n      <\/p>\n    <\/div>\n  <\/section>\n\n\n  <script>\n \/\/ 1) currentLang initial auf default oder gespeicherte Sprache\n  let currentLang = localStorage.getItem(\"lang\") || \"de\";\n\n  \/\/ 2) Alle Texte in beiden Sprachen\n  const translations = {\n    de: {\n      title: \"Willkommen im VisionBridge Wallet Playground\",\n      subtitle: \"Erstelle und teste eigene Wallet-Cards! Experimentiere mit verschiedenen Designs und Funktionen \u2013 ein sicherer Ort zum Ausprobieren.\",\n      hint: \"Hinweis: Aktuell werden hier nur iOS-Wallet-P\u00e4sse unterst\u00fctzt.\",\n      btnGeneric: \"Generic\",\n      btnEvent: \"Event\",\n      headingGeneric: \"Generic Pass erstellen\",\n      headingEvent: \"Event Pass erstellen\",\n      labelMembership: \"Mitgliedsnummer\",\n      labelName: \"Name\",\n      labelBirthDate: \"Geburtsdatum (YYYY-MM-DD)\",\n      labelOrganization: \"Organisation \/ Firma (optional)\",\n      labelEmail: \"E-Mail (Pass Empf\u00e4nger)\",\n      labelEventName: \"Event-Name\",\n      labelEventDate: \"Event-Datum (YYYY-MM-DD)\",\n      labelEventLocation: \"Ort\",\n      labelEventDesc: \"Beschreibung\",\n      submitGeneric: \"Erstellen\",\n      submitEvent: \"Erstellen\",\n      loading: \"Pass wird erstellt\u2026\",\n      testNote: \"Hinweis: Dies ist ein Test-Playground f\u00fcr Wallet-Cards. Erstellte P\u00e4sse sind nur zum Experimentieren gedacht.\",\n                    loading: \"Pass wird erstellt\u2026\",\n      success: \"Schau in dein Postfach!\",\n      errors: {\n        membershipRequired: \"Mitgliedsnummer ist erforderlich.\",\n        nameRequired: \"Name ist erforderlich.\",\n        birthDateRequired: \"Geburtsdatum ist erforderlich.\",\n        emailInvalid: \"Ung\u00fcltige E-Mail-Adresse.\",\n        eventNameRequired: \"Event-Name ist erforderlich.\",\n        eventDateRequired: \"Event-Datum ist erforderlich.\",\n        eventLocationRequired: \"Ort ist erforderlich.\",\n        eventDescRequired: \"Beschreibung ist erforderlich.\",\n      }\n    },\n    en: {\n      title: \"Welcome to the VisionBridge Wallet Playground\",\n      subtitle: \"Create and test your own Wallet Cards! Experiment with various designs and features \u2013 a safe space to try things out.\",\n      hint: \"Note: Only iOS Wallet passes are currently supported.\",\n      btnGeneric: \"Generic\",\n      btnEvent: \"Event\",\n      headingGeneric: \"Create Generic Pass\",\n      headingEvent: \"Create Event Pass\",\n      labelMembership: \"Membership ID\",\n      labelName: \"Name\",\n      labelBirthDate: \"Birth Date (YYYY-MM-DD)\",\n      labelOrganization: \"Organization \/ Company (optional)\",\n      labelEmail: \"Email (Pass Recipient)\",\n      labelEventName: \"Event Name\",\n      labelEventDate: \"Event Date (YYYY-MM-DD)\",\n      labelEventLocation: \"Location\",\n      labelEventDesc: \"Description\",\n      submitGeneric: \"Create\",\n      submitEvent: \"Create\",\n      loading: \"Creating your pass\u2026\",\n      testNote: \"Note: This is a test playground for Wallet Cards. Created passes are for experimentation only.\",\n                    loading: \"Creating your pass\u2026\",\n      success: \"Check your inbox!\",\n      errors: {\n        membershipRequired: \"Membership ID is required.\",\n        nameRequired: \"Name is required.\",\n        birthDateRequired: \"Birth date is required.\",\n        emailInvalid: \"Invalid email address.\",\n        eventNameRequired: \"Event name is required.\",\n        eventDateRequired: \"Event date is required.\",\n        eventLocationRequired: \"Location is required.\",\n        eventDescRequired: \"Description is required.\",\n      }\n    }\n  };\n\n  \/\/ 3) Beim Laden: gespeicherte Sprache anwenden (ohne Umschalten)\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    applyLanguage();\n    initFormToggle(); \/\/ falls Du Dein Formular-Toggle so brauchst\n    \/\/ initScene(), animate() etc. kannst Du hier ebenfalls aufrufen\n  });\n\n  \/\/ 4) Funktion, die **nur** die Sprache anwendet\n  function applyLanguage() {\n    const dict = translations[currentLang];\n    \/\/ alle Elemente mit data-i18n-key\n    document.querySelectorAll(\"[data-i18n-key]\").forEach(el => {\n      const key = el.getAttribute(\"data-i18n-key\");\n      if (dict[key]) el.textContent = dict[key];\n    });\n    \/\/ Fehlermeldungen updaten\n    Object.keys(translations.de.errors).forEach(k => {\n      document.querySelectorAll(\".error\").forEach(el => {\n        const txt = el.textContent.trim();\n        if (txt === translations.de.errors[k] || txt === translations.en.errors[k]) {\n          el.textContent = translations[currentLang].errors[k];\n        }\n      });\n    });\n  }\n\n  \/\/ 5) Funktion, die beim Globe-Button das tats\u00e4chliche Umschalten macht\n  function toggleLanguage() {\n    currentLang = currentLang === \"de\" ? \"en\" : \"de\";\n    localStorage.setItem(\"lang\", currentLang);\n    applyLanguage();\n  }\n\n  \/\/ 6) Beispiel-Init f\u00fcr Dein Formular-Toggle (IDs generic\/event)\n  function initFormToggle() {\n    const btnGen  = document.getElementById(\"generic\");\n    const btnEvt  = document.getElementById(\"event\");\n    const formGen = document.getElementById(\"genericForm\");\n    const formEvt = document.getElementById(\"eventForm\");\n\n    \/\/ Standardzustand\n    btnGen.classList.add(\"active\");\n    formGen.classList.add(\"active\");\n\n    btnGen.addEventListener(\"click\", () => {\n      btnGen.classList.add(\"active\");    btnEvt.classList.replace(\"active\",\"inactive\");\n      formGen.classList.add(\"active\");   formEvt.classList.remove(\"active\");\n    });\n    btnEvt.addEventListener(\"click\", () => {\n      btnEvt.classList.add(\"active\");    btnGen.classList.replace(\"active\",\"inactive\");\n      formEvt.classList.add(\"active\");   formGen.classList.remove(\"active\");\n    });\n  }\n\n  \n  \n  \n    \/\/ Funktion: Overlay ein-\/ausblenden\n    function showLoading(show) {\n      const ovl = document.getElementById('loadingOverlay');\n      if (show) {\n        ovl.classList.add('visible');\n        ovl.querySelector('.spinner').style.display = 'block';\n        ovl.querySelector('.loading-text').textContent = 'Pass wird erstellt\u2026';\n      } else {\n        ovl.classList.remove('visible');\n      }\n    }\n\n    let scene, camera, renderer, particlesMesh, sphere, velocities;\n\n    \/\/ Detect device and initialize appropriate animation\n    let isMobileOrTablet = window.innerWidth <= 1024;\n\n    function initScene() {\n      if (renderer) renderer.dispose();\n      if (particlesMesh) scene.remove(particlesMesh);\n      if (sphere) scene.remove(sphere);\n      document.getElementById('canvas-container').innerHTML = '';\n      \n      scene = new THREE.Scene();\n      camera = new THREE.PerspectiveCamera(75, window.innerWidth \/ window.innerHeight, 0.1, 1000);\n      renderer = new THREE.WebGLRenderer({ alpha: true });\n      renderer.setSize(window.innerWidth, window.innerHeight);\n      document.getElementById('canvas-container').appendChild(renderer.domElement);\n\n      if (isMobileOrTablet) {\n        const particlesGeometry = new THREE.BufferGeometry();\n        const particlesCount = 800;\n        const posArray = new Float32Array(particlesCount * 3);\n        velocities = new Float32Array(particlesCount * 3);\n        for (let i = 0; i < particlesCount; i++) {\n          posArray[i * 3] = (Math.random() - 0.5) * 15;\n          posArray[i * 3 + 1] = (Math.random() - 0.5) * 15;\n          posArray[i * 3 + 2] = (Math.random() - 0.5) * 15;\n          velocities[i * 3] = (Math.random() - 0.5) * 0.03;\n          velocities[i * 3 + 1] = (Math.random() - 0.5) * 0.03;\n          velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.03;\n        }\n        particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));\n        const particlesMaterial = new THREE.PointsMaterial({\n          size: 0.12,\n          color: 0xB8DF10,\n          transparent: true,\n          opacity: 0.6,\n          blending: THREE.AdditiveBlending\n        });\n        particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);\n        scene.add(particlesMesh);\n        camera.position.z = 20;\n      } else {\n        const geometry = new THREE.SphereGeometry(5, 32, 32);\n        const material = new THREE.MeshBasicMaterial({\n          color: 0xB8DF10,\n          wireframe: true,\n          transparent: true,\n          opacity: 0.5\n        });\n        sphere = new THREE.Mesh(geometry, material);\n        scene.add(sphere);\n\n        const particlesGeometry = new THREE.BufferGeometry();\n        const particlesCount = 1000;\n        const posArray = new Float32Array(particlesCount * 3);\n        for (let i = 0; i < particlesCount * 3; i++) {\n          posArray[i] = (Math.random() - 0.5) * 20;\n        }\n        particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));\n        const particlesMaterial = new THREE.PointsMaterial({\n          size: 0.05,\n          color: 0xB8DF10,\n          transparent: true,\n          opacity: 0.8\n        });\n        particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);\n        scene.add(particlesMesh);\n        camera.position.z = 10;\n      }\n    }\n\n    let mouseX = 0, mouseY = 0;\n    document.addEventListener('mousemove', (e) => {\n      mouseX = (e.clientX \/ window.innerWidth) * 2 - 1;\n      mouseY = -(e.clientY \/ window.innerHeight) * 2 + 1;\n    });\n    document.addEventListener('touchmove', (e) => {\n      const touch = e.touches[0];\n      mouseX = (touch.clientX \/ window.innerWidth) * 2 - 1;\n      mouseY = -(touch.clientY \/ window.innerHeight) * 2 + 1;\n    }, { passive: true });\n\n    function animate() {\n      requestAnimationFrame(animate);\n      if (isMobileOrTablet && particlesMesh) {\n        const positions = particlesMesh.geometry.attributes.position.array;\n        const time = Date.now() * 0.001;\n        for (let i = 0; i < particlesMesh.geometry.attributes.position.count; i++) {\n          positions[i * 3] += velocities[i * 3] + Math.sin(time + i * 0.1) * 0.01 * mouseX;\n          positions[i * 3 + 1] += velocities[i * 3 + 1] + Math.cos(time + i * 0.1) * 0.01 * mouseY;\n          positions[i * 3 + 2] += velocities[i * 3 + 2];\n          if (Math.abs(positions[i * 3]) > 10) velocities[i * 3] *= -0.9;\n          if (Math.abs(positions[i * 3 + 1]) > 10) velocities[i * 3 + 1] *= -0.9;\n          if (Math.abs(positions[i * 3 + 2]) > 10) velocities[i * 3 + 2] *= -0.9;\n        }\n        particlesMesh.geometry.attributes.position.needsUpdate = true;\n        particlesMesh.rotation.z += 0.002;\n      } else if (sphere && particlesMesh) {\n        sphere.rotation.y += 0.002 + mouseX * 0.01;\n        sphere.rotation.x += mouseY * 0.01;\n        particlesMesh.rotation.y += 0.001;\n      }\n      if (renderer) renderer.render(scene, camera);\n    }\n\n    initScene();\n    animate();\n\n    window.addEventListener('resize', () => {\n      const oldIsMobileOrTablet = isMobileOrTablet;\n      camera.aspect = window.innerWidth \/ window.innerHeight;\n      camera.updateProjectionMatrix();\n      renderer.setSize(window.innerWidth, window.innerHeight);\n      isMobileOrTablet = window.innerWidth <= 1024;\n      if (oldIsMobileOrTablet !== isMobileOrTablet) {\n        initScene();\n      }\n    });\n\n    gsap.from(\"#hero h1\", {\n      opacity: 0,\n      y: 100,\n      duration: 1.5,\n      ease: \"power3.out\"\n    });\n    gsap.from(\"#hero p\", {\n      opacity: 0,\n      y: 50,\n      duration: 1.5,\n      delay: 0.5,\n      ease: \"power3.out\"\n    });\n\n    gsap.to(\".form-container\", {\n      scrollTrigger: {\n        trigger: \"#wallet-form\",\n        start: \"top 80%\",\n        toggleActions: \"play none none none\"\n      },\n      opacity: 1,\n      y: 0,\n      duration: 1,\n      ease: \"power3.out\"\n    });\n    gsap.to(\".form-group\", {\n      scrollTrigger: {\n        trigger: \"#wallet-form\",\n        start: \"top 80%\",\n        toggleActions: \"play none none none\"\n      },\n      opacity: 1,\n      x: 0,\n      duration: 0.8,\n      stagger: 0.2,\n      ease: \"power3.out\"\n    });\n    gsap.to(\".submit-btn\", {\n      scrollTrigger: {\n        trigger: \"#wallet-form\",\n        start: \"top 80%\",\n        toggleActions: \"play none none none\"\n      },\n      opacity: 1,\n      scale: 1,\n      duration: 0.8,\n      delay: 0.6,\n      ease: \"power3.out\"\n    });\n\n    const layoutSel   = document.getElementById('layout');\n    const genFields   = document.getElementById('genericFields');\n    const evFields    = document.getElementById('eventFields');\n    const errors = {\n      membershipId: document.getElementById('membershipIdError'),\n      name:         document.getElementById('nameError'),\n      birthDate:    document.getElementById('birthDateError'),\n      eventName:    document.getElementById('eventNameError'),\n      eventDate:    document.getElementById('eventDateError'),\n      eventLocation: document.getElementById('eventLocationError'),\n      eventDescription: document.getElementById('eventDescriptionError'),\n      email:        document.getElementById('emailError')\n    };\n\n    document.addEventListener('DOMContentLoaded', () => {\n      const buttons = document.querySelectorAll('.button');\n      const forms = {\n        generic: document.getElementById('genericForm'),\n        event: document.getElementById('eventForm')\n      };\n\n      forms.generic.classList.add('active');\n      document.getElementById('generic').classList.add('active');\n      document.getElementById('event').classList.add('inactive');\n\n      buttons.forEach(button => {\n        button.addEventListener('click', () => {\n          buttons.forEach(btn => {\n            btn.classList.remove('active');\n            btn.classList.remove('inactive');\n          });\n          button.classList.add('active');\n          const inactiveButton = button.id === 'generic' ? document.getElementById('event') : document.getElementById('generic');\n          inactiveButton.classList.add('inactive');\n          Object.values(forms).forEach(f => f.classList.remove('active'));\n          const key = button.id === 'generic' ? 'generic' : 'event';\n          forms[key].classList.add('active');\n          layoutSel.value = key === 'generic' ? 'standard' : 'event';\n          if (key === 'event') {\n            genFields.classList.add('hidden');\n            evFields.classList.remove('hidden');\n          } else {\n            genFields.classList.remove('hidden');\n            evFields.classList.add('hidden');\n          }\n          Object.values(errors).forEach(el => el.style.display = 'none');\n        });\n      });\n    });\n\n\/\/ Event-Listener f\u00fcr Generic-Form\ndocument.getElementById('genericPassForm').addEventListener('submit', async e => {\n  e.preventDefault();\n  const form = e.target;\n  const ovl  = document.getElementById('loadingOverlay');\n\n  \/\/ Overlay anzeigen\n  ovl.querySelector('.spinner').style.display     = 'block';\n  ovl.querySelector('.loading-text').textContent = translations[currentLang].loading;\n  showLoading(true);\n\n  \/\/ Fehlermeldungen verstecken\n  form.querySelectorAll('.error').forEach(div => div.style.display = 'none');\n\n  \/\/ Felder auslesen\n  const layout           = 'standard';\n  const membershipId     = form.querySelector('#membershipId').value.trim();\n  const name             = form.querySelector('#name').value.trim();\n  const birthDate        = form.querySelector('#birthDate').value;\n  const organizationName = form.querySelector('#organizationName').value.trim() || null;\n  const email            = form.querySelector('#email').value.trim() || null;\n\n  \/\/ Validierung\n  let isValid = true;\n  if (!membershipId) { form.querySelector('#membershipIdError').style.display = 'block'; isValid = false; }\n  if (!name)         { form.querySelector('#nameError').style.display         = 'block'; isValid = false; }\n  if (!birthDate)    { form.querySelector('#birthDateError').style.display    = 'block'; isValid = false; }\n  if (email && !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(email)) {\n    form.querySelector('#emailError').style.display = 'block';\n    isValid = false;\n  }\n  if (!isValid) {\n    showLoading(false);\n    return;\n  }\n\n  \/\/ Request senden\n  try {\n    const resp = await fetch('\/api\/wallet\/generate', {\n      method:  'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body:    JSON.stringify({ layout, membershipId, name, birthDate, organizationName, email })\n    });\n    if (resp.ok) {\n \/\/ Spinner ausblenden, Success-Text setzen\n      ovl.querySelector('.spinner').style.display     = 'none';\n      ovl.querySelector('.loading-text').textContent = translations[currentLang].success;\n      \/\/ Overlay nach 3s ganz verschwinden lassen\n      setTimeout(() => showLoading(false), 3000);\n    } else {\n      showLoading(false);\n      alert(translations[currentLang].errors.generic || 'Fehler beim Erstellen des Passes.');\n    }\n  } catch (err) {\n    console.error(err);\n    showLoading(false);\n    alert(translations[currentLang].errors.network || 'Netzwerk-Fehler.');\n  }\n});\n\n\/\/ Event-Listener f\u00fcr Event-Form\ndocument.getElementById('eventPassForm').addEventListener('submit', async e => {\n  e.preventDefault();\n  const form = e.target;\n  const ovl  = document.getElementById('loadingOverlay');\n\n  \/\/ Overlay anzeigen\n  ovl.querySelector('.spinner').style.display     = 'block';\n  ovl.querySelector('.loading-text').textContent = translations[currentLang].loading;\n  showLoading(true);\n\n  \/\/ Fehlermeldungen verstecken\n  form.querySelectorAll('.error').forEach(div => div.style.display = 'none');\n\n  \/\/ Felder auslesen (mit E-Suffix)\n  const layout           = 'event';\n  const membershipId     = form.querySelector('#membershipIdE').value.trim();\n  const eventName        = form.querySelector('#eventName').value.trim();\n  const eventDate        = form.querySelector('#eventDate').value;\n  const eventLocation    = form.querySelector('#eventLocation').value.trim();\n  const eventDescription = form.querySelector('#eventDescription').value.trim();\n  const organizationName = form.querySelector('#organizationNameE').value.trim() || null;\n  const email            = form.querySelector('#emailE').value.trim() || null;\n\n  \/\/ Validierung\n  let isValid = true;\n  if (!membershipId)     { form.querySelector('#membershipIdErrorE').style.display    = 'block'; isValid = false; }\n  if (!eventName)        { form.querySelector('#eventNameError').style.display         = 'block'; isValid = false; }\n  if (!eventDate)        { form.querySelector('#eventDateError').style.display         = 'block'; isValid = false; }\n  if (!eventLocation)    { form.querySelector('#eventLocationError').style.display     = 'block'; isValid = false; }\n  if (!eventDescription) { form.querySelector('#eventDescriptionError').style.display  = 'block'; isValid = false; }\n  if (email && !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(email)) {\n    form.querySelector('#emailErrorE').style.display = 'block';\n    isValid = false;\n  }\n  if (!isValid) {\n    showLoading(false);\n    return;\n  }\n\n  \/\/ Request senden\n  try {\n    const resp = await fetch('\/api\/wallet\/generate', {\n      method:  'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body:    JSON.stringify({\n        layout,\n        membershipId,\n        eventName,\n        eventDate,\n        eventLocation,\n        eventDescription,\n        organizationName,\n        email\n      })\n    });\n    if (resp.ok) {\n      ovl.querySelector('.spinner').style.display     = 'none';\n      ovl.querySelector('.loading-text').textContent = translations[currentLang].success;\n      setTimeout(() => showLoading(false), 3000);\n    } else {\n      showLoading(false);\n      alert(translations[currentLang].errors.event || 'Fehler beim Erstellen des Event-Passes.');\n    }\n  } catch (err) {\n    console.error(err);\n    showLoading(false);\n    alert(translations[currentLang].errors.network || 'Netzwerk-Fehler.');\n  }\n});\n\n              \/\/ nur auf Mobilen aktivieren\n    if (window.matchMedia(\"(max-width: 768px)\").matches) {\n      let lastScroll = window.pageYOffset;\n      const langBtn   = document.getElementById(\"language-switcher\");\n      window.addEventListener(\"scroll\", () => {\n        const current = window.pageYOffset;\n        if (current > lastScroll) {\n          \/\/ runterscrollen \u2192 einklappen\n          langBtn.classList.add(\"hidden\");\n        } else {\n          \/\/ hochscrollen \u2192 wieder ausfahren\n          langBtn.classList.remove(\"hidden\");\n        }\n        lastScroll = current <= 0 ? 0 : current;\n      }, { passive: true });\n    }\n  <\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-a8056bf e-con-full e-flex e-con e-parent\" data-id=\"a8056bf\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1149e6b elementor-widget elementor-widget-html\" data-id=\"1149e6b\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\n<!-- Font Awesome via CDN -->\n<link rel=\"stylesheet\"\n      href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\"\n      integrity=\"sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==\"\n      crossorigin=\"anonymous\"\n      referrerpolicy=\"no-referrer\" \/>\n<!-- Footer Social-Icons Section -->\n<section id=\"base\">\n  <p>Follow us for the latest AI innovations<\/p>\n  <div class=\"base-social-icons\">\n    <a href=\"https:\/\/twitter.com\" target=\"_blank\" aria-label=\"Twitter\">\n      <i class=\"fab fa-twitter\" aria-hidden=\"true\"><\/i>\n    <\/a>\n    <a href=\"https:\/\/linkedin.com\" target=\"_blank\" aria-label=\"LinkedIn\">\n      <i class=\"fab fa-linkedin\" aria-hidden=\"true\"><\/i>\n    <\/a>\n    <a href=\"https:\/\/github.com\" target=\"_blank\" aria-label=\"GitHub\">\n      <i class=\"fab fa-github\" aria-hidden=\"true\"><\/i>\n    <\/a>\n  <\/div>\n<\/section>\n\n<style>\n  \/* Footer Social Icons *\/\n  #base {\n    position: relative !important;\n    z-index: 5 !important;\n    padding: 3rem 2rem !important;\n    text-align: center !important;\n    background: #390879 !important;\n    font-family: 'Montserrat', sans-serif !important;\n    color: #ffffff !important;\n  }\n  #base p {\n    margin: 0 !important;\n    font-size: 1rem !important;\n    opacity: 0.8 !important;\n  }\n  .base-social-icons {\n    display: flex !important;\n    justify-content: center !important;\n    gap: 1.5rem !important;\n    margin-top: 1rem !important;\n  }\n  .base-social-icons a {\n    color: #B8DF10 !important;\n    font-size: 1.5rem !important;\n    transition: transform 0.3s ease, color 0.3s ease !important;\n  }\n  .base-social-icons a:hover {\n    color: #ffffff !important;\n    transform: scale(1.2) !important;\n    text-shadow: 0 0 10px rgba(184, 223, 16, 0.7) !important;\n  }\n<\/style>\n\n<script>\n  \/\/ Einfache Fade-In-Animation ohne ScrollTrigger:\n  document.addEventListener('DOMContentLoaded', () => {\n    const icons = document.querySelectorAll('#base .base-social-icons a');\n    icons.forEach((el, i) => {\n      el.style.opacity = 0;\n      setTimeout(() => {\n        el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';\n        el.style.opacity = 1;\n        el.style.transform = 'translateY(0)';\n      }, i * 150 + 200);\n    });\n  });\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e2a63d9 e-con-full e-flex e-con e-parent\" data-id=\"e2a63d9\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-38107cf elementor-widget elementor-widget-html\" data-id=\"38107cf\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"visionbridge-footer\">\n  <div class=\"footer-grid\">\n    <div class=\"footer-logo\">\n      <img decoding=\"async\" src=\"https:\/\/visionbridge.eu\/wp-content\/uploads\/2024\/09\/white_version2_150.png\" alt=\"VisionBridge Logo\" class=\"footer-logo-img\">\n    <\/div>\n    <div class=\"footer-info\">\n      <h3>Kontakt<\/h3>\n      <p><a href=\"mailto:connect@visionbridge.eu\">connect@visionbridge.eu<\/a><\/p>\n<p>Tel: <a href=\"tel:+436648342424\">+43 664 8342424<\/a><\/p>\n      <p>Klagenfurt, \u00d6sterreich<\/p>\n    <\/div>\n    <div class=\"footer-links\">\n      <h3>Navigation<\/h3>\n      <ul>\n        <li><a href=\"\/apps\">Apps<\/a><\/li>\n        <li><a href=\"\/contact\">Kontakt<\/a><\/li>\n        <li><a href=\"\/services\">Services<\/a><\/li>\n        <li><a href=\"\/work\">Work<\/a><\/li>\n        <li><a href=\"\/playground\">Playground \ud83c\udfa1<\/a><\/li>\n      <\/ul>\n    <\/div>\n    <div class=\"footer-legal\">\n      <h3>Rechtliches<\/h3>\n      <ul>\n        <li><a href=\"\/impressum\">Impressum<\/a><\/li>\n        <li><a href=\"\/datenschutz\">Datenschutz<\/a><\/li>\n      <\/ul>\n    <\/div>\n  <\/div>\n  <div class=\"footer-bottom\">\n    <p>\u00a9 2025 VisionBridge. Alle Rechte vorbehalten. <br>Programmiert von <a href=\"mailto:dev@stefane9.pro\">stefane9 \ud83e\udd77<\/a><\/p>\n  <\/div>\n<\/div>\n<style>\n  .footer-legal {\n    text-align: center;\n  }\n  .footer-legal h3 {\n    font-family: 'Playfair Display', sans-serif;\n    color: #B8DF10;\n    font-size: 1.3rem;\n    margin-bottom: 0.6rem;\n    text-shadow: 0 0 10px rgba(184, 223, 16, 0.6);\n  }\n  .footer-legal ul {\n    list-style: none;\n    padding: 0;\n    margin: 0;\n  }\n  .footer-legal ul li {\n    padding: 0.3rem 0;\n    opacity: 0.8;\n    transition: opacity 0.3s ease;\n    display: block; \/* \u00c4nderung: Untereinander statt nebeneinander *\/\n  }\n  .footer-legal ul li a {\n    color: #ffffff;\n    text-decoration: none;\n  }\n  .footer-legal ul li:hover {\n    opacity: 1;\n  }\n  .footer-legal ul li a:hover {\n    color: #B8DF10;\n    text-shadow: 0 0 8px rgba(184, 223, 16, 0.5);\n  }\n  .visionbridge-footer {\n    background: linear-gradient(135deg, #2a065e, #390879);\n    color: #ffffff;\n    padding: 2rem 1rem;\n    font-family: 'Montserrat', sans-serif;\n    position: relative;\n    overflow: hidden;\n    box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);\n  }\n  .footer-grid {\n    display: grid;\n    grid-template-columns: minmax(150px, auto) repeat(3, 1fr); \/* Logo etwas kleiner *\/\n    gap: 2rem;\n    max-width: 1200px;\n    margin: 0 auto;\n    text-align: center;\n    padding: 2rem 0;\n    align-items: start; \/* Alle Inhalte beginnen auf der gleichen H\u00f6he *\/\n  }\n  .footer-logo {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    padding: 1rem 0;\n  }\n  .footer-logo-img {\n    max-height: 40px; \/* Logo verkleinert f\u00fcr besseren Abstand *\/\n    width: auto;\n    transition: transform 0.3s ease;\n  }\n  .footer-logo-img:hover {\n    transform: scale(1.1);\n  }\n  .footer-info h3, .footer-links h3, .footer-legal h3 {\n    font-family: 'Playfair Display', sans-serif;\n    color: #B8DF10;\n    font-size: 1.4rem;\n    margin-bottom: 0.8rem;\n    text-shadow: 0 0 12px rgba(184, 223, 16, 0.7);\n    text-align: center; \/* Mittige Ausrichtung der \u00dcberschriften *\/\n  }\n  .footer-info p {\n    margin: 0.5rem 0;\n    font-size: 0.9rem;\n    opacity: 0.9;\n    transition: opacity 0.3s ease;\n    text-align: center; \/* Mittige Ausrichtung des Textes *\/\n  }\n  .footer-info p:hover {\n    opacity: 1;\n    color: #B8DF10;\n  }\n  .footer-info p a {\n    color: #B8DF10;\n    text-decoration: none;\n  }\n  .footer-info p a:hover {\n    color: #ffffff;\n    text-shadow: 0 0 10px rgba(184, 223, 16, 0.7);\n  }\n  .footer-links ul, .footer-legal ul {\n    list-style: none;\n    padding: 0;\n    margin: 0;\n    text-align: center; \/* Mittige Ausrichtung der Listen *\/\n  }\n  .footer-links ul li, .footer-legal ul li {\n    padding: 0.4rem 0;\n    opacity: 0.8;\n    transition: opacity 0.3s ease;\n    display: block; \/* \u00c4nderung: Untereinander statt nebeneinander *\/\n  }\n  .footer-links ul li a, .footer-legal ul li a {\n    color: #ffffff;\n    text-decoration: none;\n  }\n  .footer-links ul li:hover, .footer-legal ul li:hover {\n    opacity: 1;\n  }\n  .footer-links ul li a:hover, .footer-legal ul li a:hover {\n    color: #B8DF10;\n    text-shadow: 0 0 10px rgba(184, 223, 16, 0.6);\n  }\n  .footer-bottom {\n    text-align: center;\n    margin-top: 1.5rem;\n    font-size: 0.75rem;\n    opacity: 0.7;\n    padding-top: 0.7rem;\n    border-top: 1px solid rgba(184, 223, 16, 0.3);\n    transition: opacity 0.3s ease;\n  }\n  .footer-bottom:hover {\n    opacity: 1;\n  }\n  .footer-bottom a {\n    color: #B8DF10;\n    text-decoration: none;\n  }\n  .footer-bottom a:hover {\n    color: #ffffff;\n    text-shadow: 0 0 12px rgba(184, 223, 16, 0.8);\n  }\n  \n  #footer-glow {\n      display:none;\n  }\n\n  \/* Responsive Design *\/\n  @media (max-width: 768px) {\n    .footer-grid {\n      grid-template-columns: 1fr;\n    }\n    .footer-info h3, .footer-links h3, .footer-legal h3 {\n      font-size: 1.1rem;\n    }\n    .footer-info p {\n      font-size: 0.8rem;\n    }\n    .footer-links ul li, .footer-legal ul li {\n      font-size: 0.75rem;\n      display: block; \/* Auf Mobile bleibt es untereinander *\/\n    }\n    .footer-logo-img {\n      max-height: 30px;\n    }\n    .footer-grid {\n      padding: 1rem 0;\n    }\n  }\n  @media (min-width: 769px) and (max-width: 1024px) {\n    .footer-grid {\n      grid-template-columns: repeat(2, 1fr);\n    }\n    .footer-info h3, .footer-links h3, .footer-legal h3 {\n      font-size: 1.2rem;\n    }\n    .footer-info p {\n      font-size: 0.85rem;\n    }\n    .footer-links ul li, .footer-legal ul li {\n      font-size: 0.8rem;\n      display: block; \/* Auf Tablet auch untereinander *\/\n    }\n    .footer-logo-img {\n      max-height: 35px;\n    }\n  }\n  @media (min-width: 1025px) {\n    .footer-grid {\n      grid-template-columns: minmax(150px, auto) repeat(3, 1fr); \/* Logo angepasst *\/\n    }\n    .footer-logo {\n      padding: 1rem 0;\n    }\n    .footer-logo-img {\n      max-height: 50px; \/* Verkleinertes Logo auf Desktop *\/\n    }\n  }\n<\/style>\n<script>\n  \/\/ Subtle Glow Animation for Footer Background\n  const footer = document.querySelector('.visionbridge-footer');\n  const canvas = document.createElement('canvas');\n  canvas.id = 'footer-glow';\n  footer.insertBefore(canvas, footer.firstChild);\n  const ctx = canvas.getContext('2d');\n\n  function resizeCanvas() {\n    canvas.width = footer.offsetWidth;\n    canvas.height = footer.offsetHeight;\n  }\n  window.addEventListener('resize', resizeCanvas);\n  resizeCanvas();\n\n  function drawGlow() {\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    const gradient = ctx.createRadialGradient(\n      canvas.width \/ 2, canvas.height \/ 2, 0,\n      canvas.width \/ 2, canvas.height \/ 2, canvas.width \/ 2\n    );\n    gradient.addColorStop(0, 'rgba(184, 223, 16, 0.15)');\n    gradient.addColorStop(1, 'rgba(57, 8, 121, 0)');\n    ctx.fillStyle = gradient;\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n    requestAnimationFrame(drawGlow);\n  }\n  drawGlow();\n<\/script>\t\t\t\t<\/div>\n\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>services work apps contact visionbridge menu services work apps contact close VisionBridge Wallet Playground Willkommen im VisionBridge Wallet Playground Erstelle und teste eigene Wallet-Cards! Experimentiere mit verschiedenen Designs und Funktionen \u2013 ein sicherer Ort zum Ausprobieren. Hinweis: Aktuell werden hier nur iOS-Wallet-P\u00e4sse unterst\u00fctzt. Pass wird erstellt\u2026 Generic Event Generic Pass erstellen Mitgliedsnummer Mitgliedsnummer ist erforderlich. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-6020","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/pages\/6020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visionbridge.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6020"}],"version-history":[{"count":223,"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/pages\/6020\/revisions"}],"predecessor-version":[{"id":7639,"href":"https:\/\/visionbridge.eu\/index.php?rest_route=\/wp\/v2\/pages\/6020\/revisions\/7639"}],"wp:attachment":[{"href":"https:\/\/visionbridge.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}