diff --git a/Dockerfile b/Dockerfile
index 754183c78e1d8bd2e21556141e654aaa82cffe52..7398b721fca27a3e02891cdda217ed495ac08716 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -33,6 +33,7 @@ RUN pip3 install -r /app/requirements.txt gunicorn psycopg2-binary && \
 COPY deployment/docker/app.sh /usr/local/bin/app
 COPY deployment/docker/supervisord.conf /etc/supervisord.conf
 COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
+COPY deployment/docker/check_django.sh /usr/local/bin/check_django
 
 COPY src /app/
 RUN ln -s /data/local_settings.py /app/rc3platform/local_settings.py && \
@@ -49,6 +50,8 @@ RUN echo 'appuser ALL=(ALL) NOPASSWD:SETENV: /bin/bash' >> /etc/sudoers
 
 USER appuser
 EXPOSE 80
+HEALTHCHECK --interval=5s --timeout=3s --start-period=30s --retries=3 \
+	CMD ["/bin/bash", "/usr/local/bin/check_django", "rc3.world"]
 ENTRYPOINT ["/usr/local/bin/app"]
 CMD ["all"]
 
diff --git a/README.md b/README.md
index 1e79d8061be2dca802886cc35fb6fd0328ee0e07..195f33a63baf90aa3c8fcadfdaaa45a6b0ddb3c7 100644
--- a/README.md
+++ b/README.md
@@ -33,9 +33,9 @@ Testinstanz: <https://api-test.rc3.cccv.de/>
 | Konferenz         | `/c/rc3/rooms`                                       |  x  |      |     |     | Liste aller Räume (über alle Assemblies) |
 | Konferenz         | `/c/rc3/room/<uuid>/`                                |  x  |      |     |     | Details des Raums <uuid>                 |
 | Konferenz         | `/c/rc3/events`                                      |  x  |      |     |     | Liste aller Events                       |
-| Konferenz         | `/c/rc3/events/<uuid>/`                              |  x  |      |  x  |  x  | Details des Events                       |
-| Konferenz         | `/c/rc3/events/<uuid>/attachments`                   |  x  |  x   |     |     | Anhänge                                  |
-| Konferenz         | `/c/rc3/events/<uuid>/attachments/<id>/`             |  x  |      |  x  |  x  | Download der zugehörigen Datei           |
+| Konferenz         | `/c/rc3/event/<uuid>/`                               |  x  |      |  x  |  x  | Details des Events                       |
+| Konferenz         | `/c/rc3/event/<uuid>/attachments`                    |  x  |  x   |     |     | Anhänge                                  |
+| Konferenz         | `/c/rc3/event/<uuid>/attachments/<id>/`              |  x  |      |  x  |  x  | Download der zugehörigen Datei           |
 | Konferenz         | `/c/rc3/schedule[.xml\|.json]`                       |  x  |      |     |     | Liste aller Events als Schedule XML/JSON |
 | Konferenz         | `/c/rc3/assembly/<slug>/schedule[.xml\|.json]`       |  x  |      |     |     | Liste aller Events der Assembly als Schedule XML/JSON |
 | Konferenz         | `/c/rc3/room/<uuid>/schedule[.xml\|.json]`           |  x  |      |     |     | Liste aller Events des Raumes als Schedule XML/JSON |
diff --git a/deployment/docker/check_django.sh b/deployment/docker/check_django.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2ef5ed0f9ae81328aac50430f65d5fdc57e5d87c
--- /dev/null
+++ b/deployment/docker/check_django.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Django does not allow request on "localhost" in production, so we have to
+# override curls resolver and use the public name.
+if [[ -z "$1" ]]; then
+	echo "Usage: check_django <hostname>";
+	exit 1;
+fi
+
+response="$(curl --resolve $1:8000:127.0.0.1 http://$1:8000/.well-known/health)"
+
+if [[ "$response" == "OK" ]]; then
+	exit 0;
+else
+	exit 1;
+fi
diff --git a/src/api/migrations/0003_delete_badgetoken.py b/src/api/migrations/0003_delete_badgetoken.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f7d51e2a4646e4b8f34d97fa8091d83a329b1ed
--- /dev/null
+++ b/src/api/migrations/0003_delete_badgetoken.py
@@ -0,0 +1,16 @@
+# Generated by Django 3.1.4 on 2020-12-25 21:21
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('api', '0002_rename_stickers_to_badges'),
+    ]
+
+    operations = [
+        migrations.DeleteModel(
+            name='BadgeToken',
+        ),
+    ]
diff --git a/src/api/models.py b/src/api/models.py
index d7e0894c705ae6b5cc7b894ae754a72e9721fb5c..adfc9bad774bd45c9a64abd6be79edc0b933f0b4 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -3,7 +3,6 @@ import uuid
 from django.db import models
 
 from core.models.rooms import RoomLink
-from core.models.badges import Badge
 from core.models.users import PlatformUser
 
 
@@ -18,11 +17,3 @@ class RoomLinkUserId(models.Model):
     roomlink = models.ForeignKey(RoomLink, related_name='+', on_delete=models.CASCADE)
     user = models.ForeignKey(PlatformUser, related_name='+', on_delete=models.CASCADE)
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
-
-
-class BadgeToken(models.Model):
-    badge = models.ForeignKey(Badge, related_name='+', on_delete=models.CASCADE)
-    token = models.CharField(max_length=50, primary_key=True)
-
-    permanent = models.BooleanField(default=False)
-    issued = models.DateTimeField(auto_now_add=True)
diff --git a/src/api/schedule.py b/src/api/schedule.py
index f1acb9e14ba60abaa918d9c30af4f69c39fa3337..00527d309844de0268e01f6fe77cf0ae419606c5 100644
--- a/src/api/schedule.py
+++ b/src/api/schedule.py
@@ -103,6 +103,10 @@ class Day:
             self.rooms[room.name] = RoomDay(room)
 
     def add_event(self, event):
+        if event.room is None:
+            # silently ignore events without associated rooms
+            return
+
         if not self.room_exists(event.room):
             self.add_room(event.room)
 
@@ -179,7 +183,7 @@ class Schedule:
         return self._schedule['conference']['days']
 
     def day(self, day: int):
-        return self._schedule['conference']['days'][day-1]
+        return self._schedule['conference']['days'][day - 1]
 
     def rooms(self):
         rooms = set()
@@ -202,12 +206,16 @@ class Schedule:
                 pass
 
     def add_event(self, event: Event):
+        if event.schedule_start is None:
+            # silently ignore events without a start date
+            return
+
         day = self.get_day_from_time(event.schedule_start)
         day.add_event(event)
 
     def get_day_from_time(self, start_time) -> int:
         for i in range(self.conference.days_count):
-            day = self.day(i+1)
+            day = self.day(i + 1)
             if day.day.start <= start_time < day.day.end:
                 return day
 
diff --git a/src/api/tests/schedule.py b/src/api/tests/schedule.py
index ea04dd5f52853a99f4cc718ee3d7c3a9eaed197e..873342d53bd8abdf783cedd59bb6ba5d924dd902 100644
--- a/src/api/tests/schedule.py
+++ b/src/api/tests/schedule.py
@@ -12,7 +12,8 @@ class ScheduleTest(TestCase):
     def setUp(self):
         self.conf = Conference(slug='conf', name='TestConf', is_public=True)
         self.conf.save()
-        self.conf.tracks.create(name='Community').save()
+        self.conf.tracks.create(slug='community', name='Community').save()
+        self.conf.tracks.create(slug='security', name='Security').save()
         self.assembly = Assembly(name='TestAssembly', slug='asmbly', conference=self.conf)
         self.assembly.save()
         self.room = Room(conference=self.conf, assembly=self.assembly, name='Foo Room', room_type=Room.RoomType.STAGE)
@@ -24,8 +25,8 @@ class ScheduleTest(TestCase):
         self.token = Token(user=self.user)
         self.token.save()
 
-    def test_push_existing_event(self):
-        event = Event(conference=self.conf, assembly=self.assembly, name='Example Event')
+    def testPushExistingEvent(self):
+        event = Event(conference=self.conf, assembly=self.assembly, name='Example Event', is_public=False)
         event.save()
 
         update = {
@@ -60,11 +61,27 @@ class ScheduleTest(TestCase):
         self.assertEqual(201, resp.status_code, f'Unexpected result from POST: {resp.content}')
 
         event.refresh_from_db()
+        self.assertFalse(event.is_public)
         self.assertTrue('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
         self.assertEqual(timedelta(minutes=30), event.schedule_duration)
         self.assertIsNotNone(event.schedule_end)
 
-    def test_push_new_event(self):
+        update = {
+            "public": True
+        }
+
+        with self.modify_settings(API_USERS={'append': self.user.username}):
+            resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
+
+        self.assertEqual(201, resp.status_code, f'Unexpected result from POST: {resp.content}')
+
+        event.refresh_from_db()
+        self.assertTrue(event.is_public)
+        self.assertTrue('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
+        self.assertEqual(timedelta(minutes=30), event.schedule_duration)
+        self.assertIsNotNone(event.schedule_end)
+
+    def testPushNewEvent(self):
         another_room = Room(conference=self.conf, assembly=self.assembly, room_type=Room.RoomType.STAGE)
         another_room.save()
 
@@ -81,7 +98,6 @@ class ScheduleTest(TestCase):
             "slug": "rc3-11583-rc3_eroffnung",
             "title": "#rC3 Er\u00f6ffnung",
             "subtitle": "",
-            "track": "Community",
             "type": "Talk",
             "language": "de",
             "abstract": "Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!",
@@ -90,7 +106,8 @@ class ScheduleTest(TestCase):
             "do_not_record": False,
             "persons": [{"id": 14151, "public_name": "blubbel"}],
             "links": [],
-            "attachments": []
+            "attachments": [],
+            "public": False
         }
 
         self.assertFalse(Event.objects.filter(pk=update['guid']).exists())
@@ -104,8 +121,62 @@ class ScheduleTest(TestCase):
 
         self.assertTrue(Event.objects.filter(pk=update['guid']).exists())
         event = Event.objects.get(pk=update['guid'])
+        self.assertFalse(event.is_public)
         self.assertTrue('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
         self.assertEqual(timedelta(minutes=90), event.schedule_duration)
         self.assertIsNotNone(event.schedule_end)
+        self.assertIsNone(event.track)
 
         self.assertEqual(another_room.pk, event.room_id, 'Expected import to prefer "room_id" over "room".')
+
+        update = {
+            "public": False,
+            "track": "Security"
+        }
+
+        with self.modify_settings(API_USERS={'append': self.user.username}):
+            resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
+
+        self.assertEqual(201, resp.status_code, f'Unexpected result from POST: {resp.content}')
+
+        event.refresh_from_db()
+        self.assertFalse(event.is_public)
+        self.assertTrue('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
+        self.assertEqual(timedelta(minutes=90), event.schedule_duration)
+        self.assertIsNotNone(event.schedule_end)
+        self.assertIsNotNone(event.track)
+
+    def testPushInvalidTrack(self):
+        update = {
+            "url": "https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html",
+            "id": 11583,
+            "guid": "d9334deb-f183-4aec-9c6c-137741f6ff73",
+            "logo": None,
+            "date": "2020-12-27T12:20:00+01:00",
+            "start": "12:20",
+            "duration": "01:30",
+            "room": "foo room",
+            "slug": "rc3-11583-rc3_eroffnung",
+            "title": "#rC3 Er\u00f6ffnung",
+            "subtitle": "",
+            "type": "Talk",
+            "language": "de",
+            "abstract": "Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!",
+            "description": "",
+            "recording_license": "",
+            "do_not_record": False,
+            "persons": [{"id": 14151, "public_name": "blubbel"}],
+            "links": [],
+            "attachments": [],
+            "public": False,
+            "track": "Fnord"
+        }
+
+        self.assertFalse(Event.objects.filter(pk=update['guid']).exists())
+
+        url = reverse('api:event-schedule', kwargs={'conference': self.conf.slug, 'pk': update['guid']})
+
+        with self.modify_settings(API_USERS={'append': self.user.username}):
+            resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
+
+        self.assertEqual(400, resp.status_code, f'Unexpected success result from POST: {resp.content}')
diff --git a/src/api/tests/workadventure.py b/src/api/tests/workadventure.py
index c964b609c696b3b3143223182c1f8fab2e5dc97f..bfbbbeeaf70a14ba9f69e398ee9057e63c805503 100644
--- a/src/api/tests/workadventure.py
+++ b/src/api/tests/workadventure.py
@@ -285,6 +285,7 @@ class WorkAdventureBackendTestCase(_WorkAdventureTestCase):
             self.assertTrue(required in data, f'Missing "{required}" field in avatar info.')
 
         self.assertTrue(data['active'])
+        self.assertIsNone(data['index'])
         self.assertEqual(self.human_user.username, data['username'])
 
     def testPut(self):
@@ -292,7 +293,7 @@ class WorkAdventureBackendTestCase(_WorkAdventureTestCase):
 
         resp = self.client.post(
             self.wa_url,
-            json.dumps({'index': 42, 'custom': [1, 2, 3, 4, 5, 6], 'receive_audio': False}),
+            json.dumps({'index': 42, 'custom': [1, 2, 3, 4, 5, 6], 'receive_audio': False, 'volume': 0.8, 'muted': True}),
             content_type='application/json',
             HTTP_AUTHORIZATION=f'Token {self.service_token.key}',
         )
@@ -300,4 +301,19 @@ class WorkAdventureBackendTestCase(_WorkAdventureTestCase):
 
         self.human_user.refresh_from_db()
         self.assertTrue(self.human_user.receive_audio)
+        self.assertTrue(self.human_user.audio_muted)
         self.assertEqual(42, (self.human_user.avatar_config or {}).get('index', -1))
+        self.assertEqual(0.8, self.human_user.audio_volume)
+
+        resp = self.client.post(
+            self.wa_url,
+            json.dumps({'index': None, 'custom': [2, 3, 4, 5, 6, 7], 'volume': None, 'muted': False}),
+            content_type='application/json',
+            HTTP_AUTHORIZATION=f'Token {self.service_token.key}',
+        )
+        self.assertEqual(200, resp.status_code, f'Unexpected response: {resp.content}')
+
+        self.human_user.refresh_from_db()
+        self.assertIsNone(self.human_user.avatar_config.get('index', -1))
+        self.assertIsNone(self.human_user.audio_volume)
+        self.assertFalse(self.human_user.audio_muted)
diff --git a/src/api/urls.py b/src/api/urls.py
index e13c6fde833c1ec788ae220f8af2ed58755401b9..01b004baaa93ff0e48f6cc45960eb1484763cd28 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -3,7 +3,7 @@ from rest_framework.urlpatterns import format_suffix_patterns
 from rest_framework.authtoken import views as authtoken_views
 
 from .views import api_root
-from .views import assemblies, bbb, conferencemember, conferences, events, rooms, users, schedule
+from .views import assemblies, bbb, conferencemember, conferences, events, rooms, users, schedule, badges
 
 
 app_name = 'api'
@@ -27,6 +27,9 @@ urlpatterns = [
     path('c/<slug:conference>/assembly/<slug:assembly>/events', assemblies.ConferenceAssemblyEventList.as_view(), name='assembly-events'),
     path('c/<slug:conference>/assembly/<slug:assembly>/rooms', assemblies.ConferenceAssemblyRoomList.as_view(), name='assembly-rooms'),
     path('c/<slug:conference>/assembly/<slug:assembly>/schedule', schedule.AssemblySchedule.as_view(), name='assembly-schedule'),
+    path('c/<slug:conference>/assembly/<slug:assembly>/badges/new_redeem_token', badges.create_redeem_token, name='create-badge-redeem'),
+    path('c/<slug:conference>/badges/redeem_token', badges.redeem_badge_token, name='badge-redeem'),
+    path('c/<slug:conference>/badges/redeem_map_token', badges.redeem_badge_map_token, name='badge-map-redeem'),
     path('c/<slug:conference>/rooms', rooms.ConferenceRoomList.as_view(), name='room-list'),
     path('c/<slug:conference>/rooms/wa_backend_links', rooms.WorkAdventureRoomsBackendLinkList.as_view(), name='room-wa_backend-list'),
     path('c/<slug:conference>/rooms/wa_backend_link/<slug:assembly>', rooms.WorkAdventureRoomsBackendLink.as_view(), name='room-wa_backend'),
diff --git a/src/api/views/badges.py b/src/api/views/badges.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b96f84a0ed089aca490243fc64be75511bec84c
--- /dev/null
+++ b/src/api/views/badges.py
@@ -0,0 +1,106 @@
+import logging
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponse
+
+
+from core.models.badges import Badge, BadgeToken
+from core.models.users import PlatformUser
+from core.models.conference import Conference
+from core.utils import gen_token
+
+
+logger = logging.getLogger(__name__)
+
+
+@api_view(['POST'])
+def create_redeem_token(request, conference, assembly, **kwargs):
+    issuing_token = request.POST.get('issuing_token', None)
+
+    if issuing_token is None:
+        return HttpResponse(status=400)
+
+    badge = get_object_or_404(Badge, issuing_token=issuing_token)
+
+    badge_class = request.POST.get('badge_class', None)
+    if badge_class is None:
+        return HttpResponse(status=400)
+
+    if (badge_class != BadgeToken.BadgeClass.SINGLE and badge_class != BadgeToken.BadgeClass.PERMANENT and
+            badge_class != BadgeToken.BadgeClass.LIMITED and badge_class != BadgeToken.BadgeClass.MAP):
+        return HttpResponse(status=400)
+    badge_class = BadgeToken.BadgeClass(badge_class)
+
+    if badge_class is BadgeToken.BadgeClass.LIMITED:
+        limited_amount_left = request.POST.get('amount', None)
+        if limited_amount_left is None:
+            return HttpResponse(status=400)
+    else:
+        limited_amount_left = 0
+
+    if badge.conference.slug != conference or badge.issuing_assembly.slug != assembly:
+        return HttpResponse(status=400)
+
+    redeem_token = gen_token()
+    new_token = BadgeToken(badge=badge, token=redeem_token, badge_class=badge_class, limited_amount_left=limited_amount_left)
+    new_token.save()
+
+    logger.info(f'Created badgeToken for {badge}: {redeem_token} with class {badge_class} and amount of {limited_amount_left}')
+
+    return Response(redeem_token)
+
+
+@api_view(['POST'])
+def redeem_badge_token(request, conference, **kwargs):
+    redeem_token = request.POST.get('token', None)
+    if redeem_token is None:
+        return HttpResponse(status=400)
+
+    username = request.POST.get('username', None)
+    user = get_object_or_404(PlatformUser, username=username, is_active=True)
+
+    # Todo: Propper Access Checking
+    conf = get_object_or_404(Conference, slug=conference)
+    if not user.is_in_conference(conf):
+        return HttpResponse(status=404)
+
+    badge_token = get_object_or_404(BadgeToken, token=redeem_token)
+    if badge_token.badge.conference.slug != conference:
+        return HttpResponse(status=400)
+
+    if badge_token.badge_class == BadgeToken.BadgeClass.MAP:
+        # Do not redeem Map tokens
+        return HttpResponse(status=415)
+
+    badge_token.redeem(user, False)
+
+    return Response({'badge': f'{badge_token.badge}', 'user': f'{user.username}'})
+
+
+@api_view(['POST'])
+def redeem_badge_map_token(request, conference, **kwargs):
+    redeem_token = request.POST.get('id', None)
+    if redeem_token is None:
+        return HttpResponse(status=400)
+    assembly = request.POST.get('assembly', None)
+    if assembly is None:
+        return HttpResponse(status=400)
+    username = request.POST.get('username', None)
+    user = get_object_or_404(PlatformUser, username=username, is_active=True)
+
+    # Todo: Propper Access Checking
+    conf = get_object_or_404(Conference, slug=conference)
+    if not user.is_in_conference(conf):
+        return HttpResponse(status=404)
+
+    badge_token = get_object_or_404(BadgeToken, token=redeem_token)
+    if badge_token.badge.conference.slug != conference or badge_token.badge.issuing_assembly.slug != assembly:
+        return HttpResponse(status=400)
+
+    if badge_token.badge_class == BadgeToken.BadgeClass.MAP:
+        # Redeem only MAP Tokens
+        created = badge_token.redeem(user, False)
+        return Response({'badge': f'{badge_token.badge}', 'user': f'{user.username}', 'created': created})
+    else:
+        return HttpResponse(status=415)
diff --git a/src/api/views/rooms.py b/src/api/views/rooms.py
index b3b3b83301a83b596c22eba1619a093814f734eb..f2b8cfe1e8bd4ea22058b63129813e2915ec140b 100644
--- a/src/api/views/rooms.py
+++ b/src/api/views/rooms.py
@@ -84,7 +84,7 @@ class WorkAdventureRoomsBackendLinkList(ConferenceSlugMixin, APIView):
 
         try:
             for entry in data:
-                room = Room.objects.get(conference=self.conference, id=entry.get('room_id'))
+                room = Room.objects.get(conference=self.conference, room_type=Room.RoomType.WORKADVENTURE, id=entry.get('room_id'))
                 _update_room_data(room, entry)
             return Response(status=200)
 
@@ -120,7 +120,7 @@ class WorkAdventureRoomsBackendLink(ConferenceSlugMixin, APIView):
         data = request.data
 
         try:
-            wa_room = Room.objects.get(conference=self.conference, assembly__slug__iexact=assembly)
+            wa_room = Room.objects.get(conference=self.conference, room_type=Room.RoomType.WORKADVENTURE, assembly__slug__iexact=assembly)
             _update_room_data(wa_room, data)
             return Response(status=200)
 
diff --git a/src/api/views/schedule.py b/src/api/views/schedule.py
index c3d495c270201b6a4d38648fd1cae3843d0e15d7..9efd8f8b654ef1b3bb71cae78d6abe1429db67ca 100644
--- a/src/api/views/schedule.py
+++ b/src/api/views/schedule.py
@@ -33,7 +33,7 @@ class ConferenceSchedule(ConferenceSlugMixin, View):
     def get(self, *args, **kwargs):
         schedule = Schedule(self.conference)
         events = Event.objects \
-            .accessible_by_user(user=self.request.user, conference=self.conference) \
+            .conference_accessible(conference=self.conference) \
             .select_related('track', 'room', 'assembly') \
             .order_by(F('assembly__is_official').desc(nulls_last=True), F('room__capacity').desc(nulls_last=True), 'schedule_start')
         schedule.add_events(events)
@@ -45,7 +45,7 @@ class AssemblySchedule(ConferenceSlugMixin, View):
     def get(self, *args, **kwargs):
         schedule = Schedule(self.conference)
         events = Event.objects \
-            .accessible_by_user(user=self.request.user, conference=self.conference) \
+            .conference_accessible(conference=self.conference) \
             .select_related('track', 'room') \
             .filter(assembly_id=kwargs.get('assembly')) \
             .order_by('room__room_type', F('room__capacity').desc(nulls_last=True), 'room__name', 'schedule_start')
@@ -58,7 +58,7 @@ class RoomSchedule(ConferenceSlugMixin, View):
     def get(self, *args, **kwargs):
         schedule = Schedule(self.conference)
         events = Event.objects \
-            .accessible_by_user(user=self.request.user, conference=self.conference) \
+            .conference_accessible(conference=self.conference) \
             .filter(room_id=kwargs.get('pk')) \
             .order_by('schedule_start')
         schedule.add_events(events)
@@ -94,7 +94,7 @@ class EventSchedule(ConferenceSlugMixin, APIView):
         try:
             obj = Event.objects.get(conference=self.conference, pk=pk)
         except Event.DoesNotExist:
-            obj = Event(conference=self.conference, pk=pk)
+            obj = Event(conference=self.conference, pk=pk, is_public=True)
             logger.warning('Event schedule POST: id %s did not exist yet, creating.', pk)
 
         try:
@@ -128,7 +128,8 @@ class EventSchedule(ConferenceSlugMixin, APIView):
                     return JsonResponse({'error': 'Assembly association missing, please provide assembly_id or a valid room.'}, status=400)
 
             obj.kind = 'assembly' if not obj.room.assembly.is_official else 'official'
-            obj.is_public = True
+            if (event_public := event.get('public')) is not None:
+                obj.is_public = event_public is True
 
             if (event_title := event.get('title')) is not None:
                 obj.name = event_title
diff --git a/src/backoffice/forms.py b/src/backoffice/forms.py
index e4f3d7c6355c56d56a4f0778bdde827fa989ebd3..c788233f9917a6ea1eb0383432ac81bf8e3cdd97 100644
--- a/src/backoffice/forms.py
+++ b/src/backoffice/forms.py
@@ -5,6 +5,7 @@ from django.contrib import messages
 from django.core.exceptions import ValidationError
 from django.contrib.auth.forms import UserCreationForm
 from django.utils.translation import gettext_lazy as _
+from django.core.validators import validate_slug
 
 from core.integrations import BigBlueButton, Hangar, IntegrationError, WorkAdventure
 from core.models import Application, Assembly, AssemblyMember, Event, PlatformUser, Room, RoomLink
@@ -83,6 +84,24 @@ class AssemblyEditForm(forms.ModelForm):
     tags = forms.CharField(required=False)
     parent_id = forms.UUIDField(required=False)
 
+    def clean_tags(self):
+        # try to detect people delimiting tags with space instead of comma
+
+        tags = self.cleaned_data['tags']
+        # allow empty tags
+        if tags.strip() == '':
+            return ''
+
+        split_tags = tags.split(',')
+        if tags is not None and len(split_tags) == 1 and len(tags.split(' ')) > 2:
+            raise ValidationError(_('Assembly__tags__splitwithcomma'))
+
+        for tag in split_tags:
+            tag = tag.strip()
+            validate_slug(tag)
+
+        return tags
+
     def clean(self):
         # call original .clean() which e.g. removes 'slug' from cleaned_data if that isn't a slug
         super().clean()
@@ -92,11 +111,6 @@ class AssemblyEditForm(forms.ModelForm):
         if slug is not None and Assembly.objects.filter(conference=self.instance.conference, slug=slug).exclude(pk=self.instance.pk).exists():
             self.add_error('slug', _('Assembly__slug__alreadyexists'))
 
-        # try to detect people delimiting tags with space instead of comma
-        tags = self.cleaned_data.get('tags')
-        if tags is not None and len(tags.split(',')) == 1 and len(tags.split(' ')) > 2:
-            self.add_error('tags', _('Assembly__tags__splitwithcomma'))
-
 
 class AssemblyAddApplicationForm(forms.Form):
     name = forms.CharField(label='Name')
diff --git a/src/backoffice/locale/de/LC_MESSAGES/django.po b/src/backoffice/locale/de/LC_MESSAGES/django.po
index 1a1084c7e4f6ab1500096e820f23b4b2665e47cd..5a72474118d92468fe436b1e8521b0b17f62f660 100644
--- a/src/backoffice/locale/de/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-12-23 11:52+0000\n"
+"POT-Creation-Date: 2020-12-28 01:48+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -105,14 +105,53 @@ msgstr "Diesen Badge direkt einem Benutzer zuweisen."
 msgid "Badge__award-btn"
 msgstr "Badge zuweisen"
 
+msgid "Badge__award-create-redeem-tokens-title"
+msgstr "Badge Redeem Tokens erstellen"
+
+msgid "Badge__award-create-redeem-tokens"
+msgstr "Ein Redeem Token ermöglicht es einem User diesen Badge zu seinem Profil hinzufügen. Es kann auch dazu genutzt werden, per API oder Map einem User diesen Badge zu zuordnen. Automatisch zugeordnete Badges (API oder Map) müssen vom User noch manuell bestätigt werden."
+
+msgid "Badge__award-create-redeem-tokens-question"
+msgstr "Was für ein Redeem Token möchtest du erstellen?"
+
+msgid "Badge__award-create-redeem-tokens-one-time"
+msgstr "One-Time"
+
+msgid "Badge__award-create-redeem-tokens-permanent"
+msgstr "Permanent"
+
+msgid "Badge__award-create-redeem-tokens-limited"
+msgstr "Limited"
+
+msgid "Badge__award-create-redeem-tokens-map"
+msgstr "Map"
+
+msgid "Badge__award-create-redeem-tokens-btn"
+msgstr "Erstelle Redeem Token"
+
+msgid "Badge__award-create-redeem-tokens-one-time-help"
+msgstr "Ein One-Time Redeem Token kann nur einmal eingelöst werden."
+
+msgid "Badge__award-create-redeem-tokens-permanent-help"
+msgstr "Ein Permanent Redeem Token kann unendlich oft eingelöst werden."
+
+msgid "Badge__award-create-redeem-tokens-map-help"
+msgstr "Ein Map Redeem Token kann nur für Tiles in der rc3.World benutzt werden."
+
+msgid "Badge__award-create-redeem-tokens-limited-help"
+msgstr "Ein Limited Redeem Token kann X (Anzahl) mal eingelöst werden."
+
+msgid "Badge-renew-title"
+msgstr "Issuing Token"
+
 msgid "Badge-renew-explanation"
-msgstr "Das Badge kann Nutzern mittels ein sogenanntes Redeem-Token bereitgestellt werden welches mittels einen geheimen Tokens bei der API angefragt werden kann. Durch Klick auf 'Token erneuern' kann dieses geheime Token erneuert werden."
+msgstr "Redeem Tokens können auch automatisiert via der API generiert werden. Dazu ist ein Issuing Token als geheimes Authentifizierungstoken nötig. Durch Klick auf 'Token erneuern' kann dieses geheime Token erneuert werden. Dieses geheime Token ist für jedes Badge einmalig."
 
 msgid "Badge-renew"
-msgstr "Token erneuern"
+msgstr "Issuing Token erneuern"
 
 msgid "Badge-assign-api-explanation"
-msgstr "Alternativ kann das Badge auch über einen API-Aufruf einem Nutzer zugeordnet werden. Dieser erhält eine Benachrichtigung, das Badge wird jedoch nicht automatisch sichtbar angezeigt (wie es bei Nutzung des Redeem-Tokens der Fall wäre)."
+msgstr "Alternativ kann das Badge auch über einen API-Aufruf einem Nutzer zugeordnet werden. Dazu wird ein Redeem Token und der Usernickname benötigt. Eine spezifische Erklärung ist im HowTo zu finden."
 
 msgid "Badge__remove"
 msgstr "Badge entfernen"
@@ -123,6 +162,9 @@ msgstr "Hiermit wird das Badge gelöscht und auch bei allen Nutzern von der Prof
 msgid "Badge__remove__confirm"
 msgstr "Dieser Badge wird auch bei den Nutzern entfernt. Dies kann nicht rückgängig gemacht werden. Wirklich ganz sicher?"
 
+msgid "Badge__valid__Permanent__Redeem__tokens"
+msgstr "Aktive Redeem Tokens (Permanent, Map und Limited)"
+
 msgid "Badge-new"
 msgstr "Neues Badge"
 
diff --git a/src/backoffice/locale/en/LC_MESSAGES/django.po b/src/backoffice/locale/en/LC_MESSAGES/django.po
index b88a82e2869206ad74266cf75eac6720982ae8ca..fb262627e92105002c9953a507fa91f31ec1cef2 100644
--- a/src/backoffice/locale/en/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-12-23 11:52+0000\n"
+"POT-Creation-Date: 2020-12-28 01:48+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -105,17 +105,56 @@ msgstr "You can assign this badge directly to a user by entering his name."
 msgid "Badge__award-btn"
 msgstr "Assign"
 
+msgid "Badge__award-create-redeem-tokens-title"
+msgstr "Create Badge Redeem Tokens"
+
+msgid "Badge__award-create-redeem-tokens"
+msgstr "A Redeem Token enables a user to add this badge to his/her/their profile. It can also be used to add this Badge to an user via API or rc3.world Map. An automatically assigned Badge (API or Map) needs to be accepted manually by the user."
+
+msgid "Badge__award-create-redeem-tokens-question"
+msgstr "Which kind of Redeem Token you want to create?"
+
+msgid "Badge__award-create-redeem-tokens-one-time"
+msgstr "One-Time"
+
+msgid "Badge__award-create-redeem-tokens-permanent"
+msgstr "Permanent"
+
+msgid "Badge__award-create-redeem-tokens-limited"
+msgstr "Limited"
+
+msgid "Badge__award-create-redeem-tokens-map"
+msgstr "Map"
+
+msgid "Badge__award-create-redeem-tokens-btn"
+msgstr "Create Redeem Token"
+
+msgid "Badge__award-create-redeem-tokens-one-time-help"
+msgstr "A One-Time Redeem Token can be used exaclty once."
+
+msgid "Badge__award-create-redeem-tokens-permanent-help"
+msgstr "A Permanent Redeem Token can be used an unlimited times."
+
+msgid "Badge__award-create-redeem-tokens-map-help"
+msgstr "A Map Redeem Token can be only used in rc3 World Maps"
+
+msgid "Badge__award-create-redeem-tokens-limited-help"
+msgstr "A Limited Redeem Token can be used X times."
+
+msgid "Badge-renew-title"
+msgstr "Issuing Token"
+
 msgid "Badge-renew-explanation"
-msgstr "The badge can be issued to users by requesting a 'redeem token' at the API with a secret token. If you forgot the secret token for this badge you can get a new one by clicking 'renew token'."
+msgstr "Redeem Tokens can be created automatically by the API. Therefore, an Issuing Token is needed as a secret authentication token. If you forgot the Issuing Token for this badge, you can get a new one by clicking 'renew token'."
 
 msgid "Badge-renew"
-msgstr "renew token"
+msgstr "Renew token"
 
 msgid "Badge-assign-api-explanation"
 msgstr "Alternatively, badges can be awarded to users via an API call. The user receives a notification but the badge will not be shown automatically (as it is done when the user uses the redeem token above)."
 
 msgid "Badge__remove"
-msgstr "remove badge"
+msgstr "Remove badge"
 
 msgid "Badge__remove__introduction"
 msgstr "This removes the badge and will also delete it from all users' profile who got it."
@@ -123,6 +162,9 @@ msgstr "This removes the badge and will also delete it from all users' profile w
 msgid "Badge__remove__confirm"
 msgstr "This badge will be removed from users as well. This cannot be undone. Are you 100% sure about this?!"
 
+msgid "Badge__valid__Permanent__Redeem__tokens"
+msgstr "Active Redeem Tokens (Permanent, Map und Limited)"
+
 msgid "Badge-new"
 msgstr "new badge"
 
@@ -706,3 +748,6 @@ msgstr "This slug is already in use on another page."
 
 msgid "userprofile_updated"
 msgstr "user profile updated"
+
+#~ msgid "Badge__award-create-redeem-tokens-limited-label"
+#~ msgstr "Amount of usages"
diff --git a/src/backoffice/templates/backoffice/assembly_badge.html b/src/backoffice/templates/backoffice/assembly_badge.html
index 496f4d410493b331ed8b681dddeb406d4be37107..0c62e1e5b9765d091f86143b0441d0cbed2f633e 100644
--- a/src/backoffice/templates/backoffice/assembly_badge.html
+++ b/src/backoffice/templates/backoffice/assembly_badge.html
@@ -55,12 +55,78 @@
                 {% trans 'Badge__award-title' %}
             </div>
             <div class="card-body">
+              <div>
                 <p>{% trans 'Badge__award-explanation' %}</p>
                 <form class="form-inline" action="{% url 'backoffice:assembly-badge-award' assembly=badge.issuing_assembly_id pk=badge.pk %}" method="POST">{% csrf_token %}
                         <label class="sr-only" for="nickname">Name</label>
                         {% render_field assign_form.nickname class+="form-control mb-2 mr-sm-2" placeholder="Nickname" %}
                         <button type="submit" class="btn btn-secondary mb-2">{% trans 'Badge__award-btn' %}</button>
                 </form>
+              </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+
+<div class="row mb-3">
+    <div class="col-md-12">
+        <div class="card border-secondary">
+            <div class="card-header bg-default">
+              {% trans 'Badge__award-create-redeem-tokens-title' %}
+            </div>
+            <div class="card-body">
+              <div>
+                <p>{% trans 'Badge__award-create-redeem-tokens' %}</p>
+                <div class="row">
+                  <div class="col-12">
+                    <p><b>{% trans 'Badge__award-create-redeem-tokens-question' %}</b></p>
+                  </div>
+                </div>
+                <div class="row">
+                  <form class="form-inline" action="{% url 'backoffice:assembly-badge-redeem-token' assembly=badge.issuing_assembly_id pk=badge.pk %}" method="POST">{% csrf_token %}
+                      <div class="col-4">
+                        <div class="input-group">
+                          <script>
+                            function showAmountAndHelp(that) {
+                              document.getElementById("help_single").style.display = "none";
+                              document.getElementById("help_permanent").style.display = "none";
+                              document.getElementById("help_limited").style.display = "none";
+                              document.getElementById("help_map").style.display = "none";
+                              document.getElementById("help_"+that.value).style.display = "block";
+
+                              if (that.value == "limited") {
+                                  document.getElementById("amount").style.visibility = "visible";
+                                } else {
+                                  document.getElementById("amount").style.visibility = "hidden";
+                                }
+                            }
+                          </script>
+                          <select class="form-control" name="badge_class" id="badge_class" onchange="showAmountAndHelp(this);">
+                            <option value="single" selected> {% trans 'Badge__award-create-redeem-tokens-one-time' %} </option>
+                            <option value="permanent"> {% trans 'Badge__award-create-redeem-tokens-permanent' %} </option>
+                            <option value="limited"> {% trans 'Badge__award-create-redeem-tokens-limited' %} </option>
+                            <option value="map"> {% trans 'Badge__award-create-redeem-tokens-map' %} </option>
+                          </select>
+                        </div>
+                      </div>
+                      <div class="col-4">
+                        <div class="input-group"  id="showAmountInput">
+                          <input type="number" id="amount" name="amount" style="visibility:hidden;" value="5" placeholder="Amount" class="form-control mb-2 mr-sm-2">  
+                        </div>
+                      </div>
+                      <div class="col-4">
+                        <button type="submit" class="btn btn-secondary mb-2">{% trans 'Badge__award-create-redeem-tokens-btn' %}</button>
+                      </div>
+                      <div class="col-12">
+                        <p id="help_single" style="display:block;"><i>{% trans 'Badge__award-create-redeem-tokens-one-time-help' %}</i></p>
+                        <p id="help_permanent" style="display:none;"><i>{% trans 'Badge__award-create-redeem-tokens-permanent-help' %}</i></p>
+                        <p id="help_map" style="display:none;"><i>{% trans 'Badge__award-create-redeem-tokens-map-help' %}</i></p>
+                        <p id="help_limited" style="display:none;"><i>{% trans 'Badge__award-create-redeem-tokens-limited-help' %}</i></p>
+                      </div>
+                    </form>
+                </div>
+              </div>
             </div>
         </div>
     </div>
@@ -69,16 +135,20 @@
 <div class="row mb-3">
   <div class="col-md-12">
     <div class="card border-secondary">
-      <form action="{% url 'backoffice:assembly-badge-renew' assembly=badge.issuing_assembly_id pk=badge.pk %}" method="POST">{% csrf_token %}
-      <div class="card-body">
-        <p>{% trans 'Badge-renew-explanation' %}</p>
-        <p><button type="submit" class="btn btn-secondary">{% trans 'Badge-renew' %}</button></p>
-        <p>{% trans 'Badge-assign-api-explanation' %}</p>
+      <div class="card-header bg-default">
+        {% trans 'Badge-renew-title' %}
       </div>
-    </form>
+      <form action="{% url 'backoffice:assembly-badge-renew' assembly=badge.issuing_assembly_id pk=badge.pk %}" method="POST">
+        {% csrf_token %}
+        <div class="card-body">
+          <p>{% trans 'Badge-renew-explanation' %}</p>
+          <p><button type="submit" class="btn btn-secondary">{% trans 'Badge-renew' %}</button></p>
+          <p>{% trans 'Badge-assign-api-explanation' %}</p>
+        </div>
+      </form>
+    </div>
   </div>
 </div>
-</div>
 
 {% if can_manage %}
 <div class="row mb-3">
@@ -97,4 +167,31 @@
   </div>
 </div>
 {% endif %}
+
+
+<div class="row mb-3">
+  <div class="col-md-12">
+    <div class="card border-secondary">
+        <div class="card-header bg-default ">
+          {% trans 'Badge__valid__Permanent__Redeem__tokens' %}
+        </div>
+        <div class="card-body">
+          {% for tok in valid_multiuse_tokens %}
+              <form class="form-inline"  action="{% url 'backoffice:assembly-badge-redeem-token-delete' assembly=assembly.id badge=badge.id pk=tok.pk %}" method="POST">
+                <p>
+                  <code>{{ tok.token }}</code>
+                  <b>{{ tok.badge_class }}</b>
+                  {% if tok.badge_class == 'limited' %}
+                    <i> (Amount left: <code>{{ tok.limited_amount_left }}</code>)</i>
+                  {% endif%}
+                  {% csrf_token %}
+                  <button type="submit" class="btn btn-sm btn-secondary">{% trans 'delete' %}</button>
+                </p>
+              </form>
+          {% endfor %}
+        </div>
+      </form>
+    </div>
+  </div>
+</div>
 {% endblock %}
diff --git a/src/backoffice/templates/backoffice/assembly_badge_redeem_token.html b/src/backoffice/templates/backoffice/assembly_badge_redeem_token.html
new file mode 100644
index 0000000000000000000000000000000000000000..34a0e4a4a0060365fdf2d60a1e67c821884f7ec0
--- /dev/null
+++ b/src/backoffice/templates/backoffice/assembly_badge_redeem_token.html
@@ -0,0 +1,34 @@
+{% extends 'backoffice/base.html' %}
+{% load i18n %}
+
+{% block title %}
+{{ badge.name }} | {{ assembly.name }} | {{ conference.name }}
+{% endblock %}
+
+{% block content %}
+{% include "backoffice/assembly_edit_header.html" %}
+
+<div class="row">
+  <div class="col-md-12">
+    <div class="card border-success">
+      <div class="card-header bg-success text-white">
+        {% trans 'Badge' %} "{{ badge.name }}"
+      </div>
+      <div class="card-body">
+          New token: <code>{{ token }}</code>
+          <br>
+          Class:  <code>{{ badge_class }}</code>
+          {% if badge_class == 'limited' %}
+          <br>
+          Amount: <code>{{ limited_amount_left }}</code>
+          {% endif%}
+
+      </div>
+      <div class="card-footer">
+        <a href="{% url 'backoffice:assembly-badge' assembly=assembly.id pk=badge.pk %}" class="btn btn-success">{% trans 'back' %}</a>
+      </div>
+    </div>
+    </form>
+  </div>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/src/backoffice/templates/backoffice/base.html b/src/backoffice/templates/backoffice/base.html
index 380ef4008f202c0756f0a7da9902e7297840cb04..5686809d53d8e8de6cb5bb13da172ca8bfda9d8b 100644
--- a/src/backoffice/templates/backoffice/base.html
+++ b/src/backoffice/templates/backoffice/base.html
@@ -148,7 +148,9 @@
 
 {% block footer %}
   {% if user.is_authenticated and conferencemember == None %}
-  <div class="alert alert-warning fixed-bottom ml-3 mr-3">
+  <!-- This is not very elegant but should do the job -->
+  <br><br><br><br><br><br><br><br>
+  <div class="alert alert-warning fixed-bottom mx-3">
     ⚠ {% trans "backoffice__not_a_conference_member" %}
   </div>
   {% endif %}
diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index 749620ccd09b90fedc7707102083eb6aa7c6a5c9..6f7685c07cf6af000434e1d8e1ef4790e576bf0f 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -54,6 +54,11 @@ urlpatterns = [
     path('assembly/<uuid:assembly>/badge/<int:pk>/renew_token', assemblies.RenewBadgeView.as_view(), name='assembly-badge-renew'),
     path('assembly/<uuid:assembly>/badge/<int:pk>/remove', assemblies.RemoveBadgeView.as_view(), name='assembly-badge-remove'),
     path('assembly/<uuid:assembly>/badge/<int:pk>/award', assemblies.AwardBadgeView.as_view(), name='assembly-badge-award'),
+    path('assembly/<uuid:assembly>/badge/<int:pk>/redeem_token', assemblies.RedeemTokenBadgeView.as_view(), name='assembly-badge-redeem-token'),
+    path('assembly/<uuid:assembly>/badge/<int:badge>/redeem_token/<str:pk>/delete',
+         assemblies.RemoveBadgeTokenView.as_view(), name='assembly-badge-redeem-token-delete'),
+    path('assembly/<uuid:assembly>/badge/<int:pk>/redeem_token/<str:badge_class>',
+         assemblies.RedeemTokenBadgeView.as_view(), name='assembly-badge-redeem-token'),
 
     path('assembly/<uuid:assembly>/new_event', events.AssemblyCreateEventView.as_view(), name='assembly-create-event'),
     path('assembly/<uuid:assembly>/events', events.AssemblyEventsView.as_view(), name='assembly-events'),
diff --git a/src/backoffice/views/assemblies.py b/src/backoffice/views/assemblies.py
index 278026ce64f13298cf48002c8aa5394a500be24f..33f156714cef9bacbf1f8819f5dfe7bc180c5aab 100644
--- a/src/backoffice/views/assemblies.py
+++ b/src/backoffice/views/assemblies.py
@@ -15,12 +15,13 @@ from django.utils.translation import gettext, gettext_lazy as _
 from rest_framework.authtoken.models import Token
 
 from core.models.assemblies import Assembly, AssemblyMember, AssemblyLink
-from core.models.badges import Badge
+from core.models.badges import Badge, BadgeToken
 from core.models.events import Event
 from core.models.rooms import Room, RoomLink
 from core.models.sso import Application
 from core.models.tags import ConferenceTag
 from core.models.users import PlatformUser
+from core.utils import gen_token
 from core.integrations import BigBlueButton, Hangar, IntegrationError, WorkAdventure
 
 from ..forms import \
@@ -569,6 +570,60 @@ class RenewBadgeView(AssemblyMixin, View):
         return render(self.request, 'backoffice/assembly_badge_renewed.html', ctx)
 
 
+class RedeemTokenBadgeView(AssemblyMixin, View):
+    assembly_url_param = 'assembly'
+    assembly_management = True
+
+    def post(self, *args, **kwargs):
+        badge = Badge.objects.get(
+            conference=self.conference,
+            issuing_assembly=self.assembly,
+            pk=kwargs.get('pk'),
+        )
+        tmpBadgeClass = kwargs.get('badge_class', None)
+        if tmpBadgeClass is None:
+            tmpBadgeClass = self.request.POST.get('badge_class', None)
+        limited_amount_left = 0
+        if tmpBadgeClass == "single":
+            badge_class = BadgeToken.BadgeClass.SINGLE
+        elif tmpBadgeClass == "permanent":
+            badge_class = BadgeToken.BadgeClass.PERMANENT
+        elif tmpBadgeClass == "limited":
+            badge_class = BadgeToken.BadgeClass.LIMITED
+            limited_amount_left = self.request.POST.get('amount', None)
+            if limited_amount_left is None:
+                return HttpResponse(status=400)
+        elif tmpBadgeClass == "map":
+            badge_class = BadgeToken.BadgeClass.MAP
+        else:
+            badge_class = BadgeToken.BadgeClass.SINGLE
+
+        redeem_token = gen_token()
+
+        new_token = BadgeToken(badge=badge, token=redeem_token, badge_class=badge_class, limited_amount_left=limited_amount_left)
+        new_token.save()
+        logger.info(f'Created badgeToken for {badge}: {redeem_token} by {self.request.user} ({badge_class} / limited_amount_left: {limited_amount_left})')
+
+        ctx = self.get_context_data()
+        ctx.update({'badge': badge, 'token': redeem_token, 'badge_class': badge_class, 'limited_amount_left': limited_amount_left})
+        return render(self.request, 'backoffice/assembly_badge_redeem_token.html', ctx)
+
+
+class RemoveBadgeTokenView(AssemblyMixin, DeleteView):
+    assembly_url_param = 'assembly'
+    assembly_management = True
+    model = BadgeToken
+
+    def delete(self, *args, **kwargs):
+        result = super().delete(*args, **kwargs)
+        messages.success(self.request, _('removed'))
+        logger.info(f'BadgeToken {self.object} removed by {self.request.user}')
+        return result
+
+    def get_success_url(self, *args, **kwargs):
+        return reverse('backoffice:assembly-badge', kwargs={'assembly': self.assembly.pk, 'pk': self.kwargs['badge']})
+
+
 class RemoveBadgeView(AssemblyMixin, DeleteView):
     assembly_url_param = 'assembly'
     assembly_management = True
@@ -600,6 +655,7 @@ class BadgeView(AssemblyMixin, UpdateView):
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
         ctx['assign_form'] = AssignBadgeForm()
+        ctx['valid_multiuse_tokens'] = BadgeToken.objects.filter(badge=self.object.id, badge_class__in=BadgeToken.MULTI_USE_CLASSES)
         return ctx
 
     def get_queryset(self, *args, **kwargs):
diff --git a/src/core/admin.py b/src/core/admin.py
index 819f7fa5a9d9b8a05b03067f88893ca5f3a8ef55..68a2c7c438c68ab8ac99c68e402f9a347be7caa4 100644
--- a/src/core/admin.py
+++ b/src/core/admin.py
@@ -345,7 +345,7 @@ class RoomAdmin(admin.ModelAdmin):
             'fields': ['id', 'conference', 'assembly', 'is_public_fahrplan', 'blocked', 'reserve_capacity'],
         }),
         ('Data', {
-            'fields': ['name', 'room_type', 'capacity', 'occupants'],
+            'fields': ['name', 'room_type', 'capacity', 'occupants', 'description'],
         }),
         ('Backend', {
             'fields': ['backend_link', 'backend_status', 'backend_data', 'director_data'],
diff --git a/src/core/integrations/__init__.py b/src/core/integrations/__init__.py
index 8ded4750e683cc544adbdef7582db2ef484f20e8..c6d4856c1e8d00ff2d60282b24b07bcc13b604b8 100644
--- a/src/core/integrations/__init__.py
+++ b/src/core/integrations/__init__.py
@@ -5,7 +5,12 @@ from .error import IntegrationError
 from .workadventure import WorkAdventureIntegration
 
 if settings.BIGBLUEBUTTON_API_URL is not None:
-    BigBlueButton = BigBlueButtonIntegration(settings.BIGBLUEBUTTON_API_URL, settings.BIGBLUEBUTTON_API_TOKEN, settings.BIGBLUEBUTTON_END_MEETING_CALLBACK)
+    BigBlueButton = BigBlueButtonIntegration(
+        settings.BIGBLUEBUTTON_API_URL,
+        settings.BIGBLUEBUTTON_API_TOKEN,
+        settings.BIGBLUEBUTTON_END_MEETING_CALLBACK,
+        settings.BIGBLUEBUTTON_INITIAL_PRESENTATION_URL,
+    )
 else:
     BigBlueButton = None  # type: BigBlueButtonIntegration
 
diff --git a/src/core/integrations/bigbluebutton.py b/src/core/integrations/bigbluebutton.py
index b7b4ed5ce66d6f9d06f564080da0e8665eb48f91..0e52df869e8d6c0ee6597d2d7da97fb4a665c68c 100644
--- a/src/core/integrations/bigbluebutton.py
+++ b/src/core/integrations/bigbluebutton.py
@@ -42,13 +42,14 @@ class BigBlueButtonIntegration(object):
     https://docs.bigbluebutton.org/dev/api.html
     """
 
-    def __init__(self, api_url, api_token, end_meeting_callback):
+    def __init__(self, api_url, api_token, end_meeting_callback, initial_presentation_url):
         self._api_url = api_url
         self._api_token = api_token
         self._end_meeting_callback = end_meeting_callback
+        self._initial_presentation_url = initial_presentation_url
         self._session = requests.session()
 
-    def _send_request(self, resource: str, params: Dict[str, str] = {}, raw=False):
+    def _send_request(self, resource: str, params: Dict[str, str] = {}, raw=False, post_body=None):
         encoded_params = urlencode(_params_to_str(params))
         hash_input = resource + encoded_params + self._api_token
         hash_input = hash_input.encode('utf-8')
@@ -60,7 +61,10 @@ class BigBlueButtonIntegration(object):
             params_str = 'checksum=' + checksum
         request_url = urljoin(self._api_url, resource)
 
-        resp = self._session.get(request_url, params=params_str, allow_redirects=False)
+        if post_body:
+            resp = self._session.post(request_url, params=params_str, allow_redirects=False, data=post_body)
+        else:
+            resp = self._session.get(request_url, params=params_str, allow_redirects=False)
         if raw:
             return resp
 
@@ -122,8 +126,18 @@ class BigBlueButtonIntegration(object):
             'allowModsToUnmuteUsers': False,
             'meta_endCallbackUrl': end_meeting_callback,
         }
+        request_body = None
+        if self._initial_presentation_url:
+            request_body = (
+                f'<?xml version="1.0" encoding="UTF-8"?>'
+                f'<modules>'
+                f'    <module name="presentation">'
+                f'        <document url="{self._initial_presentation_url}" />'
+                f'    </module>'
+                f'</modules>')
+
         try:
-            result = self._send_request('create', params)
+            result = self._send_request('create', params, post_body=request_body)
         except Exception:
             room.backend_status = Room.BackendStatus.ERROR
             room.save(update_fields=['backend_status'])
diff --git a/src/core/integrations/workadventure.py b/src/core/integrations/workadventure.py
index ef066a475e681de235f3ce142a1bc2748ad672a9..88a37e2f0fc3eb8cb8f300bdc1cbda744ea2d568 100644
--- a/src/core/integrations/workadventure.py
+++ b/src/core/integrations/workadventure.py
@@ -34,12 +34,15 @@ class WorkAdventureIntegration:
         return room.backend_status
 
     def join_room(self, room: Room, user: PlatformUser):
-        assert room is not None and room.room_type == Room.RoomType.WORKADVENTURE
+        assert room is None or room.room_type == Room.RoomType.WORKADVENTURE
         assert user is not None
 
-        url = settings.WORKADVENTURE_URL_SCHEME.format(
-            assembly_slug=room.assembly_slug,
-            room_id=room.id,
-            username=user.username,
-        )
-        return url
+        if room is None:
+            return settings.WORKADVENTURE_URL_SCHEME_GENERAL
+        else:
+            url = settings.WORKADVENTURE_URL_SCHEME_ASSEMBLY.format(
+                assembly_slug=room.assembly_slug,
+                room_id=room.id,
+                username=user.username,
+            )
+            return url
diff --git a/src/core/management/commands/hangar_creation.py b/src/core/management/commands/hangar_creation.py
index cf5256c107ce85af2acc4ee7f2615a37dfeeaaae..62d6e428185e203cc96f3e6c88944fdfc2988d7f 100644
--- a/src/core/management/commands/hangar_creation.py
+++ b/src/core/management/commands/hangar_creation.py
@@ -1,8 +1,8 @@
-import logging
 import subprocess
 
 from django.conf import settings
 from django.core.management.base import BaseCommand, CommandError
+from django.urls import reverse
 from django.utils import timezone
 
 from core.models.rooms import Room
@@ -10,24 +10,12 @@ from core.models.rooms import Room
 
 def create_hangar(room: Room):
     username = room.assembly.slug
-    cmd = ['ssh', settings.HANGAR_HOST, settings.HANGAR_CMD, username]
+    cmd = ['ssh', '-i', settings.HANGAR_KEY, settings.HANGAR_HOST, settings.HANGAR_CMD, username]
 
-    result = subprocess.run(cmd, capture_output=True, timeout=42, encoding='utf-8', stderr=subprocess.STDOUT)
-    if result.returncode == 0:
-        password = result.stdout
-        room.backend_link = settings.BACKEND_URL.format(username=username, password=password)
-        room.backend_status = Room.BackendStatus.ACTIVE
-
-        all_contacts = room.assembly.get_all_managing_contacts()
-        room.backend_data = {
-            'timestamp': timezone.now(),
-            'contacts': all_contacts,
-        }
-        room.save()
-
-    else:
+    result = subprocess.run(cmd, capture_output=True, timeout=42, encoding='utf-8')
+    if result.returncode != 0:
         room.backend_data = {
-            'timestamp': timezone.now(),
+            'timestamp': timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
             '_error': result.stdout,
         }
         room.backend_status = Room.BackendStatus.ERROR
@@ -35,10 +23,29 @@ def create_hangar(room: Room):
 
         raise Exception('Backend creation failed.')
 
+    password = result.stdout
+    room.backend_link = settings.HANGAR_BACKEND_URL.format(username=username, password=password)
+    room.backend_status = Room.BackendStatus.ACTIVE
+
+    all_contacts = []
+    all_addrs = set()
+    for c in room.assembly.get_all_managing_contacts():
+        all_contacts.append(c)
+        for a in c.get_all_verified_addresses():
+            all_addrs.add(a)
+
+    room.backend_data = {
+        'timestamp': timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
+        'contacts': list(all_addrs),
+    }
+    room.save()
+
+    return all_contacts
+
 
 class Command(BaseCommand):
     def handle(self, *args, **options):
-        for x in ['URL', 'BACKEND_URL', 'HOST', 'CMD']:
+        for x in ['URL', 'BACKEND_URL', 'HOST', 'CMD', 'KEY']:
             if not hasattr(settings, 'HANGAR_' + x) or getattr(settings, 'HANGAR_' + x) in [None, '']:
                 raise CommandError(f'No HANGAR_{x} configured.')
 
@@ -51,13 +58,21 @@ class Command(BaseCommand):
             room.save()
 
             try:
-                create_hangar(room)
+                contacts = create_hangar(room)
+
+                msg_subject = f'Hangar "{room.assembly.slug}"'
+                msg_text = '''Your hangar has been created. Please visit it in the Maschinenraum for access details.'''
+
+                for c in contacts:
+                    c.notify_user(msg_subject, msg_text, details_link=reverse('backoffice:assembly-room', kwargs={'assembly': room.assembly_id, 'pk': room.id}))
 
             except Exception as err:
-                logging.error('Got error on creating hangar for %s: %s', room, err)
                 room.backend_status = Room.BackendStatus.ERROR
-                room.backend_data = {
-                    'timestamp': timezone.now(),
+                room.backend_data = {**(room.backend_data or {}), **{
+                    'timestamp': timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
                     '_exception': str(err),
-                }
+                    '_url': room.backend_link,
+                }}
+                room.backend_link = ''
                 room.save()
+                self.stderr.write(self.style.ERROR, 'Got error on creating hangar:', err)
diff --git a/src/core/migrations/0051_UserBadgesModel.py b/src/core/migrations/0051_UserBadgesModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9dd6cb0d626970d38408d505d1ca323b752f347
--- /dev/null
+++ b/src/core/migrations/0051_UserBadgesModel.py
@@ -0,0 +1,24 @@
+# Generated by Django 3.1.4 on 2020-12-27 14:34
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0050_Room_is_public_fahrplan'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='BadgeToken',
+            fields=[
+                ('token', models.CharField(max_length=50, primary_key=True, serialize=False)),
+                ('badge_class', models.CharField(choices=[('single', 'BadgeToken__Class-single'), ('permanent', 'BadgeToken__Class-permanent'), ('limited', 'BadgeToken__Class-limited'), ('map', 'BadgeToken__Class-map')], default='single', max_length=20)),
+                ('limited_amount_left', models.IntegerField(default=0)),
+                ('issued', models.DateTimeField(auto_now_add=True)),
+                ('badge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.badge')),
+            ],
+        ),
+    ]
diff --git a/src/core/migrations/0052_UserBadgeVisibility.py b/src/core/migrations/0052_UserBadgeVisibility.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8b79d67380c99743111144f1bfd18b3259a5a76
--- /dev/null
+++ b/src/core/migrations/0052_UserBadgeVisibility.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.1.4 on 2020-12-27 14:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0051_UserBadgesModel'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='userbadge',
+            name='accepted_by_user',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='userbadge',
+            name='visibility',
+            field=models.CharField(choices=[('public', 'UserBadge__visibility-planned'), ('private', 'UserBadge__visibility-private'), ('friends', 'UserBadge__visibility-friends'), ('club', 'UserBadge__visibility-club'), ('clubANDfriends', 'UserBadge__visibility-clubANDfriends')], default='private', help_text='Visibility__state__help', max_length=20, verbose_name='Visibility__state'),
+        ),
+    ]
diff --git a/src/core/migrations/0053_UserBadgeRemoveHiddenField.py b/src/core/migrations/0053_UserBadgeRemoveHiddenField.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d8775b383abbc9eb0e7e848468c03c3dd32ed20
--- /dev/null
+++ b/src/core/migrations/0053_UserBadgeRemoveHiddenField.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.4 on 2020-12-27 14:36
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0052_UserBadgeVisibility'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='userbadge',
+            name='hidden',
+        ),
+    ]
diff --git a/src/core/migrations/0054_UserBadgeGrammar.py b/src/core/migrations/0054_UserBadgeGrammar.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2165abac4bd8a505f4631653e41f05b096ae3c1
--- /dev/null
+++ b/src/core/migrations/0054_UserBadgeGrammar.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.4 on 2020-12-27 14:51
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0053_UserBadgeRemoveHiddenField'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='userbadge',
+            name='visibility',
+            field=models.CharField(choices=[('public', 'UserBadge__visibility-public'), ('private', 'UserBadge__visibility-private'), ('friends', 'UserBadge__visibility-friends'), ('club', 'UserBadge__visibility-club'), ('clubANDfriends', 'UserBadge__visibility-clubANDfriends')], default='private', help_text='Visibility__state__help', max_length=20, verbose_name='Visibility__state'),
+        ),
+    ]
diff --git a/src/core/models/__init__.py b/src/core/models/__init__.py
index fc519c0945a71c7a5e6712c845666bf1731a44e2..a5b38826cc27e50b1716fae531acfbcf6e71c230 100644
--- a/src/core/models/__init__.py
+++ b/src/core/models/__init__.py
@@ -1,5 +1,5 @@
 from .assemblies import Assembly, AssemblyLikeCount, AssemblyLink, AssemblyMember
-from .badges import Badge, UserBadge
+from .badges import Badge, UserBadge, BadgeToken
 from .conference import Conference, ConferenceMember, ConferenceTrack
 from .events import Event, EventAttachment, EventLikeCount, EventParticipant
 from .pages import StaticPage, StaticPageRevision
@@ -14,7 +14,7 @@ from .users import UserCommunicationChannel, UserContact, PlatformUser
 __all__ = [
     'Application',
     'Assembly', 'AssemblyLikeCount', 'AssemblyLink', 'AssemblyMember',
-    'Badge',
+    'Badge', 'BadgeToken',
     'Conference', 'ConferenceMember', 'ConferenceMemberTicket', 'ConferenceTag', 'ConferenceTrack',
     'DirectMessage',
     'Event', 'EventAttachment', 'EventLikeCount', 'EventParticipant',
diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py
index fcbf599818e3f3ba1c7902f88884e55c3effa898..0ad1d2aa70317902dae7a1c21ec7a97a2ee2fcb6 100644
--- a/src/core/models/assemblies.py
+++ b/src/core/models/assemblies.py
@@ -298,10 +298,7 @@ class Assembly(TaggedItemMixin, models.Model):
         return sorted(t['slug'] for t in self.tags)
 
     def get_all_managing_contacts(self):
-        result = {}
-        for member in self.members.filter(role__in=AssemblyMember.MANAGEMENT_ROLES):
-            result[member.member.username] = member.member.get_verified_contacts()
-        return result
+        return PlatformUser.objects.filter(pk__in=self.members.filter(role__in=AssemblyMember.MANAGEMENT_ROLES).values_list('member_id', flat=True))
 
     def create_technical_user_if_necessary(self):
         if self.technical_user is not None:
diff --git a/src/core/models/badges.py b/src/core/models/badges.py
index f43611d07d2b959eadb18c27a2e5c299fb017eb0..c4a1e8ddc2f40616b0e2539040a6454cb074ebeb 100644
--- a/src/core/models/badges.py
+++ b/src/core/models/badges.py
@@ -93,6 +93,13 @@ class Badge(models.Model):
 
 
 class UserBadge(models.Model):
+    class Visibility(models.TextChoices):
+        PUBLIC = 'public', _('UserBadge__visibility-public')
+        PRIVATE = 'private', _('UserBadge__visibility-private')
+        FRIENDS = 'friends', _('UserBadge__visibility-friends')
+        CLUB = 'club', _('UserBadge__visibility-club')
+        CLUBFRIENDS = 'clubANDfriends', _('UserBadge__visibility-clubANDfriends')
+
     class Meta:
         constraints = [
             models.constraints.UniqueConstraint(fields=['user', 'badge'], name='unique_userbadge_user_badge'),
@@ -100,10 +107,78 @@ class UserBadge(models.Model):
     user = models.ForeignKey(PlatformUser, related_name='badges', on_delete=models.CASCADE)
     badge = models.ForeignKey(Badge, related_name='users', on_delete=models.CASCADE)
 
-    hidden = models.BooleanField(
-        default=False,
-        help_text=_('UserBadge__hidden__help'),
-        verbose_name=_('UserBadge__hidden'))
+    accepted_by_user = models.BooleanField(default=False)
+    visibility = models.CharField(
+        max_length=20, default=Visibility.PRIVATE, choices=Visibility.choices,
+        verbose_name=_('Visibility__state'),
+        help_text=_('Visibility__state__help'))
 
     def __str__(self):
         return f'{self.user}: {self.badge}'
+
+    def acceptByUser(self):
+        self.accepted_by_user = True
+        self.save()
+
+    def revokeAcceptByUser(self):
+        self.accepted_by_user = False
+        self.save()
+
+    def changeVisibiltyState(self, state: Visibility):
+        self.visibility = state
+        self.save()
+
+
+class BadgeToken(models.Model):
+    badge = models.ForeignKey(Badge, related_name='+', on_delete=models.CASCADE)
+    token = models.CharField(max_length=50, primary_key=True)
+
+    class BadgeClass(models.TextChoices):
+        SINGLE = 'single',  _('BadgeToken__Class-single')
+        PERMANENT = 'permanent',  _('BadgeToken__Class-permanent')
+        LIMITED = 'limited',  _('BadgeToken__Class-limited')
+        MAP = 'map',   _('BadgeToken__Class-map')
+
+    MULTI_USE_CLASSES = [BadgeClass.PERMANENT, BadgeClass.LIMITED, BadgeClass.MAP]
+
+    badge_class = models.CharField(max_length=20, default=BadgeClass.SINGLE, choices=BadgeClass.choices)
+    limited_amount_left = models.IntegerField(default=0)
+    issued = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return f'{self.badge}: {self.token}'
+
+    def redeem(self, user: PlatformUser, accepted_by_user=False):
+        assert user is not None
+        userBadge, created = UserBadge.objects.get_or_create(
+            user=user,
+            badge=self.badge,
+            defaults={'accepted_by_user': accepted_by_user}
+        )
+
+        # If it isnt redeemed by the user himself, then he will be notified and needs to accept it
+        if not accepted_by_user and created:
+            ctx = {
+                'badge': self.badge,
+                'issuer': f'Badge Token Redeem ({self.token})',
+            }
+
+            notify_ok = user.notify_user(
+                # TODO: Subject+Message in der Sprache des users
+                subject=f'Badge: "{self.badge.name}"',
+                message_text=render_to_string('core/badge_awarded.txt', ctx),
+            )
+
+            if not notify_ok:
+                logger.warning('User %s could not be notified about the awarded badge %s.', user, self)
+
+        stringLimitedAmountLeft = ""
+        if self.badge_class == BadgeToken.BadgeClass.LIMITED:
+            self.limited_amount_left -= 1
+            stringLimitedAmountLeft = f' (Amounts left: {self.limited_amount_left})'
+            self.save()
+
+        logger.info(f'Redeemed badgeToken via {self.token} for {self.badge} with user {user} (accepted_by_user: {accepted_by_user}){stringLimitedAmountLeft}')
+        if self.badge_class == BadgeToken.BadgeClass.SINGLE or (self.badge_class == BadgeToken.BadgeClass.LIMITED and self.limited_amount_left <= 0):
+            self.delete()
+        return created
diff --git a/src/core/models/events.py b/src/core/models/events.py
index 19b03e20b6409d27621893457745b59011abdced..d6682e365427580cf803a76da43a0d41c8b33472 100644
--- a/src/core/models/events.py
+++ b/src/core/models/events.py
@@ -175,6 +175,16 @@ class Event(TaggedItemMixin, models.Model):
         """Returns a list of all public speakers of this event."""
         return self.participants.objects.filter(role=EventParticipant.Role.SPEAKER, is_accepted=True, is_public=True).select_related('participant')
 
+    def get_all_speaker_names(self):
+        names = set()
+        names |= set(self.participants.filter(role=EventParticipant.Role.SPEAKER).values_list('participant__username', flat=True))
+
+        if self.additional_data is not None:
+            for x in self.additional_data.get('persons', []):
+                names.add(x.get('public_name'))
+
+        return names
+
     @property
     def starts_in(self):
         """
diff --git a/src/core/models/pages.py b/src/core/models/pages.py
index 60730c3b47773331b6c76a81c6d31e99ac290839..ec406dfe136cee0e3e9775f8d8bbd39290194767 100644
--- a/src/core/models/pages.py
+++ b/src/core/models/pages.py
@@ -27,7 +27,7 @@ class StaticPageManager(models.Manager):
             return qs
 
         # content team can access non-public pages
-        if user.has_conference_staffpermission(self.conference, 'static_pages'):
+        if conference is not None and user.has_conference_staffpermission(conference, 'static_pages'):
             return qs
 
         # return only pages which are marked public
diff --git a/src/core/models/ticket.py b/src/core/models/ticket.py
index 022e28dd81db9ca9dc4ed3218e90fdf454cbbf6a..f0a7c60a74afc97f0131db4e1a5a65c1b0a49583 100644
--- a/src/core/models/ticket.py
+++ b/src/core/models/ticket.py
@@ -30,7 +30,7 @@ class ConferenceMemberTicket(models.Model):
         ]
 
     @classmethod
-    def validate_pretix_ticket(cls, conference: Conference, user: PlatformUser, token: str):
+    def validate_pretix_ticket(cls, conference: Conference, user: PlatformUser, token: str, validate_only=False):
         """
         Validates an incoming JWT from Pretix which must contain at least two fields in its payload:
         'uid' which is an arbitrary ticket-identifying character string, and
@@ -93,11 +93,11 @@ class ConferenceMemberTicket(models.Model):
             logger.error('Pretix JWT is missing "uid": %s', ticket_data)
             raise TicketValidationError(_('ConferenceMemberTicket__token_missing_ident'))
 
-        if not user.is_authenticated:
-            return None, None
+        if validate_only:
+            return (pretix_ident, ticket_data)
 
         # ensure this identifier has not been used before
-        if cls.objects.filter(conference=conference, user=user).exists():
+        if user.is_authenticated and cls.objects.filter(conference=conference, user=user).exists():
             logger.warning('User already has a ticket: %s', pretix_ident)
             raise TicketValidationError(_('ConferenceMemberTicket__user_has_ticket_already'))
 
@@ -106,6 +106,9 @@ class ConferenceMemberTicket(models.Model):
             logger.warning('Pretix ident has been used before: %s', pretix_ident)
             raise TicketValidationError(_('ConferenceMemberTicket__token_already_used'))
 
+        if not user.is_authenticated:
+            return None, None
+
         return (pretix_ident, ticket_data)
 
     @classmethod
@@ -113,6 +116,8 @@ class ConferenceMemberTicket(models.Model):
         assert user.is_authenticated, "Anonymous Users can't join a conference."
 
         pretix_ident, ticket_data = cls.validate_pretix_ticket(conference, user, token)
+        assert pretix_ident is not None
+        assert ticket_data is not None
 
         # try to note down the ticket
         try:
diff --git a/src/core/models/users.py b/src/core/models/users.py
index 906132b65802e73e09da4ae9c9f0d753684a767d..88b362e36b073bd636bde48e19e5da5af06aaa25 100644
--- a/src/core/models/users.py
+++ b/src/core/models/users.py
@@ -157,6 +157,10 @@ class PlatformUser(AbstractUser):
 
         return AnonUser()
 
+    def get_all_verified_addresses(self):
+        """Return a list of all verified addresses (for legal logging purposes, e.g. hangar creation)."""
+        return self.communication_channels.filter(is_verified=True).values_list('address', flat=True)
+
     def get_avatar_color(self, conference):
         try:
             cm = conference.users.get(user=self)
@@ -173,7 +177,7 @@ class PlatformUser(AbstractUser):
         """Returns the avatar-related settings prepared for JSON."""
         info = self.avatar_config or {}
         return {
-            'index': info.get('index', 0),
+            'index': info.get('index'),
             'custom': info.get('custom', [0, 0, 0, 0, 0, 0]),
             'receive_audio': self.receive_audio,
             'receive_video': self.receive_video,
@@ -204,8 +208,11 @@ class PlatformUser(AbstractUser):
         _UNSET = object()
 
         if (avatar_index := update_data.get('index', _UNSET)) != _UNSET:
-            assert MIN_AVATAR_INDEX <= avatar_index <= MAX_AVATAR_INDEX
-            info['index'] = avatar_index
+            if avatar_index is not None:
+                assert MIN_AVATAR_INDEX <= avatar_index <= MAX_AVATAR_INDEX
+                info['index'] = avatar_index
+            else:
+                info['index'] = None
 
         if (avatar_custom := update_data.get('custom', _UNSET)) != _UNSET:
             if avatar_custom is not None:
@@ -223,16 +230,27 @@ class PlatformUser(AbstractUser):
         if (audio_volume := update_data.get('volume', _UNSET)) != _UNSET:
             if audio_volume is not None:
                 assert 0.0 <= audio_volume <= 1.0
-            self.audio_volume = float(audio_volume)
+                self.audio_volume = float(audio_volume)
+            else:
+                self.audio_volume = None
 
         if (audio_muted := update_data.get('muted', _UNSET)) != _UNSET:
             self.audio_muted = bool(audio_muted)
 
         self.avatar_config = info
 
+    def is_in_conference(self, conference):
+        return conference.users.filter(user=self).exists()
+
     def is_conference_staff(self, conference):
         return conference.users.filter(user=self, is_staff=True).exists()
 
+    @property
+    def display_name(self):
+        if self.pronouns:
+            return f'{self.username} ({self.pronouns})'
+        return self.username
+
     @property
     def guardians(self):
         return self.contacts.filter(is_guardian=True) \
diff --git a/src/core/search.py b/src/core/search.py
index fbf5148d9fefb13812b91b41bb41a5b7d0dff928..08e17f47b3d961d6ff991c8a0c482362a93b2f25 100644
--- a/src/core/search.py
+++ b/src/core/search.py
@@ -18,7 +18,10 @@ def search(user: PlatformUser, conference: Conference, search_term: str, max_per
     """
 
     search_q = SearchQuery(search_term, search_type='websearch')
-    terms = shlex.split(search_term)
+    try:
+        terms = shlex.split(search_term)
+    except ValueError:
+        return
     include_terms = [term for term in terms if not term.startswith('-')]
     exclude_terms = [term for term in terms if term.startswith('-')]
 
diff --git a/src/core/tests/badges.py b/src/core/tests/badges.py
index 746e8e8cec36cb0f3bd7fad30a1952a3e6045f8d..e254924ce4ac41cffd9786dbf5e64cd39200c21e 100644
--- a/src/core/tests/badges.py
+++ b/src/core/tests/badges.py
@@ -4,7 +4,7 @@ from django.test import TestCase
 from ..models.assemblies import Assembly
 from ..models.conference import Conference, ConferenceMember
 from ..models.users import PlatformUser
-from ..models.badges import Badge
+from ..models.badges import Badge, UserBadge
 
 
 class BadgeTests(TestCase):
@@ -44,16 +44,16 @@ class BadgeTests(TestCase):
         achievement.award_to_user(self.user)
         self.assertEqual(1, self.user.badges.count())
         ub = self.user.badges.get(badge=achievement)
-        self.assertEqual(True, ub.hidden)
+        self.assertEqual(UserBadge.Visibility.PRIVATE, ub.visibility)
         # TODO: check that notification has been sent
 
         # user makes badge visible
-        ub.hidden = False
+        ub.visibility = UserBadge.Visibility.PUBLIC
         ub.save()
 
         # award it a second time, should remain visible
         achievement.award_to_user(self.user, issuer='Unittest')
         self.assertEqual(1, self.user.badges.count())
         ub.refresh_from_db()
-        self.assertEqual(False, ub.hidden)
+        self.assertEqual(UserBadge.Visibility.PUBLIC, ub.visibility)
         # TODO: check that no notification has been sent
diff --git a/src/core/tests/bigbluebutton.py b/src/core/tests/bigbluebutton.py
index 0b1e384a28ec3de3f3f32f88fd29e9a19f117444..1a81ddfd89020fbe44cc02d463ebc169175ca349 100644
--- a/src/core/tests/bigbluebutton.py
+++ b/src/core/tests/bigbluebutton.py
@@ -5,7 +5,7 @@ from core.integrations import BigBlueButtonIntegration, IntegrationError
 from core.models import Assembly, Conference, PlatformUser, Room
 
 
-BigBlueButton = BigBlueButtonIntegration('http://localhost/', 'asdf', 'https://localhost/end_meeting')
+BigBlueButton = BigBlueButtonIntegration('http://localhost/', 'asdf', 'https://localhost/end_meeting', None)
 
 
 # from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
diff --git a/src/core/tests/search.py b/src/core/tests/search.py
index 17efd6bdc780a908a2c6a9be393c8fcc67b0f4a3..f9825e86165f8dd4f6767ca908887020a6b7f9e1 100644
--- a/src/core/tests/search.py
+++ b/src/core/tests/search.py
@@ -67,3 +67,6 @@ der in Zitronenscheiben gehüllt ist, das Gehirn aus dem Kopf gedroschen bekommt
         self.assertGreaterEqual(len(results2), 1)
 
         self.assertTrue(any(isinstance(x, Assembly) and str(x.id) == '98b4c68c-1dfc-4e2a-84ae-546ef682fcf2' for x in results2))
+
+        results = list(search(conference=self.conference, user=self.user, search_term='Universum"'))
+        self.assertEqual(results, [])
diff --git a/src/core/utils.py b/src/core/utils.py
index 98310093fc80fa25739536337c5dc53e47a62f64..d827ab9839b6f3e9242d204041fcbe2f1cc0bf8c 100644
--- a/src/core/utils.py
+++ b/src/core/utils.py
@@ -59,7 +59,7 @@ def render_markdown(markup: str):
     rendered_markup = markdown.markdown(strip_tags(markup), extensions=[ConferenceLinkExtension()] + MARKDOWN_EXTENSIONS)
 
     cleaner = bleach.Cleaner(
-        tags=['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'dl', 'table', 'th', 'td', 'tr', 'hr', 'p', 'pre'] + bleach.sanitizer.ALLOWED_TAGS,
+        tags=['br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'dl', 'table', 'th', 'td', 'tr', 'hr', 'p', 'pre'] + bleach.sanitizer.ALLOWED_TAGS,
         attributes={
             '*': ['class'],
             **bleach.sanitizer.ALLOWED_ATTRIBUTES,
diff --git a/src/plainui/forms.py b/src/plainui/forms.py
index 2d1834452e9f28d40e1282fe4e3f0a2aa45b339a..3a91ebcc548fa04e793ef36b3496677bd0e077c3 100644
--- a/src/plainui/forms.py
+++ b/src/plainui/forms.py
@@ -12,7 +12,7 @@ from django.utils.formats import localize
 from django.utils.timezone import localtime
 from django.utils.translation import gettext_lazy as _, gettext
 
-from core.models import ConferenceMemberTicket, DirectMessage, Event, PlatformUser, Room
+from core.models import ConferenceMemberTicket, DirectMessage, Event, PlatformUser, Room, UserCommunicationChannel, BadgeToken, UserBadge
 from .models import BulletinBoardEntry
 
 
@@ -56,6 +56,18 @@ class ProfileEditForm(forms.ModelForm):
         fields = ['pronouns', 'description', 'time_zone', 'high_contrast', 'receive_audio', 'receive_video']
 
 
+class RedeemBadgeForm(forms.ModelForm):
+    class Meta:
+        model = BadgeToken
+        fields = ['token']
+
+
+class ManageBadgeForm(forms.ModelForm):
+    class Meta:
+        model = UserBadge
+        fields = ['badge']
+
+
 class InputTokenForm(forms.Form):
     jwt = forms.CharField(max_length=None, required=True, label=_("Token"))
 
@@ -89,6 +101,42 @@ class RedeemTokenUserCreateForm(auth_forms.UserCreationForm):
         self['username'].help_text += '<br><b>' + gettext("Your Username will be visible to other users") + '</b>'
 
 
+class TokenPasswortResetForm(auth_forms.SetPasswordForm):
+    jwt = forms.CharField(max_length=None, required=True, widget=forms.HiddenInput())
+    username = forms.CharField(required=True, label=_("Username"))
+
+    def __init__(self, conf, user, *args, **kwargs):
+        super().__init__(user, *args, **kwargs)
+        self.conf = conf
+        self.user = user
+
+    def clean(self):
+        try:
+            ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'], validate_only=True)
+        except Exception as e:
+            raise ValidationError(str(e))
+
+        if not self.cleaned_data.get('username') or not self.cleaned_data.get('jwt'):
+            raise ValidationError(gettext('Please enter the Username that used this Ticket and your new Password'))
+
+        user = PlatformUser.objects.filter(username=self.cleaned_data['username']).first()
+        if user is None:
+            raise ValidationError(gettext("Unknown User"))
+
+        if not user.is_active:
+            raise ValidationError(gettext("User is not active!"))
+
+        if user.email or user.communication_channels.filter(channel=UserCommunicationChannel.Channel.MAIL, is_verified=True).exists():
+            raise ValidationError(gettext("User can use password reset by email!"))
+
+        pretix_ident, ticket_data = ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'], validate_only=True)
+        if not ConferenceMemberTicket.objects.filter(conference=self.conf, user=user, ident=pretix_ident).exists():
+            raise ValidationError(gettext("User didn't use this Ticket!"))
+
+        self.user = user
+        self.clean_new_password2()
+
+
 class RoomChoiceField(forms.ModelChoiceField):
     blocked_rooms = {}
 
diff --git a/src/plainui/jinja2.py b/src/plainui/jinja2.py
index 626f28b03f56134e76ee9c22e3e9ac2d022a6dd9..d41aad3882501d1e1e74606bdbb2b09dc1ea1736 100644
--- a/src/plainui/jinja2.py
+++ b/src/plainui/jinja2.py
@@ -54,6 +54,13 @@ def custom_strftime(date):
     return localize(localtime(date))
 
 
+def custom_strftimehm(date):
+    if not isinstance(date, datetime):
+        return ''
+
+    return localtime(date).strftime('%H:%M')
+
+
 def custom_strfdate(date):
     if not isinstance(date, datetime):
         return ''
@@ -61,6 +68,16 @@ def custom_strfdate(date):
     return localize(localdate(date))
 
 
+def custom_strfdates(date):
+    if not isinstance(date, datetime):
+        return ''
+
+    if get_language() == 'de':
+        return localtime(date).strftime('%d.%m.%Y')
+
+    return localdate(date).strftime('%x')
+
+
 # set up an internal represenative for an unset variable as parameter for show_vars()
 # which cannot use a default of None as that is a valid value for the variable
 _UNSET = object()
@@ -119,7 +136,9 @@ def environment(**options):
     })
     env.filters['strftdelta'] = custom_timedelta
     env.filters['strftdelta_short'] = custom_timedelta_short
+    env.filters['strftimehm'] = custom_strftimehm
     env.filters['strftime'] = custom_strftime
     env.filters['strfdate'] = custom_strfdate
+    env.filters['strfdates'] = custom_strfdates
     env.install_gettext_callables(ugettext, ungettext, newstyle=True)
     return env
diff --git a/src/plainui/jinja2/plainui/assemblies.html b/src/plainui/jinja2/plainui/assemblies.html
index 2607eecebaa41552cd99a4156576756007fbe9c7..fb8222b25eb761633836e0d47c95885a3c9c3e4b 100644
--- a/src/plainui/jinja2/plainui/assemblies.html
+++ b/src/plainui/jinja2/plainui/assemblies.html
@@ -15,47 +15,47 @@
     <h2>{{ _("Introduction") }}</h2>
     {{threeCardsMacro.three_cards(cards=[{
         "title": _("Assemblies List"),
-        "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+        "text": _("Assemblies List Description"),
         "link": {
             "url": url('plainui:assemblies_all', conf_slug=conf.slug),
-            "text": _("Explore Assemblies"),
+            "text": _("Explore Assemblies Button"),
             "type": "secondary"
         }
     },
     {
         "title": _("What are Assemblies?"),
-        "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+        "text": _("Assemblies Description"),
     },
     {
         "title": _("Assembly Events"),
-        "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+        "text": _("Assemblies Event Description"),
         "link": {
             "url": url('plainui:assemblies_events', conf_slug=conf.slug),
-            "text": _("Explore Assembly Events"),
+            "text": _("Explore Assembly Events Button"),
             "type": "secondary"
         }
     }
     ]) }}
-    {{ markdownMacro.markdown(markdown="TODO: Introduction assemblies") }}
-    <hr class="my-5">
+    {#  {{ markdownMacro.markdown(markdown="TODO: Introduction assemblies") }} #}
+    <hr class="my-8">
 
     <h2>{{ _("upcoming events") }}</h2>
     <div class="border p-6">
         {{ list_events.slider(events_upcoming, is_favorite_events, is_scheduled_events ) }}
     </div>
 
-    <hr class="my-5">
+    <hr class="my-8">
 
     <h2>{{ _("recommended events") }}</h2>
     <div class="border p-6">
-        TODO: implement recommendation algo. Actual displayes faved events
+        {# TODO: implement recommendation algo. Actual displayes faved events #}
         {{ list_events.grid(events_recommended, is_favorite_events, is_scheduled_events ) }}
     </div>
 
-    <hr class="my-5">
+    <hr class="my-8">
 
     <div class="border p-6">
-        Idea / TODO: Maybe list of assemblies which hosting the running / upcoming events?
+        {# Idea / TODO: Maybe list of assemblies which hosting the running / upcoming events?  #}
         {{ list_assm.list(assemblies, my_favorite_assemblies) }}
     </div>
 
diff --git a/src/plainui/jinja2/plainui/assembly.html b/src/plainui/jinja2/plainui/assembly.html
index b1b2abbe848d9673ab9b9420415e036d8561d2de..1906c847208f3ec3c5ba1c660886f59cfc9fcf9d 100644
--- a/src/plainui/jinja2/plainui/assembly.html
+++ b/src/plainui/jinja2/plainui/assembly.html
@@ -5,6 +5,7 @@
 {% import "plainui/components/list_events.html" as list_events with context %}
 {% import "plainui/components/list_rooms.html" as list_rooms with context %}
 {% import "plainui/components/image.html" as imageMacro %}
+{% import "plainui/components/badge_elements.html" as badge_elements with context %}
 
 
 {% block title %}{{conf.name}} - Assembly {{assembly.name}}{% endblock %}
@@ -32,15 +33,23 @@
         <hr class="my-8">
     {% endif %}
 
-
-    <div class="row">
-        <div class="col">
-            {{ tagboxMacro.tagbox(conf.slug, tags) }}
-        </div>
-        <div class="col">
-            {{ resboxMacro.resourcesbox() }}
+    {% if spokespeople %}
+        <div class="row">
+            <div class="col">
+                <h4 class="h2 mb-5">{{_("Contacts")}}</h4>
+                <ul class="border px-6 flex-grow-1 pt-6 pb-5 list-unstyled mb-0 d-flex flex-row flex-wrap justify-content-center align-items-center">
+                        {% for person in spokespeople %}
+                            <li class="pr-2 mb-2">
+                                <a href="{{ url('plainui:user', conf_slug=conf.slug, id=person.member.id) }}" class="btn btn-tag-secondary">{{person.member.display_name}}</a>
+                            </li>
+                        {% endfor %}
+                </ul>
+            </div>
+            {#<div class="col">
+                {{ resboxMacro.resourcesbox() }}
+            </div>#}
         </div>
-    </div>
+    {% endif %}
 
     <hr class="my-8">
 
@@ -58,7 +67,21 @@
 
     <hr class="my-8">
 
-    <h2 class="">{{ _("Self-organized Sessions") }}</h2>
+    <h2>{{ _("assembly badges") }}</h2>
+    <div class="border p-6">
+        <div class="row">
+            {% if not badges %}
+                {{ _("No badges publicly available.") }}
+            {% endif %}
+            {% for badge_link in badges %}
+                {{ badge_elements.badge(badge_link) }}
+            {% endfor %}
+        </div>
+    </div>
+
+    <hr class="my-8">
+
+    <h2>{{ _("Self-organized Sessions") }}</h2>
     {% if can_create_sos -%}
         <a class="btn btn-secondary mb-2 " href="{{ url('plainui:sos_new', conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ _("new Self-organized Session") }}</a>
     {%- endif %}
@@ -70,6 +93,14 @@
         {% endif %}
     </div>
 
+    <hr class="my-8">
+
+    <div class="row">
+        <div class="col">
+            {{ tagboxMacro.tagbox(conf.slug, tags) }}
+        </div>
+    </div>
+
     <hr class="my-11 border-top-0">
 
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/base.html b/src/plainui/jinja2/plainui/base.html
index e7821fcabeb4ff72934ce897a267947321eb9aa7..372c7e27b23f85a1c299ea2c6ed3403f1eccac13 100644
--- a/src/plainui/jinja2/plainui/base.html
+++ b/src/plainui/jinja2/plainui/base.html
@@ -4,7 +4,8 @@
 <html lang="{{ get_language() }}" class="no-js">
     <head>
         <meta charset="utf-8">
-        <link rel="stylesheet" href="{{ static('plainui/%s.css' % (css_scope(),)) }}">
+        <link rel="stylesheet" href="{{ static('plainui/%s.css' % (css_scope(),)) }}?v=202012172123">
+        <link rel="icon" href="{{ static('plainui/img/favicon.ico') }}" type="image/x-icon">
         <title>{% block title %}{% endblock %}</title>
         <meta name="viewport" content="width=device-width, initial-scale=1">
         {% block head %}{% endblock %}
@@ -53,16 +54,19 @@
             <footer id="footer" class="mt-auto border-top border-primary py-3 py-xl-5">
                 <ul class="container nav p-0 justify-content-center">
                     <li>
-                        <a class="nav-link" title="{{ _("Contact Us") }}" href="https://legal.cccv.de">{{ _("Contact Us") }}</a>
+                        <a class="nav-link" title="{{ _("Contact Us") }}" href="https://legal.rc3.world">{{ _("Contact Us") }}</a>
                     </li>
                     <li>
-                        <a class="nav-link" title="{{ _("Privacy Policy") }}" href="#">{{ _("Privacy Policy") }}</a>
+                        <a class="nav-link" title="{{ _("Privacy Policy") }}" href="https://legal.rc3.world">{{ _("Privacy Policy") }}</a>
                     </li>
                     <li>
                         <a class="nav-link" title="{{ _("About RC3") }}" href="https://howto.rc3.world/">{{ _("About RC3") }}</a>
                     </li>
                     <li>
-                        <a class="nav-link" title="{{ _("Disclaimer") }}" href="">{{ _("Disclaimer") }}</a>
+                        <a class="nav-link" title="{{ _("Disclaimer") }}" href="https://legal.rc3.world">{{ _("Disclaimer") }}</a>
+                    </li>
+                    <li>
+                        <a class="nav-link" title="{{ _("Principles") }}" href="https://howto.rc3.world/rules.html">{{ _("Principles") }}</a>
                     </li>
                 </ul>
             </footer>
diff --git a/src/plainui/jinja2/plainui/board.html b/src/plainui/jinja2/plainui/board.html
index a055f645afea9477028bba8e4ee0a12d4ae8a559..9b2dc189b25ccad7724b47b58bc19b90d4d3fc0c 100644
--- a/src/plainui/jinja2/plainui/board.html
+++ b/src/plainui/jinja2/plainui/board.html
@@ -62,8 +62,8 @@
                         "title": entry.title,
                         "timestamp": entry.timestamp,
                         "text": entry.text_html,
-                        "owner_name": entry.owner_name,
-                        "owner_link": url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=entry.owner_name),
+                        "owner_name": entry.owner.display_name,
+                        "owner_link": url('plainui:user', conf_slug=conf.slug, id=entry.owner_id),
                         "is_editable": current_user == entry.owner_id,
                         "edit_link": url('plainui:board_entry_edit', conf_slug=conf_slug, id=entry.id),
                         "delete_form_id": "delete_entry_form",
diff --git a/src/plainui/jinja2/plainui/ccc_events.html b/src/plainui/jinja2/plainui/ccc_events.html
index db005daf07a8498c7402b6901bc68c9302c5ea6b..490af27ff9bbd5ec0719b2638f84c87164a9baed 100644
--- a/src/plainui/jinja2/plainui/ccc_events.html
+++ b/src/plainui/jinja2/plainui/ccc_events.html
@@ -1,24 +1,35 @@
 {% extends "plainui/base.html" %}
 {% import "plainui/components/markdown.html" as markdownMacro %}
-{% import "plainui/components/livestream.html" as livestreamMacro %}
-{% import "plainui/components/list_events.html" as list_events with context %}
-
-{% block title %}{{conf.name}} - {{ _("Curated Events") }}{% endblock %}
+{% import "plainui/components/list_events.html" as listEventsMacro with context %}
+{% from "plainui/components/calendar.html" import calendar with context %}
+{% block head %}
+    <script src="{{ static('plainui/js/player.js') }}"></script>
+{% endblock %}
+{% block title %}{{conf.name}} - {{ page.title }}{% endblock %}
 {% block content %}
-    {{ titleMacro.title(title=_("Curated Events"),
+    {{ titleMacro.title(title=page.title,
         share_url = url('plainui:ccc_events', conf_slug=conf.slug),
         report_url = url('plainui:ccc_events', conf_slug=conf.slug) ) }}
 
-    {{ livestreamMacro.livestream(title=_("Explore Events")) }}
-    <hr class="my-8">
-    {{ markdownMacro.markdown(markdown="TODO: explain what ccc curated events are/ content team?") }}
+    {% if rooms %}
+        {% for room in rooms %}
+            <h2>{{_("%(room)s Currently Streaming", room=room.room)}}</h2>
+            {{ listEventsMacro.livestream(room, my_favorite_events, my_scheduled_events) }}
+            <hr class="my-8">
+        {% endfor %}
+    {% endif %}
+
+    {{ markdownMacro.markdown(markdown=page.rendered_body|safe) }}
 
     <hr class="my-8">
 
-    <h2>{{ _("curated events") }}</h2>
-    <div class="border p-6">
-        {{ list_events.list(events_ccc, is_favorite_events, is_scheduled_events ) }}
+    <div class="d-flex mb-8">
+        <a href="{{ url('plainui:public_fahrplan', conf_slug=conf.slug) }}" target="_blank" role="button" class="mr-2 btn btn-secondary">
+            {{ _('Fahrplan in new Tab') }}
+        </a>
     </div>
 
+    {{ calendar(events, [], [], public=True) }}
+
     <hr class="my-11 border-top-0">
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/component_gallery.html b/src/plainui/jinja2/plainui/component_gallery.html
index 2c5dec198b62d8984d1dda2a4141d50aecc9ab21..2666cb3574f0fd7fa2e011113d801a02fa6cefd5 100644
--- a/src/plainui/jinja2/plainui/component_gallery.html
+++ b/src/plainui/jinja2/plainui/component_gallery.html
@@ -3,7 +3,6 @@
 {% import "plainui/components/logo.html" as logoMacro %}
 {% import "plainui/components/markdown.html" as markdownMacro %}
 {% import "plainui/components/event_info.html" as eventInfoMacro %}
-{% import "plainui/components/livestream.html" as livestreamMacro %}
 {% import "plainui/components/slider.html" as sliderMacro %}
 {% import "plainui/components/resourcesbox.html" as resboxMacro %}
 {% import "plainui/components/tagbox.html" as tagboxMacro %}
@@ -75,7 +74,13 @@
 
     <dt class="h2 pb-3 mb-3 border-bottom">Livestream</dt>
     <dd class="mb-10">
-        {{ livestreamMacro.livestream() }}
+        {{ list_events.livestream({
+            "room": "My Room",
+            "stream_link": null,
+            "voc_stream": null,
+            "current_event": event1,
+            "next_event": event2
+        }) }}
     </dd>
 
     <dt class="h2 pb-3 mb-3 border-bottom">Resources box</dt>
diff --git a/src/plainui/jinja2/plainui/components/badge_elements.html b/src/plainui/jinja2/plainui/components/badge_elements.html
new file mode 100644
index 0000000000000000000000000000000000000000..ea6d91c41f322a6c5504fafd8f30bc6a512380ec
--- /dev/null
+++ b/src/plainui/jinja2/plainui/components/badge_elements.html
@@ -0,0 +1,98 @@
+{% import "plainui/components/form_elements.html" as form_elements %}
+{% macro userBadge(badge_link, showButtons, showRest) -%}
+{% if badge_link %}
+    <div class="col-3 p-2" style="max-width: 256px!important; width: 256px!important;"> 
+        <div class="card box-shadow w-100">
+        <div >
+            {% if badge_link.badge.image %}
+                <img class="card-img-top"
+                alt="Thumbnail Badge {{badge_link.badge.name}}"
+                style=" width: 254px; height: 254px; display: block;"
+                src="{{badge_link.badge.image.url}}"
+                data-holder-rendered="true">
+            {% else %}
+                <img class="card-img-top"
+                alt="Thumbnail Badge {{badge_link.badge.name}}"
+                style="height: 254px; width: 254px; display: block;"
+                src="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg"
+                data-holder-rendered="true">
+            {% endif %}
+        </div>
+        <div class="card-body">
+            <p class="card-text text-center">
+                <h4 class=" m-2">{{badge_link.badge.name}}</h4>
+                <!-- Buttonm Revoke -->
+                {% if showButtons %}
+                    <form method="POST">
+                        {{ csrf_input }}
+                        <div class="form-row align-items-center text-center">
+                            <input name="badgeid" hidden class="form-control mb-2  m-2" value={{badge_link.badge.id}} >
+                            {% if badge_link.accepted_by_user %}
+                                <input type="submit" class="btn btn-secondary p-2 mb-2  m-2" value='{{ _("Revoke Badge") }}''>
+                            {% else  %} 
+                                <input type="submit" class="btn btn-secondary p-2 mb-2  m-2" value='{{ _("Accept Badge") }}''>
+                            {% endif %}
+                        </div>
+                    </form>
+                    {% if badge_link.accepted_by_user %}
+                        <form method="POST">
+                            <div class="form-row align-items-center text-center">
+                                {{ csrf_input }}
+                                <input name="badgeid" hidden class="form-control mb-2  m-2" value={{badge_link.badge.id}} >
+                                <h6 class="m-2 mb-0">{{ _("Visibility") }}</h6>
+                                <select
+                                    class="form-control p-1 m-2 mt-1"
+                                    name="visibility"
+                                    >
+                                        <option value="private" {% if badge_link.visibility == "private" %} selected="selected" {% endif %}>{{ _( "private" ) }}</option>
+                                        <option value="public"  {% if badge_link.visibility == "public" %}  selected="selected" {% endif %}>{{ _( "public" ) }}</option>
+                                        <option value="friends" {% if badge_link.visibility == "friends" %} selected="selected" {% endif %}>{{ _( "friends" ) }}</option>
+                                        <option value="club"    {% if badge_link.visibility == "club" %} selected="selected" {% endif %}>{{ _( "club" ) }}</option>
+                                        <option value="clubANDfriends" {% if badge_link.visibility == "clubANDfriends" %} selected="selected" {% endif %}>
+                                            {{ _( "friends and club" ) }}
+                                        </option>
+                                </select>
+                                <input type="submit" class="btn btn-secondary p-2 mb-2  m-2" value='{{ _("Save visibility") }}''>
+                            </div>
+                        </form>
+                    {% endif %}
+                {% else %}
+                    {% if showRest %}
+                        <h6 class="m-2 mb-0"><small class="">{{ _("Visibility") }}:<br /></small> {{ _(badge_link.visibility) }}</h6>
+                    {% endif %}
+                {% endif %}
+            </p>
+        </div>
+        </div>
+    </div>
+{% endif %}
+{%- endmacro %}
+
+{% macro badge(badge) -%}
+{% if badge %}
+    <div class="col-3 p-2" style="max-width: 256px!important; width: 256px!important;"> 
+        <div class="card box-shadow w-100">
+        <div >
+            {% if badge.image %}
+                <img class="card-img-top"
+                alt="Thumbnail Badge {{badge.name}}"
+                style=" width: 254px; height: 254px; display: block;"
+                src="{{badge.image.url}}"
+                data-holder-rendered="true">
+            {% else %}
+                <img class="card-img-top"
+                alt="Thumbnail Badge {{badge.name}}"
+                style="height: 254px; width: 254px; display: block;"
+                src="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg"
+                data-holder-rendered="true">
+            {% endif %}
+        </div>
+        <div class="card-body">
+            <p class="card-text text-center">
+                <h4 class=" m-2">{{badge.name}}</h4>
+            </p>
+        </div>
+        </div>
+    </div>
+{% endif %}
+{%- endmacro %}
\ No newline at end of file
diff --git a/src/plainui/jinja2/plainui/components/calendar.html b/src/plainui/jinja2/plainui/components/calendar.html
index 3dfa8c79f9504e73ff7b25c6b727a4814490ba1e..9bb6cdabea9f55362cfe6d2fe101e78ee4a466d1 100644
--- a/src/plainui/jinja2/plainui/components/calendar.html
+++ b/src/plainui/jinja2/plainui/components/calendar.html
@@ -11,7 +11,7 @@
         <div class="mr-4 rc3-fahrplan__timeline">
             <h2 class="m-0 text-white rc3-fahrplan__timeline-title"></h2>
             {% for step in time_steps %}
-                <figure class="m-0 pr-2" style="height: {{ h(step_minutes) }}">{{step.ts.strftime('%d.%m.%Y %H:%M') if step.newdate else step.ts.strftime('%H:%M')}}</figure>
+                <figure class="m-0 pr-2" style="height: {{ h(step_minutes) }}">{{step.ts.strftime('%d.%m.%Y %H:%M') if step.newdate else step.ts | strftimehm}}</figure>
             {% endfor %}
         </div>
         {% for room, room_events in events.rooms_with_events %}
@@ -38,7 +38,7 @@
                                 'room_name': room.name | escape,
                                 'track_name': entry.event.track_name | escape,
                                 'language': entry.event.language | escape,
-                                'speakers': entry.event.speakers|join(', ', attribute='speaker_name') | escape,
+                                'speakers': entry.event.get_all_speaker_names()| join(', ') | escape,
                                 'link': url('plainui:event', conf_slug=conf.slug, event_slug=entry.event.slug) if not public else False
                             }, entry.event.id) }}
                             {% endif %}
@@ -51,10 +51,8 @@
                                 {% if entry.event.track_name %}
                                     <p class="fs-medium font-weight-bold">{{entry.event.track_name}}</p>
                                 {% endif %}
-                                {# <time datetime="" class="mr-4">{{ entry.event.schedule_start.strftime('%H:%M') }} - {{ entry.event.schedule_end.strftime('%H:%M') }}</time> #}
-                                {% if entry.event.speakers %}
-                                    <p class="mt-auto fs-medium font-weight-bold">{{ entry.event.speakers|join(', ', attribute='speaker_name') }}</p>
-                                {% endif %}
+                                {# <time datetime="" class="mr-4">{{ entry.event.schedule_start | strftimehm }} - {{ entry.event.schedule_end | strftimehm }}</time> #}
+                                <p class="mt-auto fs-medium font-weight-bold">{{ entry.event.get_all_speaker_names()| join(', ') | escape }}</p>
                             </figure>
                         </a>
                     {% endif %}
diff --git a/src/plainui/jinja2/plainui/components/event_info.html b/src/plainui/jinja2/plainui/components/event_info.html
index bfd5ec6b81599535c17b2fcc1806d22f8968f0cd..e6134184115d82e3ec510bdd0d42ea5510e760d0 100644
--- a/src/plainui/jinja2/plainui/components/event_info.html
+++ b/src/plainui/jinja2/plainui/components/event_info.html
@@ -1,62 +1,82 @@
 {% import "plainui/components/image.html" as imageMacro %}
 
-
-{% macro eventInfo(event, speakers) -%}
+{% macro eventInfo(event, speakers, assembly={}, conf_slug="") -%}
     <div class="rc3-event-info">
-                <h2>
-                    {{ _("Event starts in") }}
-                    {{ event.schedule_start|strftdelta }}
-                </h2>
-        <h2>{{ _("Event Information") }}</h2>
-        {% if event.banner_image and event.banner_image.url %}
-            {{ imageMacro.image(image=event.banner_image.url, alt=event.banner_image.name, title=event.banner_image.name) }}
-        {% endif %}
-        <div>
-            <dl>
-                <dt>{{ _("Time") }}</dt>
-                <dd>
-                    {% if event.schedule_start %}
-                        {{event.schedule_start | strftime}}
-                        {% if event.schedule_end %}
-                            - {{event.schedule_end | strftime}}
-                        {% endif %}
-                    {% else %}
-                        -
-                    {% endif %}
-                </dd>
-                <dt>{{ _("Speakers") }}</dt>
-                <dd>
-                    {% for speaker in speakers %}
-                        {{ speaker.participant.username }}{% if not loop.last %}, {% endif %}
-                    {% else %}
-                        {{ _("No Speakers publicated yet") }}
-                    {% endfor %}
-                </dd>
-                <dt>{{ _("Track") }}</dt>
-                <dd>
-                    {% if event.track and event.track.name %}
-                        {{event.track.name}}
-                    {% else %}
-                        -
-                    {% endif %}
-                </dd>
-                <dt>{{ _("Language") }}</dt>
-                <dd>
-                    {% if event.language %}
-                        {{event.language}}
-                    {% else %}
-                        -
+        <h3 class="flex-row align-items-center justify-content-center bg-info p-3 text-white h3 mb-lg-0">
+            {{ _("Event starts in") }}
+            {{ event.schedule_start|strftdelta }}
+        </h3>
+        <h3 class="rc3-event-info__title-info flex-row align-items-center justify-content-center bg-info p-3 text-white h3 mb-lg-0">{{ _("Event Information") }}</h3>
+            <figure class="p-0 mb-lg-0">
+                {% if event.banner_image and event.banner_image.url %}
+                    <img class="rc3-event-info__img w-100 h-100 d-block" src="{{ event.banner_image.url }}" alt="{{ event.banner_image.name }}" title="{{ event.banner_image.name }}" />
+                {% else %}
+                    <img class="w-100 d-block" src="{{ random_preview_image_url() }}" alt="{{ event.name }}" title="{{ event.name }}" />
+                {% endif %}
+            </figure>
+        <dl class="grid-list mb-0">
+            <dt class="grid-list__item grid-list__item--title">{{ _("Time") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% if event.schedule_start %}
+                    {{event.schedule_start | strftime}}
+                    {% if event.schedule_end %}
+                        - {{event.schedule_end | strftime}}
                     {% endif %}
-                </dd>
-                <dt>{{ _("Room") }}</dt>
-                <dd>
-                    {% if event.room and event.room.name %}
-                        {{event.room.name}} {%- if event.room.assembly %} @ {{event.room.assembly.name}} {% endif %}
+                {% else %}
+                    -
+                {% endif %}
+            </dd>
+            <dt class="grid-list__item grid-list__item--title">{{ _("Speakers") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% for speaker in speakers %}
+                    {{ speaker.participant.username }}{% if not loop.last %}, {% endif %}
+                {% else %}
+                    {{ _("No Speakers publicated yet") }}
+                {% endfor %}
+            </dd>
+            <dt class="grid-list__item grid-list__item--title">{{ _("Track") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% if event.track and event.track.name %}
+                    {{event.track.name}}
+                {% else %}
+                    -
+                {% endif %}
+            </dd>
+            <dt class="grid-list__item grid-list__item--title">{{ _("Language") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% if event.language %}
+                    {{event.language}}
+                {% else %}
+                    -
+                {% endif %}
+            </dd>
+            <dt class="grid-list__item grid-list__item--title">{{ _("Room") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% if event.room and event.room.name %}
+                    {% if conf_slug != "" %}
+                        <a href="{{ url('plainui:room', conf_slug=conf_slug, room_id=event.room.id) }}" title="{{ event.room.name }}" class="">
+                            {{ event.room.name }}
+                        </a>
                     {% else %}
-                        -
+                        {{event.room.name}}
                     {% endif %}
-                </dd>
-            </dl>
-        </div>
+                {% else %}
+                    -
+                {% endif %}
+            </dd>
+            <dt class="grid-list__item grid-list__item--title">{{ _("Host") }}</dt>
+            <dd class="grid-list__item grid-list__item--text">
+                {% if assembly.link and assembly.name %}
+                    <a href="{{ assembly.link }}">{{ assembly.name }}</a>
+                {% else %}
+                    -
+                {% endif %}
+            </dd>
+        </dl>
     </div>
 {%- endmacro %}
+
+{%- macro random_preview_image_url() -%}
+    {%- set imgs = [1,2,3,4,5,6,7]  -%}
+    /static/plainui/img/rc3-assembly-event-0{{ imgs | random }}.png
+{%- endmacro -%}
diff --git a/src/plainui/jinja2/plainui/components/function_btns.html b/src/plainui/jinja2/plainui/components/function_btns.html
index f4e93bc2961a4d54f5ff39c86d500970d0745083..65d86016aa0769af7ff63081e0f575a04bd57b38 100644
--- a/src/plainui/jinja2/plainui/components/function_btns.html
+++ b/src/plainui/jinja2/plainui/components/function_btns.html
@@ -8,7 +8,7 @@
 {% macro fav(fav_id, fav_type, fav_is, color="secondary", next=request.get_full_path() ) -%}
     <form action="{{ url('plainui:modify_favorites', conf_slug=conf.slug) }}" class="d-inline-block" method="POST">
         {{ csrf_input }}
-        <input type="hidden" name="next" value="{{ next }}">
+        <input type="hidden" name="next" value="{{ next ~ '#fav_' ~ fav_id}}">
         <input type="hidden" name="id" value="{{ fav_id }}">
 
         {%- if fav_type == "assembly" %}<input type="hidden" name="type" value="assembly">{% endif %}
@@ -16,14 +16,14 @@
 
         {%- if fav_is %}
             <input type="hidden" name="mode" value="remove">
-            <button type="submit" class="active ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("remove from favorites") }}">
+            <button type="submit" id="fav_{{ fav_id }}" class="active ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("remove from favorites") }}">
                 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
                   <path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
                 </svg>
             </button>
         {%- else -%}
             <input type="hidden" name="mode" value="add">
-            <button type="submit" class="ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("add to favorites") }}">
+            <button type="submit" id="fav_{{ fav_id }}" class="ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("add to favorites") }}">
                 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart" viewBox="0 0 16 16">
                   <path fill-rule="evenodd" d="M8 2.748l-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"/>
                 </svg>
@@ -35,18 +35,18 @@
 {% macro schedule(sch_id, sch_is, color="secondary", next=request.get_full_path() ) -%}
     <form action="{{ url('plainui:modify_personal_calendar', conf_slug=conf.slug) }}" class="d-inline-block" method="POST">
         {{ csrf_input }}
-        <input type="hidden" name="next" value="{{ next }}">
+        <input type="hidden" name="next" value="{{ next ~ '#sch_' ~ sch_id }}">
         <input type="hidden" name="id" value="{{ sch_id }}">
         {%- if sch_is %}
             <input type="hidden" name="mode" value="remove">
-            <button type="submit" class="active ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("remove from schedule") }}">
+            <button type="submit" id="sch_{{ sch_id }}" class="active ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("remove from schedule") }}">
                 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar2-week-fill" viewBox="0 0 16 16">
                     <path fill-rule="evenodd" d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM2 3.5c0-.276.244-.5.545-.5h10.91c.3 0 .545.224.545.5v1c0 .276-.244.5-.546.5H2.545C2.245 5 2 4.776 2 4.5v-1zM8.5 7a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1zm3 0a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1zM3 10.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm3.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1z"/>
                 </svg>
             </button>
         {%- else -%}
             <input type="hidden" name="mode" value="add">
-            <button type="submit" class="ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("add to schedule") }}">
+            <button type="submit" id="sch_{{ sch_id }}" class="ml-2 btn-icon-big btn btn-{{ color }}" title="{{ _("add to schedule") }}">
                 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar2-week" viewBox="0 0 16 16">
                   <path fill-rule="evenodd" d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM2 2a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H2z"/>
                   <path d="M2.5 4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V4zM11 7.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm-3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm-5 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1z"/>
diff --git a/src/plainui/jinja2/plainui/components/image.html b/src/plainui/jinja2/plainui/components/image.html
index 7576b6ae33a4966d663a265e11889eaecf07083f..e8e342606729f61a082557f10b3e975b56ccc4c2 100644
--- a/src/plainui/jinja2/plainui/components/image.html
+++ b/src/plainui/jinja2/plainui/components/image.html
@@ -3,3 +3,8 @@
     <img class="rc3-image__img w-100 d-block" src="{{ image }}" alt="{{ alt }}" title="{{ title }}" />
 </figure>
 {%- endmacro %}
+{% macro image_cropped(image, alt, title) -%}
+<figure class="rc3-image rc3-image--cropped p-0 m-0">
+    <img class="rc3-image__img" src="{{ image }}" alt="{{ alt }}" title="{{ title }}" />
+</figure>
+{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/integrations.html b/src/plainui/jinja2/plainui/components/integrations.html
index 45164919de6fa599397883167c12b6e39a8714b4..d2fe91297d393675f4f86fe39e89a196257ede8b 100644
--- a/src/plainui/jinja2/plainui/components/integrations.html
+++ b/src/plainui/jinja2/plainui/components/integrations.html
@@ -1,5 +1,5 @@
-{% macro vocPlayer(vocStream=None, vocLecture=None) -%}
-    <div id="player"></div>
+{% macro vocPlayer(playerId='player', vocStream=None, vocLecture=None) -%}
+    <div id="{{playerId}}" class="rc3__voc_player"></div>
 
     <script>
         new VOCPlayer.Player({
@@ -9,7 +9,7 @@
             {% if vocLecture -%}
             vocLecture: "{{ vocLecture }}",
             {%- endif %}
-            parentId: "#player",
+            parentId: "#{{playerId}}",
         });
     </script>
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/list_assemblies.html b/src/plainui/jinja2/plainui/components/list_assemblies.html
index e05745525c7fa688d4547a28c8ba4c28c381c6c5..2e2b4eeee89b6ca82e385c6cdf1c0aa00e7578fe 100644
--- a/src/plainui/jinja2/plainui/components/list_assemblies.html
+++ b/src/plainui/jinja2/plainui/components/list_assemblies.html
@@ -74,11 +74,11 @@
             class="text-decoration-none text-white"
             title="{{ assembly.name }}"
         >
-            <figure class="mb-2">
+            <figure class="rc3-image rc3-image--cropped mb-2">
                 {% if assembly.banner_image %}
-                    <img class="w-100 d-block" src="{{ assembly.banner_image.url }}" alt="{{ assembly.name }}" title="{{ assembly.name }}" />
+                    <img class="rc3-image__img" src="{{ assembly.banner_image.url }}" alt="{{ assembly.name }}" title="{{ assembly.name }}" />
                 {% else %}
-                    <img class="w-100 d-block" src="/static/plainui/img/rc3-logo-assembly.svg" alt="{{ assembly.name }}" title="{{ assembly.name }}" />
+                    <img class="rc3-image__img" src="/static/plainui/img/rc3-logo-assembly.svg" alt="{{ assembly.name }}" title="{{ assembly.name }}" />
                 {% endif %}
             </figure>
             <section class="m-2">
@@ -96,7 +96,7 @@
         </a>
 
         <footer class="mt-auto mr-2 mb-2 d-md-flex align-items-center">
-            <ul class="ml-auto list-unstyled d-flex justify-content-end">
+            <ul class="ml-auto mb-0 list-unstyled d-flex justify-content-end">
                 <li>
                     {{ fbtns.share(link, color=color) }}
                 </li>
diff --git a/src/plainui/jinja2/plainui/components/list_events.html b/src/plainui/jinja2/plainui/components/list_events.html
index 40bd07bb19e7400d067830e295050e42a05c8934..f8b315b3286b300648130a3600db972c855ab6ac 100644
--- a/src/plainui/jinja2/plainui/components/list_events.html
+++ b/src/plainui/jinja2/plainui/components/list_events.html
@@ -5,6 +5,7 @@
     csrf_input
 #}
 {% import "plainui/components/function_btns.html" as fbtns with context %}
+{% import "plainui/components/integrations.html" as integrations %}
 
 {% macro list(events, my_favorite_events, my_scheduled_events, assembly_slug=None, msg_none=_("No entries available.")) -%}
     {% if events %}
@@ -28,8 +29,8 @@
         <a href="{{ link }}" title="{{ event.name }}" class="text-white mr-auto">
             {{ event.name }}
         </a>
-        <time datetime="{{event.schedule_start}}" class="mr-2">{{ event.schedule_start.strftime('%x') }},</time>
-        <time class="mr-4">{{ event.schedule_start.strftime('%H:%M') }} - {{ event.schedule_end.strftime('%H:%M') }}</time>
+        <time datetime="{{event.schedule_start}}" class="mr-2">{{ event.schedule_start | strfdates }},</time>
+        <time class="mr-4">{{ event.schedule_start | strftimehm }} - {{ event.schedule_end | strftimehm }}</time>
         {%- if assembly and assembly.slug and (event.owner_id == request.user.id or can_manage_sos) -%}
             {{ icon_public(event.is_public) }}
             {{ fbtns.edit(url('plainui:sos_edit', conf_slug=conf.slug, assembly_slug=assembly.slug, event_slug=event.slug), color=color) }}
@@ -75,34 +76,34 @@
     {% endif %}
 {%- endmacro %}
 
-{% macro tile(event, faved, scheduled) -%}
+{% macro tile(event, faved, scheduled, custom_class="h-100") -%}
     {% set link = url('plainui:event', conf_slug=conf.slug, event_slug=event.slug ) %}
     {% set color="plattform" if event.kind == "official" else "assembly" %}
-    <article class="h-100 d-flex flex-column border border-{{ color }}-dark bg-gradient-{{ color }}-vertical">
+    <article class="{{ custom_class }} d-flex flex-column border border-{{ color }}-dark bg-gradient-{{ color }}-vertical">
         <a
             href="{{ link }}"
             class="text-decoration-none text-white"
             title="{{ event.name }}"
         >
-            <figure class="mb-2">
+            <figure class="rc3-image rc3-image--cropped mb-2">
                 {% if event.banner_image %}
-                    <img class="w-100 d-block" src="{{ event.banner_image.url }}" alt="{{ event.name }}" title="{{ event.name }}" />
+                    <img class="rc3-image__img" src="{{ event.banner_image.url }}" alt="{{ event.name }}" title="{{ event.name }}" />
                 {% else %}
-                    <img class="w-100 d-block" src="{{ random_preview_image_url() }}" alt="{{ event.name }}" title="{{ event.name }}" />
+                    <img class="rc3-image__img" src="{{ random_preview_image_url() }}" alt="{{ event.name }}" title="{{ event.name }}" />
                 {% endif %}
             </figure>
             <section class="m-2">
                 {% if event.schedule_start or event.schedule_duration %}
                     <p class="mb-2 d-flex flex-row justify-content-between font-headings fs-medium justify-space-between text-center">
                         <time datetime="{{ event.schedule_start }}">
-                            {{ event.schedule_start.strftime('%x') }}
+                            {{ event.schedule_start | strfdates }}
                         </time>
                         <time>
                             <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clock" viewBox="0 0 16 16">
                                 <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"/>
                                 <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/>
                             </svg>
-                            {{ event.schedule_start.strftime('%H:%M') }}
+                            {{ event.schedule_start | strftimehm }}
                         </time>
                         <span>{{ event.schedule_duration }}</span>
                     </p>
@@ -128,7 +129,7 @@
                     {{ event.track_name }}
                 </p>
             {% endif %}
-            <ul class="ml-auto list-unstyled d-flex justify-content-end">
+            <ul class="ml-auto mb-0 list-unstyled d-flex justify-content-end">
                 {%- if assembly and assembly.slug and (event.owner_id == request.user.id or can_manage_sos) -%}
                     <li>
                         {{ icon_public(event.is_public) }}
@@ -152,6 +153,100 @@
     </article>
 {%- endmacro %}
 
+{% macro tile_stream(room, faved, scheduled, custom_class="h-100") -%}
+    {% set link = url('plainui:event', conf_slug=conf.slug, event_slug=room.current_event.slug ) if room.current_event else '' %}
+    {% set color="plattform" if room.current_event.kind == "official" else "assembly" %}
+    <article class="{{ custom_class }} d-flex flex-column">
+
+            <figure class="mb-2">
+                {% if room.voc_stream %}
+                    {{ integrations.vocPlayer(playerId='player%s' % (room.room.id), vocStream=room.voc_stream) }}
+                {% elif room.current_event and room.current_event.banner_image %}
+                    <a
+                        href="{{ link }}"
+                        class="rc3-image rc3-image--cropped text-decoration-none text-white"
+                        title="{{ room.current_event.name }}"
+                    >
+                        <img class="rc3-image__img" src="{{ room.current_event.banner_image.url }}" alt="{{ room.current_event.name }}" title="{{ room.current_event.name }}" />
+                    </a>
+                {% else %}
+                    <a
+                        href="{{ link }}"
+                        class="text-decoration-none text-white"
+                        title="{{ room.current_event.name }}"
+                    >
+                        <img class="w-100 d-block" src="{{ random_preview_image_url() }}" alt="{{ room.current_event.name }}" title="{{ room.current_event.name }}" />
+                    </a>
+                {% endif %}
+            </figure>
+        {% if link %}
+        <a
+            href="{{ link }}"
+            class="text-decoration-none text-white"
+            title="{{ room.current_event.name }}"
+        >
+            <section class="m-2">
+                {% if room.current_event.schedule_start or room.current_event.schedule_duration %}
+                    <p class="mb-2 d-flex flex-row justify-content-between font-headings fs-medium justify-space-between text-center">
+                        <time datetime="{{ room.current_event.schedule_start }}">
+                            {{ room.current_event.schedule_start | strfdates }}
+                        </time>
+                        <time>
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clock" viewBox="0 0 16 16">
+                                <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"/>
+                                <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/>
+                            </svg>
+                            {{ room.current_event.schedule_start | strftimehm }}
+                        </time>
+                        <span>{{ room.current_event.schedule_duration }}</span>
+                    </p>
+                {% endif %}
+                {% if room.current_event.name %}
+                    <h3 class="text-white h6 mb-2">{{ room.current_event.name }}</h3>
+                {% endif %}
+                {% if room.current_event.speakers %}
+                    <b class="f s-medium d-block mb-2">{{ room.current_event.speakers|join(', ', attribute='speaker_name') }}</b>
+                {% endif %}
+                {% if room.current_event.description %}
+                    <p class="fs-medium mb-2">{{ room.current_event.description[:120] + (room.current_event.description[120:] and '...') }}</p>
+                {% endif %}
+            </section>
+        </a>
+
+        <footer class="mt-auto mr-2 d-md-flex align-items-center">
+            {% if room.current_event.track_name %}
+                <p class="mb-2 mb-md-0 font-headings fs-medium">
+                    <small class="d-block {{ "text-tertiary" if room.current_event.kind == "official" else "text-secondary"}}">
+                        {{ _("%(kind)s Event on Track", kind="Official" if room.current_event.kind == "official" else "") }}
+                    </small>
+                    {{ room.current_event.track_name }}
+                </p>
+            {% endif %}
+            <ul class="ml-auto mb-0 list-unstyled d-flex justify-content-end">
+                {%- if assembly and assembly.slug and (room.current_event.owner_id == request.user.id or can_manage_sos) -%}
+                    <li>
+                        {{ icon_public(room.current_event.is_public) }}
+                        {{ fbtns.edit(url('plainui:sos_edit', conf_slug=conf.slug, assembly_slug=assembly.slug, event_slug=event.slug), color=color) }}
+                    </li>
+                {% endif %}
+                <li>
+                    {{ fbtns.share(link, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.schedule(room.current_event.id, scheduled, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.fav(room.current_event.id, "event", faved, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.report(link, color=color) }}
+                </li>
+            </ul>
+        </footer>
+    {% endif %}
+    </article>
+{%- endmacro %}
+
 {% macro icon_public(is_public) -%}
     {% if is_public %}
         <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye-fill" viewBox="0 0 16 16">
@@ -172,3 +267,24 @@
     {%- set imgs = [1,2,3,4,5,6,7]  -%}
     /static/plainui/img/rc3-assembly-event-0{{ imgs | random }}.png
 {%- endmacro -%}
+
+{%- macro livestream(room, my_favorite_events, my_scheduled_events) -%}
+    <div class="row border p-6 mx-0">
+        <div class="col-sm-12 col-lg-7 col-xl-8 mb-3 mb-lg-0">
+            {{ tile_stream(room, faved=true if room.current_event and room.current_event.id | safe in my_favorite_events,
+                scheduled=true if room.current_event and room.current_event.id | safe in my_scheduled_events) }}
+        </div>
+        <div class="col-sm-12 col-lg-5 col-xl-4 d-lg-flex flex-lg-column flex-lg-nowrap">
+            <h3 class="text-center bg-info p-3 text-white h3">{{_("Coming Up Next")}}</h3>
+            {% if room.next_event %}
+                {{ tile(room.next_event, faved=true if room.next_event.id | safe in my_favorite_events,
+                    scheduled=true if room.next_event.id | safe in my_scheduled_events, custom_class="flex-lg-grow-1 mb-3") }}
+            {% else %}
+                <p class="text-center my-6 flex-lg-grow">{{ _("no entry availaible")}}</p>
+            {% endif %}
+            {% if room.room.id %}
+                <a href="{{ url('plainui:room', conf_slug=conf.slug, room_id=room.room.id) }}" class="mt-3 mt-lg-auto btn btn-secondary btn-lg btn-block">{{ _("View next Events") }}</a>
+            {% endif %}
+        </div>
+    </div>
+{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/livestream.html b/src/plainui/jinja2/plainui/components/livestream.html
deleted file mode 100644
index 1a7faaad7f812b3d95381f20562c28780aaa37d5..0000000000000000000000000000000000000000
--- a/src/plainui/jinja2/plainui/components/livestream.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% macro livestream(title, event, upcomingEvents, recommendedEvents) -%}
-<div>
-    <h2 class="mb-5">{{ title }}</h2>
-
-    <div class="row border p-6 text-center mx-0">
-        <div class="col-sm-12 col-5">
-            <h3 class="mb-5">{{_("Live Stream")}}<br> TODO: Streaming Data</h3>
-            <div>
-                <p class="bg-secondary h6 text-white py-1 m-0">Event Name</p>
-                <p class="border p-6 m-0">PLAY</p>
-                <div class="bg-secondary h6 text-white py-1">
-                    <p class="m-0">Event Track - Duration: 00:00</p>
-                    <p>Speaker Name - Speaker</p>
-                </div>
-            </div>
-        </div>
-        <div class="col-sm-12 col-lg-7">
-            tbd
-        </div>
-    </div>
-</div>
-{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/resourcesbox.html b/src/plainui/jinja2/plainui/components/resourcesbox.html
index d50697d5397c84968b8422dd0a9034e18bd5f220..9a240ce43a9db8f5d1a19883ba47e24cad54124d 100644
--- a/src/plainui/jinja2/plainui/components/resourcesbox.html
+++ b/src/plainui/jinja2/plainui/components/resourcesbox.html
@@ -1,13 +1,20 @@
-{% macro resourcesbox(resources) -%}
-<div>
-    <h4 class="h2 mb-5">{{_("External Ressources")}}</h4>
-    <div class="border p-6">
-        <ul class="border border-secondary p-3 pl-6 mb-0">
-            <li class="pr-2"><a href="/foo" title="title">TODO: </a></li>
-            <li class="pr-2"><a href="/foo" title="title">loop</a></li>
-            <li class="pr-2" ><a href="/foo" title="title">through</a></li>
-            <li><a href="/foo" title="title">resources</a></li>
+{% macro resourcesbox(title, resources) -%}
+{% if title %}
+    <h4 class="h2 mb-5">{{title}}</h4>
+{% endif %}
+<div class="border p-6 flex-grow-1 d-flex flex-column justify-content-center align-items-center ">
+    {% if resources %}
+        <ul class="border border-secondary p-3 pl-6 mb-0 w-100">
+            {%- for ressource in resources %}
+                {% if ressource.visibility == "public" or ressource.visibility == "conference" %}
+                    <li class="pr-2"><a href="{{ ressource.file }}" title="{{ ressource.filename }}">{{ ressource.filename }}</a></li>
+                {% endif %}
+            {% endfor -%}
         </ul>
-    </div>
+    {% else %}
+        <div class="border border-secondary p-3 pl-6 w-100 d-flex flex-row flex-wrap justify-content-center align-items-center">
+            <p>{{ _("no entry availaible")}}</p>
+        </div>
+    {% endif %}
 </div>
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/tagbox.html b/src/plainui/jinja2/plainui/components/tagbox.html
index 0a167f767761bd3d01b855f081363ccac8bd2bb5..308efd62c118fcb2417239ea2c77caa9b0c92602 100644
--- a/src/plainui/jinja2/plainui/components/tagbox.html
+++ b/src/plainui/jinja2/plainui/components/tagbox.html
@@ -1,12 +1,16 @@
 {% macro tagbox(conf_slug, tags) -%}
-    <div>
-        <h4 class="h2 mb-5">{{_("Tags")}}</h4>
-        <ul class="border px-6 pt-6 pb-5 list-unstyled mb-0 d-flex flex-row flex-wrap justify-content-center align-items-center">
+<h4 class="h2 mb-5">{{_("Tags")}}</h4>
+{% if tags %}
+    <ul class="border px-6 flex-grow-1 pt-6 pb-5 list-unstyled mb-0 d-flex flex-row flex-wrap justify-content-center align-items-center">
         {%- for tag in tags %}
             <li class="pr-2 mb-2">
                 <a href="{{ url('plainui:tag', conf_slug=conf_slug, tag_slug=tag.tag.slug) }}" class="btn btn-tag-secondary">{{tag.tag.slug}}</a>
             </li>
         {% endfor -%}
-        </ul>
+    </ul>
+{% else %}
+    <div class="border flex-grow-1 px-6 pt-6 pb-5 d-flex flex-row flex-wrap justify-content-center align-items-center">
+        <p class="my-6">{{ _("no tags availaible")}}</p>
     </div>
+{% endif %}
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/tile_board.html b/src/plainui/jinja2/plainui/components/tile_board.html
index 539fc9c38e125d2df2838e57fc3dc695d2a6ec8a..50b9afac2c327c12a6ed00471cb8a72fbedbf52a 100644
--- a/src/plainui/jinja2/plainui/components/tile_board.html
+++ b/src/plainui/jinja2/plainui/components/tile_board.html
@@ -19,7 +19,7 @@
         {% endif %}
 
         {% if item.text %}
-            {{ markdownMacro.markdown_plain(item.text | truncate( 400, false, '...', 10), "rc3-tile-board__body card-body") }}
+            {{ markdownMacro.markdown_plain(item.text | safe | truncate( 400, false, '...', 10), "rc3-tile-board__body card-body") }}
         {% endif %}
 
         {% if item.owner_name or item.timestamp %}
diff --git a/src/plainui/jinja2/plainui/event.html b/src/plainui/jinja2/plainui/event.html
index 2e2fbc8b1c51e63aaa98a9578be561155fadf0cc..05bc0a8df9814651414194200629b9aca13dcf08 100644
--- a/src/plainui/jinja2/plainui/event.html
+++ b/src/plainui/jinja2/plainui/event.html
@@ -3,8 +3,8 @@
 {% import "plainui/components/title.html" as titleMacro %}
 {% import "plainui/components/tagbox.html" as tagboxMacro %}
 {% import "plainui/components/resourcesbox.html" as resboxMacro %}
-{% import "plainui/components/integrations.html" as integrations %}
 {% import "plainui/components/list_events.html" as list_events with context %}
+{% import "plainui/components/integrations.html" as integrations %}
 
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - Event {{event.name}}{% endblock %}
@@ -23,30 +23,41 @@
         report_url = url('plainui:event', conf_slug=conf.slug, event_slug=event.slug)
         )
     }}
-    {% if assembly.slug and assembly.name %}
-        <p>hosted by: <a href="{{ url("plainui:assembly", conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ assembly.name }}</a></p>
-    {% endif %}
-    {{ eventInfoMacro.eventInfo(event, speakers) }}
-    {% if event.kind == 'official' %}
-    {{ integrations.vocPlayer(vocLecture=event.slug) }}
+
+    {% if event.kind == "official" and running_state == "running" %}
+        <div class="border p-6 my-8">
+            {{ integrations.vocPlayer(vocLecture=event.slug) }}
+        </div>
+        <hr class="my-8" />
     {% endif %}
+
+    {% set current_assembly = {
+            "link": url("plainui:assembly", conf_slug=conf.slug, assembly_slug=assembly.slug),
+            "name": assembly.name
+        } if assembly and assembly.slug else {}
+    %}
+    {{ eventInfoMacro.eventInfo(event, speakers, current_assembly, conf_slug=conf.slug) }}
+
     <hr class="my-8" />
+
     {{ markdownMacro.markdown(markdown=event.description_html | safe) }}
+
     <hr class="my-8" />
+
     <aside class="row mx-0">
-        <div class="col-6">
+        <div class="col-12 col-lg-6 mb-5 mb-lg-0 d-flex flex-column flex-nowrap">
             {{ tagboxMacro.tagbox(conf.slug, tags) }}
         </div>
-        <div class="col-6">
-            {{ resboxMacro.resourcesbox() }}
+        <div class="col-12 col-lg-6 d-flex flex-column flex-nowrap">
+            {{ resboxMacro.resourcesbox( _("attachments"), attachments) }}
         </div>
     </aside>
 
     <hr class="my-8">
 
-    <h2>{{ _("upcoming events") }}</h2>
+    <h2>{{ _("recommendations") }}</h2>
     <div class="border p-6">
-        {{ list_events.slider(events_upcoming, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.slider(suggested, is_favorite_events, is_scheduled_events ) }}
     </div>
 
     <hr class="my-11 border-top-0">
diff --git a/src/plainui/jinja2/plainui/fahrplan.html b/src/plainui/jinja2/plainui/fahrplan.html
index bf80cf662fea877703031b88932a78ea0fecce26..1cefcfe56765b9875430f40d8caec4b77c3cb6ca 100644
--- a/src/plainui/jinja2/plainui/fahrplan.html
+++ b/src/plainui/jinja2/plainui/fahrplan.html
@@ -43,7 +43,7 @@
         {% if show_day_filters %}
         <div class="row justify-content-md-center">
             {% for n in range(days) %}
-                <button type="submit" name="set" value="d{{n if n != day else ''}}" class="m-2 btn {{ 'btn-primary active' if n == day else 'btn-secondary'}}">{{ _("Day %(n)s", n=n) }}</button>
+                <button type="submit" name="set" value="d{{n if n != day else ''}}" class="m-2 btn {{ 'btn-primary active' if n == day else 'btn-secondary'}}">{{ _("Day %(n)s", n=n + 1) }}</button>
             {%- endfor %}
         </div>
         {% endif %}
diff --git a/src/plainui/jinja2/plainui/header.html b/src/plainui/jinja2/plainui/header.html
index a40d9450d57e4f21066ffabfc4e2d498f52cfe40..0a3f9c0d486dd3c61a64e8160d75fe4830a960e6 100644
--- a/src/plainui/jinja2/plainui/header.html
+++ b/src/plainui/jinja2/plainui/header.html
@@ -60,7 +60,8 @@
         <a class="btn rc3-header__additional-linkbox {{ 'btn-primary' if view_name == 'plainui:fahrplan' else 'btn-secondary' }}" href="{{ url('plainui:fahrplan', conf_slug=conf.slug) }}"  title="{{ _("Fahrplan") }}">
             {{ _("Fahr plan") }}
         </a>
-        <div class="rc3-header__additional-box-2x1">
+            {#
+        <div class="rc3-header__additional-box-2x1 rc3-header__nomob">
             <form class="rc3-header__additional-box-1x2" method="POST" action="{{ url('plainui:modify_high_contrast', conf_slug=conf.slug) }}">
                 {{ csrf_input }}
                 <input type="hidden" name="next" value="{{request.get_full_path() }}">
@@ -76,16 +77,17 @@
                     </svg>
                 </button>
             </form>
-            <form class="rc3-header__additional-box-1x2" method="POST" action="{{ url('plainui:modify_language', conf_slug=conf.slug) }}">
-                {{ csrf_input }}
-                <input type="hidden" name="next" value="{{ request.get_full_path() }}">
-                <button class="btn rc3-header__additional-button {{ 'btn-primary' if get_language() == 'de' else 'btn-secondary' }}" name="language" value="de">
-                    {{ _("de") }}
-                </button>
-                <button class="btn rc3-header__additional-button {{ 'btn-primary' if get_language() == 'en' else 'btn-secondary' }}" name="language" value="en">
-                    {{ _("en") }}
-                </button>
-            </form>
         </div>
+            #}
+        <form class="rc3-header__additional-box-2x1 rc3-header__nomob" method="POST" action="{{ url('plainui:modify_language', conf_slug=conf.slug) }}">
+            {{ csrf_input }}
+            <input type="hidden" name="next" value="{{ request.get_full_path() }}">
+            <button class="btn rc3-header__additional-button {{ 'btn-primary' if get_language() == 'de' else 'btn-secondary' }}" name="language" value="de">
+                {{ _("de") }}
+            </button>
+            <button class="btn rc3-header__additional-button {{ 'btn-primary' if get_language() == 'en' else 'btn-secondary' }}" name="language" value="en">
+                {{ _("en") }}
+            </button>
+        </form>
     </div>
 </header>
diff --git a/src/plainui/jinja2/plainui/index.html b/src/plainui/jinja2/plainui/index.html
index 790286c2a9df0cc308859f2c0f959e82f8d28bae..7d6e02c85779edd735518cbc34dcde64b9e4514e 100644
--- a/src/plainui/jinja2/plainui/index.html
+++ b/src/plainui/jinja2/plainui/index.html
@@ -1,7 +1,6 @@
 {% extends "plainui/base.html" %}
 
 {% import "plainui/components/markdown.html" as markdownMacro %}
-{% import "plainui/components/livestream.html" as livestreamMacro %}
 {% import "plainui/components/three_cards.html" as threeCardsMacro %}
 
 {% block title %}{{conf.name}} - Index{% endblock %}
@@ -14,20 +13,20 @@
 <h2>{{ _("Introduction") }}</h2>
 {{threeCardsMacro.three_cards(cards=[{
     "title": _("What's Official?"),
-    "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+    "text": _("official description"),
     "link": {
         "url": url('plainui:ccc_events', conf_slug=conf.slug),
-        "text": _("Explore Curated Events"),
+        "text": _("Explore Curated Events Button"),
         "type": "secondary"
     }
 },
 {
-    "title": _("Congress Platform"),
-    "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+    "title": _("Congress Platform Title"),
+    "text": _("platform description"),
 },
 {
-    "title": _("Community"),
-    "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.",
+    "title": _("Community Title"),
+    "text": _("community description"),
     "link": {
         "url": url('plainui:assemblies', conf_slug=conf.slug),
         "text": _("Explore Community Events"),
@@ -36,18 +35,16 @@
 }
 ]) }}
 
-<hr class="my-8">
-
-{{ livestreamMacro.livestream(title=_("Explore Events")) }}
+<hr class="my-11 border-top-0">
 
-<hr class="my-8">
+{#{{ markdownMacro.markdown(markdown="markdown module. TODO: where comes Data from") }}#}
 
-{{ markdownMacro.markdown(markdown="markdown module. TODO: where comes Data from") }}
 
+{#
 <hr class="my-8">
 
 <div class="border p-6 mt-8 mb-11">
         TODO: Tracks...
 </div>
-
+#}
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/landing.html b/src/plainui/jinja2/plainui/landing.html
index 6972b69a6f2266201bf30fcff8c80d7956d8499f..f81413c0b6d2e9910b5229b05c765f6050a483cd 100644
--- a/src/plainui/jinja2/plainui/landing.html
+++ b/src/plainui/jinja2/plainui/landing.html
@@ -5,7 +5,7 @@
 <article class="rc3-landing">
     <figure class="rc3-landing__container mb-0">
         <header class="rc3-landing__content">
-            <h1>
+            <h1 class="rc3-landing__title">
                 <a
                     href="{{ url('plainui:index', conf_slug=conf.slug ) }}"
                     title="{{ _("Skip intro") }}"
diff --git a/src/plainui/jinja2/plainui/login.html b/src/plainui/jinja2/plainui/login.html
index 546bf579173aa472133370aaf9d0287a2129690e..0c5b2d129c997aaba30dd2d9f8e108852e87a527 100644
--- a/src/plainui/jinja2/plainui/login.html
+++ b/src/plainui/jinja2/plainui/login.html
@@ -32,7 +32,7 @@
             <a
                 href="https://tickets.events.ccc.de/rc3/"
                 target="_blank"
-                rel="noreferrer"
+                rel="external, noreferrer"
                 class="btn btn-xl btn-block btn-secondary external"
                 title="{{ _("New Ticket") }}"
             >
diff --git a/src/plainui/jinja2/plainui/manage_badges.html b/src/plainui/jinja2/plainui/manage_badges.html
new file mode 100644
index 0000000000000000000000000000000000000000..4af424272496b741397615be04d3c7fb190ea902
--- /dev/null
+++ b/src/plainui/jinja2/plainui/manage_badges.html
@@ -0,0 +1,86 @@
+{% extends "plainui/base.html" %}
+{% import "plainui/components/list_assemblies.html" as list_assm with context %}
+{% import "plainui/components/list_events.html" as list_events with context %}}
+{% import "plainui/components/badge_elements.html" as badge_elements with context %}
+{% import "plainui/components/form_elements.html" as form_elements %}
+{% import "plainui/components/image.html" as imageMacro %}
+
+{% block title %}{{conf.name}} - {{ _("Profile") }}{% endblock %}
+{% block content %}
+
+{{ titleMacro.title( _("Manage Badges") ) }}
+
+<div class="row">
+    <div class="col-12  p-1">
+        <a  class="btn btn-secondary p-2 mb-2" href="{{ url('plainui:userprofile', conf_slug=conf.slug ) }}">
+            {{ _("Back") }}
+        </a>
+    </div>
+</div>
+
+<h2>{{ _("Accepted Badges") }}</h2>
+<div class="row border my-5 p-6 container-fluid content-row">
+    <div class="row">
+        {% if not badges_accepted %}
+            <div  class="row my-5 p-6">
+                {{ _("No badges Accepted.") }}
+            </div>
+        {% endif %}
+        {% for badge_link in badges_accepted %}
+            {{ badge_elements.userBadge(badge_link, True) }}
+        {% endfor %}
+    </div>
+
+    <!-- <div class="row text-center p-3">
+        <div class="col-md-12 w-100 text-center">
+            <a class="btn btn-secondary w-100" href="{{ url('plainui:redeem_badge', conf_slug=conf.slug) }}">{{ _("Redeem Badge Token") }}</a>
+        </div>
+    </div> -->
+</div>
+
+<hr class="my-8">
+
+<h2>{{ _("Pending Badges") }}</h2>
+<div class="row border my-5 p-6 container-fluid content-row">
+    <div class="row">
+        {% if not badges_not_accepted %}
+            <div  class="row my-5 p-6">
+                {{ _("No badges are waiting for approval.") }}
+            </div>
+        {% endif %}
+        {% for badge_link in badges_not_accepted %}
+            {{ badge_elements.userBadge(badge_link, True) }}
+        {% endfor %}
+    </div>
+
+    <!-- <div class="row text-center p-3">
+        <div class="col-md-12 w-100 text-center">
+            <a class="btn btn-secondary w-100" href="{{ url('plainui:redeem_badge', conf_slug=conf.slug) }}">{{ _("Redeem Badge Token") }}</a>
+        </div>
+    </div> -->
+</div>
+
+<hr class="my-8">
+
+<div class="row border my-5 p-6 container-fluid content-row">
+    <div class="row my-5 p-6">
+        <form method="POST" class="horizontal" action="{{ url('plainui:redeem_badge', conf_slug=conf.slug) }}">
+            {{ csrf_input }}
+            <div class="form-row align-items-center">
+                <div class="col-12">
+                    <h2>{{ _("Redeem Token") }}</h2>
+                </div>
+                <div class="col-9">
+                    <input name="token" class="form-control mb-2" placeholder="Token" >
+                </div>
+                <div class="col-3">
+                    <input type="submit" class="btn btn-secondary p-2 mb-2">
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+
+
+
+{% endblock %}
diff --git a/src/plainui/jinja2/plainui/my_fahrplan.html b/src/plainui/jinja2/plainui/my_fahrplan.html
index beaba0931ad839effa3886d02ef8461bf0bd53d7..bcd3ab76126b6f0a10bab0df2c74bde70ef240f2 100644
--- a/src/plainui/jinja2/plainui/my_fahrplan.html
+++ b/src/plainui/jinja2/plainui/my_fahrplan.html
@@ -1,5 +1,78 @@
 {% extends "plainui/base.html" %}
+{% from "plainui/components/calendar.html" import calendar with context %}
+{% import "plainui/components/list_events.html" as list_events with context %}
 {% block title %}{{ _("%(conf)s - Personal Schedule", conf=conf.name) }}{% endblock %}
 {% block content %}
-    {{ titleMacro.title(_("personal schedule")) }}
+
+    {{ titleMacro.title(_("personal schedule"),
+        share_url = url('plainui:my_fahrplan', conf_slug=conf.slug),
+        report_url = url('plainui:my_fahrplan', conf_slug=conf.slug)) }}
+
+    {# TODO: Download options
+    <h2>{{ _("download") }}</h2>
+    <div class="row border p-5 text-center mx-0">
+            <a href="#" class="btn btn-primary m-2">{{ _("Xcal") }}</a>
+            <a href="#" class="btn btn-primary m-2">{{ _("Xml") }}</a>
+            <a href="#" class="btn btn-primary m-2">{{ _("Json") }}</a>
+            <a href="#" class="btn btn-primary m-2">{{ _("QR-Code") }}</a>
+    </div> #}
+
+    <form method="GET" class="border p-5">
+        <input type="hidden" name="mode" value="{{mode}}">
+        {% if show_day_filters %}<input type="hidden" name="show_day_filters" value="y">{% endif %}
+        {% if show_assembly_filters %}<input type="hidden" name="show_assembly_filters" value="y">{% endif %}
+        {% if show_track_filters %}<input type="hidden" name="show_track_filters" value="y">{% endif %}
+        {% if day %}<input type="hidden" name="day" value="{{day}}">{% endif %}
+        {% if curated %}<input type="hidden" name="curated" value="y">{% endif %}
+        {% if assembly %}<input type="hidden" name="assembly" value="{{assembly.slug}}">{% endif %}
+        {% if track %}<input type="hidden" name="track" value="{{track.slug}}">{% endif %}
+
+        <div class="row justify-content-md-center">
+            <button type="submit" name="set" value="mlist" class="m-2 btn {{ 'btn-primary active' if mode == 'list' else 'btn-secondary'}}">{{ _("view as list") }}</button>
+            <button type="submit" name="set" value="mcalendar" class="m-2 btn {{ 'btn-primary active' if mode == 'calendar' else 'btn-secondary'}}">{{ _("view as calendar") }}</button>
+        </div>
+        <div class="row justify-content-md-center">
+            <button type="submit" name="set" value="fday" class="m-2 btn {{ 'btn-primary active' if show_day_filters else 'btn-secondary'}}">{{ _("by day") }}</button>
+            <button type="submit" name="set" value="ftrack" class="m-2 btn {{ 'btn-primary active' if show_track_filters else 'btn-secondary'}}">{{ _("by track") }}</button>
+            <button type="submit" name="set" value="curated" class="m-2 btn {{ 'btn-primary active' if curated else 'btn-secondary'}}">{{ _("curated only") }}</button>
+            {# Assembly events are displayed on assmbly page. filter here by assembly will mean display serveral hundred assemblies. leave for the future
+                <button type="submit" name="set" value="fassembly" class="m-2 btn {{ 'btn-primary active' if show_assembly_filters else 'btn-secondary'}}">Assembly</button> #}
+        </div>
+
+        {% if show_day_filters %}
+        <div class="row justify-content-md-center">
+            {% for n in range(days) %}
+                <button type="submit" name="set" value="d{{n if n != day else ''}}" class="m-2 btn {{ 'btn-primary active' if n == day else 'btn-secondary'}}">{{ _("Day %(n)s", n=n+1) }}</button>
+            {%- endfor %}
+        </div>
+        {% endif %}
+
+        {# Leave for future, see above
+        {% if show_assembly_filters %}
+        <div class="row justify-content-md-center border">
+            {% for asmbly in assemblies %}
+                <button type="submit" name="set" value="a{{asmbly.slug if asmbly != assembly else ''}}" class="btn btn-primary{% if asmbly == assembly %} active{% endif %} col m-2">{{ asmbly.name }}</button>
+            {%- endfor %}
+        </div>
+        {% endif %} #}
+
+        {% if show_track_filters %}
+        <div class="row justify-content-md-center">
+            {% for t in tracks %}
+                <button type="submit" name="set" value="t{{t.slug if t != track else ''}}" class="m-2 btn {{ 'btn-primary active' if t == track else 'btn-secondary'}}">{{ t.name }}</button>
+            {%- endfor %}
+        </div>
+        {% endif %}
+    </form>
+
+    <hr class="my-8">
+
+    {% if mode == 'list' %}
+        {{ list_events.list(events, my_favorite_events, my_scheduled_events) }}
+    {% elif mode == 'calendar' %}
+        {{ calendar(events) }}
+    {% endif %}
+
+    <hr class="my-11 border-top-0">
+
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/password_reset_form.html b/src/plainui/jinja2/plainui/password_reset_form.html
index 12438d6c1595b2e9579e0a7593d00e1ec6902f3a..8c5bd3ef3789244b0dba455b555e2e3cff65bbeb 100644
--- a/src/plainui/jinja2/plainui/password_reset_form.html
+++ b/src/plainui/jinja2/plainui/password_reset_form.html
@@ -18,6 +18,7 @@
     {{ formElements.text(form, 'email') }}
 
     <p class="my-5 font-headings text-white text-center">{{ _("You will receive an e-mail with a reset link.") }}</p>
+    <p class="my-5 font-headings text-white text-center">{{ _("If you have no configured e-mail address, revisit your ticket activation link") }}</p>
 
     <ul class="row row-cols-1 row-cols-md-2 list-unstyled">
         <li class="col mb-3 md-md-0">
diff --git a/src/plainui/jinja2/plainui/personal_message_list.html b/src/plainui/jinja2/plainui/personal_message_list.html
index bb97eaf0324a5e923b2bc54355bd1a6f69a6bdcd..39e86236bfd8e500343c538b9747f4134669f635 100644
--- a/src/plainui/jinja2/plainui/personal_message_list.html
+++ b/src/plainui/jinja2/plainui/personal_message_list.html
@@ -63,22 +63,23 @@
                             </a>
                         </figure>
                     <section class="col pt-3 px-2">
-                        {% set recipient = msg.sender_name if not sent_mode else msg.recipient_name %}
-                        <a class="text-white" href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=recipient) }}">
-                            <h3 class="px-2 card-title h4 text-white">
-                                {{recipient}}
-                            </h3>
-                        </a>
+
+                        {% set recipient = msg.sender.display_name if not sent_mode else msg.recipient.display_name %}
+                        {% set recipient_pronouns = msg.sender.pronouns if not sent_mode else msg.recipient.pronouns %}
+                        {% set recipient_id = msg.sender_id if not sent_mode else msg.recipient_id %}
 
                         <a class="text-white" href="{{ url('plainui:personal_message_show', conf_slug=conf.slug, msg_id=msg.id) }}">
-                            <p class="px-2">
+                            <p class="px-2 card-title h4 text-white">
                                 {{msg.subject}}
                             </p>
                         </a>
 
-                        <footer class="card-footer d-flex justify-content-between align-items-center bg-transparent text-white font-headings fs-medium">
+                        <footer class="card-footer d-flex align-items-center bg-transparent text-white font-headings fs-medium">
+                            {% if recipient and recipient_id %}
+                                <a class="pr-1" href="{{ url('plainui:user', conf_slug=conf.slug, id=recipient_id) }}">{{recipient}},</a>
+                            {% endif %}
                             <time datetime="{{ msg.timestamp }}">{{msg.timestamp | strftime}}</time>
-                            <div class="d-inline-flex">
+                            <div class="d-inline-flex ml-auto">
                                 <a class="ml-2 btn-icon-big btn btn-secondary" title="{{_("read")}}" href="{{ url('plainui:personal_message_show', conf_slug=conf.slug, msg_id=msg.id) }}">
                                     <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-double-right" viewBox="0 0 16 16">
                                         <path fill-rule="evenodd" d="M3.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L9.293 8 3.646 2.354a.5.5 0 0 1 0-.708z"/>
diff --git a/src/plainui/jinja2/plainui/personal_message_show.html b/src/plainui/jinja2/plainui/personal_message_show.html
index cd7038280b3b481fa23d22e302c2f2da7ffa8e7d..518ad34aba4cb02b7285fabfd7565c5e6a015de1 100644
--- a/src/plainui/jinja2/plainui/personal_message_show.html
+++ b/src/plainui/jinja2/plainui/personal_message_show.html
@@ -6,7 +6,7 @@
 {% block content %}
 
 <article class="mb-11">
-    {% set title = _("messages_from_sender") + " " + msg.sender.username %}
+    {% set title = _("Message from %(user)s", user=msg.sender.display_name) %}
     {{ titleMacro.title( title , report_url=msg.id, report_kind="pn") }}
 
     <div class="border my-8 p-6 text-center">
@@ -37,9 +37,14 @@
 
     <h2 class="d-flex flex-row flex-wrap mt-11">
         {{ msg.subject }}
-        <time datetime="{{msg.timestamp}}" class="d-block font-sans-serif fs-medium pl-2 ml-auto align-self-center">
-            {{ msg.timestamp | strftime }}
-        </time>
+        <span class="d-block text-transform-none font-sans-serif fs-medium text-transform-none ml-auto align-self-center">
+            <a class="pl-2" href="{{ url('plainui:user', conf_slug=conf.slug, id=msg.sender_id) }}">
+                {{ msg.sender.display_name }}
+            </a>
+            <time datetime="{{msg.timestamp}}" class="text-white pl-2">
+                {{ msg.timestamp | strftime }}
+            </time>
+        </span>
     </h2>
 
     {{ markdown(msg_body) }}
diff --git a/src/plainui/jinja2/plainui/profile.html b/src/plainui/jinja2/plainui/profile.html
index a6af9a4c13fa9f143ff9bab29684b1f2bf20830f..191eb4a5ec601aba3686ab6657cb14b76395fa88 100644
--- a/src/plainui/jinja2/plainui/profile.html
+++ b/src/plainui/jinja2/plainui/profile.html
@@ -2,6 +2,7 @@
 {% import "plainui/components/list_assemblies.html" as list_assm with context %}
 {% import "plainui/components/list_events.html" as list_events with context %}
 {% import "plainui/components/form_elements.html" as form_elements %}
+{% import "plainui/components/badge_elements.html" as badge_elements with context %}
 {% import "plainui/components/image.html" as imageMacro %}
 
 {% block title %}{{conf.name}} - {{ _("Profile") }}{% endblock %}
@@ -9,24 +10,29 @@
 
 {{ titleMacro.title( _("My Dashboard") ) }}
 
-<div class="row border p-6 m-0">
-    <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{{ user.username }}</h2>
+<div class="border p-6 m-0">
+    <h2 class="bg bg-info p-2 px-5 h3 text-white text-center">{{ user.username }}</h2>
     <div class="row">
-        <div class="col-5">
-            {{ imageMacro.image(image="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg", alt=_("Avatar image"), title=_("Avatar image") ) }}
+        {{ imageMacro.image(image="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg", alt=_("Avatar image"), title=_("Avatar image") ) }}
+        <div class="col">
+            <dl class="grid-list">
+                <dt class="grid-list__item grid-list__item--title">{{ _("username") }}</dt>
+                <dd class="grid-list__item grid-list__item--text">{{ user.username }}</dd>
+
+                <dt class="grid-list__item grid-list__item--title">{{ _("email") }}</dt>
+                <dd class="grid-list__item grid-list__item--text">{{ user.email }}</dd>
+
+                <dt class="grid-list__item grid-list__item--title">{{ _("last login") }}</dt>
+                <dd class="grid-list__item grid-list__item--text">{{ user.last_login | strftime }}</dd>
+
+                {% if user.id %}
+                    <dt class="grid-list__item grid-list__item--title">{{ _("My public User Page") }}</dt>
+                    <dd class="grid-list__item grid-list__item--text"><a href="{{ url('plainui:user', conf_slug=conf.slug, id=user.id) }}" class="btn btn-secondary">{{ _('View')}}</a></dd>
+                {% endif %}
+            </dl>
         </div>
-        <dl clasS="col-7 p-2">
-            <dt>{{ _("username") }}</dt>
-            <dd>{{ user.username }}</dd>
-
-            <dt>{{ _("email") }}</dt>
-            <dd>{{ user.email }}</dd>
-
-            <dt>{{ _("last login") }}</dt>
-            <dd>{{ user.last_login.strftime('%Y-%m-%d %H:%M:%S') }}</dd>
-        </dl>
     </div>
-    <form class="col-12" method="POST">
+    <form method="POST">
         <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{{ _("custom preferences") }}</h2>
         {{ csrf_input }}
 
@@ -50,21 +56,58 @@
         {{ form_elements.errors(form) }}
     </form>
 </div>
+<hr class="my-5 border-primary">
 
-{# Badges disabled until further work.
-<div class="row border my-5 p-6">
-    <form action="{{ url('plainui:badge_token_submit', conf_slug=conf.slug) }}" method="POST">
-        {{ csrf_input }}
-        <input class="form-control" placeholder='{{ _("Badge-Token") }}' name="token">
-        <button type="submit" class="btn btn-primary">{{ _("Submit") }}</button>
-    </form>
+<h2>{{ _("My Badges") }}</h2>
+<div class="row border my-5 p-6 container-fluid content-row">
+    <div class="row">
+        {% if not badges %}
+            <div  class="row my-5 p-6">
+                {{ _("No entries available.") }}
+            </div>
+        {% endif %}
+        {% for badge_link in badges %}
+            {{ badge_elements.userBadge(badge_link, False, True) }}
+        {% endfor %}
+    </div>
+
+    <div class="row my-1 p-2">
+        <form method="POST" class="horizontal" action="{{ url('plainui:redeem_badge', conf_slug=conf.slug) }}">
+            {{ csrf_input }}
+            <div class="form-row align-items-center">
+                <div class="col-12">
+                    <h2>{{ _("Redeem Token") }}</h2>
+                </div>
+                <div class="col-9">
+                    <input name="token" class="form-control mb-2" placeholder="Token" >
+                </div>
+                <div class="col-3">
+                    <input type="submit" class="btn btn-secondary p-2 mb-2">
+                </div>
+            </div>
+        </form>
+    </div>
 
-    <ul>
-    {% for badge_link in badges %}
-        <li>{{badge_link.badge.name}} {% if badge_link.hidden %}(HIDDEN){% endif %}</li>
-    {% endfor %}
-    </ul>
-</div> #}
+    <div class="row my-1 p-2">
+        <div class="col-12 p-0">
+            <h2>{{ _("Manage Badges") }}</h2>
+        </div>
+        <div class="col-12">
+            {% if amount_badges_not_accepted > 0 %}
+            {{ _("Pending badges") }}: {{ amount_badges_not_accepted }}<br>
+            {% endif %}
+            <a  class="btn btn-secondary p-2 mb-2" href="{{ url('plainui:manage_badges', conf_slug=conf.slug ) }}">
+                {{ _("Badges: Accept / Revoke / Visibility") }}
+            </a>
+        </div>
+    </div>
+
+    <!-- <div class="row text-center p-3">
+        <div class="col-md-12 w-100 text-center">
+            <a class="btn btn-secondary w-100" href="{{ url('plainui:redeem_badge', conf_slug=conf.slug) }}">{{ _("Redeem Badge Token") }}</a>
+        </div>
+    </div> -->
+</div>
 
 <hr class="my-8">
 
diff --git a/src/plainui/jinja2/plainui/public_fahrplan.html b/src/plainui/jinja2/plainui/public_fahrplan.html
index 8748851dd9994500c0de6f88888778c5ac0b27d0..f506ab812e7d1596ea2cb09a966757b37c60a65a 100644
--- a/src/plainui/jinja2/plainui/public_fahrplan.html
+++ b/src/plainui/jinja2/plainui/public_fahrplan.html
@@ -1,18 +1,48 @@
-{% extends "plainui/base.html" %}
 {% from "plainui/components/calendar.html" import calendar with context %}
-{% import "plainui/components/list_events.html" as list_events with context %}
 
-{% block title %}{{ _("%(conf)s - Public Fahrplan", conf=conf.name) }}{% endblock %}
-{% block fullpage %}
+<html lang="{{ get_language() }}" class="no-js">
+    <head>
+        <meta charset="utf-8">
+        <link rel="stylesheet" href="{{ static('plainui/%s.css' % (css_scope(),)) }}">
+        <link rel="icon" href="{{ static('plainui/img/favicon.ico') }}" type="image/x-icon">
+        <title>{{ _("%(conf)s - Public Fahrplan", conf=conf.name) }}</title>
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        {% block head %}{% endblock %}
+        <script>
+            document.addEventListener('DOMContentLoaded', (e) => {
+                document.querySelector('html').classList.remove('no-js');
+                document.querySelector('html').classList.add('js');
+            });
+        </script>
+    </head>
+    <body class="rc3-fahrplan__pub-body p-4">
+        <h1 class="h2">{{ _("%(conf)s Fahrplan", conf=conf.name) }}</h1>
 
-    {{ titleMacro.title(_("%(conf)s Fahrplan", conf=conf.name)) }}
+        {% if mode == 'list' %}
+            {{ list_events.list(events, [], []) }}
+        {% elif mode == 'calendar' %}
+            {{ calendar(events, [], [], public=True) }}
+        {% endif %}
 
-    {% if mode == 'list' %}
-        {{ list_events.list(events, [], []) }}
-    {% elif mode == 'calendar' %}
-        {{ calendar(events, [], [], public=True) }}
-    {% endif %}
-{% endblock %}
-{% block jstools %}
-<script async src="{{ static('plainui/js/modal.js') }}"></script>
-{% endblock %}
+        <footer id="footer" class="mt-auto border-top border-primary py-3 py-xl-5">
+            <ul class="container nav p-0 justify-content-center">
+                <li>
+                    <a class="nav-link" title="{{ _("Contact Us") }}" href="https://legal.rc3.world">{{ _("Contact Us") }}</a>
+                </li>
+                <li>
+                    <a class="nav-link" title="{{ _("Privacy Policy") }}" href="https://legal.rc3.world">{{ _("Privacy Policy") }}</a>
+                </li>
+                <li>
+                    <a class="nav-link" title="{{ _("About RC3") }}" href="https://howto.rc3.world/">{{ _("About RC3") }}</a>
+                </li>
+                <li>
+                    <a class="nav-link" title="{{ _("Disclaimer") }}" href="https://legal.rc3.world">{{ _("Disclaimer") }}</a>
+                </li>
+                <li>
+                    <a class="nav-link" title="{{ _("Principles") }}" href="https://howto.rc3.world/rules.html">{{ _("Principles") }}</a>
+                </li>
+            </ul>
+        </footer>
+    </body>
+    <script async src="{{ static('plainui/js/modal.js') }}"></script>
+</html>
diff --git a/src/plainui/jinja2/plainui/redeem_badge.html b/src/plainui/jinja2/plainui/redeem_badge.html
new file mode 100644
index 0000000000000000000000000000000000000000..ee0c37b6ba1c6004222921bfc5f6f8655ca3e503
--- /dev/null
+++ b/src/plainui/jinja2/plainui/redeem_badge.html
@@ -0,0 +1,28 @@
+{% extends "plainui/base.html" %}
+{% import "plainui/components/list_assemblies.html" as list_assm with context %}
+{% import "plainui/components/list_events.html" as list_events with context %}
+{% import "plainui/components/form_elements.html" as form_elements %}
+
+
+{% block title %}{{conf.name}} - {{ _("Profile") }}{% endblock %}
+{% block content %}
+
+
+{{ titleMacro.title( _("Redeem Badge Token") ) }}
+
+<div class="row my-5 p-6">
+    <form method="POST">
+        {{ csrf_input }}
+        {{ form_elements.errors(form) }}
+        <div class="form-row align-items-center">
+            <div class="col-9">
+                <input name="token" class="form-control mb-2" placeholder="Token" {% if urlToken %} value="{{ urlToken }}" {% endif %}>
+            </div>
+            <div class="col-3">
+                <input type="submit" class="btn btn-secondary p-2 mb-2">
+            </div>
+        </div>
+    </form>
+</div>
+
+{% endblock %}
diff --git a/src/plainui/jinja2/plainui/redeem_token.html b/src/plainui/jinja2/plainui/redeem_token.html
index c0de89b6c6d9b4028607d8b57f80e2c80d154a23..1b0027b1374d01bf3c63116f165a898e4dcc02e3 100644
--- a/src/plainui/jinja2/plainui/redeem_token.html
+++ b/src/plainui/jinja2/plainui/redeem_token.html
@@ -5,50 +5,76 @@
     {{ titleMacro.title(_("Redeem Token")) }}
 
     {% if step == 'authenticated' %}
-    <form class="border p-6 my-8" method="POST" action="{{ url('plainui:redeem_token_loggedin', conf_slug=conf.slug) }}">
-        <h2 class="text-center bg-info p-3 text-white h3">{{ _("You are Logged in as '%(user)s', do you want to join the Conference with this User?", user=user.username) }}</h2>
-        {{ csrf_input }}
-        <input type="hidden" name="token" value="{{ form['jwt'].value() }}">
-        <a href="{{ url('plainui:logout', conf_slug=conf.slug) }}" class="btn btn-secondary">{{ _("No")}}</a>
-        <button type="submit" class="btn btn-primary">{{ _("Yes")}}</button>
-    </form>
-    {% elif step == 'token' %}
-    <form class="border p-6 my-8" method="GET" action="{{ url('plainui:redeem_token', conf_slug=conf.slug) }}">
-        {{ form_elements.errors(form) }}
-        {{ form_elements.text(form, 'jwt') }}
-        <div class="d-flex">
-            <button type="submit" class="btn btn-primary ml-auto">{{ _("Submit Token")}}</button>
-        </div>
-    </form>
-    {%- else %}
-    <div>
-        <form class="border p-6 my-8" method="POST" action="{{ url('plainui:redeem_token_create_user', conf_slug=conf.slug) }}">
-            <h2 class="text-center bg-info p-3 text-white h3">{{ _("Create new Account") }}</h2>
-            {% if footnote %}<small>{{ _("Use the next form if you already have an account") }}<small>{% endif %}
+        <form class="mw-810 border p-6 mx-auto mb-11{% if form.errors %} border-danger{% endif %}" method="POST" action="{{ url('plainui:redeem_token_loggedin', conf_slug=conf.slug) }}">
+            <h2 class="text-center bg-info p-3 text-white h3">{{ _("Hello '%(user)s'", user=user.username) }}</h2>
+            <p class="my-5 font-headings text-white text-center">{{ _("You are Logged in as '%(user)s', do you want to join the Conference with this User?", user=user.username) }}</p>
             {{ csrf_input }}
-            {{ form_elements.errors(form_create) }}
-            {{ form_elements.hidden(form_create, 'token') }}
-            {{ form_elements.text(form_create, 'username') }}
-            {{ form_elements.password(form_create, 'password1') }}
-            {{ form_elements.password(form_create, 'password2') }}
-            <div class="d-flex">
-                <button type="submit" class="btn btn-primary ml-auto">{{ _("Create a new Account with your Ticket")}}</button>
-            </div>
+            <input type="hidden" name="token" value="{{ form['jwt'].value() }}">
+            <ul class="row row-cols-1 row-cols-lg-2 list-unstyled">
+                <li class="col mb-3 mb-lg-0">
+                    <a href="{{ url('plainui:logout', conf_slug=conf.slug) }}" class="btn btn-xl btn-block btn-secondary">{{ _("No")}}</a>
+                </li>
+                <li class="col">
+                    <button type="submit" class="btn btn-xl btn-block btn-primary">{{ _("Yes")}}</button>
+                </li>
+            </ul>
+            {{ form_elements.errors(form) }}
         </form>
-    </div>
+    {% elif step == 'token' %}
+        <form class="mw-810 border p-6 mx-auto mb-11{% if form.errors %} border-danger{% endif %}" method="GET" action="{{ url('plainui:redeem_token', conf_slug=conf.slug) }}">
+            {{ form_elements.text(form, 'jwt') }}
 
-    <div>
-        <form class="border p-6 my-8" method="POST" action="{{ url('plainui:redeem_token_add_to_user', conf_slug=conf.slug) }}">
-            <h2  class="text-center bg-info p-3 text-white h3" id="#login">{{ _("Got an Account already? Login here") }}</h2>
-            {{ csrf_input }}
-            {{ form_elements.errors(form_add) }}
-            {{ form_elements.hidden(form_add, 'token') }}
-            {{ form_elements.text(form_add, 'username') }}
-            {{ form_elements.password(form_add, 'password') }}
-            <div class="d-flex">
-                <button type="submit" class="btn btn-primary ml-auto">{{ _("Add your Ticket to this existing Account")}}</button>
-            </div>
+            <ul class="row row-cols-1 row-cols-lg-3 list-unstyled">
+                <li class="col">
+                    <button type="submit" class="btn btn-xl btn-block btn-secondary"
+                        formaction="{{ url('plainui:token_password_reset', conf_slug=conf.slug) }}"
+                        formmethod="POST"
+                        name="csrfmiddlewaretoken"
+                        value="{{csrf_token}}"
+                    >{{ _("Use Token for Password Reset") }}</button>
+                </li>
+                <li class="col ml-auto">
+                    <button type="submit" class="btn btn-xl btn-block btn-primary">{{ _("Submit Token")}}</button>
+                </li>
+            </ul>
+            {{ form_elements.errors(form) }}
         </form>
-    </div>
+    {%- else %}
+        <div>
+            <form class="mw-810 border p-6 mx-auto mb-11{% if form_create.errors %} border-danger{% endif %}" method="POST" action="{{ url('plainui:redeem_token_create_user', conf_slug=conf.slug) }}">
+                <h2 class="text-center bg-info p-3 text-white h3">{{ _("Create new Account") }}</h2>
+                {% if footnote %}<p class="my-5 font-headings text-white text-center">{{ _("Use the next form if you already have an account") }}</p>{% endif %}
+                {{ csrf_input }}
+                {{ form_elements.hidden(form_create, 'token') }}
+                {{ form_elements.text(form_create, 'username') }}
+                {{ form_elements.password(form_create, 'password1') }}
+                {{ form_elements.password(form_create, 'password2') }}
+                <ul class="row row-cols-1 row-cols-lg-3 list-unstyled">
+                    <li class="col ml-auto">
+                        <button type="submit" class="btn btn-xl btn-block btn-primary ml-auto">{{ _("Create a new Account with your Ticket")}}</button>
+                    </li>
+                </ul>
+                {{ form_elements.errors(form_create) }}
+            </form>
+        </div>
+
+        <div>
+            <form class="mw-810 border p-6 mx-auto mb-11{% if form_add.errors %} border-danger{% endif %}" method="POST" action="{{ url('plainui:redeem_token_add_to_user', conf_slug=conf.slug) }}">
+                <h2 class="text-center bg-info p-3 text-white h3" id="#login">{{ _("Got an Account already? Login here") }}</h2>
+                {{ csrf_input }}
+                {{ form_elements.hidden(form_add, 'token') }}
+                {{ form_elements.text(form_add, 'username') }}
+                {{ form_elements.password(form_add, 'password') }}
+                <ul class="row row-cols-1 row-cols-lg-3 list-unstyled">
+                    <li class="col ml-auto">
+                        <button type="submit" class="btn btn-xl btn-block btn-primary ml-auto">{{ _("Add your Ticket to this existing Account")}}</button>
+                    </li>
+                </ul>
+                {{ form_elements.errors(form_add) }}
+            </form>
+        </div>
     {%- endif %}
+    <p class="alert alert-info my-8" role="alert">
+        {{ _("User accounts from signup.c3assemblies.de and maschinenraum.rc3.world are imported and can be used to login.") }}
+    </p>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/room.html b/src/plainui/jinja2/plainui/room.html
index d2dd77024104a2dc4f0d12493587247b2e01fc29..cdda7fa5165a82e81fdc5b9019513afba26cf1b7 100644
--- a/src/plainui/jinja2/plainui/room.html
+++ b/src/plainui/jinja2/plainui/room.html
@@ -1,5 +1,7 @@
 {% import "plainui/components/integrations.html" as integrations %}
 {% import "plainui/components/markdown.html" as markdownMacro %}
+{% import "plainui/components/list_events.html" as list_events with context %}
+
 {% extends "plainui/base.html" %}
 {% block title %}Conference {{conf.name}} - Room {{room.name}}{% endblock %}
 {% block head %}
@@ -10,25 +12,45 @@
         share_url = url('plainui:room', conf_slug=conf.slug, room_id=room.id),
         report_url = url('plainui:room', conf_slug=conf.slug, room_id=room.id)) }}
 
-    <dl>
-        <dt>Assembly</dt>
-        <dd><a href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=room.assembly.slug) }}">{{room.assembly.name}}</a></dd>
-    </dl>
+    {% if room.assembly %}
+        <p class="font-headings text-white">{{ _("Assembly") }} <a href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=room.assembly.slug) }}">{{room.assembly.name}}</a></p>
+    {% endif %}
+
     {% if voc_stream %}
-    {{ integrations.vocPlayer(vocStream=voc_stream) }}
+        <h2>{{ _("Currently Streaming") }}</h2>
+        <div class="border p-6">
+            {{ integrations.vocPlayer(vocStream=voc_stream) }}
+        </div>
     {% endif %}
 
     {% if room.description %}
-    <h4>{{ _("Description") }}</h4>
-    <p>{{ markdownMacro.markdown(markdown=room.description | safe) }}</p>
+        <hr class="my-8" />
+
+        <h2>{{ _("Description") }}</h2>
+        {{ markdownMacro.markdown(markdown=room.description_html | safe) }}
+    {% endif %}
+
+    {% if events %}
+        <hr class="my-8">
+
+        <h2>{{ _("Events") }}</h2>
+
+        <div class="border p-6">
+            {{ list_events.list(events, my_favorite_events, my_scheduled_events ) }}
+        </div>
     {% endif %}
 
-    <div class="mt-4 mb-11">
-        <h4>{{ _("Links") }}</h4>
-        <ul>
-        {% for link in room.links.all() %}
-            <li>{{ link.get_link_type_display() }}: <a href="#TODO_DEREFFERER">{{ link.name }}</a></li>
-        {% endfor %}
-        </ul>
-    </div>
+    {% if links %}
+        <hr class="my-8">
+
+        <div class="mt-4 mb-11">
+            <h2>{{ _("Links") }}</h2>
+            <ul class="border p-6">
+            {% for link in links %}
+                {% set url = '/' + link.link if link.conference_internal else url('plainui:dereferrer', conf_slug=conf.slug, dst=link.link) %}
+                <li><b>{{ link.get_link_type_display() }}:</b> <a href="{{ url }}" target="{{"_self" if link.conference_internal else "_blank"}}" rel="{{"" if link.conference_internal else "external, noreferrer"}}">{{ link.name }}</a></li>
+            {% endfor %}
+            </ul>
+        </div>
+    {% endif %}
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/rooms.html b/src/plainui/jinja2/plainui/rooms.html
index af545cc56483b9c27c3638831be2e87e0ef7798e..1ccc19a8a5154371b5bf34ff92111bf01c9b2031 100644
--- a/src/plainui/jinja2/plainui/rooms.html
+++ b/src/plainui/jinja2/plainui/rooms.html
@@ -7,13 +7,7 @@
 
     <ul class="mt-8 mb-11">
     {% for room in rooms %}
-        <li><a href="{{ url('plainui:room', conf_slug=conf.slug, room_id=room.id) }}">{{room.name}}</a>
-            <ul>
-            {% for link in room.links.all() %}
-                <li><a href="{{ url('plainui:stream', conf_slug=conf.slug, room_id=room.id, link_id=link.id) }}">{{ link.name }}</a></li>
-            {% endfor %}
-            </ul>
-        </li>
+        <li><a href="{{ url('plainui:room', conf_slug=conf.slug, room_id=room.id) }}">{{room.name}}</a></li>
     {% endfor %}
     </ul>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/search.html b/src/plainui/jinja2/plainui/search.html
index ed2195ca3d4ca8822b883ce0541018fc4a7a7769..1154d899f37e308a9b838c23dd6b613031a5a651 100644
--- a/src/plainui/jinja2/plainui/search.html
+++ b/src/plainui/jinja2/plainui/search.html
@@ -1,31 +1,28 @@
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - {{ _("Search Results") }}{% endblock %}
 {% block content %}
-<div class="mb-11">
-    <div class="row justify-content-center">
-        <div class="col-auto"><h1>{{ _("Search Results") }}</h1></div>
-        <div class="w-100"></div>
-        <div class="col-auto">
-            <ul>
-            {%- for result in search_results %}
-                {% if result.type == 'Event' %}
-                    <li><a href="{{ url('plainui:event', conf_slug=conf.slug, event_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
-                {% elif result.type == 'Assembly' %}
-                    <li><a href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
-                {% elif result.type == 'ConferenceTag' %}
-                    <li><a href="{{ url('plainui:tag', conf_slug=conf.slug, tag_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
-                {% elif result.type == 'ConferenceTrack' %}
-                    <li>{{result.type}}: {{result.item}}</li>
-                {% elif result.type == 'StaticPage' %}
-                    <li><a href="{{ url('plainui:static_page', conf_slug=conf.slug, page_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
-                {% else %}
-                    <li>{{result.type}}: {{result.item}}</li>
-                {% endif %}
-            {%- else %}
-                {{ _("No results, sorry!") }}
-            {%- endfor %}
-            </ul>
-        </div>
-    </div>
+
+{{ titleMacro.title(_("Search Results")) }}
+
+<div class="mt-8 mb-11 border p-6">
+    <ul>
+        {%- for result in search_results %}
+            {% if result.type == 'Event' %}
+                <li><a href="{{ url('plainui:event', conf_slug=conf.slug, event_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
+            {% elif result.type == 'Assembly' %}
+                <li><a href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
+            {% elif result.type == 'ConferenceTag' %}
+                <li><a href="{{ url('plainui:tag', conf_slug=conf.slug, tag_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
+            {% elif result.type == 'ConferenceTrack' %}
+                <li>{{result.type}}: {{result.item}}</li>
+            {% elif result.type == 'StaticPage' %}
+                <li><a href="{{ url('plainui:static_page', conf_slug=conf.slug, page_slug=result.item.slug) }}">{{result.type}}: {{result.item}}</a></li>
+            {% else %}
+                <li>{{result.type}}: {{result.item}}</li>
+            {% endif %}
+        {%- else %}
+            {{ _("No results, sorry!") }}
+        {%- endfor %}
+    </ul>
 </div>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/stream.html b/src/plainui/jinja2/plainui/stream.html
deleted file mode 100644
index 7d6cdfd5061d86b8749959f9738498b5db2a115a..0000000000000000000000000000000000000000
--- a/src/plainui/jinja2/plainui/stream.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% import "plainui/components/markdown.html" as markdownMacro %}
-
-{% extends "plainui/base.html" %}
-{% block title %}Conference {{conf.name}}{% endblock %}
-{% block content %}
-    {{ titleMacro.title(room.name,
-        share_url = url('plainui:stream', conf_slug=conf.slug, room_id=room.id, link_id=link.id),
-        report_url = url('plainui:stream', conf_slug=conf.slug, room_id=room.id, link_id=link.id)
-        ) }}
-    <figure class="border p-6 my-8">
-        <p>TODO: Stream</p>
-    </figure>
-
-    <div class="mt-8 mb-11">
-        <h2 class="mb-5">{{_("External Ressources")}}</h2>
-        {{ markdownMacro.markdown(markdown="TODO: add some notes as markdown for the livestream event") }}
-    </div>
-
-{% endblock %}
diff --git a/src/plainui/jinja2/plainui/tag.html b/src/plainui/jinja2/plainui/tag.html
index 09d4d9c5701d8608784b2fe8b16ac63f84eab082..6daccc6bfe7484540cf38e3a6d705200f99cfe56 100644
--- a/src/plainui/jinja2/plainui/tag.html
+++ b/src/plainui/jinja2/plainui/tag.html
@@ -1,25 +1,20 @@
+{% import "plainui/components/list_assemblies.html" as list_assm with context %}
+{% import "plainui/components/list_events.html" as list_events with context %}
+
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - {{ _("Tag %(name)s", name=tag.slug) }}{% endblock %}
 {% block content %}
 <div class="mb-11">
-    <div class="row justify-content-center">
-        <div class="col-auto"><h1>{{ _("Tag %(name)s", name=tag.slug) }}</h1></div>
-        <div class="w-100"></div>
-        <div class="col-auto">
-            <h2>Assemblies</h2>
-            <ul>
-            {%- for assembly in assemblies %}
-                <li><a href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{assembly.name}}</a></li>
-            {%- endfor %}
-            </ul>
+    {{ titleMacro.title( _("Tag %(name)s", name=tag.slug) ) }}
 
-            <h2>Events</h2>
-            <ul>
-            {%- for event in events %}
-                <li><a href="{{ url('plainui:event', conf_slug=conf.slug, event_slug=event.slug) }}">{{event}}</a></li>
-            {%- endfor %}
-            </ul>
-        </div>
+    <h2>{{ _("Assemblies") }}</h2>
+    <div class="border p-6">
+        {{ list_assm.list(assemblies,my_favorite_assemblies ) }}
+    </div>
+    <hr class="my-8">
+    <h2>{{ _("Events") }}</h2>
+    <div class="border p-6">
+        {{ list_events.list( events, my_favorite_events, my_scheduled_events ) }}
     </div>
 </div>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/token_password_reset.html b/src/plainui/jinja2/plainui/token_password_reset.html
new file mode 100644
index 0000000000000000000000000000000000000000..58aa879abe3fe81ddaeb95860b372d2557566143
--- /dev/null
+++ b/src/plainui/jinja2/plainui/token_password_reset.html
@@ -0,0 +1,24 @@
+{% extends "plainui/base.html" %}
+{% import "plainui/components/form_elements.html" as form_elements %}
+{% block title %}{{ _("Conference %(conf)s - Password Reset", conf=conf.name) }}{% endblock %}
+{% block content %}
+    {{ titleMacro.title(_("Password Reset")) }}
+
+    <div>
+        <form class="mw-810 border p-6 mx-auto mb-11{% if form.errors %} border-danger{% endif %}" method="POST" action="">
+            <h2 class="text-center bg-info p-3 text-white h3">{{ _("Password Reset") }}</h2>
+            {{ csrf_input }}
+            {{ form_elements.hidden(form, 'jwt') }}
+            {{ form_elements.text(form, 'username') }}
+            {{ form_elements.password(form, 'new_password1') }}
+            {{ form_elements.password(form, 'new_password2') }}
+            <ul class="row row-cols-1 row-cols-lg-3 list-unstyled">
+                <li class="col ml-auto">
+                    <button type="submit" class="btn btn-xl btn-block btn-primary ml-auto">{{ _("Reset Password")}}</button>
+                </li>
+            </ul>
+            {{ form_elements.errors(form) }}
+        </form>
+    </div>
+
+{% endblock %}
diff --git a/src/plainui/jinja2/plainui/user.html b/src/plainui/jinja2/plainui/user.html
index 08405936e6942e53be57cc40376ed055af0cdeec..1b483cb08a66ccaf2090250dd65e926d03907bf1 100644
--- a/src/plainui/jinja2/plainui/user.html
+++ b/src/plainui/jinja2/plainui/user.html
@@ -1,47 +1,89 @@
+{% import "plainui/components/markdown.html" as markdownMacro %}
+
 {% extends "plainui/base.html" %}
 {% import "plainui/components/list_assemblies.html" as list_assm with context %}
 {% import "plainui/components/list_events.html" as list_events with context %}
 {% import "plainui/components/form_elements.html" as form_elements %}
-
+{% import "plainui/components/badge_elements.html" as badge_elements with context %}
 
 {% block title %}{{ _("%(conf)s - User %(name)s", conf=conf.name, name=display_user.username) }}{% endblock %}
 {% block content %}
 
+{{ titleMacro.title( _("User Profile") ) }}
+
 <div class="border mt-8 mb-11 p-6">
+    <h2 class="text-center bg-info p-3 text-white h3">{{ display_user.username }}</h2>
     <div class="row">
-        <div class="col">
-            <h2 class="w-100 text-center bg-secondary p-2">{{ display_user.username }}</h2>
-        </div>
-    </div>
-    <div class="row">
-        <div class="col-4 border p-2">
+        <figure class="col-auto">
             <img class="img-fluid" src="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg" alt="{{ _("Avatar image") }}">
-        </div>
-        <div class="col-8 border p-2">
-            {{ display_user.description_html }}
-            {% if display_user.receive_dms %}
-                <a href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=display_user.username) }}" class="btn btn-primary">{{ _("Send PN") }}</a>
-            {% endif %}
+        </figure>
+        <div class="col">
+            <dl class="grid-list mb-0">
+                <dt class="grid-list__item grid-list__item--title">
+                    {{ _("pronouns") }}
+                </dt>
+                <dd class="grid-list__item grid-list__item--text">
+                    {% if display_user.pronouns %}
+                        {{ display_user.pronouns }}
+                    {% else %}
+                        -
+                    {% endif %}
+                </dd>
+
+                <dt class="grid-list__item grid-list__item--title">
+                    {{ _("description") }}
+                </dt>
+                <dd class="grid-list__item grid-list__item--text">
+                    {% if display_user.description_html %}
+                        {{ markdownMacro.markdown_plain(markdown=display_user.description_html | safe) }}
+                    {% else %}
+                        -
+                    {% endif %}
+                </dd>
+
+                {% if display_user.receive_dms or display_user == user %}
+                    <dt class="grid-list__item grid-list__item--title">
+                        {{ _("Get in Touch") }}
+                    </dt>
+                    <dd class="grid-list__item grid-list__item--text">
+                        {% if display_user != user %}
+                            <a
+                                href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=display_user.username) }}"
+                                class="btn btn-secondary mr-2"
+                            >
+                                {{ _("Send PN") }}
+                            </a>
+                        {% endif %}
+
+                        {% if display_user == user %}
+                            <a
+                                href="{{ url('plainui:userprofile', conf_slug=conf.slug) }}"
+                                class="btn btn-secondary"
+                            >
+                                {{ _("Profile") }}
+                            </a>
+                        {% endif %}
+                    </dd>
+                {% endif %}
+            </dl>
         </div>
     </div>
 </div>
 
+<hr class="my-8">
+
 <div class="border my-8 p-6">
-    <div class="row">
-        <div class="col">
-            <h2 class="bg-secondary text-center p-2">{{ _("badges") }}</h2>
-        </div>
-    </div>
+    <h2 class="text-center bg-info p-3 text-white h3">{{ _("badges") }}</h2>
 
     <div class="row">
-        <div class="col">
-            <ul>
-            {% for badge_link in badges %}
-                <li>{{badge_link.badge.name}}</li>
-            {% endfor %}
-            </ul>
-        </div>
+        {% if not badges %}
+            <div  class="row my-5 p-6">
+                {{ _("No entries available.") }}
+            </div>
+        {% endif %}
+        {% for badge_link in badges %}
+            {{ badge_elements.userBadge(badge_link, False, False) }}
+        {% endfor %}
     </div>
 </div>
-
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/world.html b/src/plainui/jinja2/plainui/world.html
index 3d67ceae24982f1c8678bb97d7f1b282691b7cfa..19f841a3d399781cdd565e5eb35a3b8a12437305 100644
--- a/src/plainui/jinja2/plainui/world.html
+++ b/src/plainui/jinja2/plainui/world.html
@@ -6,25 +6,18 @@
 {% block content %}
     <article>
         <a
-            href="https://play.at.rc3.world/"
+            href="{{join_url}}"
             class="row my-8"
             target="_blank"
-            rel="external, noreferrer"
         >
-            {{ imageMacro.image(image=static('plainui/img/start2dworld.gif'), alt="Start 2D World", title="Start 2D World") }}
+            {{ imageMacro.image(image=static(['plainui/img/start2dworld.gif', 'plainui/img/enter2dworld.gif']|random), alt=_("Start 2D World"), title=_("Start 2D World")) }}
         </a>
 
         <h1 class="h2">{{ page.title }}</h1>
 
         {{ markdownMacro.markdown(markdown=page.rendered_body|safe) }}
-
-        <a
-            href="https://play.at.rc3.world"
-            class="row my-8"
-            target="_blank"
-            rel="external, noreferrer"
-        >
-            {{ imageMacro.image(image=static('plainui/img/enter2dworld.gif'), alt="Enter 2D World", title="Enter 2D World") }}
-        </a>
     </article>
+
+    <hr class="my-8">
+
 {% endblock %}
diff --git a/src/plainui/locale/de/LC_MESSAGES/django.po b/src/plainui/locale/de/LC_MESSAGES/django.po
index 02bf1e0b2830f65038c93cd8a37c54c8aa0ba5ca..08786f946453bef2ce15d44fa19fc0e7860ad028 100644
--- a/src/plainui/locale/de/LC_MESSAGES/django.po
+++ b/src/plainui/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-12-23 13:29+0000\n"
+"POT-Creation-Date: 2020-12-26 21:44+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,89 +19,148 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 msgid "Unknown User!"
-msgstr ""
+msgstr "Unbekannter Nutzer!"
 
 msgid "Please enter the recipient name"
-msgstr ""
+msgstr "Bitte einen Empfänger angeben"
 
 msgid "Please enter a subject"
-msgstr ""
+msgstr "Bitte einen Betreff eingeben"
 
 msgid "Please enter a title"
-msgstr ""
+msgstr "Bitte einen Titel eingeben"
 
 msgid "Placeholder text"
+msgstr "Platzhalter text"
+
+msgid "Token"
 msgstr ""
 
+msgid "Your Username will be visible to other users"
+msgstr "Dein Nutzername wird für andere sichtbar sein"
+
+#, fuzzy
+#| msgid "username"
+msgid "Username"
+msgstr "Benutzername"
+
+msgid "Please enter the Username that used this Ticket and your new Password"
+msgstr "Bitte Usernamen verwenden der dieses Ticket benutzt hat und Dein neues Passwort"
+
+#, fuzzy
+#| msgid "Unknown User!"
+msgid "Unknown User"
+msgstr "Unbekannter Nutzer!"
+
+msgid "User is not active!"
+msgstr "User ist nich aktiv"
+
+msgid "User can use password reset by email!"
+msgstr "Paswort über E-Mail reset ist möglich"
+
+msgid "User didn't use this Ticket!"
+msgstr "Dieser Benutzer ist nicht mit diesem Ticket verknüpft."
+
 msgid "blocked"
-msgstr ""
+msgstr "blockiert"
 
 msgid "Please enter a name"
-msgstr ""
+msgstr "Bitte einen Namen eingeben"
+
+msgid "BBB or Workshop Rooms only."
+msgstr "Nur BBB oder Workshop-Räume"
+
+msgid "ISO 8601 format, e.g. '2020-12-27 13:37'"
+msgstr "ISO 8601 format, z.B. '2020-12-27 13:37'"
+
+msgid "'HH:MM' or even 'DD HH:MM' e.g. 3hours ='03:00'"
+msgstr "'HH:MM' oder auch 'DD HH:MM' z. B. 3Stunden ='03:00'"
 
 msgid "Must end with the conference!"
-msgstr ""
+msgstr "Muss mit der Veranstaltung enden!"
 
-msgid "Room is not free!"
-msgstr ""
+msgid "Room is not free, choose other time or room"
+msgstr "Raum ist nicht frei, andere Zeit oder Raum wählen"
 
 msgid "Can't begin before the conference begins!"
-msgstr ""
+msgstr "Kann nicht vor der Veranstaltung beginnen!"
 
-msgid "Legal (Copyright, Nazi shit, ..)"
-msgstr ""
+msgid "Abuse - legally problematic content"
+msgstr "Missbrauch - rechtlich problematische Inhalte"
 
-msgid "General Report (-> Security Team)"
-msgstr ""
+msgid "Content - I think the content is problematic"
+msgstr "Inhalt - Ich denke, der Inhalt ist problematisch"
 
-msgid "Awareness Team"
-msgstr ""
+msgid "Attendee - there is a problem with a user"
+msgstr "Teilnehmer - es gibt ein Problem mit einem Benutzer"
 
-msgid "Technical Problem"
-msgstr ""
+msgid "Technical - a technical problem with the site"
+msgstr "Technisch - ein technisches Problem mit der Website"
+
+msgid "Misc - something else"
+msgstr "Verschiedenes - Alles andere"
+
+msgid "describe the problem"
+msgstr "Beschreibe das Problem"
+
+msgid "describe a solution"
+msgstr "Beschreibe eine Lösung"
 
 msgid "Assemblies"
 msgstr ""
 
 msgid "Introduction"
-msgstr ""
+msgstr "Einführung"
 
 msgid "upcoming events"
-msgstr ""
+msgstr "Kommende Events"
 
 msgid "recommended events"
-msgstr ""
+msgstr "Empfohlene Events"
 
 msgid "all assemblies"
-msgstr ""
+msgstr "alle Assemblies"
 
 msgid "assemblies startseite"
-msgstr ""
+msgstr "Assemblies-Startseite"
 
 msgid "assemblies_official_only"
-msgstr "Nur offizielle Assemblies"
+msgstr "Nur ccc-Assemblys"
 
 msgid "assemblies_community_only"
-msgstr "Nur Community-Assemblies"
+msgstr "Nur Community-Assemblys"
 
 msgid "assembly events"
-msgstr ""
+msgstr "Assembly-Events"
 
-msgid "running and upcoming events"
-msgstr ""
+msgid "Running and Upcoming Events"
+msgstr "Laufende und kommende Events"
 
-msgid "all assemblies events"
-msgstr ""
+msgid "All Assemblies Events"
+msgstr "Alle Assembly-Events"
 
-msgid "assmebly events"
-msgstr ""
+#, fuzzy
+#| msgid "Contact Us"
+msgid "Contacts"
+msgstr "Kontakt"
 
-msgid "New Selforganized Session"
-msgstr ""
+msgid "assembly rooms"
+msgstr "Assembly Räume"
 
-msgid "Edit"
+#, fuzzy
+#| msgid "Assembly Page"
+msgid "assembly badges"
+msgstr "Assembly Events"
+
+msgid "No badges publicly available."
+msgstr "Es sind keine Badges öffentlich verfügbar. "
+
+msgid "Self-organized Sessions"
 msgstr ""
 
+msgid "new Self-organized Session"
+msgstr "neue Self-organized Session"
+
 msgid "Your Browser is broken. Get a better one here!"
 msgstr "Dein Browser ist defekt. Hier bekommst du einen Besseren!"
 
@@ -109,110 +168,152 @@ msgid "Contact Us"
 msgstr "Kontakt"
 
 msgid "Privacy Policy"
-msgstr ""
+msgstr "Datenschutzerklärung"
 
 msgid "About RC3"
-msgstr ""
+msgstr "Über die rC3"
 
 msgid "Disclaimer"
-msgstr ""
+msgstr "Impressum"
+
+msgid "Principles"
+msgstr "Spielregeln"
 
 msgid "Bulletin Board"
 msgstr ""
 
 msgid "search"
-msgstr ""
+msgstr "suchen"
 
-msgid "My Board Entries"
-msgstr ""
+msgid "My Bulletin Board"
+msgstr "Mein Bulletin Board"
 
 msgid "New Entry"
-msgstr ""
+msgstr "Neuer Eintrag"
 
-msgid "Update"
-msgstr ""
+msgid "Edit Board Entry"
+msgstr "Eintrag bearbeiten"
 
-msgid "My Bulletin Board"
-msgstr ""
+msgid "Save"
+msgstr "Speichern"
 
 msgid "Public Bulletin Board"
-msgstr ""
+msgstr "Öffentliches Bulletin Board"
 
-msgid "Curated Events"
-msgstr ""
+msgid "Fahrplan in new Tab"
+msgstr "Fahrplan in neuem Tab"
 
-msgid "curated events"
-msgstr ""
+msgid "Revoke Badge"
+msgstr "Widerrufen"
+
+msgid "Accept Badge"
+msgstr "Akzeptieren"
+
+msgid "Visibility"
+msgstr "Sichtbarkeit"
+
+msgid "Save visibility"
+msgstr "Sichtbarkeit speichern"
 
 msgid "No entries available."
 msgstr "Keine Einträge vorhanden."
 
 msgid "Event starts in"
-msgstr "Event startet in"
+msgstr "Event Start:"
 
 msgid "Event Information"
 msgstr "Event Information"
 
 msgid "Time"
-msgstr "Time"
+msgstr "Zeit"
 
 msgid "Speakers"
-msgstr "Speakers"
+msgstr "Speaker"
 
 msgid "No Speakers publicated yet"
-msgstr "No Speakers publicated yet"
+msgstr "Noch keine Speaker bekannt gegeben"
 
 msgid "Track"
 msgstr "Track"
 
 msgid "Language"
-msgstr "Language"
+msgstr "Sprache"
 
 msgid "Room"
-msgstr "Room"
+msgstr "Raum"
 
-msgid "remove from favorites"
+msgid "Host"
 msgstr ""
 
+msgid "remove from favorites"
+msgstr "von Favoriten entfernen"
+
 msgid "add to favorites"
-msgstr ""
+msgstr "zu Favoriten hinzufügen"
 
-#, fuzzy
-#| msgid "personal schedule"
 msgid "remove from schedule"
-msgstr "Persönlicher Fahrplan"
+msgstr "Aus Zeitplan entfernen"
 
-#, fuzzy
-#| msgid "personal schedule"
 msgid "add to schedule"
-msgstr "Persönlicher Fahrplan"
+msgstr "zu Zeitplan hinzufügen"
 
 msgid "share this "
-msgstr ""
+msgstr "dies teilen"
 
 msgid "open stream"
-msgstr ""
+msgstr "Stream öffnen"
 
 msgid "report this url"
-msgstr ""
+msgstr "dies melden"
+
+msgid "edit this"
+msgstr "dies bearbeiten"
 
-msgid "Live Stream"
+msgid "Official Page"
 msgstr ""
 
-msgid "External Ressources"
-msgstr "External Ressources"
+#, python-format
+msgid "%(kind)s Event on Track"
+msgstr "%(kind)s Event auf Track"
+
+msgid "Coming Up Next"
+msgstr "Als nächstes"
+
+msgid "no entry availaible"
+msgstr "Keine Einträge vorhanden."
+
+msgid "View next Events"
+msgstr "Nächste Events"
+
+msgid "no rooms available."
+msgstr "Keine Räume verfügbar."
+
+msgid "roomtype"
+msgstr "Raum-Typ"
+
+msgid "capacity"
+msgstr "Kapazität"
 
 msgid "Tags"
 msgstr "Tags"
 
+msgid "no tags availaible"
+msgstr "Nicht getaggt."
+
 msgid "by"
+msgstr "von"
+
+msgid "Mehr anzeigen"
 msgstr ""
 
+msgid "Edit"
+msgstr "Bearbeiten"
+
 msgid "Delete"
-msgstr ""
+msgstr "Löschen"
 
 msgid "Report Content"
-msgstr ""
+msgstr "Inhalt melden"
 
 msgid "Conferences"
 msgstr "Konferenzen"
@@ -223,8 +324,16 @@ msgstr "Hey"
 msgid "You are leaving the »RC3-area«. For external sites, streams and applications the actual owners are completely and solely responsible regarding data protection, copyright, youth protection, etc.!"
 msgstr "Du verlässt gerade das »RC3-Gelände«. Für externe Seiten, Streams und Angebote sind vollinhaltlich die jeweiligen Betreiber in bezug auf Datenschutz, Copyright und Jugendschutz etc. verantwortlich."
 
+msgid "Back"
+msgstr "Zurück"
+
 msgid "External Link"
-msgstr ""
+msgstr "Externer Link"
+
+#, fuzzy
+#| msgid "recommended events"
+msgid "recommendations"
+msgstr "Empfohlene Events"
 
 msgid "download"
 msgstr ""
@@ -242,27 +351,25 @@ msgid "QR-Code"
 msgstr ""
 
 msgid "view as list"
-msgstr ""
+msgstr "Als Liste"
 
 msgid "view as calendar"
-msgstr ""
+msgstr "Als Kalender"
 
 msgid "by day"
-msgstr ""
+msgstr "nach Tag"
 
-#, fuzzy
-#| msgid "Track"
 msgid "by track"
-msgstr "Track"
+msgstr "nach Track"
 
 msgid "curated only"
-msgstr ""
+msgstr "Nur Hauptprogramm"
 
 msgid "world"
 msgstr ""
 
 msgid "platform"
-msgstr ""
+msgstr "Plattform"
 
 msgid "info"
 msgstr ""
@@ -271,7 +378,7 @@ msgid "Profile"
 msgstr "Profil"
 
 msgid "My Plan"
-msgstr ""
+msgstr "Mein Plan"
 
 msgid "logout"
 msgstr ""
@@ -282,15 +389,11 @@ msgstr ""
 msgid "board"
 msgstr ""
 
-#, fuzzy
-#| msgid "External Ressources"
 msgid "Messages"
-msgstr "External Ressources"
+msgstr "Messages"
 
-#, fuzzy
-#| msgid "External Ressources"
 msgid "Mess ages"
-msgstr "External Ressources"
+msgstr "Nach richten"
 
 msgid "Fahrplan"
 msgstr ""
@@ -299,10 +402,10 @@ msgid "Fahr plan"
 msgstr ""
 
 msgid "Low Contrast"
-msgstr ""
+msgstr "geringer Kontrast"
 
 msgid "High Contrast"
-msgstr ""
+msgstr "hoher Kontrast"
 
 msgid "de"
 msgstr ""
@@ -313,76 +416,80 @@ msgstr ""
 msgid "Skip intro"
 msgstr "Intro überspringen"
 
-msgid "welcome to the rc3"
-msgstr ""
+msgid "Welcome to the rc3"
+msgstr "Willkommen bei der rC3"
 
 msgid "ticket"
-msgstr ""
+msgstr "Ticket"
 
-msgid "enter username"
-msgstr ""
+msgid "By logging in you accept our use of cookies to store your user session."
+msgstr "Mit dem Login stimmst du unserer Verwendung von Cookies zum Speichern deiner Usersession zu."
 
-msgid "username"
-msgstr ""
+msgid "Reset Password"
+msgstr "Passwort zurücksetzen"
 
-msgid "enter password"
-msgstr ""
+msgid "New Ticket"
+msgstr "Neues Ticket"
 
-msgid "password"
+msgid "Login"
 msgstr ""
 
-msgid "By logging in you accept our use of cookies to store your user session."
-msgstr "Mit dem Login stimmst du unserer Verwendung von Cookies zum Speichern deiner Usersession zu."
+msgid "Accepted Badges"
+msgstr "Akzeptierte Badges"
 
-msgid "reset password"
-msgstr ""
+msgid "No badges Accepted."
+msgstr "Keine Badges akzeptiert"
 
-msgid "new Ticket"
-msgstr ""
+msgid "Redeem Badge Token"
+msgstr "Redeem Badge Token"
 
-msgid "Change Password"
-msgstr ""
+msgid "Pending Badges"
+msgstr "Ausstehende Badges"
 
-msgid "Your password has been set. You may go ahead and log in now."
-msgstr ""
+msgid "No badges are waiting for approval."
+msgstr "Hier warten gerade keine Badges auf dich."
 
-msgid "back"
-msgstr ""
+msgid "Redeem Token"
+msgstr "Token einlösen"
 
-msgid "Login"
-msgstr ""
+msgid "Change Password"
+msgstr "Passwort ändern"
 
-msgid "new password"
-msgstr ""
+msgid "Change password"
+msgstr "Passwort ändern"
+
+msgid "Your password has been set. You may go ahead and log in now."
+msgstr "Dein Passwort wurde geändert. Du kannst dich jetzt anmelden."
+
+msgid "enter new password"
+msgstr "Gib ein neues Passwort ein"
 
 msgid "We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly."
-msgstr ""
+msgstr "Wir haben dir eine Anleitung zum ändern deines Passworts per E-Mail geschickt, sofern ein Konto mit der von dir eingegebenen E-Mail existiert."
 
 msgid "If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder."
-msgstr ""
-
-msgid "enter your registered e-mail"
-msgstr ""
+msgstr "Wenn du keine E-Mail erhälst, vergewissere dich bitte, dass du die Adresse eingegeben hast, mit der du dich registriert hast, und überprüfen deinen Spam-Ordner."
 
-msgid "e-mail"
-msgstr ""
+msgid "reset password"
+msgstr "Passwort zurücksetzen"
 
 msgid "You will receive an e-mail with a reset link."
-msgstr ""
+msgstr "Du erhälst eine E-Mail mit einem Reset-Link."
+
+msgid "If you have no configured e-mail address, revisit your ticket activation link"
+msgstr "Falls du keine E-Mail-Adresse konfiguriert hast, klick noch einmal auf deinen Ticket-Aktivierungslink"
 
-#, fuzzy
-#| msgid "External Ressources"
 msgid "Personal Messages"
-msgstr "External Ressources"
+msgstr "Persönliche Nachrichten"
 
 msgid "Inbox"
-msgstr ""
+msgstr "Posteingang"
 
 msgid "Outbox"
-msgstr ""
+msgstr "Postausgang"
 
 msgid "New PM"
-msgstr ""
+msgstr "Neue PN"
 
 msgid "Received Messages"
 msgstr "Empfangene Nachrichten"
@@ -390,105 +497,137 @@ msgstr "Empfangene Nachrichten"
 msgid "Sent Messages"
 msgstr "Gesendete Nachrichten"
 
-msgid "messages_subject"
-msgstr "Betreff"
-
-msgid "messages_from"
-msgstr "Absender"
-
-msgid "messages_to"
-msgstr "Empfänger"
-
-msgid "messages_date"
-msgstr "Datum"
-
-msgid "messages_state"
-msgstr "Status"
+msgid "messages_x_of_n"
+msgstr "von"
 
-msgid "messages_delete"
-msgstr "Löschen"
+msgid "messages_was_responded"
+msgstr "geantwortet"
 
 msgid "messages_was_read"
 msgstr "gelesen"
 
+msgid "messages_is_new"
+msgstr "neu"
+
 msgid "messages_was_sent"
-msgstr "gesendet"
+msgstr "Nachricht gesendet"
 
-msgid "messages_x_of_n"
-msgstr "von"
+msgid "read"
+msgstr "lesen"
+
+msgid "messages_delete"
+msgstr "löschen"
 
 msgid "Personal Messages - Send"
-msgstr ""
+msgstr "Persönliche Nachrichten - Senden"
 
-msgid "Send Personal Message"
-msgstr "Nachricht senden"
+msgid "new message"
+msgstr "Neue Nachricht"
 
 msgid "Send"
-msgstr "Abschicken"
+msgstr "senden"
 
 msgid "Personal Message"
 msgstr "Persönliche Nachricht"
 
-msgid "messages_from_sender"
-msgstr "Nachricht von"
-
-msgid "messages_from_short"
-msgstr "Von"
+msgid "Reply"
+msgstr "Antwort"
 
-msgid "messages_to_short"
-msgstr "An"
-
-msgid "messages_at"
-msgstr "am"
-
-msgid "Avatar image"
-msgstr ""
+msgid "username"
+msgstr "Benutzername"
 
 msgid "email"
 msgstr ""
 
 msgid "last login"
-msgstr ""
+msgstr "Letzter Login"
 
-msgid "overview"
-msgstr ""
+msgid "My public User Page"
+msgstr "Meine öffentliche Benutzerseite"
 
-msgid "total events added schedule"
-msgstr ""
+msgid "View"
+msgstr "Anzeigen"
 
-msgid "total events attended"
-msgstr ""
+msgid "custom preferences"
+msgstr "Persönliche Einstellungen"
 
-msgid "total assemblies involved"
-msgstr ""
+msgid "save"
+msgstr "speichern"
 
-msgid "total tokens found"
-msgstr ""
+msgid "My Badges"
+msgstr "Meine Badges"
 
-#, fuzzy
-#| msgid "Conferences"
-msgid "custom preferences"
-msgstr "Konferenzen"
+msgid "Manage Badges"
+msgstr "Badgeverwaltung"
 
-msgid "Badge-Token"
-msgstr ""
+#, fuzzy
+#| msgid "Pending Badges"
+msgid "Pending badges"
+msgstr "Ausstehende Badges"
 
-msgid "Submit"
-msgstr ""
+msgid "Badges: Accept / Revoke / Visibility"
+msgstr "Badges: Akzeptieren / Widerrufen / Sichtbarkeit"
 
 msgid "My Favorites"
-msgstr ""
+msgstr "Meine Favoriten"
 
 msgid "My Fahrplan"
-msgstr ""
+msgstr "Mein Fahrplan"
+
+msgid "No"
+msgstr "Nein"
+
+msgid "Yes"
+msgstr "Ja"
+
+msgid "Use Token for Password Reset"
+msgstr "Benutze ein Token zum Passwort zurück setzten"
+
+msgid "Submit Token"
+msgstr "Token einlösen"
+
+msgid "Create new Account"
+msgstr "Neues Konto erstellen"
+
+msgid "Use the next form if you already have an account"
+msgstr "Verwende das nächste Formular, wenn du bereits ein Konto hast"
+
+msgid "Create a new Account with your Ticket"
+msgstr "Erstellen Sie ein neues Konto mit deinem Ticket"
+
+msgid "Got an Account already? Login here"
+msgstr "Du hast bereits ein Konto? Hier anmelden"
+
+msgid "Add your Ticket to this existing Account"
+msgstr "Ihr Ticket zu diesem bestehenden Konto hinzufügen"
+
+msgid "User accounts from signup.c3assemblies.de and maschinenraum.rc3.world are imported and can be used to login."
+msgstr "Accounts die auf signup.c3assemblies.de und maschinenraum.rc3.world bereits angelegt wurden, können weiter verwendet werden."
 
 msgid "Please go to the following page and choose a new password:"
-msgstr ""
+msgstr "Bitte gehen Sie auf die folgende Seite und wählen Sie ein neues Passwort:"
 
 msgid "Thanks for using our site!"
-msgstr ""
+msgstr "Vielen Dank für die Nutzung unserer Seite!"
+
+msgid "Your message will be read and processed by one of our angels. We assure you to keep your personal data safe and hidden if not needed to solve your problem."
+msgstr "Deine Nachricht wird von einem unserer Engel gelesen und bearbeitet. Wir versichern dir, deine persönlichen Daten sicher und verborgen zu halten, wenn sie nicht zur Lösung des Problems benötigt werden."
+
+msgid "Assembly"
+msgstr "Assembly"
 
-msgid "report content"
+msgid "Currently Streaming"
+msgstr "Streaming"
+
+#, fuzzy
+#| msgid "official_description"
+msgid "Description"
+msgstr "Wie bei jedem C3-Event gibt es viele organisierte Vorträge, Sessions, Workshops, ... die du besuchen kannst. Schau die Liste durch und sieh nach, wofür du beim rc3 deine Zeit verschw^Z verwenden willst."
+
+msgid "Events"
+msgstr "Events"
+
+msgid "Links"
 msgstr ""
 
 msgid "Search Results"
@@ -497,110 +636,169 @@ msgstr "Suchergebnisse"
 msgid "No results, sorry!"
 msgstr "Nichts gefunden, sorry!"
 
-msgid "Cancel"
+msgid "Self-organized Session"
+msgstr "Self-organized Session"
+
+msgid "back"
+msgstr "zurück"
+
+msgid "Update"
+msgstr "Update"
+
+msgid "Password Reset"
 msgstr ""
 
 msgid "Upcoming"
+msgstr "Demnächst"
+
+msgid "Avatar image"
+msgstr "Avatar Bild"
+
+msgid "pronouns"
+msgstr "Pronomen"
+
+msgid "description"
+msgstr "Beschreibung"
+
+msgid "Get in Touch"
 msgstr ""
 
 msgid "Send PN"
-msgstr ""
+msgstr "PN senden"
 
 msgid "badges"
-msgstr ""
+msgstr "Badges"
 
 msgid "2D World"
 msgstr ""
 
-msgid "Please activate your Ticket to access this conference!"
-msgstr ""
-
-msgid "Not allowed to edit this Self Organized Session"
-msgstr ""
+msgid "Created Self Organized Session"
+msgstr "Self Organized Session erstellt"
 
 msgid "Updated Self Organized Session"
-msgstr ""
+msgstr "Self Organized Session aktualisiert"
 
-msgid "Created Self Organized Session"
-msgstr ""
+msgid "Please activate your Ticket to access this conference!"
+msgstr "Bitte löse dein Ticket ein um an der Veranstaltung teilzunehmen."
+
+msgid "Not allowed to edit this Self Organized Session"
+msgstr "Nicht erlaubt diese Self Organized Session zu bearbeiten"
 
-#, fuzzy
-#| msgid "Profile"
 msgid "Updated Profile"
-msgstr "Profil"
+msgstr "Profil aktualisiert"
 
 msgid "Message sent."
-msgstr ""
+msgstr "Nachricht verschickt"
 
 msgid "Message deleted."
-msgstr ""
-
-msgid "Unknown Username or Password"
-msgstr "Unbekannter Benutzername oder Passwort falsch"
+msgstr "Nachricht gelöscht"
 
 msgid "Password changed successfully."
-msgstr ""
+msgstr "Passwort erfolgreich geändert"
+
+#, fuzzy
+#| msgid "Password changed successfully."
+msgid "Passwort successfully reset!"
+msgstr "Passwort erfolgreich geändert"
 
 msgid "Bulletin Board Entry updated."
-msgstr ""
+msgstr "Bulletin Board Eintrag aktualisiert."
 
 msgid "Bulletin Board Entry created."
-msgstr ""
+msgstr "Bulletin Board Eintrag erstellt."
 
 msgid "Bulletin Board Entry deleted."
-msgstr ""
+msgstr "Bulletin Board Eintrag gelöscht."
 
-msgid "Report sent"
+msgid "Thank you for your help to make this plattform safer and better! Please give us some time to find a solution and keep an eye on your Messages, we may contact you."
 msgstr ""
 
 msgid "Assemblies List"
-msgstr ""
+msgstr "Assembly-Liste"
 
-msgid "Explore Assemblies"
-msgstr ""
+msgid "Assemblies List Description"
+msgstr "In dieser Liste findet Ihr alle bei der rC3 registrierten Assemblies. Sind Eure Nachbarn vom letzen Event auch hier und welche Projekte bringen sie dieses Mal mit? Findet es heraus!"
+
+msgid "Explore Assemblies Button"
+msgstr "Assembly-Liste entdecken"
 
 msgid "What are Assemblies?"
-msgstr ""
+msgstr "Was sind Assemblies?"
+
+msgid "Assemblies Description"
+msgstr "Die Assemblies sind Gruppen von Euch Chaos-Wesen, die sich aufgrund örtlicher oder thematischer Nähe gefunden haben und sich austauschen, zusammen arbeiten und ihr Wissen mit Euch teilen wollen. Die Vielfalt ist riesig. Vorsicht - hier kann jede:r von Euch ein neues Hobby finden."
 
 msgid "Assembly Events"
-msgstr ""
+msgstr "Assembly-Events"
 
-msgid "Explore Assembly Events"
-msgstr ""
+msgid "Assemblies Event Description"
+msgstr "Die Assemblies sind ein integraler Bestandteil der rC3 und machen einen großen Teil des Programms aus: Der Wissensaustausch und die Präsentation Ihrer Projekte sind zentral für die Konferenz. Auf diesen Seiten findet Ihr Talks und Workshops der Assemblies."
 
-msgid "Bulletin Board Entry"
-msgstr ""
+msgid "Explore Assembly Events Button"
+msgstr "Assembly-Events entdecken"
 
-msgid "Create"
+#, python-format
+msgid "%(room)s Currently Streaming"
 msgstr ""
 
-msgid "Explore Events"
-msgstr ""
+msgid "private"
+msgstr "Privat"
+
+msgid "public"
+msgstr "Öffentlich"
+
+msgid "friends"
+msgstr "Freunde"
+
+msgid "club"
+msgstr "Club (alle die den Badge ebenfalls besitzen)"
+
+msgid "friends and club"
+msgstr "Freunde und Club"
+
+msgid "Assembly Page"
+msgstr "Assembly Events"
+
+msgid "attachments"
+msgstr "Anhänge"
 
 msgid "schedule"
-msgstr ""
+msgstr "Zeitplan"
 
 #, python-format
 msgid "Day %(n)s"
 msgstr ""
 
 msgid "Welcome to rC3"
-msgstr ""
+msgstr "Willkommen bei der rC3"
 
 msgid "What's Official?"
-msgstr ""
+msgstr "Was ist der Fahrplan?"
 
-msgid "Explore Curated Events"
-msgstr ""
+msgid "official description"
+msgstr "Die virtuellen Bühnen 1 und 2 nenne sich passend \"rC1\" and \"rC2\", natürlich das \"rc3 Lounge\" nicht fehlen. Zusätzlich steuern 15 Community Bühnen ihr unabhängig kuratiertes Program zu den freien Streams der dezentralisierten Remote Chaos Experience bei."
 
-msgid "Congress Platform"
-msgstr ""
+msgid "Explore Curated Events Button"
+msgstr "Hauptprogramm entdecken"
 
-msgid "Community"
-msgstr ""
+msgid "Congress Platform Title"
+msgstr "Plattform & 2D-Welt"
+
+msgid "platform description"
+msgstr "Ihr seid angekommen! Von hier geht es mitten rein in die remote Chaos Experience: Findet Eure favorisierten Vorträge und Workshops, springt von den Assembly Seiten direkt in deren 2D Karten, erkundet die 2D-Welt oder trefft Euch mit Freunden im Videochat. Viel Spaß!"
+
+msgid "Community Title"
+msgstr "Community Content"
+
+msgid "community description"
+msgstr "Die Assemblies - das seid Ihr: All die verschiedenen Gruppen, die das Chaos bunt und entdeckenswert machen. Bei der rC3 gibt es weit über 300 Assemblies die Talks, Workshops, 2D Welten und Diskussionsrunden anbieten und natürlich die beliebten Self-organised Sessions für all Eure spannenden Wissensgebiete."
 
 msgid "Explore Community Events"
-msgstr ""
+msgstr "Community Content entdecken"
+
+#, python-format
+msgid "%(conf)s - Login"
+msgstr "%(conf)s - Login"
 
 #, python-format
 msgid "%(conf)s - Personal Schedule"
@@ -611,21 +809,28 @@ msgstr "Persönlicher Fahrplan"
 
 #, python-format
 msgid "%(conf)s - Password Reset complete"
-msgstr ""
+msgstr "%(conf)s - Passwort zurückgesetzt"
 
 #, python-format
 msgid "%(conf)s - Reset Password"
-msgstr ""
-
-msgid "enter new password"
-msgstr ""
+msgstr "%(conf)s - Passwort zurücksetzten"
 
 #, python-format
 msgid "%(conf)s - Password Reset sent"
-msgstr ""
+msgstr "%(conf)s - Passwort reset gesendet."
+
+msgid "report this message"
+msgstr "diese Nachricht melden"
+
+msgid "Send Personal Message"
+msgstr "Persönliche Nachricht senden"
+
+#, python-format
+msgid "Message from %(user)s"
+msgstr "Nachricht von %(user)s"
 
 msgid "My Dashboard"
-msgstr ""
+msgstr "Mein Dashboard"
 
 #, python-format
 msgid "%(conf)s - Public Fahrplan"
@@ -637,34 +842,81 @@ msgstr ""
 
 #, python-format
 msgid "Conference %(conf)s - Redeem Token"
-msgstr ""
+msgstr "Veranstaltung %(conf)s - Token einlösen"
 
-msgid "Redeem Token"
-msgstr ""
+#, python-format
+msgid "Hello '%(user)s'"
+msgstr "Hallo '%(user)s'"
+
+#, python-format
+msgid "You are Logged in as '%(user)s', do you want to join the Conference with this User?"
+msgstr "Du bist als '%(user)s' eingeloggt. Möchtest Du mit diesem Benutzer der Konferenz beitreten?"
 
 #, python-format
 msgid "You're receiving this email because you requested a password reset for your user account at %(site_name)s."
-msgstr ""
+msgstr "Du bekommst diese Mail weil Du ein Passwort reset für Deinen Accoutn auf der Seite '%(site_name)s.' angefordert hast."
 
 #, python-format
 msgid "Your username, in case you’ve forgotten: %(username)s"
-msgstr ""
+msgstr "Falls vergessen, Dein Username: %(username)s"
 
 #, python-format
 msgid "The %(site_name)s team"
-msgstr ""
+msgstr "Das Team von %(site_name)s "
 
-msgid "Selforganized Session"
-msgstr ""
+msgid "Help, there is a Problem..."
+msgstr "Hilfe, da gibt es ein Problem..."
+
+msgid "Rooms"
+msgstr "Räume"
+
+msgid "Create"
+msgstr "Erstellen"
 
 #, python-format
 msgid "Tag %(name)s"
 msgstr ""
 
+#, fuzzy, python-format
+#| msgid "Conference %(conf)s - Redeem Token"
+msgid "Conference %(conf)s - Password Reset"
+msgstr "Veranstaltung %(conf)s - Token einlösen"
+
 msgid "Upcoming events"
-msgstr ""
+msgstr "Kommende Veranstaltungen"
 
-#, fuzzy, python-format
-#| msgid "%(conf)s - Personal Schedule"
+#, python-format
 msgid "%(conf)s - User %(name)s"
-msgstr "%(conf)s - Persönlicher Fahrplan"
+msgstr "%(conf)s - Benutzer %(name)s"
+
+#, fuzzy
+#| msgid "Updated Profile"
+msgid "User Profile"
+msgstr "Profil aktualisiert"
+
+msgid "Start 2D World"
+msgstr ""
+
+#~ msgid "Submit"
+#~ msgstr "Abschicken"
+
+#~ msgid "no Badges availaible"
+#~ msgstr "Keine Badges."
+
+#~ msgid "Curated Events Notes Title"
+#~ msgstr "Veranstaltungshinweise"
+
+#~ msgid "Curated Events Notes"
+#~ msgstr "Hier finden ihr die Streams des öffentlichen Programms, das auf den beiden Hauptbühnen der rC3 World gespielt wird. Wie immer sind die Streams des öffentlichen Programms frei verfügbar und werden auch auf media.ccc.de kuratiert. Es wird kein Ticket benötigt, um die Streams zu sehen."
+
+#~ msgid "curated events"
+#~ msgstr "Hauptprogramm"
+
+#~ msgid "External Ressources"
+#~ msgstr "Externe Ressourcen"
+
+#~ msgid "assembly_events_description"
+#~ msgstr "Du hast neben den offiziellen und community-Events immer noch zu viel freie Zeit übrig? Dann sieh dir an was die Assemblies vorhaben, oder organisiere einfach spontan selbst etwas."
+
+#~ msgid "Unknown Username or Password"
+#~ msgstr "Unbekannter Benutzername oder Passwort falsch"
diff --git a/src/plainui/locale/en/LC_MESSAGES/django.po b/src/plainui/locale/en/LC_MESSAGES/django.po
index 4ee1cb9c6c410c4eb907f77086771e27ddfee7da..398c56773becf3cfdbb597c840e8854e86e151bd 100644
--- a/src/plainui/locale/en/LC_MESSAGES/django.po
+++ b/src/plainui/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-12-23 13:29+0000\n"
+"POT-Creation-Date: 2020-12-27 12:28+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -33,31 +33,73 @@ msgstr ""
 msgid "Placeholder text"
 msgstr ""
 
+msgid "Token"
+msgstr ""
+
+msgid "Your Username will be visible to other users"
+msgstr ""
+
+msgid "Username"
+msgstr ""
+
+msgid "Please enter the Username that used this Ticket and your new Password"
+msgstr ""
+
+msgid "Unknown User"
+msgstr ""
+
+msgid "User is not active!"
+msgstr ""
+
+msgid "User can use password reset by email!"
+msgstr ""
+
+msgid "User didn't use this Ticket!"
+msgstr ""
+
 msgid "blocked"
 msgstr ""
 
 msgid "Please enter a name"
 msgstr ""
 
+msgid "BBB or Workshop Rooms only."
+msgstr ""
+
+msgid "ISO 8601 format, e.g. '2020-12-27 13:37'"
+msgstr ""
+
+msgid "'HH:MM' or even 'DD HH:MM' e.g. 3hours ='03:00'"
+msgstr ""
+
 msgid "Must end with the conference!"
 msgstr ""
 
-msgid "Room is not free!"
+msgid "Room is not free, choose other time or room"
 msgstr ""
 
 msgid "Can't begin before the conference begins!"
 msgstr ""
 
-msgid "Legal (Copyright, Nazi shit, ..)"
+msgid "Abuse - legally problematic content"
+msgstr ""
+
+msgid "Content - I think the content is problematic"
 msgstr ""
 
-msgid "General Report (-> Security Team)"
+msgid "Attendee - there is a problem with a user"
 msgstr ""
 
-msgid "Awareness Team"
+msgid "Technical - a technical problem with the site"
 msgstr ""
 
-msgid "Technical Problem"
+msgid "Misc - something else"
+msgstr ""
+
+msgid "describe the problem"
+msgstr ""
+
+msgid "describe a solution"
 msgstr ""
 
 msgid "Assemblies"
@@ -87,19 +129,30 @@ msgstr "only community assemblies"
 msgid "assembly events"
 msgstr ""
 
-msgid "running and upcoming events"
+msgid "Running and Upcoming Events"
 msgstr ""
 
-msgid "all assemblies events"
+msgid "All Assemblies Events"
 msgstr ""
 
-msgid "assmebly events"
+msgid "Contacts"
 msgstr ""
 
-msgid "New Selforganized Session"
+msgid "assembly rooms"
 msgstr ""
 
-msgid "Edit"
+#, fuzzy
+#| msgid "Assembly Events"
+msgid "assembly badges"
+msgstr "Assembly Events"
+
+msgid "No badges publicly available."
+msgstr ""
+
+msgid "Self-organized Sessions"
+msgstr ""
+
+msgid "new Self-organized Session"
 msgstr ""
 
 msgid "Your Browser is broken. Get a better one here!"
@@ -112,9 +165,12 @@ msgid "Privacy Policy"
 msgstr ""
 
 msgid "About RC3"
-msgstr ""
+msgstr "About rC3"
 
 msgid "Disclaimer"
+msgstr "Imprint"
+
+msgid "Principles"
 msgstr ""
 
 msgid "Bulletin Board"
@@ -123,32 +179,41 @@ msgstr ""
 msgid "search"
 msgstr ""
 
-msgid "My Board Entries"
+msgid "My Bulletin Board"
 msgstr ""
 
 msgid "New Entry"
 msgstr ""
 
-msgid "Update"
+msgid "Edit Board Entry"
 msgstr ""
 
-msgid "My Bulletin Board"
+msgid "Save"
 msgstr ""
 
 msgid "Public Bulletin Board"
 msgstr ""
 
-msgid "Curated Events"
+msgid "Fahrplan in new Tab"
 msgstr ""
 
-msgid "curated events"
-msgstr ""
+msgid "Revoke Badge"
+msgstr "Revoke Badge"
+
+msgid "Accept Badge"
+msgstr "Accept Badge"
+
+msgid "Visibility"
+msgstr "Visibility"
+
+msgid "Save visibility"
+msgstr "Save visibility"
 
 msgid "No entries available."
 msgstr ""
 
 msgid "Event starts in"
-msgstr ""
+msgstr "Event start:"
 
 msgid "Event Information"
 msgstr ""
@@ -171,6 +236,9 @@ msgstr ""
 msgid "Room"
 msgstr ""
 
+msgid "Host"
+msgstr ""
+
 msgid "remove from favorites"
 msgstr ""
 
@@ -192,18 +260,49 @@ msgstr ""
 msgid "report this url"
 msgstr ""
 
-msgid "Live Stream"
+msgid "edit this"
+msgstr ""
+
+msgid "Official Page"
+msgstr ""
+
+#, python-format
+msgid "%(kind)s Event on Track"
+msgstr ""
+
+msgid "Coming Up Next"
 msgstr ""
 
-msgid "External Ressources"
+msgid "no entry availaible"
+msgstr ""
+
+msgid "View next Events"
+msgstr ""
+
+msgid "no rooms available."
+msgstr ""
+
+msgid "roomtype"
+msgstr ""
+
+msgid "capacity"
 msgstr ""
 
 msgid "Tags"
 msgstr ""
 
+msgid "no tags availaible"
+msgstr ""
+
 msgid "by"
 msgstr ""
 
+msgid "Mehr anzeigen"
+msgstr "show more"
+
+msgid "Edit"
+msgstr ""
+
 msgid "Delete"
 msgstr ""
 
@@ -219,9 +318,15 @@ msgstr ""
 msgid "You are leaving the »RC3-area«. For external sites, streams and applications the actual owners are completely and solely responsible regarding data protection, copyright, youth protection, etc.!"
 msgstr ""
 
+msgid "Back"
+msgstr ""
+
 msgid "External Link"
 msgstr ""
 
+msgid "recommendations"
+msgstr ""
+
 msgid "download"
 msgstr ""
 
@@ -303,46 +408,52 @@ msgstr ""
 msgid "Skip intro"
 msgstr ""
 
-msgid "welcome to the rc3"
+msgid "Welcome to the rc3"
 msgstr ""
 
 msgid "ticket"
 msgstr ""
 
-msgid "enter username"
+msgid "By logging in you accept our use of cookies to store your user session."
 msgstr ""
 
-msgid "username"
+msgid "Reset Password"
 msgstr ""
 
-msgid "enter password"
+msgid "New Ticket"
 msgstr ""
 
-msgid "password"
+msgid "Login"
 msgstr ""
 
-msgid "By logging in you accept our use of cookies to store your user session."
-msgstr ""
+msgid "Accepted Badges"
+msgstr "Accepted Badges"
 
-msgid "reset password"
+msgid "No badges Accepted."
 msgstr ""
 
-msgid "new Ticket"
-msgstr ""
+msgid "Redeem Badge Token"
+msgstr "Redeem Badge Token"
 
-msgid "Change Password"
+msgid "Pending Badges"
+msgstr "Pending Badges"
+
+msgid "No badges are waiting for approval."
+msgstr "No badges are waiting for approval."
+
+msgid "Redeem Token"
 msgstr ""
 
-msgid "Your password has been set. You may go ahead and log in now."
+msgid "Change Password"
 msgstr ""
 
-msgid "back"
+msgid "Change password"
 msgstr ""
 
-msgid "Login"
+msgid "Your password has been set. You may go ahead and log in now."
 msgstr ""
 
-msgid "new password"
+msgid "enter new password"
 msgstr ""
 
 msgid "We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly."
@@ -351,13 +462,13 @@ msgstr ""
 msgid "If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder."
 msgstr ""
 
-msgid "enter your registered e-mail"
+msgid "reset password"
 msgstr ""
 
-msgid "e-mail"
+msgid "You will receive an e-mail with a reset link."
 msgstr ""
 
-msgid "You will receive an e-mail with a reset link."
+msgid "If you have no configured e-mail address, revisit your ticket activation link"
 msgstr ""
 
 msgid "Personal Messages"
@@ -378,35 +489,40 @@ msgstr "Received Messages"
 msgid "Sent Messages"
 msgstr "Sent Messages"
 
-msgid "messages_subject"
-msgstr "Subject"
-
-msgid "messages_from"
-msgstr "From"
+msgid "messages_x_of_n"
+msgstr "of"
 
-msgid "messages_to"
-msgstr "To"
+#, fuzzy
+#| msgid "messages_was_read"
+msgid "messages_was_responded"
+msgstr "read"
 
-msgid "messages_date"
-msgstr "Date"
+msgid "messages_was_read"
+msgstr "read"
 
-msgid "messages_state"
+#, fuzzy
+#| msgid "messages_state"
+msgid "messages_is_new"
 msgstr "State"
 
-msgid "messages_delete"
-msgstr "Delete"
-
-msgid "messages_was_read"
+#, fuzzy
+#| msgid "messages_was_read"
+msgid "messages_was_sent"
 msgstr "read"
 
-msgid "messages_x_of_n"
-msgstr "of"
+msgid "read"
+msgstr ""
+
+msgid "messages_delete"
+msgstr "Delete"
 
 msgid "Personal Messages - Send"
 msgstr ""
 
-msgid "Send Personal Message"
-msgstr ""
+#, fuzzy
+#| msgid "Sent Messages"
+msgid "new message"
+msgstr "Sent Messages"
 
 msgid "Send"
 msgstr ""
@@ -414,16 +530,10 @@ msgstr ""
 msgid "Personal Message"
 msgstr ""
 
-msgid "messages_from_short"
-msgstr "From"
-
-msgid "messages_to_short"
-msgstr "To"
-
-msgid "messages_at"
-msgstr "At"
+msgid "Reply"
+msgstr ""
 
-msgid "Avatar image"
+msgid "username"
 msgstr ""
 
 msgid "email"
@@ -432,34 +542,66 @@ msgstr ""
 msgid "last login"
 msgstr ""
 
-msgid "overview"
+msgid "My public User Page"
 msgstr ""
 
-msgid "total events added schedule"
+msgid "View"
 msgstr ""
 
-msgid "total events attended"
+msgid "custom preferences"
 msgstr ""
 
-msgid "total assemblies involved"
+msgid "save"
 msgstr ""
 
-msgid "total tokens found"
+msgid "My Badges"
+msgstr "My Badges"
+
+msgid "Manage Badges"
+msgstr "Manage Badges"
+
+#, fuzzy
+#| msgid "Pending Badges"
+msgid "Pending badges"
+msgstr "Pending Badges"
+
+msgid "Badges: Accept / Revoke / Visibility"
+msgstr "Manage Badges (Acceptance / Revoke / Visibility)"
+
+msgid "My Favorites"
 msgstr ""
 
-msgid "custom preferences"
+msgid "My Fahrplan"
 msgstr ""
 
-msgid "Badge-Token"
+msgid "No"
 msgstr ""
 
-msgid "Submit"
+msgid "Yes"
 msgstr ""
 
-msgid "My Favorites"
+msgid "Use Token for Password Reset"
 msgstr ""
 
-msgid "My Fahrplan"
+msgid "Submit Token"
+msgstr ""
+
+msgid "Create new Account"
+msgstr ""
+
+msgid "Use the next form if you already have an account"
+msgstr ""
+
+msgid "Create a new Account with your Ticket"
+msgstr ""
+
+msgid "Got an Account already? Login here"
+msgstr ""
+
+msgid "Add your Ticket to this existing Account"
+msgstr ""
+
+msgid "User accounts from signup.c3assemblies.de and maschinenraum.rc3.world are imported and can be used to login."
 msgstr ""
 
 msgid "Please go to the following page and choose a new password:"
@@ -468,7 +610,26 @@ msgstr ""
 msgid "Thanks for using our site!"
 msgstr ""
 
-msgid "report content"
+msgid "Your message will be read and processed by one of our angels. We assure you to keep your personal data safe and hidden if not needed to solve your problem."
+msgstr ""
+
+#, fuzzy
+#| msgid "Assembly Events"
+msgid "Assembly"
+msgstr "Assembly Events"
+
+msgid "Currently Streaming"
+msgstr ""
+
+#, fuzzy
+#| msgid "official_description"
+msgid "Description"
+msgstr "Like every C3 event, there are many organized, talks, sessions, workshops, ... to attend. Browse the list and see what you can was^Z spend your time on rc3 on."
+
+msgid "Events"
+msgstr ""
+
+msgid "Links"
 msgstr ""
 
 msgid "Search Results"
@@ -477,12 +638,35 @@ msgstr ""
 msgid "No results, sorry!"
 msgstr ""
 
-msgid "Cancel"
+msgid "Self-organized Session"
+msgstr ""
+
+msgid "back"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Password Reset"
 msgstr ""
 
 msgid "Upcoming"
 msgstr ""
 
+msgid "Avatar image"
+msgstr ""
+
+msgid "pronouns"
+msgstr ""
+
+#, fuzzy
+#| msgid "official_description"
+msgid "description"
+msgstr "Like every C3 event, there are many organized, talks, sessions, workshops, ... to attend. Browse the list and see what you can was^Z spend your time on rc3 on."
+
+msgid "Get in Touch"
+msgstr ""
+
 msgid "Send PN"
 msgstr ""
 
@@ -492,16 +676,16 @@ msgstr ""
 msgid "2D World"
 msgstr ""
 
-msgid "Please activate your Ticket to access this conference!"
+msgid "Created Self Organized Session"
 msgstr ""
 
-msgid "Not allowed to edit this Self Organized Session"
+msgid "Updated Self Organized Session"
 msgstr ""
 
-msgid "Updated Self Organized Session"
+msgid "Please activate your Ticket to access this conference!"
 msgstr ""
 
-msgid "Created Self Organized Session"
+msgid "Not allowed to edit this Self Organized Session"
 msgstr ""
 
 msgid "Updated Profile"
@@ -513,10 +697,10 @@ msgstr ""
 msgid "Message deleted."
 msgstr ""
 
-msgid "Unknown Username or Password"
+msgid "Password changed successfully."
 msgstr ""
 
-msgid "Password changed successfully."
+msgid "Passwort successfully reset!"
 msgstr ""
 
 msgid "Bulletin Board Entry updated."
@@ -528,31 +712,56 @@ msgstr ""
 msgid "Bulletin Board Entry deleted."
 msgstr ""
 
-msgid "Report sent"
+msgid "Thank you for your help to make this plattform safer and better! Please give us some time to find a solution and keep an eye on your Messages, we may contact you."
 msgstr ""
 
 msgid "Assemblies List"
-msgstr ""
+msgstr "Assembly List"
 
-msgid "Explore Assemblies"
-msgstr ""
+msgid "Assemblies List Description"
+msgstr "In this list you will find all assemblies registered at rC3. Are your neighbours from the last event here as well and what projects are they bringing this time? Come and find out!"
+
+msgid "Explore Assemblies Button"
+msgstr "Explore Assemblies"
 
 msgid "What are Assemblies?"
-msgstr ""
+msgstr "What are Assemblies?"
+
+msgid "Assemblies Description"
+msgstr "The assemblies are groups of you Chaos beings who have found each other due to local or thematic proximity to exchange ideas, work together and share their knowledge with you. The diversity is huge. Be warned - here each of you can find a new hobby."
 
 msgid "Assembly Events"
-msgstr ""
+msgstr "Assembly Events"
 
-msgid "Explore Assembly Events"
-msgstr ""
+msgid "Assemblies Event Description"
+msgstr "The Assemblies are an integral part of the rC3 and make up a lot of the program: Sharing knowledge and presenting your projects is central for the conference. On these pages you will find talks and workshops hosted by the Assemblies."
 
-msgid "Bulletin Board Entry"
+msgid "Explore Assembly Events Button"
+msgstr "Explore Assembly Events"
+
+#, python-format
+msgid "%(room)s Currently Streaming"
 msgstr ""
 
-msgid "Create"
+msgid "private"
+msgstr "Private"
+
+msgid "public"
+msgstr "Public"
+
+msgid "friends"
+msgstr "Friends"
+
+msgid "club"
+msgstr "Club (all who own this badge)"
+
+msgid "friends and club"
+msgstr "Friends and Club"
+
+msgid "Assembly Page"
 msgstr ""
 
-msgid "Explore Events"
+msgid "attachments"
 msgstr ""
 
 msgid "schedule"
@@ -566,18 +775,31 @@ msgid "Welcome to rC3"
 msgstr ""
 
 msgid "What's Official?"
-msgstr ""
+msgstr "What's the Fahrplan?"
 
-msgid "Explore Curated Events"
-msgstr ""
+msgid "official description"
+msgstr "The virtual halls 1 and 2 are appropriately titled \"rC1\" and \"rC2\" and the music program of the \"rc3 Lounge\" may of course not be missing. In addition, 15 community streams contribute their independently curated program to the decentralized Remote Chaos Experience."
 
-msgid "Congress Platform"
-msgstr ""
+msgid "Explore Curated Events Button"
+msgstr "Explore Fahrplan"
 
-msgid "Community"
-msgstr ""
+msgid "Congress Platform Title"
+msgstr "This is the platform"
+
+msgid "platform description"
+msgstr "You found us! This is where you will get set on track for the remote Chaos Experience, to attend lectures, explore the 2D world, participate in workshops, meet your friends via video chat and hopefully have a mindblowningly good time. "
+
+msgid "Community Title"
+msgstr "What are assemblies?"
+
+msgid "community description"
+msgstr "The assemblies - that's you: All the different groups that make Chaos colourful and worth exploring. At rC3 there are over 300assemblies offering talks, workshops, 2D worlds and discussion panels. And of course there are the popular self-organised sessions for all your exciting fields of interest."
 
 msgid "Explore Community Events"
+msgstr "Explore Community"
+
+#, python-format
+msgid "%(conf)s - Login"
 msgstr ""
 
 #, python-format
@@ -595,13 +817,21 @@ msgstr ""
 msgid "%(conf)s - Reset Password"
 msgstr ""
 
-msgid "enter new password"
-msgstr ""
-
 #, python-format
 msgid "%(conf)s - Password Reset sent"
 msgstr ""
 
+msgid "report this message"
+msgstr ""
+
+msgid "Send Personal Message"
+msgstr ""
+
+#, fuzzy, python-format
+#| msgid "messages_from_short"
+msgid "Message from %(user)s"
+msgstr "From"
+
 msgid "My Dashboard"
 msgstr ""
 
@@ -617,7 +847,12 @@ msgstr ""
 msgid "Conference %(conf)s - Redeem Token"
 msgstr ""
 
-msgid "Redeem Token"
+#, python-format
+msgid "Hello '%(user)s'"
+msgstr ""
+
+#, python-format
+msgid "You are Logged in as '%(user)s', do you want to join the Conference with this User?"
 msgstr ""
 
 #, python-format
@@ -632,16 +867,56 @@ msgstr ""
 msgid "The %(site_name)s team"
 msgstr ""
 
-msgid "Selforganized Session"
+msgid "Help, there is a Problem..."
+msgstr ""
+
+msgid "Rooms"
+msgstr ""
+
+msgid "Create"
 msgstr ""
 
 #, python-format
 msgid "Tag %(name)s"
 msgstr ""
 
+#, python-format
+msgid "Conference %(conf)s - Password Reset"
+msgstr ""
+
 msgid "Upcoming events"
 msgstr ""
 
 #, python-format
 msgid "%(conf)s - User %(name)s"
 msgstr ""
+
+msgid "User Profile"
+msgstr ""
+
+msgid "Start 2D World"
+msgstr ""
+
+#~ msgid "Curated Events Notes Title"
+#~ msgstr "Event Notes"
+
+#~ msgid "Curated Events Notes"
+#~ msgstr "Here you can find the streams of the public programm which is playing on the two mainstages of the rC3 World. As always, the streams of the public programme is freely available and will be curated on media.ccc.de as well. You do not need a ticket to watch the streams."
+
+#~ msgid "messages_subject"
+#~ msgstr "Subject"
+
+#~ msgid "messages_from"
+#~ msgstr "From"
+
+#~ msgid "messages_to"
+#~ msgstr "To"
+
+#~ msgid "messages_date"
+#~ msgstr "Date"
+
+#~ msgid "messages_to_short"
+#~ msgstr "To"
+
+#~ msgid "messages_at"
+#~ msgstr "At"
diff --git a/src/plainui/static/plainui/bbb-background.jpg b/src/plainui/static/plainui/bbb-background.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..69841bb8ae77c2e21d128af836d7da233b866bbe
Binary files /dev/null and b/src/plainui/static/plainui/bbb-background.jpg differ
diff --git a/src/plainui/static/plainui/img/favicon.ico b/src/plainui/static/plainui/img/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..37651b6b2fa787697519b108ead8b7285a362b3a
Binary files /dev/null and b/src/plainui/static/plainui/img/favicon.ico differ
diff --git a/src/plainui/static/plainui/js/modal.js b/src/plainui/static/plainui/js/modal.js
index 69cbf10c68839b059dfc6a655f346b16a61e813a..6b1134e7b970e586b7492948bf9deefd917b45b1 100644
--- a/src/plainui/static/plainui/js/modal.js
+++ b/src/plainui/static/plainui/js/modal.js
@@ -9,6 +9,10 @@ fpReady(() => {
     document.addEventListener('click',function onclicked(e){
         documentClicked(e);
     });
+    // check for the URL hash and open modal if hash is present
+    if (window.location.hash) {
+        showModal2(window.location.hash.substr(1));
+    }
 });
 
 
@@ -27,12 +31,13 @@ function documentClicked(e) {
 
 function showModal2(event_id) {
     let data_el = document.getElementById(event_id);
-    if (data_el == 'undefined') {
+    if (!data_el) {
         return;
     }
     let data = JSON.parse(data_el.innerText);
     if (!data) { return; }
     let modal = document.createElement('div');
+    window.location.hash = event_id;
     modal.classList.add('rc3-fahrplan__modal-box');
     modal.innerHTML = `
     <div class="modal show modal-close" tabindex="-1" style="display:block;">
@@ -82,6 +87,7 @@ function eventContent(data) {
 }
 
 function removeFahrplanModal() {
+    window.location.hash = '_';
     document.querySelectorAll('.rc3-fahrplan__modal-box').forEach( function(el) {
         console.log('should remove');
         el.remove();
diff --git a/src/plainui/static/plainui/js/player.js b/src/plainui/static/plainui/js/player.js
index 0bbaad96da2617d31091465a7db9887cfc0aeca9..4cc1df60500b37d388325407c5aa34025f7b21ee 100644
--- a/src/plainui/static/plainui/js/player.js
+++ b/src/plainui/static/plainui/js/player.js
@@ -1140,5 +1140,5 @@ function dc(){}function fc(){Mu?console.debug("EmeEncryptionSchemePolyfill: Alre
 /*!******************************************************************************************!*\
   !*** external {"amd":"clappr","commonjs":"clappr","commonjs2":"clappr","root":"Clappr"} ***!
   \******************************************************************************************/
-/*! no static exports found */function(module,exports){eval("module.exports = __WEBPACK_EXTERNAL_MODULE_clappr__;\n\n//# sourceURL=webpack://AudioTrackSelector/external_%7B%22amd%22:%22clappr%22,%22commonjs%22:%22clappr%22,%22commonjs2%22:%22clappr%22,%22root%22:%22Clappr%22%7D?")}}).default},module.exports=factory(__webpack_require__(0))},function(e,t,n){var r=n(5),i=n(6);"string"==typeof(i=i.__esModule?i.default:i)&&(i=[[e.i,i,""]]);var a={insert:"head",singleton:!1};r(i,a);e.exports=i.locals||{}},function(e,t,n){"use strict";var r,i=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},a=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),o=[];function s(e){for(var t=-1,n=0;n<o.length;n++)if(o[n].identifier===e){t=n;break}return t}function l(e,t){for(var n={},r=[],i=0;i<e.length;i++){var a=e[i],l=t.base?a[0]+t.base:a[0],u=n[l]||0,c="".concat(l," ").concat(u);n[l]=u+1;var d=s(c),f={css:a[1],media:a[2],sourceMap:a[3]};-1!==d?(o[d].references++,o[d].updater(f)):o.push({identifier:c,updater:g(f,t),references:1}),r.push(c)}return r}function u(e){var t=document.createElement("style"),r=e.attributes||{};if(void 0===r.nonce){var i=n.nc;i&&(r.nonce=i)}if(Object.keys(r).forEach((function(e){t.setAttribute(e,r[e])})),"function"==typeof e.insert)e.insert(t);else{var o=a(e.insert||"head");if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(t)}return t}var c,d=(c=[],function(e,t){return c[e]=t,c.filter(Boolean).join("\n")});function f(e,t,n,r){var i=n?"":r.media?"@media ".concat(r.media," {").concat(r.css,"}"):r.css;if(e.styleSheet)e.styleSheet.cssText=d(t,i);else{var a=document.createTextNode(i),o=e.childNodes;o[t]&&e.removeChild(o[t]),o.length?e.insertBefore(a,o[t]):e.appendChild(a)}}function h(e,t,n){var r=n.css,i=n.media,a=n.sourceMap;if(i?e.setAttribute("media",i):e.removeAttribute("media"),a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleSheet)e.styleSheet.cssText=r;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(r))}}var p=null,m=0;function g(e,t){var n,r,i;if(t.singleton){var a=m++;n=p||(p=u(t)),r=f.bind(null,n,a,!1),i=f.bind(null,n,a,!0)}else n=u(t),r=h.bind(null,n,t),i=function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)};return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else i()}}e.exports=function(e,t){(t=t||{}).singleton||"boolean"==typeof t.singleton||(t.singleton=i());var n=l(e=e||[],t);return function(e){if(e=e||[],"[object Array]"===Object.prototype.toString.call(e)){for(var r=0;r<n.length;r++){var i=s(n[r]);o[i].references--}for(var a=l(e,t),u=0;u<n.length;u++){var c=s(n[u]);0===o[c].references&&(o[c].updater(),o.splice(c,1))}n=a}}}},function(e,t,n){(t=n(7)(!1)).push([e.i,"button.media-control-button[data-hd-indicator]{display:none !important}.media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar]{height:40px}.media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar]{height:2px;background-color:#ccc}.player-poster[data-poster] .play-wrapper[data-poster] svg path{fill:#ccc}.spinner-three-bounce[data-spinner]>div{background-color:#ccc}.clappr-watermark[data-watermark]{transition:opacity .5s ease-out;width:8%;min-width:50px;max-width:100px}.clappr-watermark[data-watermark].clappr-watermark-hide{opacity:0}.clappr-watermark[data-watermark-top-left]{top:0px;left:15px;text-align:left}@media(min-width: 768px){.clappr-watermark[data-watermark-top-left]{top:10px;left:30px}}",""]),e.exports=t},function(e,t,n){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=(o=r,s=btoa(unescape(encodeURIComponent(JSON.stringify(o)))),l="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(s),"/*# ".concat(l," */")),a=r.sources.map((function(e){return"/*# sourceURL=".concat(r.sourceRoot||"").concat(e," */")}));return[n].concat(a).concat([i]).join("\n")}var o,s,l;return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n})).join("")},t.i=function(e,n,r){"string"==typeof e&&(e=[[null,e,""]]);var i={};if(r)for(var a=0;a<this.length;a++){var o=this[a][0];null!=o&&(i[o]=!0)}for(var s=0;s<e.length;s++){var l=[].concat(e[s]);r&&i[l[0]]||(n&&(l[2]?l[2]="".concat(n," and ").concat(l[2]):l[2]=n),t.push(l))}},t}},function(e,t,n){"use strict";n.r(t),n.d(t,"Player",(function(){return m})),n.d(t,"Mediator",(function(){return r.Mediator})),n.d(t,"Events",(function(){return r.Events})),n.d(t,"Browser",(function(){return r.Browser})),n.d(t,"PlayerInfo",(function(){return r.PlayerInfo})),n.d(t,"MediaControl",(function(){return r.MediaControl})),n.d(t,"ContainerPlugin",(function(){return r.ContainerPlugin})),n.d(t,"UIContainerPlugin",(function(){return r.UIContainerPlugin})),n.d(t,"CorePlugin",(function(){return r.CorePlugin})),n.d(t,"UICorePlugin",(function(){return r.UICorePlugin})),n.d(t,"Playback",(function(){return r.Playback})),n.d(t,"Container",(function(){return r.Container})),n.d(t,"Core",(function(){return r.Core})),n.d(t,"PlayerError",(function(){return r.PlayerError})),n.d(t,"Loader",(function(){return r.Loader})),n.d(t,"BaseObject",(function(){return r.BaseObject})),n.d(t,"UIObject",(function(){return r.UIObject})),n.d(t,"Utils",(function(){return r.Utils})),n.d(t,"BaseFlashPlayback",(function(){return r.BaseFlashPlayback})),n.d(t,"Flash",(function(){return r.Flash})),n.d(t,"FlasHLS",(function(){return r.FlasHLS})),n.d(t,"HLS",(function(){return r.HLS})),n.d(t,"HTML5Audio",(function(){return r.HTML5Audio})),n.d(t,"HTML5Video",(function(){return r.HTML5Video})),n.d(t,"HTMLImg",(function(){return r.HTMLImg})),n.d(t,"NoOp",(function(){return r.NoOp})),n.d(t,"ClickToPausePlugin",(function(){return r.ClickToPausePlugin})),n.d(t,"DVRControls",(function(){return r.DVRControls})),n.d(t,"Favicon",(function(){return r.Favicon})),n.d(t,"Log",(function(){return r.Log})),n.d(t,"Poster",(function(){return r.Poster})),n.d(t,"SpinnerThreeBouncePlugin",(function(){return r.SpinnerThreeBouncePlugin})),n.d(t,"WaterMarkPlugin",(function(){return r.WaterMarkPlugin})),n.d(t,"Styler",(function(){return r.Styler})),n.d(t,"Vendor",(function(){return r.Vendor})),n.d(t,"version",(function(){return r.version})),n.d(t,"template",(function(){return r.template})),n.d(t,"$",(function(){return r.$}));var r=n(0),i=n.n(r),a=n(1),o=n.n(a),s=n(2),l=n.n(s),u=n(3),c=n.n(u);n(4);class d extends i.a.ContainerPlugin{get name(){return"error_plugin"}get background(){return"data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%20viewBox%3D%220%200%2026.458318%2026.458333%22%3E%3Cpath%20d%3D%22M13.23.302C6.07.302.264%206.107.264%2013.267a12.965%2012.965%200%200%200%20.847%204.595c.19-.497.408-.982.682-1.438.14-.232.294-.457.396-.707.103-.25.15-.533.072-.792a1.362%201.362%200%200%200-.22-.404c-.092-.123-.192-.24-.275-.37a1.662%201.662%200%200%201-.255-1.12%201.5%201.5%200%200%201%20.58-.987c.28-.208.635-.3.985-.288a1.757%201.757%200%200%201%20.346.048c.452.11.852.393%201.148.75.368.447.584%201.01.637%201.586a3.574%203.574%200%200%201-.275%201.693c-.4.955-1.15%201.725-1.565%202.673-.338.775-.435%201.638-.39%202.483.007.077.018.155.025.234a12.965%2012.965%200%200%200%203.62%203.18%2017.63%2017.63%200%200%201-.13-2.11c.002-.56.03-1.12.085-1.675-.34-.236-.65-.51-.87-.86-.392-.62-.466-1.408-.305-2.124.16-.717.54-1.37.997-1.945a7.833%207.833%200%200%201%202.835-2.223%2010.305%2010.305%200%200%201-.09-.126%204.854%204.854%200%200%201-.702-2.176c-.06-.777.064-1.554.115-2.33.037-.543.04-1.085.07-1.627.038-.627.114-1.255.29-1.858a2.36%202.36%200%200%201%20.266-.63%201.4%201.4%200%200%201%20.594-.514c.274-.108.51-.132.776-.087.22.046.425.156.604.294.18.138.335.304.48.477a7.298%207.298%200%200%201%201.04%201.617%203.57%203.57%200%200%201%201.09%200%207.287%207.287%200%200%201%201.04-1.616%203.21%203.21%200%200%201%20.48-.476c.18-.14.383-.248.604-.295a1.268%201.268%200%200%201%20.78.086%201.402%201.402%200%200%201%20.595.517c.124.19.202.408.266.626.175.602.252%201.23.29%201.856.03.543.033%201.087.07%201.628.05.777.175%201.554.116%202.33a4.855%204.855%200%200%201-.705%202.178c-.03.05-.07.096-.103.145.247.278.598.513.898.614a1.956%201.956%200%200%200%201.05.044%201.65%201.65%200%200%200%20.533-.226%201.253%201.253%200%200%200%20.397-.418c.118-.21.166-.45.192-.687.067-.61%200-1.224-.05-1.835-.034-.396-.062-.8.027-1.187.06-.26.177-.518.373-.7a1.106%201.106%200%200%201%20.465-.255%201.312%201.312%200%200%201%20.53-.03c.38.057.736.274.948.594.12.18.194.39.238.604.044.213.06.43.072.648.04.76.04%201.522.018%202.284-.018.665-.055%201.348-.32%201.957-.343.782-1.032%201.366-1.775%201.786a7.052%207.052%200%200%201-1.588.647c.482%201.54.733%203.24.733%204.968a17.6%2017.6%200%200%201-.135%202.125%2012.964%2012.964%200%200%200%206.384-11.152c0-7.16-5.806-12.965-12.965-12.965zM9.602%2016.284v1.483a1.88%201.88%200%200%201%201.083.362%201.738%201.738%200%200%201%20.556.68c.122.27.166.576.116.868a1.493%201.493%200%200%201-.332.708%201.647%201.647%200%200%201-.635.458%201.738%201.738%200%200%201-.787.122v3.73l7.762-4.208-7.762-4.204z%22%20fill%3D%22%23999%22%2F%3E%3C%2Fsvg%3E"}constructor(...e){super(...e),this.timeout=1,this.max_timeout=10}bindEvents(){this.listenTo(this.container,i.a.Events.CONTAINER_ERROR,this.onError)}hide(){this._err&&this._err.remove()}show(e){const t=i.a.$;this.hide();const n=e&&e.title||"Oh no, we encountered an error",r=e&&e.subtitle||"Please reload the page";this._err=t("<div>").css({position:"absolute","z-index":"999",width:"100%",height:"100%","background-image":"url("+this.background+")","background-size":"18%","background-repeat":"no-repeat","background-color":"black","background-position":"center","text-align":"center","font-weight":"bold",color:"#eee"});const a=t("<div>").css({position:"absolute",width:"100%","padding-bottom":"5%",bottom:0}).append(t("<h2>").text(n).css({"font-size":"2em"})).append(t("<p>").text(r).css({"font-size":"1.2em",margin:"15px"}));this._err.append(a),this.container&&this.container.$el.prepend(this._err)}onError(e){if(!this.container)return;const t=()=>{this.hide(),this.container.getPlugin("click_to_pause").enable()},n=this.options.errorPlugin.onError;let r=null;n&&"function"==typeof n&&(r=n(e,t)),this.show(r),this.container.getPlugin("click_to_pause").disable()}}const f=function(e){return function(e,t,n={}){new Headers;return fetch("https://media.ccc.de/graphql",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({operationName:e,query:t,variables:n})}).then(e=>{const t=e.body.getReader();let n="",r=new TextDecoder("utf-8");return t.read().then((function e({done:i,value:a}){return i?JSON.parse(n):(n+=r.decode(a),t.read().then(e))}))})}("LectureBySlug",`\n    query LectureBySlug {\n      lectureBySlug(slug: "${e}") {\n        originalLanguage\n        videos {\n          label\n          url\n          mimeType\n        }\n      }\n    }\n  `).then(e=>{if(!e.data.lectureBySlug)throw new Error("Lecture could not be found");return e.data.lectureBySlug.videos.map(({label:e,url:t,mimeType:n})=>({label:e,mimeType:n,source:t}))})},h=(e,t)=>"dash_shaka_playback"==e.origin&&(e.raw.code==t||e.raw.detail&&e.raw.detail.code==t),p=(e,t)=>"hls"==e.origin&&e.raw.response.code==t;class m extends r.BaseObject{constructor(e){super(),this.timeout=5,this.maxTimeout=15,this._playerPromise=this._getConfig(e).then(e=>(this._options=e,this._player=new r.Player(this._options),this._player.core&&this._player.core.isReady?this._addEventListeners():this.listenToOnce(this._player,r.Events.PLAYER_READY,this._addEventListeners.bind(this)),this._player))}attachTo(){this._playerPromise.then(e=>{e.attachTo.apply(e,arguments)})}_getConfig(e){let t=[c.a,l.a,o.a,d];e.plugins&&e.plugins.length&&(t=t.concat(e.plugins),console.log("loading plugins"),t.forEach(e=>console.log(e.name,e.type)));let n=Promise.resolve({});var r;return e.vocStream?n=((e,t,n,r)=>{const i="MediaSource"in window,a={levelSelectorConfig:{labelCallback:function(e){var t=e.videoBandwidth||e.level.bitrate;return t<=1e5?"Slides":t<=8e5?"SD":"HD"},title:"Quality"},disableErrorScreen:!0,errorPlugin:{onError:r}};return i&&MediaSource.isTypeSupported('video/webm; codecs="vp9,opus"')?(a.source={source:`//cdn.c3voc.de/dash/${e}/manifest.mpd`},a.shakaConfiguration={preferredAudioLanguage:n,abr:{defaultBandwidthEstimate:1e6},streaming:{jumpLargeGaps:!0},manifest:{dash:{defaultPresentationDelay:3,ignoreSuggestedPresentationDelay:!0}}}):t||!i&&""==document.createElement("video").canPlayType("application/vnd.apple.mpegURL")?a.source=t?{source:`//cdn.c3voc.de/${e}_native.mp3`,mimeType:"audio/mp3"}:{source:`//cdn.c3voc.de/${e}_native_hd.webm`,mimeType:"video/webm"}:a.source={source:`//cdn.c3voc.de/hls/${e}/native_hd.m3u8`,mimeType:"application/vnd.apple.mpegURL"},Promise.resolve(a)})(e.vocStream,e.audioOnly,e.preferredAudioLanguage,this._handleError.bind(this)):e.vocLecture&&(r=e.vocLecture,n=f(r).then(e=>({sources:e,levelSelectorConfig:{labelCallback:function(e,t){console.log("labelCallback",arguments);var n=e.videoBandwidth||e.level.bitrate;return n<=1e5?"Slides":n<=8e5?"SD":"HD"},title:"Quality"}})).catch(e=>(console.log("Failed to fetch media sources",e),{playbackNotSupportedMessage:""+e.message}))),n.then(n=>Object.assign({width:"100%",height:"100%",hideMediaControlDelay:1e3,position:"top-left",watermark:"data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20xmlns%3Adc%3D%22http%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%22%0A%20%20%20xmlns%3Acc%3D%22http%3A%2F%2Fcreativecommons.org%2Fns%23%22%0A%20%20%20xmlns%3Ardf%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20id%3D%22svg4568%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20viewBox%3D%220%200%2026.458318%2026.458333%22%0A%20%20%20height%3D%22100%22%0A%20%20%20width%3D%22100%22%3E%0A%20%20%3Cmetadata%0A%20%20%20%20%20id%3D%22metadata4574%22%3E%0A%20%20%20%20%3Crdf%3ARDF%3E%0A%20%20%20%20%20%20%3Ccc%3AWork%0A%20%20%20%20%20%20%20%20%20rdf%3Aabout%3D%22%22%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Aformat%3Eimage%2Fsvg%2Bxml%3C%2Fdc%3Aformat%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Atype%0A%20%20%20%20%20%20%20%20%20%20%20rdf%3Aresource%3D%22http%3A%2F%2Fpurl.org%2Fdc%2Fdcmitype%2FStillImage%22%20%2F%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Atitle%3E%3C%2Fdc%3Atitle%3E%0A%20%20%20%20%20%20%3C%2Fcc%3AWork%3E%0A%20%20%20%20%3C%2Frdf%3ARDF%3E%0A%20%20%3C%2Fmetadata%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs4572%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20style%3D%22fill%3A%23ffffff%3Bfill-opacity%3A0.8627451%3Bstroke-width%3A0.79176539%22%0A%20%20%20%20%20id%3D%22path4566%22%0A%20%20%20%20%20d%3D%22m%2012.91039%2C7.1445417%20c%20-5.6690402%2C0%20-10.2660306%2C4.5961993%20-10.2660306%2C10.2652403%20a%2010.265238%2C10.265238%200%200%200%200.6706253%2C3.63816%20c%200.1504354%2C-0.393506%200.3230402%2C-0.777512%200.539984%2C-1.138557%200.1108472%2C-0.18369%200.2327789%2C-0.361837%200.313539%2C-0.559779%200.081551%2C-0.197941%200.1187648%2C-0.42201%200.057007%2C-0.627078%20A%201.0783844%2C1.0783844%200%200%200%204.0513264%2C18.402655%20C%203.9784841%2C18.305267%203.8993075%2C18.212631%203.833591%2C18.109702%20A%201.3159141%2C1.3159141%200%200%201%203.6316909%2C17.222924%201.1876481%2C1.1876481%200%200%201%204.0909148%2C16.441452%20c%200.2216944%2C-0.164688%200.5027709%2C-0.23753%200.7798889%2C-0.228028%20a%201.3911318%2C1.3911318%200%200%201%200.2739508%2C0.03801%20c%200.3578779%2C0.08709%200.6745841%2C0.311164%200.9089467%2C0.593824%200.2913696%2C0.353919%200.462391%2C0.799683%200.5043546%2C1.25574%20a%202.8297696%2C2.8297696%200%200%201%20-0.2177355%2C1.34046%20C%206.0236142%2C20.197593%205.42979%2C20.807252%205.1012074%2C21.557847%204.8335907%2C22.171464%204.7567894%2C22.854758%204.7924189%2C23.5238%20c%200.00554%2C0.06096%200.014251%2C0.122723%200.019794%2C0.185272%20a%2010.265238%2C10.265238%200%200%200%202.866191%2C2.517815%2013.958824%2C13.958824%200%200%201%20-0.1029298%2C-1.670626%20c%200.00161%2C-0.443389%200.023751%2C-0.886777%200.067304%2C-1.326206%20C%207.3735785%2C23.043191%207.1281312%2C22.826248%206.9539421%2C22.54913%206.6435705%2C22.058235%206.5849797%2C21.434324%206.712454%2C20.867421%206.8391365%2C20.299724%207.1400069%2C19.782702%207.5018439%2C19.327437%20A%206.2018984%2C6.2018984%200%200%201%209.7464993%2C17.567343%208.1591425%2C8.1591425%200%200%201%209.6752356%2C17.46758%203.8432293%2C3.8432293%200%200%201%209.1194163%2C15.744698%20c%20-0.047503%2C-0.615201%200.050669%2C-1.230403%200.091055%2C-1.844814%200.02929%2C-0.429928%200.031672%2C-0.859064%200.055423%2C-1.288201%200.030084%2C-0.496437%200.090261%2C-0.993667%200.2296124%2C-1.471101%20a%201.8685664%2C1.8685664%200%200%201%200.21061%2C-0.498812%201.1084716%2C1.1084716%200%200%201%200.4703083%2C-0.406968%20c%200.216945%2C-0.0855%200.403801%2C-0.104512%200.614411%2C-0.06888%200.174189%2C0.03642%200.3365%2C0.123516%200.478227%2C0.232779%200.142518%2C0.109264%200.26524%2C0.240698%200.380047%2C0.377673%20a%205.7783039%2C5.7783039%200%200%201%200.823436%2C1.280285%202.8266025%2C2.8266025%200%200%201%200.863024%2C0%205.7695944%2C5.7695944%200%200%201%200.823436%2C-1.279493%202.5415669%2C2.5415669%200%200%201%200.380047%2C-0.376881%20c%200.142518%2C-0.110847%200.303246%2C-0.196358%200.478227%2C-0.23357%20a%201.0039585%2C1.0039585%200%200%201%200.617577%2C0.06809%201.1100551%2C1.1100551%200%200%201%200.4711%2C0.409343%20c%200.09818%2C0.150436%200.159936%2C0.323041%200.21061%2C0.495645%200.138558%2C0.476643%200.199525%2C0.973872%200.229612%2C1.469517%200.02375%2C0.429928%200.02612%2C0.860649%200.05542%2C1.288995%200.0396%2C0.615201%200.138559%2C1.230403%200.09185%2C1.844813%20a%203.844021%2C3.844021%200%200%201%20-0.558194%2C1.724465%20c%20-0.02375%2C0.0396%20-0.05542%2C0.076%20-0.08154%2C0.114805%200.195565%2C0.220111%200.473476%2C0.406176%200.711006%2C0.486144%20a%201.5486932%2C1.5486932%200%200%200%200.831353%2C0.03484%201.3064129%2C1.3064129%200%200%200%200.42201%2C-0.17894%200.99208205%2C0.99208205%200%200%200%200.314331%2C-0.330957%20c%200.09343%2C-0.166272%200.131433%2C-0.356295%200.152019%2C-0.543944%200.05305%2C-0.482977%200%2C-0.96912%20-0.0396%2C-1.452889%20-0.02692%2C-0.313539%20-0.04909%2C-0.633412%200.02138%2C-0.939826%200.0475%2C-0.205858%200.140142%2C-0.410133%200.295328%2C-0.554235%20a%200.87569253%2C0.87569253%200%200%201%200.36817%2C-0.2019%201.0387963%2C1.0387963%200%200%201%200.419637%2C-0.02375%20c%200.30087%2C0.04514%200.582739%2C0.216942%200.750593%2C0.470308%200.09502%2C0.142517%200.153603%2C0.308788%200.18844%2C0.478226%200.03484%2C0.168646%200.0475%2C0.340459%200.05701%2C0.513064%200.03167%2C0.601741%200.03167%2C1.205067%200.01426%2C1.808392%20-0.01426%2C0.526524%20-0.04355%2C1.0673%20-0.253366%2C1.549486%20-0.271575%2C0.619159%20-0.817101%2C1.08155%20-1.405383%2C1.414092%20a%205.5835296%2C5.5835296%200%200%201%20-1.257323%2C0.512272%20c%200.38163%2C1.219319%200.580363%2C2.56532%200.580363%2C3.93349%20a%2013.935071%2C13.935071%200%200%201%20-0.106901%2C1.682498%2010.264446%2C10.264446%200%200%200%205.054631%2C-8.829768%20c%200%2C-5.669041%20-4.59699%2C-10.2652391%20-10.265238%2C-10.2652391%20z%20M%2010.037865%2C19.798537%20v%201.174188%20a%201.488519%2C1.488519%200%200%201%200.857482%2C0.286619%201.3760882%2C1.3760882%200%200%201%200.440222%2C0.538402%20c%200.0966%2C0.213775%200.131432%2C0.456056%200.09184%2C0.687252%20a%201.1821057%2C1.1821057%200%200%201%20-0.262867%2C0.560568%201.3040376%2C1.3040376%200%200%201%20-0.502772%2C0.36263%201.3760882%2C1.3760882%200%200%201%20-0.623119%2C0.0966%20v%202.953287%20l%206.145683%2C-3.33175%20-6.145683%2C-3.328583%20z%22%20%2F%3E%0A%3C%2Fsvg%3E",watermarkLink:"https://c3voc.de",levelSelectorConfig:{labelCallback:function(e){let t="unknown";return e.height?t=e.height:e.level&&e.level.height&&(t=e.level.height),t+"p"},title:"Quality"},audioTrackSelectorConfig:{title:"Language"}},n,e,{plugins:t}))}_containerChanged(){this.stopListening(),this._addEventListeners()}_addEventListeners(){const e=this._player.core;this._container=e.activeContainer,this.listenTo(this._player,r.Events.PLAYER_PLAY,this._handlePlay),this.listenTo(this._player,r.Events.PLAYER_STOP,this._handleStop),this.listenTo(e,r.Events.CORE_ACTIVE_CONTAINER_CHANGED,this._containerChanged),this.listenTo(this._container,r.Events.CONTAINER_STATE_BUFFERFULL,this._handleBufferFull),this.listenTo(this._container,r.Events.CONTAINER_MEDIACONTROL_HIDE,this._handleMediaControlHide),this.listenTo(this._container,r.Events.CONTAINER_MEDIACONTROL_SHOW,this._handleMediaControlShow)}_handleMediaControlHide(){this._container.$el.find(".clappr-watermark[data-watermark]").addClass("clappr-watermark-hide")}_handleMediaControlShow(){this._container.$el.find(".clappr-watermark[data-watermark]").removeClass("clappr-watermark-hide")}_getTimeout(){const e=.6*this.timeout+.4*this.timeout*Math.random();return this.timeout=Math.min(2*this.timeout,this.maxTimeout),e}_resetTimeout(){this.timeout=5}_handleError(e,t){this._recovery?clearTimeout(this._recovery.timeout):this._player.stop();const n=this._getTimeout();return console.log("got error",e,`retrying in ${Math.round(n)}s`),this._recovery={clearOverlay:t,state:"restarting",timeout:setTimeout(this._waitForMedia.bind(this),1e3*n)},h(e,1001)||p(e,404)?{title:"Stream is offline",subtitle:"We will be right back"}:h(e,1002)||p(0)?{title:"A network error ocurred",subtitle:"Please check your internet connection"}:{title:"Oh no, an unknown error occured",subtitle:"Please try reloading the page"}}_handlePlay(){this._recovery&&(console.log("soft recovery: play"),this._recovery.clearOverlay(),clearTimeout(this._recovery.timeout),this._recovery=null),this._resetTimeout()}_handleStop(e){this._recovery&&this._container&&(console.log("soft recovery: stop"),this._container.playback.play.call(this._container.playback))}_handleBufferFull(){if(this._recovery){console.log("seeking to end for recovery");const e=Math.max(this._player.getDuration()-6,0);this._player.seek(e)}}_handleMediaCheck(e){if(e)console.log("try playing again, media should be available"),this._player.play();else{const e=this._getTimeout();console.log(`test for media failed, retrying in ~${Math.round(e)}s`),setTimeout(this._waitForMedia.bind(this),1e3*e)}}_waitForMedia(){let e=this._player.options.source;e&&e.source&&(e=e.source),"string"==typeof e?function(e,t){if(!t||"function"!=typeof t)throw new Error(`Excepted function, got '${t}'`);const n=new XMLHttpRequest;n.onreadystatechange=function(){this.readyState===XMLHttpRequest.HEADERS_RECEIVED&&(200===this.status?t(!0):t(!1),n.abort())},n.open("GET",e,!0),n.send(null)}(e,this._handleMediaCheck.bind(this)):this.reset()}reset(){console.log("performing hard reset"),this._recovery=null;const e=0==this._player.getVolume();e||this._player.mute(),this._player.configure({source:this._player.options.source,autoPlay:!0}),e||this._player.unmute()}}}])}));
+/*! no static exports found */function(module,exports){eval("module.exports = __WEBPACK_EXTERNAL_MODULE_clappr__;\n\n//# sourceURL=webpack://AudioTrackSelector/external_%7B%22amd%22:%22clappr%22,%22commonjs%22:%22clappr%22,%22commonjs2%22:%22clappr%22,%22root%22:%22Clappr%22%7D?")}}).default},module.exports=factory(__webpack_require__(0))},function(e,t,n){var r=n(5),i=n(6);"string"==typeof(i=i.__esModule?i.default:i)&&(i=[[e.i,i,""]]);var a={insert:"head",singleton:!1};r(i,a);e.exports=i.locals||{}},function(e,t,n){"use strict";var r,i=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},a=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),o=[];function s(e){for(var t=-1,n=0;n<o.length;n++)if(o[n].identifier===e){t=n;break}return t}function l(e,t){for(var n={},r=[],i=0;i<e.length;i++){var a=e[i],l=t.base?a[0]+t.base:a[0],u=n[l]||0,c="".concat(l," ").concat(u);n[l]=u+1;var d=s(c),f={css:a[1],media:a[2],sourceMap:a[3]};-1!==d?(o[d].references++,o[d].updater(f)):o.push({identifier:c,updater:g(f,t),references:1}),r.push(c)}return r}function u(e){var t=document.createElement("style"),r=e.attributes||{};if(void 0===r.nonce){var i=n.nc;i&&(r.nonce=i)}if(Object.keys(r).forEach((function(e){t.setAttribute(e,r[e])})),"function"==typeof e.insert)e.insert(t);else{var o=a(e.insert||"head");if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(t)}return t}var c,d=(c=[],function(e,t){return c[e]=t,c.filter(Boolean).join("\n")});function f(e,t,n,r){var i=n?"":r.media?"@media ".concat(r.media," {").concat(r.css,"}"):r.css;if(e.styleSheet)e.styleSheet.cssText=d(t,i);else{var a=document.createTextNode(i),o=e.childNodes;o[t]&&e.removeChild(o[t]),o.length?e.insertBefore(a,o[t]):e.appendChild(a)}}function h(e,t,n){var r=n.css,i=n.media,a=n.sourceMap;if(i?e.setAttribute("media",i):e.removeAttribute("media"),a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleSheet)e.styleSheet.cssText=r;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(r))}}var p=null,m=0;function g(e,t){var n,r,i;if(t.singleton){var a=m++;n=p||(p=u(t)),r=f.bind(null,n,a,!1),i=f.bind(null,n,a,!0)}else n=u(t),r=h.bind(null,n,t),i=function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)};return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else i()}}e.exports=function(e,t){(t=t||{}).singleton||"boolean"==typeof t.singleton||(t.singleton=i());var n=l(e=e||[],t);return function(e){if(e=e||[],"[object Array]"===Object.prototype.toString.call(e)){for(var r=0;r<n.length;r++){var i=s(n[r]);o[i].references--}for(var a=l(e,t),u=0;u<n.length;u++){var c=s(n[u]);0===o[c].references&&(o[c].updater(),o.splice(c,1))}n=a}}}},function(e,t,n){(t=n(7)(!1)).push([e.i,"button.media-control-button[data-hd-indicator]{display:none !important}.media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar]{height:40px}.media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-background[data-seekbar]{height:2px;background-color:#ccc}.player-poster[data-poster] .play-wrapper[data-poster] svg path{fill:#ccc}.spinner-three-bounce[data-spinner]>div{background-color:#ccc}.clappr-watermark[data-watermark]{transition:opacity .5s ease-out;width:8%;min-width:50px;max-width:100px}.clappr-watermark[data-watermark].clappr-watermark-hide{opacity:0}.clappr-watermark[data-watermark-top-left]{top:0px;left:15px;text-align:left}@media(min-width: 768px){.clappr-watermark[data-watermark-top-left]{top:10px;left:30px}}",""]),e.exports=t},function(e,t,n){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=(o=r,s=btoa(unescape(encodeURIComponent(JSON.stringify(o)))),l="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(s),"/*# ".concat(l," */")),a=r.sources.map((function(e){return"/*# sourceURL=".concat(r.sourceRoot||"").concat(e," */")}));return[n].concat(a).concat([i]).join("\n")}var o,s,l;return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n})).join("")},t.i=function(e,n,r){"string"==typeof e&&(e=[[null,e,""]]);var i={};if(r)for(var a=0;a<this.length;a++){var o=this[a][0];null!=o&&(i[o]=!0)}for(var s=0;s<e.length;s++){var l=[].concat(e[s]);r&&i[l[0]]||(n&&(l[2]?l[2]="".concat(n," and ").concat(l[2]):l[2]=n),t.push(l))}},t}},function(e,t,n){"use strict";n.r(t),n.d(t,"Player",(function(){return L})),n.d(t,"Mediator",(function(){return r.Mediator})),n.d(t,"Events",(function(){return r.Events})),n.d(t,"Browser",(function(){return r.Browser})),n.d(t,"PlayerInfo",(function(){return r.PlayerInfo})),n.d(t,"MediaControl",(function(){return r.MediaControl})),n.d(t,"ContainerPlugin",(function(){return r.ContainerPlugin})),n.d(t,"UIContainerPlugin",(function(){return r.UIContainerPlugin})),n.d(t,"CorePlugin",(function(){return r.CorePlugin})),n.d(t,"UICorePlugin",(function(){return r.UICorePlugin})),n.d(t,"Playback",(function(){return r.Playback})),n.d(t,"Container",(function(){return r.Container})),n.d(t,"Core",(function(){return r.Core})),n.d(t,"PlayerError",(function(){return r.PlayerError})),n.d(t,"Loader",(function(){return r.Loader})),n.d(t,"BaseObject",(function(){return r.BaseObject})),n.d(t,"UIObject",(function(){return r.UIObject})),n.d(t,"Utils",(function(){return r.Utils})),n.d(t,"BaseFlashPlayback",(function(){return r.BaseFlashPlayback})),n.d(t,"Flash",(function(){return r.Flash})),n.d(t,"FlasHLS",(function(){return r.FlasHLS})),n.d(t,"HLS",(function(){return r.HLS})),n.d(t,"HTML5Audio",(function(){return r.HTML5Audio})),n.d(t,"HTML5Video",(function(){return r.HTML5Video})),n.d(t,"HTMLImg",(function(){return r.HTMLImg})),n.d(t,"NoOp",(function(){return r.NoOp})),n.d(t,"ClickToPausePlugin",(function(){return r.ClickToPausePlugin})),n.d(t,"DVRControls",(function(){return r.DVRControls})),n.d(t,"Favicon",(function(){return r.Favicon})),n.d(t,"Log",(function(){return r.Log})),n.d(t,"Poster",(function(){return r.Poster})),n.d(t,"SpinnerThreeBouncePlugin",(function(){return r.SpinnerThreeBouncePlugin})),n.d(t,"WaterMarkPlugin",(function(){return r.WaterMarkPlugin})),n.d(t,"Styler",(function(){return r.Styler})),n.d(t,"Vendor",(function(){return r.Vendor})),n.d(t,"version",(function(){return r.version})),n.d(t,"template",(function(){return r.template})),n.d(t,"$",(function(){return r.$}));var r=n(0),i=n.n(r),a=n(1),o=n.n(a),s=n(2),l=n.n(s),u=n(3),c=n.n(u);n(4);function d(e){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function f(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function h(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function p(e,t,n){return t&&h(e.prototype,t),n&&h(e,n),e}function m(e,t){return(m=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function g(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=y(e);if(t){var i=y(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return v(this,n)}}function v(e,t){return!t||"object"!==d(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function y(e){return(y=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var b=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&m(e,t)}(n,e);var t=g(n);function n(){var e;f(this,n);for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];return(e=t.call.apply(t,[this].concat(i))).timeout=1,e.max_timeout=10,e}return p(n,[{key:"name",get:function(){return"error_plugin"}},{key:"background",get:function(){return"data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%20viewBox%3D%220%200%2026.458318%2026.458333%22%3E%3Cpath%20d%3D%22M13.23.302C6.07.302.264%206.107.264%2013.267a12.965%2012.965%200%200%200%20.847%204.595c.19-.497.408-.982.682-1.438.14-.232.294-.457.396-.707.103-.25.15-.533.072-.792a1.362%201.362%200%200%200-.22-.404c-.092-.123-.192-.24-.275-.37a1.662%201.662%200%200%201-.255-1.12%201.5%201.5%200%200%201%20.58-.987c.28-.208.635-.3.985-.288a1.757%201.757%200%200%201%20.346.048c.452.11.852.393%201.148.75.368.447.584%201.01.637%201.586a3.574%203.574%200%200%201-.275%201.693c-.4.955-1.15%201.725-1.565%202.673-.338.775-.435%201.638-.39%202.483.007.077.018.155.025.234a12.965%2012.965%200%200%200%203.62%203.18%2017.63%2017.63%200%200%201-.13-2.11c.002-.56.03-1.12.085-1.675-.34-.236-.65-.51-.87-.86-.392-.62-.466-1.408-.305-2.124.16-.717.54-1.37.997-1.945a7.833%207.833%200%200%201%202.835-2.223%2010.305%2010.305%200%200%201-.09-.126%204.854%204.854%200%200%201-.702-2.176c-.06-.777.064-1.554.115-2.33.037-.543.04-1.085.07-1.627.038-.627.114-1.255.29-1.858a2.36%202.36%200%200%201%20.266-.63%201.4%201.4%200%200%201%20.594-.514c.274-.108.51-.132.776-.087.22.046.425.156.604.294.18.138.335.304.48.477a7.298%207.298%200%200%201%201.04%201.617%203.57%203.57%200%200%201%201.09%200%207.287%207.287%200%200%201%201.04-1.616%203.21%203.21%200%200%201%20.48-.476c.18-.14.383-.248.604-.295a1.268%201.268%200%200%201%20.78.086%201.402%201.402%200%200%201%20.595.517c.124.19.202.408.266.626.175.602.252%201.23.29%201.856.03.543.033%201.087.07%201.628.05.777.175%201.554.116%202.33a4.855%204.855%200%200%201-.705%202.178c-.03.05-.07.096-.103.145.247.278.598.513.898.614a1.956%201.956%200%200%200%201.05.044%201.65%201.65%200%200%200%20.533-.226%201.253%201.253%200%200%200%20.397-.418c.118-.21.166-.45.192-.687.067-.61%200-1.224-.05-1.835-.034-.396-.062-.8.027-1.187.06-.26.177-.518.373-.7a1.106%201.106%200%200%201%20.465-.255%201.312%201.312%200%200%201%20.53-.03c.38.057.736.274.948.594.12.18.194.39.238.604.044.213.06.43.072.648.04.76.04%201.522.018%202.284-.018.665-.055%201.348-.32%201.957-.343.782-1.032%201.366-1.775%201.786a7.052%207.052%200%200%201-1.588.647c.482%201.54.733%203.24.733%204.968a17.6%2017.6%200%200%201-.135%202.125%2012.964%2012.964%200%200%200%206.384-11.152c0-7.16-5.806-12.965-12.965-12.965zM9.602%2016.284v1.483a1.88%201.88%200%200%201%201.083.362%201.738%201.738%200%200%201%20.556.68c.122.27.166.576.116.868a1.493%201.493%200%200%201-.332.708%201.647%201.647%200%200%201-.635.458%201.738%201.738%200%200%201-.787.122v3.73l7.762-4.208-7.762-4.204z%22%20fill%3D%22%23999%22%2F%3E%3C%2Fsvg%3E"}}]),p(n,[{key:"bindEvents",value:function(){this.listenTo(this.container,i.a.Events.CONTAINER_ERROR,this.onError)}},{key:"hide",value:function(){this._err&&this._err.remove()}},{key:"show",value:function(e){var t=i.a.$;this.hide();var n=e&&e.title||"Oh no, we encountered an error",r=e&&e.subtitle||"Please reload the page";this._err=t("<div>").css({position:"absolute","z-index":"999",width:"100%",height:"100%","background-image":"url("+this.background+")","background-size":"18%","background-repeat":"no-repeat","background-color":"black","background-position":"center","text-align":"center","font-weight":"bold",color:"#eee"});var a=t("<div>").css({position:"absolute",width:"100%","padding-bottom":"5%",bottom:0}).append(t("<h2>").text(n).css({"font-size":"2em"})).append(t("<p>").text(r).css({"font-size":"1.2em",margin:"15px"}));this._err.append(a),this.container&&this.container.$el.prepend(this._err)}},{key:"onError",value:function(e){var t=this;if(this.container){var n=this.options.errorPlugin.onError,r=null;n&&"function"==typeof n&&(r=n(e,(function(){t.hide(),t.container.getPlugin("click_to_pause").enable()}))),this.show(r),this.container.getPlugin("click_to_pause").disable()}}}]),n}(i.a.ContainerPlugin),_=function(e){return function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};new Headers;return fetch("https://media.ccc.de/graphql",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({operationName:e,query:t,variables:n})}).then((function(e){var t=e.body.getReader(),n="",r=new TextDecoder("utf-8");return t.read().then((function e(i){var a=i.done,o=i.value;return a?JSON.parse(n):(n+=r.decode(o),t.read().then(e))}))}))}("LectureBySlug",'\n    query LectureBySlug {\n      lectureBySlug(slug: "'.concat(e,'") {\n        originalLanguage\n        videos {\n          label\n          url\n          mimeType\n        }\n      }\n    }\n  ')).then((function(e){if(!e.data.lectureBySlug)throw new Error("Lecture could not be found");return e.data.lectureBySlug.videos.map((function(e){var t=e.label,n=e.url;return{label:t,mimeType:e.mimeType,source:n}}))}))};function A(e){return(A="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function E(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function T(e,t){return(T=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function w(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=C(e);if(t){var i=C(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return S(this,n)}}function S(e,t){return!t||"object"!==A(t)&&"function"!=typeof t?k(e):t}function k(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function C(e){return(C=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var x=function(e,t){return"dash_shaka_playback"==e.origin&&(e.raw.code==t||e.raw.detail&&e.raw.detail.code==t)},R=function(e,t){return"hls"==e.origin&&e.raw.response.code==t},L=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&T(e,t)}(s,e);var t,n,i,a=w(s);function s(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,s),(t=a.call(this)).timeout=5,t.maxTimeout=15,console.log("initializing player with",e,t._options),t._playerPromise=t._getConfig(e).then((function(e){return t._options=e,t._player=new r.Player(t._options),t._player.core&&t._player.core.isReady?t._addEventListeners():t.listenToOnce(t._player,r.Events.PLAYER_READY,t._addEventListeners.bind(k(t))),t._player})),t}return t=s,(n=[{key:"attachTo",value:function(){var e=arguments;this._playerPromise.then((function(t){t.attachTo.apply(t,e)}))}},{key:"_getConfig",value:function(e){var t=[c.a,l.a,o.a,b];e.plugins&&e.plugins.length&&(t=t.concat(e.plugins),console.log("loading plugins"),t.forEach((function(e){return console.log(e.name,e.type)})));var n,r,i,a,s,u,d,f=Promise.resolve({});return e.vocStream?(r=e.vocStream,i=e.audioOnly,a=e.preferredAudioLanguage,s=this._handleError.bind(this),u="MediaSource"in window,d={levelSelectorConfig:{labelCallback:function(e){var t=e.videoBandwidth||e.level.bitrate;return t<=1e5?"Slides":t<=8e5?"SD":"HD"},title:"Quality"},disableErrorScreen:!0,errorPlugin:{onError:s}},-1==navigator.userAgent.indexOf("Firefox")&&u&&MediaSource.isTypeSupported('video/webm; codecs="vp9,opus"')?(d.source={source:"//cdn.c3voc.de/dash/".concat(r,"/manifest.mpd")},d.shakaConfiguration={preferredAudioLanguage:a,abr:{defaultBandwidthEstimate:1e6},streaming:{jumpLargeGaps:!0},manifest:{dash:{defaultPresentationDelay:3,ignoreSuggestedPresentationDelay:!0}}}):i||!u&&""==document.createElement("video").canPlayType("application/vnd.apple.mpegURL")?d.source=i?{source:"//cdn.c3voc.de/".concat(r,"_native.mp3"),mimeType:"audio/mp3"}:{source:"//cdn.c3voc.de/".concat(r,"_native_hd.webm"),mimeType:"video/webm"}:d.source={source:"//cdn.c3voc.de/hls/".concat(r,"/native_hd.m3u8"),mimeType:"application/vnd.apple.mpegURL"},f=Promise.resolve(d)):e.vocLecture&&(n=e.vocLecture,f=_(n).then((function(e){return{sources:e,levelSelectorConfig:{labelCallback:function(e,t){console.log("labelCallback",arguments);var n=e.videoBandwidth||e.level.bitrate;return n<=1e5?"Slides":n<=8e5?"SD":"HD"},title:"Quality"}}})).catch((function(e){return console.log("Failed to fetch media sources",e),{playbackNotSupportedMessage:"".concat(e.message)}}))),f.then((function(n){console.log("sourceConfig",n,e);var r=Object.assign({width:"100%",height:"100%",hideMediaControlDelay:1e3,position:"top-left",watermark:"data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20xmlns%3Adc%3D%22http%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%22%0A%20%20%20xmlns%3Acc%3D%22http%3A%2F%2Fcreativecommons.org%2Fns%23%22%0A%20%20%20xmlns%3Ardf%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20id%3D%22svg4568%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20viewBox%3D%220%200%2026.458318%2026.458333%22%0A%20%20%20height%3D%22100%22%0A%20%20%20width%3D%22100%22%3E%0A%20%20%3Cmetadata%0A%20%20%20%20%20id%3D%22metadata4574%22%3E%0A%20%20%20%20%3Crdf%3ARDF%3E%0A%20%20%20%20%20%20%3Ccc%3AWork%0A%20%20%20%20%20%20%20%20%20rdf%3Aabout%3D%22%22%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Aformat%3Eimage%2Fsvg%2Bxml%3C%2Fdc%3Aformat%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Atype%0A%20%20%20%20%20%20%20%20%20%20%20rdf%3Aresource%3D%22http%3A%2F%2Fpurl.org%2Fdc%2Fdcmitype%2FStillImage%22%20%2F%3E%0A%20%20%20%20%20%20%20%20%3Cdc%3Atitle%3E%3C%2Fdc%3Atitle%3E%0A%20%20%20%20%20%20%3C%2Fcc%3AWork%3E%0A%20%20%20%20%3C%2Frdf%3ARDF%3E%0A%20%20%3C%2Fmetadata%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs4572%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20style%3D%22fill%3A%23ffffff%3Bfill-opacity%3A0.8627451%3Bstroke-width%3A0.79176539%22%0A%20%20%20%20%20id%3D%22path4566%22%0A%20%20%20%20%20d%3D%22m%2012.91039%2C7.1445417%20c%20-5.6690402%2C0%20-10.2660306%2C4.5961993%20-10.2660306%2C10.2652403%20a%2010.265238%2C10.265238%200%200%200%200.6706253%2C3.63816%20c%200.1504354%2C-0.393506%200.3230402%2C-0.777512%200.539984%2C-1.138557%200.1108472%2C-0.18369%200.2327789%2C-0.361837%200.313539%2C-0.559779%200.081551%2C-0.197941%200.1187648%2C-0.42201%200.057007%2C-0.627078%20A%201.0783844%2C1.0783844%200%200%200%204.0513264%2C18.402655%20C%203.9784841%2C18.305267%203.8993075%2C18.212631%203.833591%2C18.109702%20A%201.3159141%2C1.3159141%200%200%201%203.6316909%2C17.222924%201.1876481%2C1.1876481%200%200%201%204.0909148%2C16.441452%20c%200.2216944%2C-0.164688%200.5027709%2C-0.23753%200.7798889%2C-0.228028%20a%201.3911318%2C1.3911318%200%200%201%200.2739508%2C0.03801%20c%200.3578779%2C0.08709%200.6745841%2C0.311164%200.9089467%2C0.593824%200.2913696%2C0.353919%200.462391%2C0.799683%200.5043546%2C1.25574%20a%202.8297696%2C2.8297696%200%200%201%20-0.2177355%2C1.34046%20C%206.0236142%2C20.197593%205.42979%2C20.807252%205.1012074%2C21.557847%204.8335907%2C22.171464%204.7567894%2C22.854758%204.7924189%2C23.5238%20c%200.00554%2C0.06096%200.014251%2C0.122723%200.019794%2C0.185272%20a%2010.265238%2C10.265238%200%200%200%202.866191%2C2.517815%2013.958824%2C13.958824%200%200%201%20-0.1029298%2C-1.670626%20c%200.00161%2C-0.443389%200.023751%2C-0.886777%200.067304%2C-1.326206%20C%207.3735785%2C23.043191%207.1281312%2C22.826248%206.9539421%2C22.54913%206.6435705%2C22.058235%206.5849797%2C21.434324%206.712454%2C20.867421%206.8391365%2C20.299724%207.1400069%2C19.782702%207.5018439%2C19.327437%20A%206.2018984%2C6.2018984%200%200%201%209.7464993%2C17.567343%208.1591425%2C8.1591425%200%200%201%209.6752356%2C17.46758%203.8432293%2C3.8432293%200%200%201%209.1194163%2C15.744698%20c%20-0.047503%2C-0.615201%200.050669%2C-1.230403%200.091055%2C-1.844814%200.02929%2C-0.429928%200.031672%2C-0.859064%200.055423%2C-1.288201%200.030084%2C-0.496437%200.090261%2C-0.993667%200.2296124%2C-1.471101%20a%201.8685664%2C1.8685664%200%200%201%200.21061%2C-0.498812%201.1084716%2C1.1084716%200%200%201%200.4703083%2C-0.406968%20c%200.216945%2C-0.0855%200.403801%2C-0.104512%200.614411%2C-0.06888%200.174189%2C0.03642%200.3365%2C0.123516%200.478227%2C0.232779%200.142518%2C0.109264%200.26524%2C0.240698%200.380047%2C0.377673%20a%205.7783039%2C5.7783039%200%200%201%200.823436%2C1.280285%202.8266025%2C2.8266025%200%200%201%200.863024%2C0%205.7695944%2C5.7695944%200%200%201%200.823436%2C-1.279493%202.5415669%2C2.5415669%200%200%201%200.380047%2C-0.376881%20c%200.142518%2C-0.110847%200.303246%2C-0.196358%200.478227%2C-0.23357%20a%201.0039585%2C1.0039585%200%200%201%200.617577%2C0.06809%201.1100551%2C1.1100551%200%200%201%200.4711%2C0.409343%20c%200.09818%2C0.150436%200.159936%2C0.323041%200.21061%2C0.495645%200.138558%2C0.476643%200.199525%2C0.973872%200.229612%2C1.469517%200.02375%2C0.429928%200.02612%2C0.860649%200.05542%2C1.288995%200.0396%2C0.615201%200.138559%2C1.230403%200.09185%2C1.844813%20a%203.844021%2C3.844021%200%200%201%20-0.558194%2C1.724465%20c%20-0.02375%2C0.0396%20-0.05542%2C0.076%20-0.08154%2C0.114805%200.195565%2C0.220111%200.473476%2C0.406176%200.711006%2C0.486144%20a%201.5486932%2C1.5486932%200%200%200%200.831353%2C0.03484%201.3064129%2C1.3064129%200%200%200%200.42201%2C-0.17894%200.99208205%2C0.99208205%200%200%200%200.314331%2C-0.330957%20c%200.09343%2C-0.166272%200.131433%2C-0.356295%200.152019%2C-0.543944%200.05305%2C-0.482977%200%2C-0.96912%20-0.0396%2C-1.452889%20-0.02692%2C-0.313539%20-0.04909%2C-0.633412%200.02138%2C-0.939826%200.0475%2C-0.205858%200.140142%2C-0.410133%200.295328%2C-0.554235%20a%200.87569253%2C0.87569253%200%200%201%200.36817%2C-0.2019%201.0387963%2C1.0387963%200%200%201%200.419637%2C-0.02375%20c%200.30087%2C0.04514%200.582739%2C0.216942%200.750593%2C0.470308%200.09502%2C0.142517%200.153603%2C0.308788%200.18844%2C0.478226%200.03484%2C0.168646%200.0475%2C0.340459%200.05701%2C0.513064%200.03167%2C0.601741%200.03167%2C1.205067%200.01426%2C1.808392%20-0.01426%2C0.526524%20-0.04355%2C1.0673%20-0.253366%2C1.549486%20-0.271575%2C0.619159%20-0.817101%2C1.08155%20-1.405383%2C1.414092%20a%205.5835296%2C5.5835296%200%200%201%20-1.257323%2C0.512272%20c%200.38163%2C1.219319%200.580363%2C2.56532%200.580363%2C3.93349%20a%2013.935071%2C13.935071%200%200%201%20-0.106901%2C1.682498%2010.264446%2C10.264446%200%200%200%205.054631%2C-8.829768%20c%200%2C-5.669041%20-4.59699%2C-10.2652391%20-10.265238%2C-10.2652391%20z%20M%2010.037865%2C19.798537%20v%201.174188%20a%201.488519%2C1.488519%200%200%201%200.857482%2C0.286619%201.3760882%2C1.3760882%200%200%201%200.440222%2C0.538402%20c%200.0966%2C0.213775%200.131432%2C0.456056%200.09184%2C0.687252%20a%201.1821057%2C1.1821057%200%200%201%20-0.262867%2C0.560568%201.3040376%2C1.3040376%200%200%201%20-0.502772%2C0.36263%201.3760882%2C1.3760882%200%200%201%20-0.623119%2C0.0966%20v%202.953287%20l%206.145683%2C-3.33175%20-6.145683%2C-3.328583%20z%22%20%2F%3E%0A%3C%2Fsvg%3E",watermarkLink:"https://c3voc.de",levelSelectorConfig:{labelCallback:function(e){var t="unknown";return e.height?t=e.height:e.level&&e.level.height&&(t=e.level.height),t+"p"},title:"Quality"},audioTrackSelectorConfig:{title:"Language"}},n,e,{plugins:t});return console.log("res",r),r}))}},{key:"_containerChanged",value:function(){this.stopListening(),this._addEventListeners()}},{key:"_addEventListeners",value:function(){var e=this._player.core;this._container=e.activeContainer,this.listenTo(this._player,r.Events.PLAYER_PLAY,this._handlePlay),this.listenTo(this._player,r.Events.PLAYER_STOP,this._handleStop),this.listenTo(e,r.Events.CORE_ACTIVE_CONTAINER_CHANGED,this._containerChanged),this.listenTo(this._container,r.Events.CONTAINER_STATE_BUFFERFULL,this._handleBufferFull),this.listenTo(this._container,r.Events.CONTAINER_MEDIACONTROL_HIDE,this._handleMediaControlHide),this.listenTo(this._container,r.Events.CONTAINER_MEDIACONTROL_SHOW,this._handleMediaControlShow)}},{key:"_handleMediaControlHide",value:function(){this._container.$el.find(".clappr-watermark[data-watermark]").addClass("clappr-watermark-hide")}},{key:"_handleMediaControlShow",value:function(){this._container.$el.find(".clappr-watermark[data-watermark]").removeClass("clappr-watermark-hide")}},{key:"_getTimeout",value:function(){var e=.6*this.timeout+.4*this.timeout*Math.random();return this.timeout=Math.min(2*this.timeout,this.maxTimeout),e}},{key:"_resetTimeout",value:function(){this.timeout=5}},{key:"_handleError",value:function(e,t){this._recovery?clearTimeout(this._recovery.timeout):this._player.stop();var n=this._getTimeout();return console.log("got error",e,"retrying in ".concat(Math.round(n),"s")),this._recovery={clearOverlay:t,state:"restarting",timeout:setTimeout(this._waitForMedia.bind(this),1e3*n)},x(e,1001)||R(e,404)?{title:"Stream is offline",subtitle:"We will be right back"}:x(e,1002)||R(0)?{title:"A network error ocurred",subtitle:"Please check your internet connection"}:{title:"Oh no, an unknown error occured",subtitle:"Please try reloading the page"}}},{key:"_handlePlay",value:function(){this._recovery&&(console.log("soft recovery: play"),this._recovery.clearOverlay(),clearTimeout(this._recovery.timeout),this._recovery=null),this._resetTimeout()}},{key:"_handleStop",value:function(e){this._recovery&&this._container&&(console.log("soft recovery: stop"),this._container.playback.play.call(this._container.playback))}},{key:"_handleBufferFull",value:function(){if(this._recovery){console.log("seeking to end for recovery");var e=Math.max(this._player.getDuration()-6,0);this._player.seek(e)}}},{key:"_handleMediaCheck",value:function(e){if(e)console.log("try playing again, media should be available"),this._player.play();else{var t=this._getTimeout();console.log("test for media failed, retrying in ~".concat(Math.round(t),"s")),setTimeout(this._waitForMedia.bind(this),1e3*t)}}},{key:"_waitForMedia",value:function(){var e=this._player.options.source;e&&e.source&&(e=e.source),"string"==typeof e?function(e,t){if(!t||"function"!=typeof t)throw new Error("Excepted function, got '".concat(t,"'"));var n=new XMLHttpRequest;n.onreadystatechange=function(){this.readyState===XMLHttpRequest.HEADERS_RECEIVED&&(200===this.status?t(!0):t(!1),n.abort())},n.open("GET",e,!0),n.send(null)}(e,this._handleMediaCheck.bind(this)):this.reset()}},{key:"reset",value:function(){console.log("performing hard reset"),this._recovery=null;var e=0==this._player.getVolume();e||this._player.mute(),this._player.configure({source:this._player.options.source,autoPlay:!0}),e||this._player.unmute()}}])&&E(t.prototype,n),i&&E(t,i),s}(r.BaseObject)}])}));
 //# sourceMappingURL=player.js.map
\ No newline at end of file
diff --git a/src/plainui/styles/_util-classes.scss b/src/plainui/styles/_util-classes.scss
index 0484e6b7b3f9fb522b3de71e55116a8c94f7a8ba..d6ed7204d11312108a6a7c9a2514e6efa8011c6c 100644
--- a/src/plainui/styles/_util-classes.scss
+++ b/src/plainui/styles/_util-classes.scss
@@ -92,8 +92,9 @@ h6,
 }
 
 h6,
-.h6 {
-    text-transform: unset;
+.h6,
+.text-transform-none {
+    text-transform: none;
 }
 
 .mw-664 {
@@ -121,6 +122,30 @@ h6,
     }
 }
 
+.grid-list {
+    display: grid;
+    grid-template-columns: 1fr 2fr;
+    grid-template-rows: auto;
+    grid-gap: map-get($spacers, 2);
+
+    &__item {
+        display: flex;
+        flex-flow: row nowrap;
+        box-shadow: $box-shadow-morphism;
+        font-family: $headings-font-family;
+        padding: map-get($spacers, 3);
+        align-items: center;
+
+        &--title {
+            justify-content: end;
+        }
+
+        &--text {
+            margin-bottom: 0;
+        }
+    }
+}
+
 .no-js .d-js-only {
     display: none !important;
 }
diff --git a/src/plainui/styles/components/_event-info.scss b/src/plainui/styles/components/_event-info.scss
index b5c7f9ab7867bf73665bdfe415f010731994c61b..429d6138fc99d11d7d8c7c0613a3ab03fa61f7fa 100644
--- a/src/plainui/styles/components/_event-info.scss
+++ b/src/plainui/styles/components/_event-info.scss
@@ -1,5 +1,24 @@
 .rc3-event-info {
-    display: grid;
-    grid-template-columns: 1fr 2fr;
-    grid-gap: 0.625rem;
+    display: flex;
+    flex-flow: column nowrap;
+
+    &__img {
+        object-fit: cover;
+    }
+
+    @include media-breakpoint-down("md") {
+        &__title-info {
+            order: 3;
+        }
+
+        &__list {
+            order: 4;
+        }
+    }
+
+    @include media-breakpoint-up("lg") {
+        display: grid;
+        grid-template-columns: 1fr 2fr;
+        grid-gap: map-get($spacers, 2);
+    }
 }
diff --git a/src/plainui/styles/components/_fahrplan.scss b/src/plainui/styles/components/_fahrplan.scss
index 7ecd1bf93eaefa0c226b453900ff2cc6b9a2017b..0ab60c42a5644f92cf795418114d58b9bb4106fb 100644
--- a/src/plainui/styles/components/_fahrplan.scss
+++ b/src/plainui/styles/components/_fahrplan.scss
@@ -1,11 +1,13 @@
+
 .rc3-fahrplan {
 
     display: flex;
-    overflow-x: auto;
+    overflow: auto;
     z-index: 5;
     font-size: 0.9em;
     background-image: repeating-linear-gradient(rgba(0,0,0, 0) 0 119px, rgba(255,255,255, 0.4) 120px);
     background-position: 0 -29px;
+    max-height: 75vh;
 
     &__link * {
         pointer-events: none;
@@ -29,6 +31,8 @@
         text-align: center;
         font-size: 1rem;
         margin-bottom: 15px !important;
+        position: sticky;
+        top: 0;
     }
 
     &__timeline {
@@ -86,3 +90,11 @@
 
 }
 
+.rc3-fahrplan__pub-body {
+    overflow: auto !important;
+}
+
+.rc3-fahrplan__pub-body .rc3-fahrplan {
+    overflow: visible;
+    max-height: none;
+}
diff --git a/src/plainui/styles/components/_header.scss b/src/plainui/styles/components/_header.scss
index 37baabc2018aa364d10cf79af60e9674e0496f1b..a73196a62c694121d9b362e2a178e3a9021dc42a 100644
--- a/src/plainui/styles/components/_header.scss
+++ b/src/plainui/styles/components/_header.scss
@@ -6,6 +6,10 @@
     grid-gap: var(--header-gridgap);
     font-family: $headings-font-family;
 
+    .rc3-logo img {
+        max-height: 100%;
+    }
+
     &__main {
         display: grid;
         grid-gap: var(--header-gridgap);
@@ -13,6 +17,7 @@
         grid-template-rows: 7rem 3rem;
         justify-self: center;
 
+
         &-linkbox {
             display: flex;
             align-items: center;
@@ -85,4 +90,46 @@
             line-height: 0;
         }
     }
+
+    @media (max-width: 768px) {
+        grid: 6rem 2.8rem 3rem / auto;
+
+        .rc3-logo {
+          justify-self: center;
+        }
+
+        &__nomob {
+            visibility: hidden !important;
+            display: none !important;
+        }
+
+        &__main {
+            grid-template-columns: repeat(6, 1fr);
+            grid-template-rows: 100%;
+
+            &-linkbox {
+                font-size: 0.8em;
+            }
+        }
+
+        &__additional {
+            grid-template-columns: repeat(6, 1fr);
+            grid-template-rows: 100%;
+
+            &-linkbox {
+                font-size: 0.8em;
+
+                & .rc3-image__img.w-100 {
+                    max-width: 100% !important;
+                    max-height: 100% !important;
+                    width: auto !important;
+                }
+            }
+
+            &-box-2x1 {
+                grid: 1fr / 1fr 1fr;
+                grid-column-start: span 2;
+            }
+        }
+    }
 }
diff --git a/src/plainui/styles/components/_image.scss b/src/plainui/styles/components/_image.scss
new file mode 100644
index 0000000000000000000000000000000000000000..43e759ce8dba9130fbc2fd28fcd5471b81f5d927
--- /dev/null
+++ b/src/plainui/styles/components/_image.scss
@@ -0,0 +1,20 @@
+.rc3-image {
+    &--cropped {
+        position: relative;
+        display: block;
+        padding-bottom: 56.25%;
+        height: 0;
+        overflow: hidden;
+
+        .rc3-image__img {
+            display: block;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+        }
+    }
+}
diff --git a/src/plainui/styles/components/_index.scss b/src/plainui/styles/components/_index.scss
index 6ff5732fe9841535ec435ad49c336e11c25317c4..f9b17aa5d22e6ff41310cfdcbb9b869c8883dc72 100644
--- a/src/plainui/styles/components/_index.scss
+++ b/src/plainui/styles/components/_index.scss
@@ -9,3 +9,4 @@
 @import "tile-board";
 @import "tile-message";
 @import "slider";
+@import "image";
diff --git a/src/plainui/styles/components/_landingpage.scss b/src/plainui/styles/components/_landingpage.scss
index 2d0aa2521afdfdafeadb802cc7ebfbd75ff32810..2717e85acf29c80e1a25540311a27dda43ef7838 100644
--- a/src/plainui/styles/components/_landingpage.scss
+++ b/src/plainui/styles/components/_landingpage.scss
@@ -18,13 +18,17 @@
 
     &__content {
         position: absolute;
-        top: 50%;
+        top: map-get($spacers, 10);
         left: 50%;
         width: 100%;
         text-align: center;
         padding: $spacer;
         z-index: 6;
-        transform: translate(-50%, -50%);
+        transform: translate(-50%, 0);
+    }
+
+    &__title {
+        font-size: $h2-font-size;
     }
 
     @media screen and (max-height: 770px) {
@@ -33,6 +37,16 @@
         }
     }
 
+    @include media-breakpoint-up('md') {
+        &__container {
+            width: 90%;
+        }
+
+        &__title {
+            font-size: 3.125rem;
+        }
+    }
+
     @include media-breakpoint-up('xxl') {
         &__container {
             max-width: 59.188rem;
diff --git a/src/plainui/styles/components/_player.scss b/src/plainui/styles/components/_player.scss
index bba1e5a2795901a4043f34be7c70bde97e1ff9ab..c8d1dd9eafb7a428eb4b2a4394e0222f18074678 100644
--- a/src/plainui/styles/components/_player.scss
+++ b/src/plainui/styles/components/_player.scss
@@ -1,8 +1,8 @@
-#player > [data-player] {
+.rc3__voc_player > [data-player] {
     padding-bottom: 56.25%;
     height: auto !important;
   }
-  #player > .fullscreen[data-player] {
+  .rc3__voc_player > .fullscreen[data-player] {
     padding-bottom: 0;
     height: 100% !important;
   }
diff --git a/src/plainui/tests.py b/src/plainui/tests.py
index b89515936f4d55331f3d32222d2d7e7b64758ea0..cd59057e9fb4a97edcf8b56550b8418341eb58d0 100644
--- a/src/plainui/tests.py
+++ b/src/plainui/tests.py
@@ -15,6 +15,7 @@ from django.utils import timezone
 from django.utils.formats import localize
 from django.utils.timezone import localtime
 from django.contrib.auth import SESSION_KEY
+from django.utils.translation import gettext as _
 
 
 from core.models import Assembly, AssemblyLikeCount, AssemblyMember, Badge, Conference, ConferenceMember, \
@@ -240,7 +241,7 @@ class ViewsTest(TestCase):
         self.assertEqual(sos.schedule_start, datetime(2020, 12, 25, 14, 0, tzinfo=utc))
         self.assertEqual(sos.schedule_duration, timedelta(minutes=42))
         self.assertEqual(sos.schedule_end, datetime(2020, 12, 25, 14, 42, tzinfo=utc))
-        self.assertSetsMessage(resp, 'Created Self Organized Session')
+        self.assertSetsMessage(resp, _('Created Self Organized Session'))
 
         self.assertNeedsLogin(reverse('plainui:sos_edit', kwargs={'conf_slug': self.conf.slug, 'assembly_slug': assembly.slug, 'event_slug': sos.slug}))
         resp = self.client.get(reverse('plainui:sos_edit', kwargs={'conf_slug': self.conf.slug, 'assembly_slug': assembly.slug, 'event_slug': sos.slug}))
@@ -270,7 +271,7 @@ class ViewsTest(TestCase):
         self.assertEqual(sos.schedule_start, datetime(2020, 12, 26, 14, 0, tzinfo=utc))
         self.assertEqual(sos.schedule_duration, timedelta(minutes=17))
         self.assertEqual(sos.schedule_end, datetime(2020, 12, 26, 14, 17, tzinfo=utc))
-        self.assertSetsMessage(resp, 'Updated Self Organized Session')
+        self.assertSetsMessage(resp, _('Updated Self Organized Session'))
 
         blocking = Event(
             conference=self.conf, assembly=assembly, room=room, name="blocking1", is_public=True,
@@ -323,8 +324,14 @@ class ViewsTest(TestCase):
         self.assertTrue(resp.context_data['form']['room'].errors)
 
     def test_AssemblyView(self):
+        speaker1 = PlatformUser(username='speaker1')
+        speaker1.save()
+        speaker2 = PlatformUser(username='speaker2')
+        speaker2.save()
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state=Assembly.State.PLACED)
         assembly.save()
+        AssemblyMember(member=speaker1, role=AssemblyMember.Role.SPOKESPERSON, show_public=True, assembly=assembly).save()
+        AssemblyMember(member=speaker2, role=AssemblyMember.Role.SPOKESPERSON, show_public=False, assembly=assembly).save()
         suggested_assembly1 = Assembly(conference=self.conf, slug='suggested1', name='Suggested1', state=Assembly.State.PLACED)
         suggested_assembly1.save()
         suggested_assembly2 = Assembly(conference=self.conf, slug='suggested2', name='Suggested2', state=Assembly.State.PLACED)
@@ -363,6 +370,7 @@ class ViewsTest(TestCase):
         self.assertEqual(list(resp.context_data['sos']), [sos1])
         self.assertFalse(resp.context_data['can_create_sos'])
         self.assertEqual(list(resp.context_data['suggested']), [suggested_assembly2, suggested_assembly1])
+        self.assertEqual([sp.member for sp in resp.context_data['spokespeople']], [speaker1])
 
         assembly.favorited_by.add(self.user)
         session = self.client.session
@@ -528,7 +536,8 @@ class ViewsTest(TestCase):
         session['fav_e'] = session['fav_a'] = session['sch_e'] = None
         session.save()
 
-        user_badge = UserBadge(user=self.user, badge=badge)
+        user_badge = UserBadge(user=self.user, badge=badge, accepted_by_user=True, visibility=UserBadge.Visibility.PUBLIC)
+        user_badge.acceptByUser()
         user_badge.save()
         resp = self.client.get(reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
         self.assertEqual(resp.context_data['conf'], self.conf)
@@ -736,6 +745,66 @@ class ViewsTest(TestCase):
         self.assertTrue(ConferenceMember.objects.filter(conference=self.conf, user=self.user).exists())
         self.assertRedirects(resp, reverse('plainui:index', kwargs={'conf_slug': self.conf.slug}))
 
+    @override_settings(LANGUAGE_CODE='en', PRETIX_SECRET_KEY='^v^', PRETIX_ISSUER='tickets.events.ccc.de', AUTH_PASSWORD_VALIDATORS=[])
+    def test_TokenPasswordResetView(self):
+        ticket_data = {
+            'aud': self.conf.slug,
+            'exp': datetime.utcnow() + timedelta(hours=1),
+            'iat': datetime.utcnow() - timedelta(hours=1),
+            'iss': 'tickets.events.ccc.de',
+        }
+        valid_token = jwt.encode({'uid': 'blblbl123', **ticket_data}, key='^v^').decode('utf-8')
+        decoded_token = jwt.decode(valid_token, key='^v^', audience=self.conf.slug, issuer='tickets.events.ccc.de')
+        del decoded_token['uid']
+        ConferenceMemberTicket(user=self.user, ident='blblbl123', conference=self.conf, data=decoded_token).save()
+        self.user.set_password('asdf')
+        self.user.email = ''
+        self.user.save()
+
+        # invalid token
+        invalid_token = 'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA' \
+            '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
+        resp = self.client.post(reverse('plainui:token_password_reset', kwargs={'conf_slug': self.conf.slug}), {
+            'jwt': invalid_token,
+            'username': self.user.username,
+            'new_password1': 'new',
+            'new_password2': 'new',
+        })
+        self.assertTrue(resp.context_data['form'].errors)
+        self.user.refresh_from_db()
+        self.assertFalse(self.user.check_password('new'))
+
+        resp = self.client.post(reverse('plainui:token_password_reset', kwargs={'conf_slug': self.conf.slug}), {
+            'jwt': valid_token,
+            'username': 'doesnotexist',
+            'new_password1': 'new',
+            'new_password2': 'new',
+        })
+        self.assertTrue(resp.context_data['form'].errors)
+        self.user.refresh_from_db()
+        self.assertFalse(self.user.check_password('new'))
+
+        resp = self.client.post(reverse('plainui:token_password_reset', kwargs={'conf_slug': self.conf.slug}), {
+            'jwt': valid_token,
+            'username': self.user.username,
+            'new_password1': 'new',
+            'new_password2': 'new2',
+        })
+        self.assertTrue(resp.context_data['form'].errors)
+        self.user.refresh_from_db()
+        self.assertFalse(self.user.check_password('new'))
+
+        resp = self.client.post(reverse('plainui:token_password_reset', kwargs={'conf_slug': self.conf.slug}), {
+            'jwt': valid_token,
+            'username': self.user.username,
+            'new_password1': 'new',
+            'new_password2': 'new',
+        })
+        self.assertRedirects(resp, reverse('plainui:login', kwargs={'conf_slug': self.conf.slug}))
+        self.user.refresh_from_db()
+        self.assertTrue(self.user.check_password('new'))
+        self.assertSetsMessage(resp, 'Passwort successfully reset!')
+
     def test_ModifyFavoritesView_get(self):
         self.assertNeedsLogin(reverse('plainui:modify_favorites', kwargs={'conf_slug': self.conf.slug}))
         resp = self.client.get(reverse('plainui:modify_favorites', kwargs={'conf_slug': self.conf.slug}))
@@ -910,29 +979,6 @@ class ViewsTest(TestCase):
         self.assertFalse(self.user.calendar_events.filter(pk=event.pk).exists())
         self.assertEqual(self.client.session['sch_e'], [])
 
-    def test_BadgeSubmitTokenView_get(self):
-        self.assertNeedsLogin(reverse('plainui:badge_token_submit', kwargs={'conf_slug': self.conf.slug}))
-        resp = self.client.get(reverse('plainui:badge_token_submit', kwargs={'conf_slug': self.conf.slug}))
-        self.assertRedirects(resp, reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
-
-    def test_BadgeSubmitTokenView_post(self):
-        assembly = Assembly(conference=self.conf, slug='s', name='a')
-        assembly.save()
-        badge = Badge(conference=self.conf, name='badge-name', issuing_token='chesho5eey4aiZi5oaxoo0eipohquuof', issuing_assembly=assembly)
-        badge.save()
-
-        self.assertNeedsLogin(reverse('plainui:badge_token_submit', kwargs={'conf_slug': self.conf.slug}), post=True)
-        resp = self.client.post(reverse('plainui:badge_token_submit', kwargs={'conf_slug': self.conf.slug}), {
-            'token': 'asdf',
-        })
-        self.assertRedirects(resp, reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
-
-        resp = self.client.post(reverse('plainui:badge_token_submit', kwargs={'conf_slug': self.conf.slug}), {
-            'token': badge.issuing_token,
-        })
-        self.assertRedirects(resp, reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
-        self.assertTrue(self.user.badges.filter(badge=badge).exists())
-
     def test_PersonalMessageListView(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
@@ -948,21 +994,21 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['conf'], self.conf)
         msgs = list(resp.context_data['msgs'])
         self.assertEqual(msgs, [dm_recv2, dm_recv1])
-        self.assertEqual(msgs[0].sender_name, user2.username)
+        self.assertEqual(msgs[0].sender, user2)
         self.assertEqual(resp.context_data['total'], 2)
 
         resp = self.client.get(reverse('plainui:personal_message', kwargs={'conf_slug': self.conf.slug}), {'start': 1})
         self.assertEqual(resp.context_data['conf'], self.conf)
         msgs = list(resp.context_data['msgs'])
         self.assertEqual(msgs, [dm_recv1])
-        self.assertEqual(msgs[0].sender_name, user2.username)
+        self.assertEqual(msgs[0].sender, user2)
         self.assertEqual(resp.context_data['total'], 2)
 
         resp = self.client.get(reverse('plainui:personal_message_outbox', kwargs={'conf_slug': self.conf.slug}))
         self.assertEqual(resp.context_data['conf'], self.conf)
         msgs = list(resp.context_data['msgs'])
         self.assertEqual(msgs, [dm_sent])
-        self.assertEqual(msgs[0].recipient_name, user2.username)
+        self.assertEqual(msgs[0].recipient, user2)
         self.assertEqual(resp.context_data['total'], 1)
 
     def test_PersonalMessageSendView_get(self):
@@ -1359,6 +1405,7 @@ class ViewsTest(TestCase):
         resp = self.client.get(reverse('plainui:board_entry_delete', kwargs={'conf_slug': self.conf.slug}))
         self.assertRedirects(resp, reverse('plainui:board_private', kwargs={'conf_slug': self.conf.slug}))
 
+    @override_settings(LANGUAGE_CODE='en')
     def test_BoardEntryDeleteView_post(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
@@ -1541,18 +1588,6 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(list(resp.context_data['rooms']), [room])
 
-    def test_StreamView(self):
-        room = Room(conference=self.conf, name='Room1')
-        room.save()
-        room_link = RoomLink(room=room, name='stream', link_type=RoomLink.LinkType.VIDEO)
-        room_link.save()
-
-        self.assertNeedsLogin(reverse('plainui:stream', kwargs={'conf_slug': self.conf.slug, 'room_id': room.pk, 'link_id': room_link.id}))
-        resp = self.client.get(reverse('plainui:stream', kwargs={'conf_slug': self.conf.slug, 'room_id': room.pk, 'link_id': room_link.id}))
-        self.assertEqual(resp.context_data['conf'], self.conf)
-        self.assertEqual(resp.context_data['room'], room)
-        self.assertEqual(resp.context_data['link'], room_link)
-
     def test_UpcomingView(self):
         now = timezone.now()
         self.conf.start = now - timedelta(days=3)
@@ -1595,11 +1630,48 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['hide_header'], True)
 
+    @freeze_time(datetime(2020, 1, 1, 1, 0, 0, tzinfo=utc))
     def test_CccEventsView(self):
+        assembly = Assembly(conference=self.conf, slug='official', name='Official Assembly', state=Assembly.State.PLACED, is_official=True)
+        assembly.save()
+        room1 = Room(conference=self.conf, assembly=assembly, name='Stream1')
+        room1.save()
+        room_link1 = RoomLink(room=room1, link_type=RoomLink.LinkType.MEDIA_CCC_DE, link='some.where')
+        room_link1.save()
+        room2 = Room(conference=self.conf, assembly=assembly, name='Stream2')
+        room2.save()
+        room_link2 = RoomLink(room=room2, link_type=RoomLink.LinkType.MEDIA_CCC_DE, link='else.where')
+        room_link2.save()
+
+        event = Event(
+            conference=self.conf, assembly=assembly, room=room1, slug='Event1_1', name='Event1_1', is_public=True, kind=Event.Kind.ASSEMBLY,
+            schedule_start=datetime(2020, 1, 1, 0, 45, 0, tzinfo=utc), schedule_duration=timedelta(minutes=45)
+        )
+        event.save()
+        event2 = Event(
+            conference=self.conf, assembly=assembly, room=room1, slug='Event1_2', name='Event1_2', is_public=True,
+            schedule_start=datetime(2020, 1, 1, 1, 50, 1, tzinfo=utc), schedule_duration=timedelta(minutes=45)
+        )
+        event2.save()
+
         self.assertNeedsLogin(reverse('plainui:ccc_events', kwargs={'conf_slug': self.conf.slug}))
         resp = self.client.get(reverse('plainui:ccc_events', kwargs={'conf_slug': self.conf.slug}))
         self.assertEqual(resp.context_data['conf'], self.conf)
-
+        self.assertEqual(resp.context_data['rooms'], [{
+            'room': room1,
+            'stream_link': room_link1,
+            'voc_stream': room_link1.link,
+            'current_event': event,
+            'next_event': event2,
+        }, {
+            'room': room2,
+            'stream_link': room_link2,
+            'voc_stream': room_link2.link,
+            'current_event': None,
+            'next_event': None,
+        }])
+
+    @override_settings(SSO_COOKIE_NAME='BLGRG', SSO_COOKIE_DOMAIN='.testserver')
     def test_WorldView(self):
         self.assertNeedsLogin(reverse('plainui:world', kwargs={'conf_slug': self.conf.slug}))
         resp = self.client.get(reverse('plainui:world', kwargs={'conf_slug': self.conf.slug}))
@@ -1614,6 +1686,7 @@ class ViewsTest(TestCase):
         resp = self.client.get(reverse('plainui:world', kwargs={'conf_slug': self.conf.slug}))
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['page'], sp)
+        self.assertTrue(resp.cookies['BLGRG'])
 
     def test_ReportContentView(self):
         from .forms import REPORT_CATEGORIES
@@ -1740,9 +1813,10 @@ class ViewsTest(TestCase):
         badge1.save()
         badge2 = Badge(conference=self.conf, name='hidden-badge', issuing_assembly=assembly)
         badge2.save()
-        user_badge1 = UserBadge(user=user2, badge=badge1)
+        user_badge1 = UserBadge(user=user2, badge=badge1, accepted_by_user=True, visibility=UserBadge.Visibility.PUBLIC)
         user_badge1.save()
-        user_badge2 = UserBadge(user=user2, badge=badge2, hidden=True)
+        user_badge1.acceptByUser()
+        user_badge2 = UserBadge(user=user2, badge=badge2)
         user_badge2.save()
 
         self.assertNeedsLogin(reverse('plainui:user', kwargs={'conf_slug': self.conf.slug, 'id': str(user2.pk)}))
diff --git a/src/plainui/urls.py b/src/plainui/urls.py
index d496fe3fe090d9e8a9e0369ae62bd11a614ebb50..e446ca8c5f1daaaade3e004297097915c89ee2ce 100644
--- a/src/plainui/urls.py
+++ b/src/plainui/urls.py
@@ -16,13 +16,16 @@ urlpatterns = [
     path('<slug:conf_slug>/reset/done/', views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
     path('<slug:conf_slug>/logout', views.LogoutView.as_view(), name='logout'),
     path('<slug:conf_slug>/me', views.ProfileView.as_view(), name='userprofile'),
+    path('<slug:conf_slug>/me/redeem_badge', views.RedeemBadgeView.as_view(), name='redeem_badge'),
+    path('<slug:conf_slug>/me/redeem_badge/<str:redeem_token_badge>', views.RedeemBadgeView.as_view(), name='redeem_badge'),
+    path('<slug:conf_slug>/me/manage_badges', views.ManageBadgeView.as_view(), name='manage_badges'),
     path('<slug:conf_slug>/redeem_token', views.RedeemTokenView.as_view(), name='redeem_token'),
     path('<slug:conf_slug>/redeem_token_add', views.RedeemTokenAddToUserView.as_view(), name='redeem_token_add_to_user'),
     path('<slug:conf_slug>/redeem_token_create', views.RedeemTokenUserCreateView.as_view(), name='redeem_token_create_user'),
     path('<slug:conf_slug>/redeem_token_loggedin', views.RedeemTokenLoggedIn.as_view(), name='redeem_token_loggedin'),
+    path('<slug:conf_slug>/token_password_reset', views.TokenPasswordResetView.as_view(), name='token_password_reset'),
 
     path('<slug:conf_slug>/', views.LandingView.as_view(), name='landing'),
-    path('<slug:conf_slug>/badge_token', views.BadgeSubmitTokenView.as_view(), name='badge_token_submit'),
     path('<slug:conf_slug>/board', views.BoardView.as_view(), name='board'),
     path('<slug:conf_slug>/my_board', views.BoardPrivateView.as_view(), name='board_private'),
     path('<slug:conf_slug>/board/<int:id>', views.BoardEntryView.as_view(), name='board_entry'),
@@ -47,7 +50,6 @@ urlpatterns = [
     path('<slug:conf_slug>/rooms', views.RoomsView.as_view(), name='rooms'),
     path('<slug:conf_slug>/room/<uuid:room_id>/', views.RoomView.as_view(), name='room'),
     path('<slug:conf_slug>/static/<slug:page_slug>/', views.StaticPageView.as_view(), name='static_page'),
-    path('<slug:conf_slug>/stream/<uuid:room_id>/<int:link_id>/', views.StreamView.as_view(), name='stream'),
     path('<slug:conf_slug>/search', views.SearchView.as_view(), name='search'),
     path('<slug:conf_slug>/event/<slug:event_slug>/', views.EventView.as_view(), name='event'),
     path('<slug:conf_slug>/tag/<slug:tag_slug>/', views.TagView.as_view(), name='tag'),
diff --git a/src/plainui/views.py b/src/plainui/views.py
index 3d406d1cfb42bb9e70e16af09ba3fc9b81eda7f3..a426f9dd2113e1439d24ad8743e902b995f07bb0 100644
--- a/src/plainui/views.py
+++ b/src/plainui/views.py
@@ -1,4 +1,5 @@
 from datetime import time, timedelta
+from functools import lru_cache
 from typing import List
 
 from django.conf import settings
@@ -23,14 +24,17 @@ from django.views.generic.edit import FormView, UpdateView
 
 from core import integrations
 from core.forms import PasswordResetForm
-from core.models import Assembly, AssemblyLikeCount, Badge, Conference, ConferenceMember, ConferenceMemberTicket, ConferenceTag, \
-        ConferenceTrack, DirectMessage, Event, EventAttachment, EventLikeCount, EventParticipant, PlatformUser, Room, RoomLink, StaticPage, TagItem, UserBadge
+from core.models import Assembly, AssemblyMember, AssemblyLikeCount, Badge, Conference, ConferenceMember, ConferenceMemberTicket, ConferenceTag, UserContact,\
+        ConferenceTrack, DirectMessage, Event, EventAttachment, EventLikeCount, EventParticipant, PlatformUser, Room, RoomLink, StaticPage, TagItem, UserBadge,\
+        BadgeToken
 from core.models.ticket import TicketValidationError
 from core.search import search
+from core.sso import SSO
 from core.utils import render_markdown
 
 from .forms import BulletinBoardEntryForm, ExampleForm, InputTokenForm, NewDirectMessageForm, \
-    ProfileEditForm, SelfOrganizedSessionForm, RedeemTokenAddToUserForm, RedeemTokenUserCreateForm, ReportForm
+    ProfileEditForm, SelfOrganizedSessionForm, RedeemTokenAddToUserForm, RedeemTokenUserCreateForm, ReportForm, \
+    RedeemBadgeForm, TokenPasswortResetForm
 from .models import BulletinBoardEntry
 
 
@@ -132,7 +136,6 @@ class EventView(ConferenceRequiredMixin, TemplateView):
         context['is_scheduled'] = str(event.id) in personal_calendar
         context['is_favorite_events'] = favorites
         context['is_scheduled_events'] = personal_calendar
-        context['events_upcoming'] = _event_filter(self.request.user, self.conf, upcoming=True)
         context['speakers'] = event.participants.filter(is_public=True, role=EventParticipant.Role.SPEAKER).select_related('participant')
         context['tags'] = TagItem.objects.select_related('tag').filter(
             tag__is_public=True, target_type=ContentType.objects.get_for_model(Event), target_id=event.pk
@@ -141,6 +144,16 @@ class EventView(ConferenceRequiredMixin, TemplateView):
         context['suggested'] = [
             s.event2 for s in event.suggestions.select_related('event2').exclude(event2=event.pk).exclude(event2__is_public=False).order_by('-like_ratio')[:5]
         ]
+        now = timezone.now()
+        if event.schedule_start is None or event.schedule_end is None:
+            running_state = ''
+        elif now > event.schedule_end:
+            running_state = 'complete'
+        elif event.schedule_start <= now <= event.schedule_end:
+            running_state = 'running'
+        else:
+            running_state = 'upcoming'
+        context['running_state'] = running_state
 
         context['attachments'] = EventAttachment.objects.filter(
             event=event,
@@ -164,9 +177,14 @@ class TagView(ConferenceRequiredMixin, TemplateView):
         context['events'] = Event.objects.conference_accessible(self.conf).filter(
             id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Event)).values_list('target_id')
         )
+        context['my_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
+        context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
+
         context['assemblies'] = Assembly.objects.conference_accessible(self.conf).filter(
             id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Assembly)).values_list('target_id')
         )
+        context['my_favorite_assemblies'] = _session_get_favorite_assemblies(self.request.session, self.request.user)
+
         return context
 
 
@@ -267,6 +285,7 @@ class AssemblyView(ConferenceRequiredMixin, TemplateView):
         context['scope'] = 'assembly'
         context['can_create_sos'] = assembly.has_user(self.request.user) or assembly == self.conf.self_organized_sessions_assembly
         context['sos'] = assembly.events.filter(is_public=True, kind=Event.Kind.SELF_ORGANIZED)
+        context['badges'] = Badge.objects.filter(conference=self.conf, issuing_assembly=assembly, show_public=True)
 
         suggestions = assembly.suggestions.select_related('assembly2')
         suggestions = suggestions.exclude(assembly2=assembly.pk).filter(assembly2__state__in=Assembly.PUBLIC_STATES).order_by('-like_ratio')[:5]
@@ -274,6 +293,8 @@ class AssemblyView(ConferenceRequiredMixin, TemplateView):
             s.assembly2 for s in suggestions
         ]
 
+        context['spokespeople'] = assembly.members.filter(role=AssemblyMember.Role.SPOKESPERSON, show_public=True).select_related('member')
+
         can_manage_sos = context['can_manage_sos'] = assembly.user_can_manage(self.request.user)
         if can_manage_sos:
             context['sos_private'] = assembly.events.filter(is_public=False, kind=Event.Kind.SELF_ORGANIZED)
@@ -288,13 +309,12 @@ class AssembliesView(ConferenceRequiredMixin, TemplateView):
     def get_context_data(self, conf_slug, **kwargs):
         context = super().get_context_data(conf_slug=conf_slug, **kwargs)
         context['conf'] = self.conf
-
         context['events_upcoming'] = _event_filter(self.request.user, self.conf, upcoming=True)
         # TODO: reecommended events
         context['events_recommended'] = _event_filter(self.request.user, self.conf, user_schedule_only=True)
         context['is_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['is_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
-        context['assemblies'] = Assembly.objects.conference_accessible(self.conf)
+        context['assemblies'] = Assembly.objects.accessible_by_user(self.request.user, self.conf)
         context['my_favorite_assemblies'] = _session_get_favorite_assemblies(self.request.session, self.request.user)
         context['scope'] = 'assembly'
         return context
@@ -376,6 +396,11 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
     template_name = 'plainui/profile.html'
     form_class = ProfileEditForm
 
+    def get(self, request, *args, **kwargs):
+        resp = super().get(request, *args, **kwargs)
+        resp.set_cookie(settings.SSO_COOKIE_NAME, SSO.generate_token(self.request.user), domain=settings.SSO_COOKIE_DOMAIN)
+        return resp
+
     def get_object(self, *args, **kwargs):
         return self.request.user
 
@@ -384,14 +409,15 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
         context['conf'] = self.conf
 
         user = self.request.user
-        context['badges'] = UserBadge.objects.filter(user=user).select_related('badge')
+        context['badges'] = UserBadge.objects.filter(user=user, accepted_by_user=True).select_related('badge')
+        context['amount_badges_not_accepted'] = len(UserBadge.objects.filter(user=user, accepted_by_user=False).select_related('badge'))
         context['is_favorite_events'] = favorite_events = _session_get_favorite_events(self.request.session, self.request.user)
         context['my_favorite_events'] = Event.objects.accessible_by_user(user=user, conference=self.conf).filter(pk__in=favorite_events)
         context['is_favorite_assemblies'] = favorite_assemblies = _session_get_favorite_assemblies(self.request.session, self.request.user)
         context['my_favorite_assemblies'] = Assembly.objects.accessible_by_user(user=user, conference=self.conf).filter(pk__in=favorite_assemblies)
         context['is_fahrplan_events'] = scheduled_events = _session_get_scheduled_events(self.request.session, self.request.user)
         context['my_fahrplan_events'] = Event.objects.accessible_by_user(user=user, conference=self.conf).filter(pk__in=scheduled_events)
-
+        context['redeem_badge_form'] = RedeemBadgeForm
         return context
 
     def get_success_url(self):
@@ -403,6 +429,108 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
         return resp
 
 
+class ManageBadgeView(ConferenceRequiredMixin, UpdateView):
+    template_name = 'plainui/manage_badges.html'
+    form_class = RedeemBadgeForm
+
+    def get_object(self, *args, **kwargs):
+        return self.request.user
+
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
+        context['conf'] = self.conf
+
+        user = self.request.user
+        context['badges_accepted'] = UserBadge.objects.filter(user=user, accepted_by_user=True).select_related('badge')
+        context['badges_not_accepted'] = UserBadge.objects.filter(user=user, accepted_by_user=False).select_related('badge')
+        context['redeem_badge_form'] = RedeemBadgeForm
+        return context
+
+    def get_success_url(self):
+        return reverse('plainui:manage_badges', kwargs={'conf_slug': self.kwargs['conf_slug']})
+
+    def form_valid(self, form):
+        resp = super().form_valid(form)
+        return resp
+
+    def post(self, request, *args, **kwargs):
+        badgeid = request.POST['badgeid']
+        visibility = request.POST.get('visibility', None)
+        user = request.user
+        try:
+            badge = Badge.objects.filter(id=badgeid).first()
+            userbadge = UserBadge.objects.filter(user=user, badge=badge).first()
+
+            if not userbadge:
+                raise Exception("The badge is not found.")
+            if not visibility:
+                # Change acceptanceState
+                if userbadge.accepted_by_user:
+                    userbadge.revokeAcceptByUser()
+                    messages.success(request, f'Revoked Badge: {badge}')
+                else:
+                    userbadge.acceptByUser()
+            else:
+                # Change Visibility State
+                userbadge.changeVisibiltyState(visibility)
+                messages.success(request, f'Changed visibility of Badge {badge} to {visibility}')
+
+            return HttpResponseRedirect(reverse('plainui:manage_badges', kwargs={'conf_slug': self.conf.slug}))
+        except Exception as e:
+            messages.error(request, str(e))
+            return HttpResponseRedirect(reverse('plainui:manage_badges', kwargs={'conf_slug': self.conf.slug}))
+
+
+class RedeemBadgeView(ConferenceRequiredMixin, UpdateView):
+    template_name = 'plainui/redeem_badge.html'
+    form_class = RedeemBadgeForm
+
+    def get_object(self, *args, **kwargs):
+        return self.request.user
+
+    def get_context_data(self):
+        context = super().get_context_data()
+        context['conf'] = self.conf
+        context['urlToken'] = self.kwargs.get('redeem_token_badge', None)
+
+        user = self.request.user
+        context['badges'] = UserBadge.objects.filter(user=user).select_related('badge')
+        return context
+
+    def get_success_url(self):
+        return reverse('plainui:userprofile', kwargs={'conf_slug': self.kwargs['conf_slug']})
+
+    def form_valid(self, form):
+        resp = super().form_valid(form)
+        return resp
+
+    def post(self, request, *args, **kwargs):
+        token = request.POST['token']
+        user = request.user
+
+        try:
+            if token is None:
+                raise Exception("Please provide token.")
+            if user is None:
+                raise Exception("Please provide a valid user.")
+
+            badge_token = BadgeToken.objects.filter(token=token).first()
+
+            if not badge_token:
+                raise Exception("The token is not valid.")
+
+            if badge_token.badge.conference.slug != self.kwargs['conf_slug']:
+                raise Exception("Badge is not associated with the given conference.")
+
+            badge_token.redeem(user, True)
+
+            messages.success(request, f'Redeemed Badge: {badge_token.badge}')
+            return HttpResponseRedirect(reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
+        except Exception as e:
+            messages.error(request, str(e))
+            return HttpResponseRedirect(reverse('plainui:redeem_badge', kwargs={'conf_slug': self.conf.slug}))
+
+
 class RedeemTokenView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/redeem_token.html'
     require_login = False
@@ -656,18 +784,6 @@ class ModifyPersonalCalendarView(ConferenceRequiredMixin, View):
         return redirect(redirect_to)
 
 
-class BadgeSubmitTokenView(ConferenceRequiredMixin, View):
-    def get(self, request, conf_slug, **kwargs):
-        return redirect(reverse('plainui:userprofile', kwargs={'conf_slug': conf_slug}))
-
-    def post(self, request, conf_slug):
-        badge = Badge.objects.filter(issuing_token=request.POST['token']).first()
-        if badge:
-            UserBadge.objects.bulk_create([UserBadge(user=self.request.user, badge=badge, hidden=False)], ignore_conflicts=True)
-
-        return redirect(reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
-
-
 class PersonalMessageListView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/personal_message_list.html'
     sent = False
@@ -684,9 +800,9 @@ class PersonalMessageListView(ConferenceRequiredMixin, TemplateView):
         except (ValueError, KeyError):
             start = 0
         if self.sent:
-            dms = dms.filter(sender=self.request.user, deleted_by_sender=False).annotate(recipient_name=F('recipient__username'))
+            dms = dms.filter(sender=self.request.user, deleted_by_sender=False).select_related('recipient')
         else:
-            dms = dms.filter(recipient=self.request.user, deleted_by_recipient=False).annotate(sender_name=F('sender__username'))
+            dms = dms.filter(recipient=self.request.user, deleted_by_recipient=False).select_related('sender')
 
         context['start'] = start
         context['total'] = dms.count()
@@ -869,6 +985,29 @@ class PasswordResetCompleteView(ConferenceRequiredMixin, auth_views.PasswordRese
         return context
 
 
+class TokenPasswordResetView(ConferenceRequiredMixin, FormView):
+    template_name = 'plainui/token_password_reset.html'
+    require_conference_member = False
+    require_login = False
+    form_class = TokenPasswortResetForm
+
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
+        context['conf'] = self.conf
+        return context
+
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['conf'] = self.conf
+        kwargs['user'] = self.request.user
+        return kwargs
+
+    def form_valid(self, form):
+        form.save()
+        messages.success(self.request, gettext("Passwort successfully reset!"))
+        return HttpResponseRedirect(reverse('plainui:login', kwargs={'conf_slug': self.conf.slug}))
+
+
 class LogoutView(View):
     def dispatch(self, request, *args, conf_slug):
         logout(request)
@@ -888,7 +1027,7 @@ class BoardView(ConferenceRequiredMixin, TemplateView):
         except (ValueError, KeyError):
             start = 0
         entries = BulletinBoardEntry.objects \
-            .annotate(owner_name=F('owner__username')) \
+            .select_related('owner') \
             .filter(conference=self.conf, is_public=True) \
             .order_by('-timestamp')
         context['board'] = entries[start:start + PAGE_SIZE]
@@ -1042,7 +1181,7 @@ def _event_filter(user, conf, day=None, curated=None, assembly=None, track=None,
     filters = {}
     if day is not None:
         filters['schedule_start__gte'] = min_date + timedelta(day)
-        filters['schedule_start__lt'] = min_date + timedelta(1)
+        filters['schedule_start__lt'] = min_date + timedelta(day+1)
     if curated is not None:
         filters['kind'] = Event.Kind.OFFICIAL if curated else Event.Kind.ASSEMBLY
     if assembly:
@@ -1189,6 +1328,7 @@ class PublicFahrplanView(ConferenceRequiredMixin, TemplateView):
         context = super().get_context_data(conf=self.conf, **kwargs)
         context['mode'] = 'calendar'
         events = _event_filter(self.request.user, self.conf, public_fahrplan=True)
+        # events = _event_filter(self.request.user, self.conf)
         context['events'] = _organize_events_for_calendar(self.conf, events)
         return context
 
@@ -1201,9 +1341,11 @@ class RoomView(ConferenceRequiredMixin, TemplateView):
 
         try:
             if self.room.room_type == Room.RoomType.BIGBLUEBUTTON:
-                return redirect(integrations.BigBlueButton.join_room(self.room, self.request.user, self.request.user.show_name))
+                return HttpResponseRedirect(integrations.BigBlueButton.join_room(self.room, self.request.user, self.request.user.show_name))
             elif self.room.room_type == Room.RoomType.WORKADVENTURE:
-                return redirect(integrations.Workadventure.join_room(self.room, self.request.user))
+                resp = HttpResponseRedirect(integrations.WorkAdventure.join_room(self.room, self.request.user))
+                resp.set_cookie(settings.SSO_COOKIE_NAME, SSO.generate_token(self.request.user), domain=settings.SSO_COOKIE_DOMAIN)
+                return resp
         except integrations.IntegrationError as e:
             messages.error(request, str(e))
 
@@ -1214,14 +1356,20 @@ class RoomView(ConferenceRequiredMixin, TemplateView):
         context['conf'] = self.conf
         context['room'] = self.room
 
-        room_links = {}
+        linking_links = []
+        media_link = None
         for link in self.room.links.all():
-            room_links[link.link_type] = link
-        context['links'] = room_links
+            if link.link_type == RoomLink.LinkType.MEDIA_CCC_DE:
+                media_link = link.link
+            elif link.link_type in {RoomLink.LinkType.WEBSITE, RoomLink.LinkType.CHAT, RoomLink.LinkType.JITSI, RoomLink.LinkType.PAD}:
+                linking_links.append(link)
+
+        context['voc_stream'] = media_link
+        context['links'] = linking_links
+        context['events'] = _event_filter(self.request.user, self.conf, room=self.room)
+        context['my_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
+        context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
-        context['voc_stream'] = None
-        if RoomLink.LinkType.MEDIA_CCC_DE in room_links:
-            context['voc_stream'] = room_links[RoomLink.LinkType.MEDIA_CCC_DE].link  # TODO das ist vermutlich nicht die richtige quelle für den stream-slug.
         return context
 
 
@@ -1235,18 +1383,6 @@ class RoomsView(ConferenceRequiredMixin, TemplateView):
         return context
 
 
-class StreamView(ConferenceRequiredMixin, TemplateView):
-    template_name = 'plainui/stream.html'
-
-    def get_context_data(self, conf_slug, room_id, link_id, **kwargs):
-        context = super().get_context_data(conf_slug=conf_slug, **kwargs)
-        context['conf'] = self.conf
-        room = Room.objects.conference_accessible(self.conf).get(pk=room_id)
-        context['room'] = room
-        context['link'] = room.links.get(pk=link_id)
-        return context
-
-
 class UpcomingView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/upcoming.html'
 
@@ -1274,21 +1410,65 @@ class LandingView(ConferenceRequiredMixin, TemplateView):
         return context
 
 
+@lru_cache(None)
+def _stream_assembly(conf):
+    return Assembly.objects.filter(conference=conf, is_official=True, rooms__links__link_type=RoomLink.LinkType.MEDIA_CCC_DE).distinct().first()
+
+
 class CccEventsView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/ccc_events.html'
 
     def get_context_data(self, conf_slug, **kwargs):
         context = super().get_context_data(conf_slug=conf_slug, **kwargs)
         context['conf'] = self.conf
-        context['events_ccc'] = _event_filter(self.request.user, self.conf, curated=True)
-        context['is_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
-        context['is_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
+        assembly = _stream_assembly(self.conf)
+
+        rooms = []
+        now = timezone.now()
+        for room in assembly.rooms.filter(links__link_type=RoomLink.LinkType.MEDIA_CCC_DE).order_by('name'):
+            stream_link = room.links.get(link_type=RoomLink.LinkType.MEDIA_CCC_DE) if room else False
+            voc_stream = stream_link.link if stream_link and stream_link.link else False
+            current_event = Event.objects.accessible_by_user(self.request.user, self.conf)\
+                .filter(room=room, schedule_start__lte=now, schedule_end__gte=now).order_by('schedule_start').first()
+            next_event = Event.objects.accessible_by_user(self.request.user, self.conf)\
+                .filter(room=room, schedule_start__gt=now).order_by('schedule_start').first()
+            rooms.append({
+                'room': room,
+                'stream_link': stream_link,
+                'voc_stream': voc_stream,
+                'current_event': current_event,
+                'next_event': next_event,
+            })
+
+        context['rooms'] = rooms
+
+        context['my_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
+        context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
+
+        try:
+            static_page = StaticPage.objects.conference_accessible(conference=self.conf).get(slug='ccc')
+        except StaticPage.DoesNotExist:
+            static_page = StaticPage(conference=self.conf, title='Page Missing', rendered_body='Please configure the static page "ccc".')
+
+        context['page'] = static_page
+
+        events = _event_filter(self.request.user, self.conf, public_fahrplan=True)
+        # events = _event_filter(self.request.user, self.conf)
+        context['events'] = _organize_events_for_calendar(self.conf, events)
+
+        return context
+
         return context
 
 
 class WorldView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/world.html'
 
+    def get(self, request, *args, **kwargs):
+        resp = super().get(request, *args, **kwargs)
+        resp.set_cookie(settings.SSO_COOKIE_NAME, SSO.generate_token(self.request.user), domain=settings.SSO_COOKIE_DOMAIN)
+        return resp
+
     def get_context_data(self, conf_slug, **kwargs):
         context = super().get_context_data(conf_slug=conf_slug, **kwargs)
         context['conf'] = self.conf
@@ -1299,6 +1479,7 @@ class WorldView(ConferenceRequiredMixin, TemplateView):
 
         context['page'] = static_page
         context['scope'] = 'world'
+        context['join_url'] = integrations.WorkAdventure.join_room(None, self.request.user)
         return context
 
 
@@ -1369,10 +1550,28 @@ class UserView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/user.html'
 
     def get_context_data(self, **kwargs):
+        user = self.request.user
         context = super().get_context_data(**kwargs)
+
         context['conf'] = self.conf
         context['display_user'] = display_user = get_object_or_404(PlatformUser.objects.filter(pk=self.kwargs['id']))
-        context['badges'] = UserBadge.objects.filter(user=display_user, hidden=False).select_related('badge')
+
+        # GEt public badges from user
+        userBadges = UserBadge.objects.filter(user=user, accepted_by_user=True).values_list('badge_id', flat=True)
+
+        contact = UserContact.objects.filter(user=display_user, contact=user).first()
+        if contact and not contact.pending:
+            # Show Friend Badges
+            context['badges'] = UserBadge.objects.filter(user=display_user, accepted_by_user=True) \
+                                .filter(Q(visibility__in=[UserBadge.Visibility.PUBLIC, UserBadge.Visibility.FRIENDS, UserBadge.Visibility.CLUBFRIENDS]) |
+                                        Q(badge__in=userBadges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])) \
+                                .select_related('badge')
+        else:
+            # Show only public badges
+            context['badges'] = UserBadge.objects.filter(user=display_user, accepted_by_user=True) \
+                                .filter(Q(visibility__in=[UserBadge.Visibility.PUBLIC]) |
+                                        Q(badge__in=userBadges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])) \
+                                .select_related('badge')
         return context
 
 
diff --git a/src/rc3platform/settings/base.py b/src/rc3platform/settings/base.py
index 4ef40a5bd8ac6d4c3c6a8be1a05980e8c602bfcf..85f85aab470813be34c554a51ee056d435da4ac8 100644
--- a/src/rc3platform/settings/base.py
+++ b/src/rc3platform/settings/base.py
@@ -193,6 +193,9 @@ FORBIDDEN_ASSEMBLY_SLUGS = ['visit', 'maps', 'api', 'pusher']
 MAIL_REPLY_TO = []
 SUPPORT_HTML_MAILS = False
 
+# Sentry
+SENTRY_ENDPOINT = None
+
 # API access
 API_USERS = []
 
@@ -216,9 +219,12 @@ BIGBLUEBUTTON_API_URL = None
 BIGBLUEBUTTON_API_TOKEN = None
 BIGBLUEBUTTON_END_MEETING_CALLBACK = None  # url bbb will call to notify us of ending meetings
 # BIGBLUEBUTTON_END_MEETING_CALLBACK = 'https://rc3.world/api/bbb_meeting_end'
+BIGBLUEBUTTON_INITIAL_PRESENTATION_URL = None  # url to tell bbb to use as initial presentation
+# BIGBLUEBUTTON_INITIAL_PRESENTATION_URL = 'https://rc3.world/static/plainui/bbb-background.jpg'
 
 # Hangar
 HANGAR_URL = None
 
 # Workadventure
-WORKADVENTURE_URL_SCHEME = 'http://play.{assembly_slug}.localhost:8080/?u={username}'
+WORKADVENTURE_URL_SCHEME_GENERAL = 'http://visit.at.localhost:8080/'
+WORKADVENTURE_URL_SCHEME_ASSEMBLY = 'http://visit.at.localhost:8080/as/{assembly_slug}'
diff --git a/src/rc3platform/settings/default.py b/src/rc3platform/settings/default.py
index e325a00215849faae1b4876b610dd70a0a737629..97c7587b3b10f35edbec4c9003cb5252e17caadd 100644
--- a/src/rc3platform/settings/default.py
+++ b/src/rc3platform/settings/default.py
@@ -101,6 +101,22 @@ if IS_FRONTEND:
         },
     }] + TEMPLATES  # noqa F405
 
+# activate sentry upon request
+if SENTRY_ENDPOINT is not None:  # noqa F405
+    import sentry_sdk
+    from sentry_sdk.integrations.django import DjangoIntegration
+
+    sentry_sdk.init(
+        dsn=SENTRY_ENDPOINT,  # noqa F405
+        integrations=[DjangoIntegration()],
+
+        # trace sample rate in percent
+        traces_sample_rate=0.5 if not DEBUG else 1.0,
+
+        # do not send user information (django.contrib.auth)
+        send_default_pii=False
+    )
+
 if IS_API and SSO_SECRET is None:  # noqa F405
     SSO_SECRET = SECRET_KEY[::-1]
     print('*' * 72)
diff --git a/src/requirements.txt b/src/requirements.txt
index c542ed21dd5e1d2bab046692fc0181f711f287b5..b7fcede31021760b57a44f2520e62c7abb3dd2ed 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -16,3 +16,4 @@ Pygments >= 2.7.2, <3.0
 pyjwt >= 1.7.1, <2.0
 requests >= 2.25.0, <3.0
 pytz >= 2020.4
+sentry-sdk >= 0.19.5, <0.20