diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7a212b2e869bd9b4127929231d57ca74c4a3cd22..6cc89c48922c8da92e75d37d1c37d03b0fdd8815 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -69,6 +69,7 @@ build:
     - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
     - echo "{\"tag\":\"$CI_COMMIT_TAG\",\"commit\":\"$CI_COMMIT_SHA\",\"branch\":\"$CI_COMMIT_BRANCH\",\"ci\":true}" > src/version.json
     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:ci-$CI_PIPELINE_ID --single-snapshot
+    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --target nginx --destination $CI_REGISTRY_IMAGE/nginx:ci-$CI_PIPELINE_ID --single-snapshot
   only:
     - develop
     - production
@@ -166,6 +167,9 @@ test_image_frontend:
 
 publish:
   stage: publish
+  needs:
+    - test_image_api
+    - test_image_frontend
   image:
     name: gcr.io/go-containerregistry/crane:debug
     entrypoint: [""]
@@ -174,6 +178,7 @@ publish:
   script:
     - crane auth login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
     - crane tag $CI_REGISTRY_IMAGE:ci-${CI_PIPELINE_ID} $CI_COMMIT_REF_NAME
+    - crane tag $CI_REGISTRY_IMAGE/nginx:ci-${CI_PIPELINE_ID} $CI_COMMIT_REF_NAME
   rules:
     # skip if Deployment Freeze is active
     - if: $CI_DEPLOY_FREEZE != null
diff --git a/Dockerfile b/Dockerfile
index 818d64a107810044563db33fb6fed11b550b1dba..26b82448d195dac45c70e4363343a0179c43ffc8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.8-buster
+FROM python:3.8-buster as monolith
 
 VOLUME /data /media
 
@@ -6,21 +6,22 @@ ENV PYTHONDONTWRITEBYTECODE 1
 ENV PYTHONUNBUFFERED 1
 ENV LC_ALL=C.UTF-8
 ENV DJANGO_SETTINGS_MODULE=rc3platform.settings.default
+ENV DOCKER_UID=1000
 
 RUN apt-get update && \
     apt-get install -y --no-install-recommends \
         build-essential \
         gettext \
         locales \
-        nginx \
+        nginx-light \
         sudo \
         supervisor && \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/* && \
     dpkg-reconfigure locales && \
-	locale-gen C.UTF-8 && \
-	/usr/sbin/update-locale LANG=C.UTF-8 && \
-    useradd -ms /bin/bash -d /app appuser && \
+       locale-gen C.UTF-8 && \
+       /usr/sbin/update-locale LANG=C.UTF-8 && \
+    useradd -u $DOCKER_UID -ms /bin/bash -d /app appuser && \
     echo 'appuser ALL=(ALL) NOPASSWD:SETENV: /usr/bin/supervisord' >> /etc/sudoers && \
     echo 'appuser ALL=(ALL) NOPASSWD: /usr/bin/tail -F /var/log/nginx/error.log' >> /etc/sudoers && \
     chown appuser: /media
@@ -50,3 +51,28 @@ USER appuser
 EXPOSE 80
 ENTRYPOINT ["/usr/local/bin/app"]
 CMD ["all"]
+
+
+FROM docker.io/bitnami/minideb as base
+
+ENV DOCKER_UID=1000
+RUN useradd -u $DOCKER_UID -ms /bin/bash -d /app appuser
+VOLUME /data /media
+
+
+FROM base as nginx
+
+EXPOSE 80 443
+STOPSIGNAL SIGQUIT
+
+RUN install_packages nginx-light
+
+RUN chown -R appuser:appuser /var/lib/nginx /var/log/nginx
+
+COPY deployment/docker/nginx-standalone.conf /etc/nginx/nginx.conf
+COPY --from=monolith /app/static.dist /app/static.dist
+
+CMD ["nginx"]
+
+
+FROM monolith as default_image
diff --git a/README.md b/README.md
index 5be7f61af3430d5c8bac964ceb12f43695a11a5c..1e79d8061be2dca802886cc35fb6fd0328ee0e07 100644
--- a/README.md
+++ b/README.md
@@ -155,3 +155,20 @@ oder:
 ## Übersetzungen definineren
 * in Python: `gettext` wie üblich in Django, siehe [Doku](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#internationalization-in-python-code)
 * in jinja2-Templates via `_`, bspw. `{{ _("Settings") }}` oder mit Platzhalter `{{ _("Hello, %(username)s", username=user.display_name) }}`
+
+## Tests
+Es gibt Django (Unit-)Tests für die einzelnen Apps. Diese finden sich jeweils im `tests` Ordner der App.
+Außerdem gibt es im repository root unter [tests](tests) auch noch einfache Integrationstests.
+
+### Django Tests ausführen
+Um die Tests ausführen zu können muss der Datenbanknutzer das Recht haben neue Datenbaken anzulegen.
+Dafür mit `psql postgres` die Datenbankkonsole starten. Dort `ALTER USER rc3app CREATEDB;` ausführen (ggf. `rc3app` durch den gewählten Nutzernamen ersetzen). Am Ende mit `Strg-D` Konsole wieder schließen.
+
+Manche Tests benötigen, die kompilierten Übersetzungen. Deshalb ist es sinvoll vor der Testausführung `./manage.py compilemessages` auszuführen.
+
+Um alle Tests auszuführen in das `src` Verzeichnis wechseln und `./manage.py test` ausführen. \
+Um nur die Tests einer App auszführen stattdessen `./manage.py test <app>.tests` ausführen. \
+Hilfreiche Argumente sind `-v 2` um die ausgeführten Tests anzuzeigen und `--failfast` um nach dem ersten Fehler abzubrechen. \
+Für weitere Infos zu dem Befehl ist https://docs.djangoproject.com/en/3.1/ref/django-admin/#django-admin-test hilfreich.
+
+Um eine Ausführungsgebung zu verwenden die ähnlicher zu der der CI ist, kann `DJANGO_SETTINGS_MODULE=rc3platform.settings.ci ./manage.py test` verwendet werden.
diff --git a/deployment/docker/app.sh b/deployment/docker/app.sh
index c34d38b9e487aed507690c6154412de4a899d985..0fe5f684db7f0d84dc78fcdff48918ad878755c8 100644
--- a/deployment/docker/app.sh
+++ b/deployment/docker/app.sh
@@ -29,6 +29,7 @@ if [ "$1" == "webworker" ]; then
         --max-requests 1200 \
         --max-requests-jitter 50 \
         --log-level=info \
+        --bind=0.0.0.0:8000 \
         --bind=unix:/tmp/rc3platform.sock
 fi
 
diff --git a/deployment/docker/nginx-standalone.conf b/deployment/docker/nginx-standalone.conf
new file mode 100644
index 0000000000000000000000000000000000000000..450dbf77151bd49079185399caf48cb05a74e3f6
--- /dev/null
+++ b/deployment/docker/nginx-standalone.conf
@@ -0,0 +1,77 @@
+user appuser appuser;
+worker_processes auto;
+daemon off;
+
+events {
+	worker_connections 2048;
+}
+
+http {
+	server_tokens off;
+	sendfile on;
+	charset utf-8;
+	tcp_nopush on;
+	tcp_nodelay on;
+	client_max_body_size 50M;
+
+	types_hash_max_size 2048;
+	server_names_hash_bucket_size 64;
+
+	include /etc/nginx/mime.types;
+	default_type application/octet-stream;
+	add_header X-Content-Type-Options nosniff;
+
+	access_log /data/access.log combined;
+	error_log /data/error.log;
+	add_header Referrer-Policy same-origin;
+
+	gzip on;
+	gzip_disable "msie6";
+	gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml application/rdf+xml image/svg+xml;
+	gzip_vary on;
+	gzip_proxied any;
+	gzip_comp_level 6;
+	gzip_buffers 16 8k;
+
+	include /etc/nginx/conf.d/*.conf;
+
+	server {
+		listen 80 backlog=4096 default_server;
+		listen [::]:80 ipv6only=on default_server;
+		server_name _;
+
+		return 301 https://$host$request_uri;
+	}
+
+	server {
+		listen 443 backlog=4096 ssl http2;
+		listen [::]:443 ipv6only=on ssl http2;
+		server_name _;
+		index index.html;
+		root /var/www;
+
+		ssl_certificate     /data/fullchain.pem;
+		ssl_certificate_key /data/privkey.pem;
+
+		location /static/ {
+			alias /app/static.dist/;
+			access_log off;
+			expires 365d;
+			add_header Cache-Control "public";
+		}
+
+		location /media/ {
+			alias /media/;
+			autoindex off;
+			access_log off;
+			expires 365d;
+			add_header Cache-Control "public";
+		}
+
+		location / {
+			proxy_pass http://hub:8000/;
+			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+			proxy_set_header Host $http_host;
+		}
+	}
+}
diff --git a/deployment/docker/nginx.conf b/deployment/docker/nginx.conf
index ec4223f8a8efc900e04acfd9e65ad172073a54a7..8cfe255e6aafa4047846b0e48d8427fb19162e2a 100644
--- a/deployment/docker/nginx.conf
+++ b/deployment/docker/nginx.conf
@@ -1,6 +1,5 @@
-user www-data www-data;
-worker_processes 1;
-pid /var/run/nginx.pid;
+user appuser appuser;
+worker_processes auto;
 daemon off;
 
 events {
diff --git a/src/api/permissions.py b/src/api/permissions.py
index 5daddb88dc3542d793f106d56ee2d0c4dd64a66a..1859a68e036db313e3b6644de4734256648f36e2 100644
--- a/src/api/permissions.py
+++ b/src/api/permissions.py
@@ -1,3 +1,5 @@
+from django.conf import settings
+
 from rest_framework import permissions
 
 from core.models.assemblies import Assembly, AssemblyMember
@@ -35,6 +37,18 @@ class IsConferenceService(permissions.BasePermission):
             return False
 
 
+class IsApiUserOrReadOnly(permissions.BasePermission):
+    def has_permission(self, request, view):
+        if request.method in permissions.SAFE_METHODS:
+            return True
+
+        if settings.API_USERS is None:
+            return False
+
+        assert isinstance(settings.API_USERS, list)
+        return request.user.is_authenticated and request.user.username in settings.API_USERS
+
+
 class IsReadOnly(permissions.BasePermission):
     def has_permission(self, request, view):
         return request.method in permissions.SAFE_METHODS
diff --git a/src/api/schedule.py b/src/api/schedule.py
index 0bca9c43e1c286cbc5d1c68609a1bd70a006a3c6..f1acb9e14ba60abaa918d9c30af4f69c39fa3337 100644
--- a/src/api/schedule.py
+++ b/src/api/schedule.py
@@ -1,5 +1,6 @@
 import json
 import pytz
+import re
 
 from collections import OrderedDict
 from uuid import UUID
@@ -32,22 +33,24 @@ class ScheduleEncoder(json.JSONEncoder):
     tz = None
 
     def encode_event(self, event, tz=None):
-        start = event.schedule_start.astimezone(tz or self.tz)
-        duration = event.schedule_duration.seconds
+        start = event.schedule_start.astimezone(tz or self.tz) if event.schedule_start is not None else None
+        duration = event.schedule_duration.seconds if event.schedule_start is not None else None
+        additional_data = event.additional_data or {}
 
         return OrderedDict({
             **event_template,
-            **(event.additional_data or {}),
+            'id': additional_data.get('id') or int(re.sub('[^0-9]+', '', str(event.id))[0:6]),
+            'description': event.description,  # TODO: if the description also exists in additional_data it is overwritten due to concatination with abstract
+            'slug': event.slug,
+            **additional_data,
             'guid': event.id,
-            'date': start.isoformat(),
-            'start': start.strftime('%H:%M'),
-            'duration': '%d:%02d' % (duration/3600, duration % 3600/60),
-            'room': event.room.name,
-            # 'slug': None,  # TODO
+            'date': start.isoformat() if start is not None else None,
+            'start': start.strftime('%H:%M') if start is not None else None,
+            'duration': ('%d:%02d' % (duration / 3600, duration % 3600 / 60)) if duration is not None else None,
+            'room': event.room.name if event.room is not None else None,
             'title': event.name,
             'language': event.language,
             'track': event.track.name if event.track else None,
-            'description': event.description,  # TODO: duplicates abstract...
         })
 
     def encode_day(self, obj):
diff --git a/src/api/tests/__init__.py b/src/api/tests/__init__.py
index 63b4d921c6f34af9b38bc3ffc46446c07af9df8d..82daa5fb6d222d49d90eab1eb749750a57920fe0 100644
--- a/src/api/tests/__init__.py
+++ b/src/api/tests/__init__.py
@@ -1,4 +1,6 @@
+from .bbb import *  # noqa: F401, F403
 from .engelsystem import *  # noqa: F401, F403
+from .schedule import *  # noqa: F401, F403
 from .workadventure import *  # noqa: F401, F403
 
 __all__ = '*'
diff --git a/src/api/tests/bbb.py b/src/api/tests/bbb.py
new file mode 100644
index 0000000000000000000000000000000000000000..c285cbc3e17c5e3c81d4182a18a3338cc59c47df
--- /dev/null
+++ b/src/api/tests/bbb.py
@@ -0,0 +1,33 @@
+from uuid import uuid4
+from django.test import TestCase
+from django.urls import reverse
+
+from core.models import Assembly, Conference, Room
+
+
+class BBBTest(TestCase):
+    def test_MeetingEnded(self):
+        conf = Conference(slug='conf', name='TestConf')
+        conf.save()
+        assembly = Assembly(name='TestAssembly', slug='asmbly', conference=conf)
+        assembly.save()
+        room = Room(
+            conference=conf, assembly=assembly, name='Room 1',
+            room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.ACTIVE,
+            backend_link=str(uuid4()), backend_data={'close_secret': 'asdf'}
+        )
+        room.save()
+
+        self.client.get(reverse('api:bbb_meeting_end'), {
+            'meetingID': room.backend_link,
+            'close_secret': 'invalid',
+        })
+        room.refresh_from_db()
+        self.assertEqual(room.backend_status, Room.BackendStatus.ACTIVE)
+
+        self.client.get(reverse('api:bbb_meeting_end'), {
+            'meetingID': room.backend_link,
+            'close_secret': 'asdf',
+        })
+        room.refresh_from_db()
+        self.assertEqual(room.backend_status, Room.BackendStatus.INACTIVE)
diff --git a/src/api/tests/schedule.py b/src/api/tests/schedule.py
new file mode 100644
index 0000000000000000000000000000000000000000..6bd544d494d6a948103ce8529df925e249d1f406
--- /dev/null
+++ b/src/api/tests/schedule.py
@@ -0,0 +1,100 @@
+import json
+
+from django.test import TestCase
+from django.urls import reverse
+from rest_framework.authtoken.models import Token
+
+from core.models import Assembly, Conference, Event, PlatformUser, Room
+
+
+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.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)
+        self.room.save()
+
+        self.user = PlatformUser(username='bernd', is_active=True)
+        self.user.save()
+
+        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')
+        event.save()
+
+        update = {
+            "url": "https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html",
+            "id": 11583,
+            "guid": str(event.id),
+            "logo": None,
+            "date": "2020-12-27T12:20:00+01:00",
+            "start": "12:20",
+            "duration": "00:30",
+            "room": "foo room",
+            "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!",
+            "description": "",
+            "recording_license": "",
+            "do_not_record": False,
+            "persons": [{"id": 14151, "public_name": "blubbel"}],
+            "links": [],
+            "attachments": []
+        }
+
+        url = reverse('api:event-schedule', kwargs={'conference': self.conf.slug, 'pk': event.pk})
+
+        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('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
+
+    def test_push_new_event(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": "00:30",
+            "room": "foo room",
+            "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!",
+            "description": "",
+            "recording_license": "",
+            "do_not_record": False,
+            "persons": [{"id": 14151, "public_name": "blubbel"}],
+            "links": [],
+            "attachments": []
+        }
+
+        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(201, resp.status_code, f'Unexpected result from POST: {resp.content}')
+
+        self.assertTrue(Event.objects.filter(pk=update['guid']).exists())
+        event = Event.objects.get(pk=update['guid'])
+        self.assertTrue('rC3' in event.name, f'Expected "rC3" in event name "{event.name}".')
diff --git a/src/api/urls.py b/src/api/urls.py
index 2d9880ae6e4284a5122f28c8aff0ea9a5c8facc8..e13c6fde833c1ec788ae220f8af2ed58755401b9 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, conferencemember, conferences, events, rooms, users, schedule
+from .views import assemblies, bbb, conferencemember, conferences, events, rooms, users, schedule
 
 
 app_name = 'api'
@@ -39,6 +39,9 @@ urlpatterns = [
     # integration with other components
     path('c/<slug:conference>/is_angel/<str:username>', conferencemember.AngelView.as_view(), name='user-angel'),
     path('wa/<slug:conference>', conferencemember.WorkadventureView.as_view(), name='user-wa'),
+
+    # BBB meeting ended callback
+    path('bbb_meeting_end', bbb.MeetingEnded.as_view(), name='bbb_meeting_end'),
 ]
 
 urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/src/api/views/bbb.py b/src/api/views/bbb.py
new file mode 100644
index 0000000000000000000000000000000000000000..79876416a5cd1c1bdb3962c1a6ca5d8ce02ec3d1
--- /dev/null
+++ b/src/api/views/bbb.py
@@ -0,0 +1,17 @@
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponse
+from django.views import View
+
+from core.models import Room
+
+
+class MeetingEnded(View):
+    def get(self, request):
+        meeting_id = request.GET['meetingID']
+        close_secret = request.GET['close_secret']
+
+        room = get_object_or_404(Room.objects.filter(room_type=Room.RoomType.BIGBLUEBUTTON), backend_link=meeting_id)
+        if room.backend_data.get('close_secret') == close_secret:
+            room.backend_status = Room.BackendStatus.INACTIVE
+            room.save(update_fields=['backend_status'])
+        return HttpResponse()
diff --git a/src/api/views/rooms.py b/src/api/views/rooms.py
index cdfd050c5e3184ec1c43cb16cfaddba3b2e70ceb..b3b3b83301a83b596c22eba1619a093814f734eb 100644
--- a/src/api/views/rooms.py
+++ b/src/api/views/rooms.py
@@ -1,7 +1,7 @@
 import logging
 
 from django.http import Http404
-from rest_framework import generics, permissions
+from rest_framework import generics
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
@@ -18,7 +18,6 @@ logger = logging.getLogger(__name__)
 
 class ConferenceRoomList(ConferenceSlugMixin, generics.ListAPIView):
     serializer_class = RoomSerializer
-    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
 
     def get_queryset(self, **kwargs):
         return Room.objects.conference_accessible(conference=self.conference).order_by('name')
@@ -26,7 +25,6 @@ class ConferenceRoomList(ConferenceSlugMixin, generics.ListAPIView):
 
 class ConferenceRoomDetail(ConferenceSlugMixin, generics.RetrieveAPIView):
     serializer_class = RoomSerializer
-    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
 
     def get_object(self, **kwargs):
         room_id = self.request.resolver_match.kwargs['pk']
diff --git a/src/api/views/schedule.py b/src/api/views/schedule.py
index ce1ed6cf8fa5644840ec122e48509eeb50ea697e..e9249d4e4001796431cd94477a7b1a747bdb944e 100644
--- a/src/api/views/schedule.py
+++ b/src/api/views/schedule.py
@@ -1,14 +1,27 @@
-import pytz
-from rest_framework.response import Response
+from datetime import timedelta
+import logging
+
 from django.db.models import F
 from django.http import JsonResponse, HttpResponse
+from django.utils.dateparse import parse_datetime
 from django.views.generic import View
+from rest_framework import authentication
+from rest_framework.response import Response
+from rest_framework.views import APIView
+import pytz
 
 from core.models.events import Event
+from core.models.rooms import Room
+from core.models.conference import ConferenceTrack
+
+from ..permissions import IsApiUserOrReadOnly
 from ..schedule import Schedule, ScheduleEncoder
 from .mixins import ConferenceSlugMixin
 
 
+logger = logging.getLogger(__name__)
+
+
 def schedule_response(schedule, kwargs):
     if kwargs.get('format') == 'json':
         return JsonResponse(schedule, encoder=ScheduleEncoder, safe=False)
@@ -52,10 +65,77 @@ class RoomSchedule(ConferenceSlugMixin, View):
         return schedule_response(schedule, kwargs)
 
 
-class EventSchedule(ConferenceSlugMixin, View):
-    def get(self, *args, **kwargs):
+def schedulexml_time_to_timedelta(s):
+    if ':' in s:
+        hours, minutes = s.split(':')
+    else:
+        hours, minutes = 0, s
+
+    timedelta(hours=int(hours), minutes=int(minutes))
+
+
+class EventSchedule(ConferenceSlugMixin, APIView):
+    authentication_classes = [authentication.TokenAuthentication]
+    permission_classes = [IsApiUserOrReadOnly]
+
+    def get(self, request, conference, pk, format=None, **kwargs):
         tz = pytz.timezone(self.conference.timezone)
         event = Event.objects \
             .accessible_by_user(conference=self.conference, user=self.request.user) \
-            .get(pk=kwargs.get('pk'))
-        return Response(ScheduleEncoder.encode_event(event, tz))
+            .get(pk=pk)
+        return Response(ScheduleEncoder().encode_event(event, tz))
+
+    def post(self, request, conference, pk, format=None, **kwargs):
+        event = request.data
+        if len(event) == 0:
+            return Response({'error': 'No data.'}, status=400)
+
+        try:
+            obj = Event.objects.get(conference=self.conference, pk=pk)
+        except Event.DoesNotExist:
+            obj = Event(conference=self.conference, pk=pk)
+            logger.warning('Event schedule POST: id %s did not exist yet, creating.', pk)
+
+        try:
+            if 'guid' in event:
+                if event['guid'] != str(obj.pk):
+                    logger.warning('Attempted update of event %s with guid "%s".', obj.pk, event["guid"])
+                    return JsonResponse({'error': 'GUID mismatch.'})
+
+            if 'slug' in event:
+                obj.slug = event['slug']
+
+            obj.room = Room.objects.get(conference=self.conference, name__iexact=event['room'])
+            obj.assembly = obj.room.assembly
+            obj.kind = 'assembly' if not obj.room.assembly.is_official else 'official'
+            obj.is_public = True
+
+            if 'title' in event:
+                obj.name = event['title']
+
+            if 'language' in event:
+                obj.language = event['language']
+
+            obj.description = str(event['abstract']) + "\n\n" + str(event['description'])
+
+            obj.schedule_start = parse_datetime(event['date'])
+            obj.schedule_duration = schedulexml_time_to_timedelta(event['duration'])
+
+            obj.track = ConferenceTrack.objects.get(conference=self.conference, name__iexact=event['track'])
+
+            obj.additional_data = filter_additional_data(event)
+
+        except Room.DoesNotExist:
+            return Response({'error': 'Room {} does not exist'.format(event['room'])}, status=400)
+
+        except ConferenceTrack.DoesNotExist:
+            return Response({'error': 'Track {} does not exist'.format(event['track'])}, status=400)
+
+        obj.save()
+        logger.info('Event %s updated via POST by %s', obj, request.user)
+
+        return HttpResponse(status=201)
+
+
+def filter_additional_data(data):
+    return {k: v for k, v in data.items() if k not in ['guid', 'slug', 'room', 'start', 'date', 'duration', 'track']}
diff --git a/src/backoffice/forms.py b/src/backoffice/forms.py
index ade95ab8be3cb76d4f88d2b8885e394dfc6eade1..e4f3d7c6355c56d56a4f0778bdde827fa989ebd3 100644
--- a/src/backoffice/forms.py
+++ b/src/backoffice/forms.py
@@ -32,6 +32,7 @@ class ProfileForm(forms.ModelForm):
             'show_name',
             'description',
             'status', 'status_public',
+            'time_zone',
             'no_animations', 'colorblind', 'high_contrast',
             'receive_dms', 'receive_dm_images',
             'autoaccept_contacts',
@@ -337,3 +338,7 @@ class StaticPageForm(forms.Form):
         if self.cleaned_data['is_draft'] and self.cleaned_data['publish']:
             raise ValidationError(_('StaticPage__cannot_publish_draft'))
         return self.cleaned_data['publish']
+
+
+class AssignBadgeForm(forms.Form):
+    nickname = forms.CharField()
diff --git a/src/backoffice/locale/de/LC_MESSAGES/django.po b/src/backoffice/locale/de/LC_MESSAGES/django.po
index 324caab396f77aae0bd6a09e122932a877da09e6..888f387e9d1a970cbf0a3f4a99c497412615a55b 100644
--- a/src/backoffice/locale/de/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/de/LC_MESSAGES/django.po
@@ -96,13 +96,22 @@ msgstr "speichern"
 msgid "Badge"
 msgstr "Badge"
 
+msgid "Badge__award-title"
+msgstr "Badge zuweisen"
+
+msgid "Badge__award-explanation"
+msgstr "Diesen Badge direkt einem Benutzer zuweisen."
+
+msgid "Badge__award-btn"
+msgstr "Badge zuweisen"
+
 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."
 
 msgid "Badge-renew"
 msgstr "Token erneuern"
 
-msgid "Badge-assign-explanation"
+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)."
 
 msgid "Badge__remove"
@@ -642,6 +651,12 @@ msgstr "Für die OAuth2-Applikation wurde ein \"Client Secret\" generiert, diese
 msgid "Assembly__authentication__newtoken"
 msgstr "Ein neues Token für \"{assembly}\" wurde erstellt, an der API zu verwenden mit \"Authorization: Token {token}\"."
 
+msgid "Badge__awarded-to-user"
+msgstr "Badge zugewiesen an: "
+
+msgid "404__User Not Found: "
+msgstr "404 Benutzer nicht gefunden: "
+
 msgid "removed"
 msgstr "entfernt"
 
diff --git a/src/backoffice/locale/en/LC_MESSAGES/django.po b/src/backoffice/locale/en/LC_MESSAGES/django.po
index b5e41fe51cfda6d6f16b264339ca1810fb4b3fd2..8ab10ac7f18dfa5e02fcbe0169d839cdcbce4502 100644
--- a/src/backoffice/locale/en/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/en/LC_MESSAGES/django.po
@@ -96,13 +96,22 @@ msgstr "save"
 msgid "Badge"
 msgstr "badge"
 
+msgid "Badge__award-title"
+msgstr "Assign Badge"
+
+msgid "Badge__award-explanation"
+msgstr "You can assign this badge directly to a user by entering his name."
+
+msgid "Badge__award-btn"
+msgstr "Assign"
+
 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'."
 
 msgid "Badge-renew"
 msgstr "renew token"
 
-msgid "Badge-assign-explanation"
+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"
@@ -643,6 +652,12 @@ msgstr "For the OAuth2 application a new \"client secret\" has been generated. I
 msgid "Assembly__authentication__newtoken"
 msgstr "A new token for \"{assembly}\" has been generated, use it on the API with \"Authorization: Token {token}\"."
 
+msgid "Badge__awarded-to-user"
+msgstr "Badge zugewiesen an: "
+
+msgid "404__User Not Found: "
+msgstr "404 User Not Found: "
+
 msgid "removed"
 msgstr "removed"
 
diff --git a/src/backoffice/templates/backoffice/assembly_badge.html b/src/backoffice/templates/backoffice/assembly_badge.html
index 35f92502e555c3547198c2bf850538003bce45ba..496f4d410493b331ed8b681dddeb406d4be37107 100644
--- a/src/backoffice/templates/backoffice/assembly_badge.html
+++ b/src/backoffice/templates/backoffice/assembly_badge.html
@@ -1,5 +1,6 @@
 {% extends 'backoffice/base.html' %}
 {% load bootstrap4 %}
+{% load widget_tweaks %}
 {% load i18n %}
 
 {% block title %}
@@ -46,6 +47,25 @@
   </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-title' %}
+            </div>
+            <div class="card-body">
+                <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 class="row mb-3">
   <div class="col-md-12">
     <div class="card border-secondary">
@@ -53,12 +73,12 @@
       <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-explanation' %}</p>
+        <p>{% trans 'Badge-assign-api-explanation' %}</p>
       </div>
-    </div>
     </form>
   </div>
 </div>
+</div>
 
 {% if can_manage %}
 <div class="row mb-3">
diff --git a/src/backoffice/templates/backoffice/assembly_detail.html b/src/backoffice/templates/backoffice/assembly_detail.html
index c78809a72eb371d8e6195d6c3eae292150f911db..74fa805da38f8cf1568c09e6c1a7105f695fddcd 100644
--- a/src/backoffice/templates/backoffice/assembly_detail.html
+++ b/src/backoffice/templates/backoffice/assembly_detail.html
@@ -23,7 +23,7 @@
         {% endif %}
 
         <div id="description">
-          {{ assembly.description_html|default:"¯\_(ツ)_/¯" }}
+          {{ assembly.description_html|safe|default:"¯\_(ツ)_/¯" }}
         </div>
 
         {% if assembly.public_contact != None and assembly.public_contact != '' %}
diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index 7942101673647df1723e2f1be8c2ccdb3af07294..749620ccd09b90fedc7707102083eb6aa7c6a5c9 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -53,6 +53,7 @@ urlpatterns = [
     path('assembly/<uuid:assembly>/badge/<int:pk>', assemblies.BadgeView.as_view(), name='assembly-badge'),
     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>/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 42e8eceff4ae048ac1aa6e441e3d77a1c0ad7c7d..278026ce64f13298cf48002c8aa5394a500be24f 100644
--- a/src/backoffice/views/assemblies.py
+++ b/src/backoffice/views/assemblies.py
@@ -28,7 +28,9 @@ from ..forms import \
     AssemblyCreateForm, AssemblyCreateRoomGenericForm, AssemblyCreateRoomBigBlueButtonForm, AssemblyCreateRoomWorkAdventureForm, AssemblyCreateRoomHangarForm, \
     AssemblyEditForm, \
     CreateAssemblyRoomLinkForm, \
-    EditAssemblyRoomForm, EditAssemblyRoomWorkAdventureForm
+    EditAssemblyRoomForm, EditAssemblyRoomWorkAdventureForm, \
+    AssignBadgeForm
+
 from .mixins import ConferenceMixin, AssemblyMixin
 
 
@@ -503,6 +505,32 @@ class BadgesView(AssemblyMixin, ListView):
         return Badge.objects.filter(conference=self.conference, issuing_assembly=self.assembly)
 
 
+class AwardBadgeView(AssemblyMixin, FormView):
+    form_class = AssignBadgeForm
+    assembly_url_param = 'assembly'
+    assembly_management = True
+
+    def get_context_data(self, *args, **kwargs):
+        ctx = super().get_context_data(*args, **kwargs)
+        ctx['assign_form'] = AssignBadgeForm()
+        return ctx
+
+    def form_valid(self, form, **kwargs):
+        pk = self.kwargs['pk']
+        data = form.cleaned_data
+        try:
+            user = PlatformUser.objects.only('id', 'username').get(is_active=True, username=data['nickname'])
+            badge = get_object_or_404(Badge, pk=pk)
+            badge.award_to_user(user)
+            messages.success(self.request, _('Badge__awarded-to-user') + str(user))
+            logger.info(f'assigned badge { pk } to { user } by { self.request.user }')
+
+            return redirect('backoffice:assembly-badge', assembly=self.assembly.pk, pk=self.kwargs['pk'])
+        except PlatformUser.DoesNotExist:
+            messages.error(self.request, _('404__User Not Found: ') + data['nickname'])
+            return redirect('backoffice:assembly-badge', assembly=self.assembly.pk, pk=self.kwargs['pk'])
+
+
 class CreateBadgeView(AssemblyMixin, CreateView):
     model = Badge
     fields = ['name', 'is_achievement', 'image']
@@ -569,6 +597,11 @@ class BadgeView(AssemblyMixin, UpdateView):
 
     assembly_url_param = 'assembly'
 
+    def get_context_data(self, *args, **kwargs):
+        ctx = super().get_context_data(*args, **kwargs)
+        ctx['assign_form'] = AssignBadgeForm()
+        return ctx
+
     def get_queryset(self, *args, **kwargs):
         return Badge.objects.filter(conference=self.conference, issuing_assembly=self.assembly)
 
diff --git a/src/core/admin.py b/src/core/admin.py
index 8dbe821a63c0e6b39f0388e3d0fab28060af4603..670b8fd99bcf77c81e6c55f983ecae3c413630a8 100644
--- a/src/core/admin.py
+++ b/src/core/admin.py
@@ -72,7 +72,7 @@ class PlatformUserAdmin(UserAdmin):
     list_filter = ['user_type', 'is_active', 'is_staff']
 
     fieldsets = (
-        (None, {'fields': ('username', 'password', 'user_type')}),
+        (None, {'fields': ('username', 'password', 'user_type', 'time_zone')}),
         ('Personal info', {'fields': ('first_name', 'last_name', 'email')}),
         ('Legal stuff', {'fields': ('accepted_speakersagreement',)}),
         ('Self Portrayal', {'fields': (('pronouns', 'show_name'), ('status', 'status_public'), 'description', ('avatar_url', 'avatar_config'))}),
@@ -337,7 +337,7 @@ class RoomAdmin(admin.ModelAdmin):
     list_filter = ['conference', 'room_type', 'backend_status', 'blocked']
     search_fields = ['assembly__name', 'name']
     inlines = [RoomLinkInline]
-    readonly_fields = ['id', 'conference', 'occupants']
+    readonly_fields = ['id', 'conference', 'occupants', 'reserve_capacity']
     ordering = ('-conference__id', F('assembly__is_official').desc(nulls_last=True), 'assembly__name', F('capacity').desc(nulls_last=True), 'name')
 
     fieldsets = (
diff --git a/src/core/fields.py b/src/core/fields.py
index 4335fca9dda1eba850c0a9d1c7876ad8c4485082..b5cf6aebfafad87a5dfe173be369ea9ca96f4c73 100644
--- a/src/core/fields.py
+++ b/src/core/fields.py
@@ -1,6 +1,10 @@
+import pytz
+
 from django.db import models
 from django.utils.translation import gettext_lazy as _
 
+from . import form_fields
+
 
 class ConferenceReference(models.ForeignKey):
     def __init__(self, **kwargs):
@@ -33,3 +37,18 @@ class FskField(models.CharField):
         if kwargs is not None:
             options.update(kwargs)
         super().__init__(**options)
+
+
+class TimeZoneField(models.CharField):
+    default_choices = [(tz, tz) for tz in pytz.common_timezones]
+
+    def __init__(self, **kwargs):
+        options = {
+            'max_length': 63,
+        }
+        if kwargs is not None:
+            options.update(kwargs)
+        super().__init__(**options)
+
+    def formfield(self, form_class=None, choices_form_class=None, **kwargs):
+        return super().formfield(form_class=form_fields.TimeZoneField, choices=self.default_choices, **kwargs)
diff --git a/src/core/fixtures/anhalter.json b/src/core/fixtures/anhalter.json
index 2d1840f5cf031ad8fee2864762e77a99d15312e7..4b2e4914e16b37045f4948a51633d2353280de81 100644
--- a/src/core/fixtures/anhalter.json
+++ b/src/core/fixtures/anhalter.json
@@ -74,6 +74,28 @@
         "model": "core.conferencetag",
         "pk": 3
     },
+    {
+        "fields": {
+            "conference": "017c0749-a2ea-4f86-92cd-e60b4508dd98",
+            "slug": "guter Geschmack",
+            "value_type": "boolean",
+            "is_public": true,
+            "description": null
+        },
+        "model": "core.conferencetag",
+        "pk": 4
+    },
+    {
+        "fields": {
+            "conference": "017c0749-a2ea-4f86-92cd-e60b4508dd98",
+            "slug": "Autor des besten Gedichts",
+            "value_type": "string",
+            "is_public": true,
+            "description": null
+        },
+        "model": "core.conferencetag",
+        "pk": 5
+    },
     {
         "fields": {
             "conference": "017c0749-a2ea-4f86-92cd-e60b4508dd98",
@@ -164,4 +186,4 @@
         "model": "core.event",
         "pk": "35e10dfc-11c2-475b-b682-da1ddd291770"
     }
-]
\ No newline at end of file
+]
diff --git a/src/core/form_fields.py b/src/core/form_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f12fe63ded86d9e67d11c38fbfe30f96f9800ec
--- /dev/null
+++ b/src/core/form_fields.py
@@ -0,0 +1,15 @@
+import pytz
+from django import forms
+from django.utils.translation import gettext as _
+
+
+class TimeZoneField(forms.ChoiceField):
+    def __init__(self, max_length=None, **kwargs):
+        super().__init__(**kwargs)
+
+    def validate(self, value):
+        try:
+            pytz.timezone(value)
+        except pytz.UnknownTimeZoneError:
+            raise forms.ValidationError(_("Invalid Timezone")) from None
+        return super().validate(value)
diff --git a/src/core/integrations/__init__.py b/src/core/integrations/__init__.py
index ead3150ea37c17dc19aaf3ed688b6af170ff6fcd..8ded4750e683cc544adbdef7582db2ef484f20e8 100644
--- a/src/core/integrations/__init__.py
+++ b/src/core/integrations/__init__.py
@@ -5,9 +5,9 @@ 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)
+    BigBlueButton = BigBlueButtonIntegration(settings.BIGBLUEBUTTON_API_URL, settings.BIGBLUEBUTTON_API_TOKEN, settings.BIGBLUEBUTTON_END_MEETING_CALLBACK)
 else:
-    BigBlueButton = None
+    BigBlueButton = None  # type: BigBlueButtonIntegration
 
 if settings.HANGAR_URL is not None:
     Hangar = HangarIntegration(settings.HANGAR_URL)
diff --git a/src/core/integrations/bigbluebutton.py b/src/core/integrations/bigbluebutton.py
index 9f1b938b2c96ba10b784c3323f7db4024435f515..b7b4ed5ce66d6f9d06f564080da0e8665eb48f91 100644
--- a/src/core/integrations/bigbluebutton.py
+++ b/src/core/integrations/bigbluebutton.py
@@ -1,12 +1,39 @@
+from hashlib import sha1
+import logging
+from random import SystemRandom
 import requests
+import string
+from typing import Dict, Union
+from urllib.parse import urlencode, urljoin, quote
+from uuid import uuid4
+from xml.etree import ElementTree as ET
 
-from core.models.assemblies import Assembly
+from django.utils.translation import gettext as _
+
+from core.models.assemblies import Assembly, AssemblyMember
 from core.models.rooms import Room
 from core.models.users import PlatformUser
 
 from .error import IntegrationError
 
 
+logger = logging.getLogger(__name__)
+PASSWORD_CHARS = string.ascii_letters + string.digits
+
+
+def _params_to_str(data: Dict[str, Union[str, int, bool]]):
+    res = {}
+    for k, v in data.items():
+        if isinstance(v, bool):
+            v = 'true' if v else 'false'
+        elif isinstance(v, int):
+            v = str(v)
+        else:
+            assert isinstance(v, str), f'{v!r} is no string!'
+        res[k] = v
+    return res
+
+
 class BigBlueButtonIntegration(object):
     """
     This class talks with a BigBlueButton server's API.
@@ -15,14 +42,45 @@ class BigBlueButtonIntegration(object):
     https://docs.bigbluebutton.org/dev/api.html
     """
 
-    def __init__(self, api_url, api_token):
+    def __init__(self, api_url, api_token, end_meeting_callback):
         self._api_url = api_url
         self._api_token = api_token
+        self._end_meeting_callback = end_meeting_callback
         self._session = requests.session()
 
+    def _send_request(self, resource: str, params: Dict[str, str] = {}, raw=False):
+        encoded_params = urlencode(_params_to_str(params))
+        hash_input = resource + encoded_params + self._api_token
+        hash_input = hash_input.encode('utf-8')
+        checksum = sha1(hash_input).hexdigest()
+
+        if encoded_params:
+            params_str = encoded_params + '&checksum=' + checksum
+        else:
+            params_str = 'checksum=' + checksum
+        request_url = urljoin(self._api_url, resource)
+
+        resp = self._session.get(request_url, params=params_str, allow_redirects=False)
+        if raw:
+            return resp
+
+        if resp.status_code != 200:
+            logger.warning("Request for resource %r failed with status code %r", resource, resp.status_code)
+            raise IntegrationError(_("Request failed"))
+
+        try:
+            resp = ET.fromstring(resp.text)
+        except UnicodeDecodeError:
+            logger.exception("Response decoding failed")
+            raise IntegrationError(_("Invalid Response"))
+
+        return resp
+
     def is_available(self):
-        # TODO: do a proper check based an API response
-        return False
+        if not self._api_token:
+            return False
+
+        return True
 
     def can_create_for_assembly(self, assembly: Assembly):
         assert assembly is not None
@@ -31,26 +89,178 @@ class BigBlueButtonIntegration(object):
         return True
 
     def create_room(self, room: Room):
-        assert room is not None and room.room_type == Room.RoomType.BIGBLUEBUTTON and room.backend_status == Room.BackendStatus.NEW
+        assert room is not None and room.room_type == Room.RoomType.BIGBLUEBUTTON
+
+        if room.backend_status in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL}:
+            # room was already created, don't need to create it twice
+            return
 
         if not self.is_available():
             raise IntegrationError('Currently not available.')
 
-        raise NotImplementedError()
+        room.backend_status = Room.BackendStatus.SETUP
+        room.save(update_fields=['backend_status'])
+
+        room_id = str(uuid4())
+        join_pw = ''.join(SystemRandom().choices(PASSWORD_CHARS, k=32))
+        mod_pw = ''.join(SystemRandom().choices(PASSWORD_CHARS, k=32))
+        close_secret = ''.join(SystemRandom().choices(PASSWORD_CHARS, k=32))
+
+        if self._end_meeting_callback:
+            end_meeting_callback = f'{self._end_meeting_callback}?close_secret={quote(close_secret)}'
+        else:
+            end_meeting_callback = ''
+
+        params = {
+            'name': room.name,
+            'meetingID': room_id,
+            'attendeePW': join_pw,
+            'moderatorPW': mod_pw,
+            # 'maxParticipants': 100,
+            'record': False,
+            'muteOnStart': False,
+            'allowModsToUnmuteUsers': False,
+            'meta_endCallbackUrl': end_meeting_callback,
+        }
+        try:
+            result = self._send_request('create', params)
+        except Exception:
+            room.backend_status = Room.BackendStatus.ERROR
+            room.save(update_fields=['backend_status'])
+            raise
+
+        retcode = result.find('returncode')
+        if retcode is not None and retcode.text != 'SUCCESS':
+            room.backend_status = Room.BackendStatus.ERROR
+            room.save(update_fields=['backend_status'])
+            message = result.find('message')
+            if message is not None:
+                message = message.text
+            logger.warning("Request to create Room %s failed. Message: %r", room, message)
+            raise IntegrationError(_("Request failed"))
+
+        room.backend_link = result.find('meetingID').text
+        room.backend_status = Room.BackendStatus.ACTIVE
+        room.backend_data = {
+            'attendeePW': result.find('attendeePW').text,
+            'moderatorPW': result.find('moderatorPW').text,
+            'createTime': result.find('createTime').text,
+            'close_secret': close_secret,
+        }
+        room.occupants = 0
+        room.capacity = None
+
+        if room.backend_link != room_id:
+            logger.warning('Requested meetingID %s but got %s', room_id, room.backend_link)
+        if room.backend_data['attendeePW'] != join_pw:
+            logger.warning('Requested attendeePW %s but got %s', join_pw, room.backend_data['attendeePW'])
+        if room.backend_data['moderatorPW'] != mod_pw:
+            logger.warning('Requested moderatorPW %s but got %s', mod_pw, room.backend_data['moderatorPW'])
+
+        room.save(update_fields=['backend_data', 'backend_status', 'backend_link', 'occupants', 'capacity'])
+        return result
 
     def remove_room(self, room: Room):
         assert room is not None and room.room_type == Room.RoomType.BIGBLUEBUTTON
 
-        raise NotImplementedError()
+        if room.backend_status not in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL, Room.BackendStatus.INACTIVE}:
+            return
 
-    def room_status(self, room: Room):
+        mod_pw = room.backend_data.get('moderatorPW')
+        params = {
+            'meetingID': room.backend_link,
+            'password': mod_pw,
+        }
+        result = self._send_request('end', params)
+
+        retcode = result.find('returncode')
+        if retcode is not None and retcode.text != 'SUCCESS':
+            room.backend_status = Room.BackendStatus.ERROR
+            room.save(update_fields=['backend_status'])
+            message = result.find('message')
+            if message is not None:
+                message = message.text
+            logger.warning("Request to delete room %s failed. Message: %r", room, message)
+            raise IntegrationError(_("Request failed"))
+
+        room.backend_status = Room.BackendStatus.INACTIVE
+        room.save(update_fields=['backend_status'])
+        return result
+
+    def _room_status(self, room: Room, commit=True):
         assert room is not None and room.room_type == Room.RoomType.BIGBLUEBUTTON
 
-        raise NotImplementedError()
+        if room.backend_status not in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL, Room.BackendStatus.INACTIVE}:
+            return False
 
-    def join_room(self, room: Room, user: PlatformUser, anonymous: bool = False):
+        resp = self._send_request('getMeetingInfo', {'meetingID': room.backend_link})
+        retcode = resp.find('returncode')
+        if retcode is not None and retcode.text != 'SUCCESS':
+            message_key = resp.find('messageKey')
+            if message_key is not None and message_key.text == 'notFound':
+                room.backend_status = Room.BackendStatus.INACTIVE
+            else:
+                logger.warning("getMeetingInfo request for room %r failed with message %r", room, None if message_key is None else message_key.text)
+                raise IntegrationError(_("Request failed"))
+
+        try:
+            room.capacity = int(resp.find('maxUsers').text)
+        except (ValueError, AttributeError):
+            room.capacity = None
+        try:
+            room.occupants = int(resp.find('participantCount').text)
+        except (ValueError, AttributeError):
+            room.occupants = None
+
+        if commit:
+            room.save(update_fields=['backend_status', 'capacity', 'occupants'])
+
+    def room_status(self, room: Room):
+        return self._room_status(room, True)
+
+    def _join_room(self, room: Room, user: PlatformUser, anonymous: bool = False, retrying: bool = False):
         assert room is not None and room.room_type == Room.RoomType.BIGBLUEBUTTON
         assert user is not None
 
-        raise IntegrationError("Not Implemented Yet")
-        raise NotImplementedError()
+        create_room = False
+        if room.backend_status in {Room.BackendStatus.NEW, Room.BackendStatus.ERROR}:
+            create_room = True
+        else:
+            self._room_status(room, False)
+            if room.backend_status == Room.BackendStatus.FULL:
+                room.save(update_fields=['backend_status', 'capacity', 'occupants'])
+                return None
+            if room.backend_status == Room.BackendStatus.INACTIVE:
+                create_room = True
+
+        if create_room:
+            self.create_room(room)
+
+        if room.backend_status != Room.BackendStatus.ACTIVE:
+            return None  # can't join a nonexistent room
+
+        conf = room.conference
+        is_moderator = conf.users.filter(user=user, is_staff=True).exists()
+
+        if not is_moderator and room.assembly_id:
+            if AssemblyMember.objects.filter(assembly_id=room.assembly_id, member=user).exclude(role=AssemblyMember.Role.BLOCKED).exists():
+                is_moderator = True
+
+        if is_moderator:
+            join_pw = room.backend_data.get('moderatorPW')
+        else:
+            join_pw = room.backend_data.get('attendeePW')
+
+        params = {
+            'fullName': user.username,
+            'meetingID': room.backend_link,
+            'password': join_pw,
+            'userID': str(user.id),
+            'createTime': room.backend_data.get('createTime'),
+            'redirect': True,
+        }
+        result = self._send_request('join', params, raw=True)
+        return result.headers['Location']
+
+    def join_room(self, room: Room, user: PlatformUser, anonymous: bool = False):
+        return self._join_room(room, user, anonymous, False)
diff --git a/src/core/locale/de/LC_MESSAGES/django.po b/src/core/locale/de/LC_MESSAGES/django.po
index 78b9106efc54dc898c8b56a7e7e928489fcb34a3..2ba3b3a1edabb4f68915231c26f8c4d831f7e54e 100644
--- a/src/core/locale/de/LC_MESSAGES/django.po
+++ b/src/core/locale/de/LC_MESSAGES/django.po
@@ -60,6 +60,9 @@ msgstr "Angabe zur Eignung anhand des Publikum-Alters (nach deutschem Recht)"
 msgid "FSK"
 msgstr "Alterseinstufung"
 
+msgid "Invalid Timezone"
+msgstr "Fehlerhafte Zeitzone"
+
 msgid "Assembly"
 msgstr "Assembly"
 
@@ -228,6 +231,9 @@ msgstr "Favorisiert von"
 msgid "Assembly__technical_user__must_be_assembly"
 msgstr "Der technische Benutzer muss vom Typ 'Assembly' sein."
 
+msgid "Assembly__slug__is_forbidden"
+msgstr "Dieser Kurzname ist verboten!"
+
 msgid "AssemblyLink__type-related"
 msgstr "themenverwandt"
 
@@ -1029,6 +1035,12 @@ msgstr "den eigenen Status für Jedermann sichtbar machen"
 msgid "PlatformUser__status_public"
 msgstr "öffentlicher Status"
 
+msgid "PlatformUser__timezone__help"
+msgstr "Zeitzone, in der Uhrzeiten angezeigt werden"
+
+msgid "PlatformUser__timezone"
+msgstr "Zeitzone"
+
 msgid "PlatformUser__no_animations__help"
 msgstr "alle Animationen ausschalten, so wenig Gewackel wie möglich"
 
diff --git a/src/core/locale/en/LC_MESSAGES/django.po b/src/core/locale/en/LC_MESSAGES/django.po
index 0f74240110b388ea065e3f68ff28aa3c5d180db4..d415042cbe8c753fec3705d942da123539d03f66 100644
--- a/src/core/locale/en/LC_MESSAGES/django.po
+++ b/src/core/locale/en/LC_MESSAGES/django.po
@@ -60,6 +60,9 @@ msgstr "content is suitable for people of this age (based on Germany's law)"
 msgid "FSK"
 msgstr "age restriction"
 
+msgid "Invalid Timezone"
+msgstr "Invalid Timezone"
+
 msgid "Assembly"
 msgstr "Assembly"
 
@@ -228,6 +231,9 @@ msgstr "favorited by these users"
 msgid "Assembly__technical_user__must_be_assembly"
 msgstr "The technical user must be of type 'assembly'."
 
+msgid "Assembly__slug__is_forbidden"
+msgstr "this short name is forbidden"
+
 msgid "AssemblyLink__type-related"
 msgstr "related (similar topic)"
 
@@ -1029,6 +1035,12 @@ msgstr "show status to everyone"
 msgid "PlatformUser__status_public"
 msgstr "public status"
 
+msgid "PlatformUser__timezone__help"
+msgstr "timezone to use when displaying times"
+
+msgid "PlatformUser__timezone"
+msgstr "timezone"
+
 msgid "PlatformUser__no_animations__help"
 msgstr "do not show animations, try to skip anything wobbling/moving/whatever"
 
diff --git a/src/core/management/commands/bbb_integration_revisit.py b/src/core/management/commands/bbb_integration_revisit.py
new file mode 100644
index 0000000000000000000000000000000000000000..af4041322d03057260f4a826cda43689e924cf6e
--- /dev/null
+++ b/src/core/management/commands/bbb_integration_revisit.py
@@ -0,0 +1,45 @@
+import time
+
+from django.core.management.base import BaseCommand
+from django.db import transaction
+
+from core.models import Conference, Room
+from core.integrations import BigBlueButton, IntegrationError
+
+
+class Command(BaseCommand):
+    def add_arguments(self, parser):
+        parser.add_argument(
+            '-a', '--all', action='store_true',
+            help='Revisit all Rooms, not just failing ones',
+        )
+
+    def handle(self, *args, **options):
+        revisit_active = options['all']
+        conf = Conference.objects.current_conference
+
+        start = time.time()
+        n_fail = 0
+        n_healthy = 0
+
+        for room in Room.objects.filter(conference=conf, room_type=Room.RoomType.BIGBLUEBUTTON):
+            if room.backend_status in {Room.BackendStatus.NEW, Room.BackendStatus.ERROR, Room.BackendStatus.SETUP}:
+                n_fail += 1
+                try:
+                    with transaction.atomic():
+                        BigBlueButton.create_room(room)
+                        room.save()
+                except IntegrationError as e:
+                    print(f"Refreshing room {room!s} failed: {e!s}")
+
+            elif revisit_active and room.backend_status in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL}:
+                n_healthy += 1
+                try:
+                    with transaction.atomic():
+                        BigBlueButton.room_status(room)
+                        room.save()
+                except IntegrationError as e:
+                    print(f"Refreshing room {room!s} failed: {e!s}")
+
+        run_time = time.time() - start
+        print(f"Revisited {n_fail} failing rooms {f'and {n_healthy} healthy ones'} in {run_time} Seconds.")
diff --git a/src/core/middleware.py b/src/core/middleware.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d03464d86ffe7d6ee3558ebd11185ff9b3ea25b
--- /dev/null
+++ b/src/core/middleware.py
@@ -0,0 +1,15 @@
+import pytz
+
+from django.utils import timezone
+
+
+class TimezoneMiddleware:
+    def __init__(self, get_response):
+        self.get_response = get_response
+
+    def __call__(self, request):
+        if request.user.is_authenticated:
+            timezone.activate(pytz.timezone(request.user.time_zone))
+        else:
+            timezone.deactivate()
+        return self.get_response(request)
diff --git a/src/core/migrations/0047_longer_event_slugs.py b/src/core/migrations/0047_longer_event_slugs.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b150c019dfc590d0605358608aa40d78f16a798
--- /dev/null
+++ b/src/core/migrations/0047_longer_event_slugs.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.4 on 2020-12-25 18:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0046_additional_fields_for_apis'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='event',
+            name='slug',
+            field=models.SlugField(help_text='Event__slug__help', max_length=150, verbose_name='Event__slug'),
+        ),
+    ]
diff --git a/src/core/migrations/0048_Platformuser_time_zone.py b/src/core/migrations/0048_Platformuser_time_zone.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c72d324f440c6c36621fe8e56232b8c128950c5
--- /dev/null
+++ b/src/core/migrations/0048_Platformuser_time_zone.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.1.4 on 2020-12-25 20:51
+
+import core.fields
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0047_longer_event_slugs'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='platformuser',
+            name='time_zone',
+            field=core.fields.TimeZoneField(default='Europe/Berlin', help_text='PlatformUser__timezone__help', max_length=63, verbose_name='PlatformUser__timezone'),
+        ),
+    ]
diff --git a/src/core/migrations/0049_Event_repair_start.py b/src/core/migrations/0049_Event_repair_start.py
new file mode 100644
index 0000000000000000000000000000000000000000..b96c89c6c8282c035cca2f1180e07cc6bfec55d1
--- /dev/null
+++ b/src/core/migrations/0049_Event_repair_start.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.1.4 on 2020-12-25 18:43
+
+from datetime import timedelta
+
+from django.db import migrations
+
+
+def move_events_by(apps, delta):
+    Event = apps.get_model('core', 'Event')
+    for evt in Event.objects.all():
+        if evt.schedule_start:
+            evt.schedule_start = evt.schedule_start + delta
+            if evt.schedule_end:
+                evt.schedule_end = evt.schedule_end + delta
+            evt.save()
+
+
+def do(apps, schema_editor):
+    move_events_by(apps, timedelta(hours=-1))
+
+
+def undo(apps, schema_editor):
+    move_events_by(apps, timedelta(hours=1))
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0048_Platformuser_time_zone'),
+    ]
+
+    operations = [
+        migrations.RunPython(do, undo, elidable=True)
+    ]
diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py
index a7f4466bbc34955cb4a2f206a2b8d0dd3709b7cd..fcbf599818e3f3ba1c7902f88884e55c3effa898 100644
--- a/src/core/models/assemblies.py
+++ b/src/core/models/assemblies.py
@@ -2,6 +2,7 @@ import logging
 import re
 from uuid import uuid4
 
+from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.utils.html import escape as html_escape, format_html
@@ -248,6 +249,9 @@ class Assembly(TaggedItemMixin, models.Model):
             if self.technical_user.user_type != PlatformUser.Type.ASSEMBLY:
                 raise ValidationError({'technical_user': _('Assembly__technical_user__must_be_assembly')})
 
+        if self.slug in settings.FORBIDDEN_ASSEMBLY_SLUGS:
+            raise ValidationError({'slug': _('Assembly__slug__is_forbidden')})
+
     @cached_property
     def conference_slug(self):
         return self.conference.slug
@@ -341,6 +345,10 @@ class Assembly(TaggedItemMixin, models.Model):
             else:
                 self.description_html = render_markdown(self.description)
 
+        if update_fields is None or 'slug' in update_fields:
+            if self.slug in settings.FORBIDDEN_ASSEMBLY_SLUGS:
+                raise Exception("Won't save assembly with forbidden slug!")
+
         return super().save(*args, update_fields=update_fields, **kwargs)
 
 
diff --git a/src/core/models/events.py b/src/core/models/events.py
index 611a095d0d4659fc2b5d994433d5c87e3ee9fc0b..19b03e20b6409d27621893457745b59011abdced 100644
--- a/src/core/models/events.py
+++ b/src/core/models/events.py
@@ -4,6 +4,7 @@ from uuid import uuid4
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.db import models
+from django.utils import timezone
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
 
@@ -93,6 +94,7 @@ class Event(TaggedItemMixin, models.Model):
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
     conference = ConferenceReference(related_name='events')
     slug = models.SlugField(
+        max_length=150,
         help_text=_('Event__slug__help'),
         verbose_name=_('Event__slug'))
     kind = models.CharField(
@@ -173,6 +175,19 @@ 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')
 
+    @property
+    def starts_in(self):
+        """
+        Returns a timedelta between now and the scheduled start is in the future.
+        Returns false otherwise
+        """
+        if self.schedule_start is not None:
+            now = timezone.now()
+            if now > self.schedule_start:
+                return False
+            return self.schedule_start - now
+        return False
+
     def __str__(self):
         return self.name
 
diff --git a/src/core/models/pages.py b/src/core/models/pages.py
index f9116042cb4d51c59fefa140ef1b08e3b993aaaa..60730c3b47773331b6c76a81c6d31e99ac290839 100644
--- a/src/core/models/pages.py
+++ b/src/core/models/pages.py
@@ -26,6 +26,10 @@ class StaticPageManager(models.Manager):
         if user.is_superuser or user.is_staff:
             return qs
 
+        # content team can access non-public pages
+        if user.has_conference_staffpermission(self.conference, 'static_pages'):
+            return qs
+
         # return only pages which are marked public
         return qs.filter(public_revision__gt=0)
 
diff --git a/src/core/models/tags.py b/src/core/models/tags.py
index 96368d7c2a7779c9384a83f48edab681b3c7bbab..c2d7272009ea5c91966e103302845d2cbaddf0b7 100644
--- a/src/core/models/tags.py
+++ b/src/core/models/tags.py
@@ -66,7 +66,7 @@ class TagItem(models.Model):
             return self._value_int == 0
 
     @value.setter
-    def _set_value(self, new_value):
+    def value(self, new_value):
         self.set_value(new_value)
 
     def set_value(self, new_value):
diff --git a/src/core/models/users.py b/src/core/models/users.py
index 9eb0a264031edf9f6bae80ff329ffef11baf3b6a..3b23ae1200ebe6998afd47a458780158617f2550 100644
--- a/src/core/models/users.py
+++ b/src/core/models/users.py
@@ -14,7 +14,7 @@ from django.utils.functional import cached_property
 from django.utils.timezone import now
 from django.utils.translation import gettext_lazy as _
 
-from ..fields import ConferenceReference
+from ..fields import ConferenceReference, TimeZoneField
 from ..utils import render_markdown
 
 
@@ -71,6 +71,11 @@ class PlatformUser(AbstractUser):
         null=True,
         help_text=_('PlatformUser__status_public__help'),
         verbose_name=_('PlatformUser__status_public'))
+    time_zone = TimeZoneField(
+        help_text=_('PlatformUser__timezone__help'),
+        verbose_name=_('PlatformUser__timezone'),
+        default='Europe/Berlin',
+    )
 
     # Accessibility Options
     no_animations = models.BooleanField(
diff --git a/src/core/tests/__init__.py b/src/core/tests/__init__.py
index 0a2ae62164cf7f461b6416bb226e135606caef91..312ffef6bb1fe94d89b47874bca3de4ddd828669 100644
--- a/src/core/tests/__init__.py
+++ b/src/core/tests/__init__.py
@@ -1,4 +1,5 @@
 from .badges import *  # noqa: F401, F403
+from .bigbluebutton import *  # noqa: F401, F403
 from .events import *  # noqa: F401, F403
 from .search import *  # noqa: F401, F403
 from .users import *  # noqa: F401, F403
diff --git a/src/core/tests/bigbluebutton.py b/src/core/tests/bigbluebutton.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b1e384a28ec3de3f3f32f88fd29e9a19f117444
--- /dev/null
+++ b/src/core/tests/bigbluebutton.py
@@ -0,0 +1,240 @@
+from django.test import TestCase
+from unittest.mock import Mock, patch
+
+from core.integrations import BigBlueButtonIntegration, IntegrationError
+from core.models import Assembly, Conference, PlatformUser, Room
+
+
+BigBlueButton = BigBlueButtonIntegration('http://localhost/', 'asdf', 'https://localhost/end_meeting')
+
+
+# from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
+class Pattern(object):
+    def __req__(self, lhs):
+        return self.__eq__(lhs)
+
+    __hash__ = None
+
+
+# from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
+class Any(Pattern):
+    """ Equals everything """
+
+    def __eq__(self, rhs):
+        return True
+
+
+class BigBlueButtonTest(TestCase):
+    def setUp(self):
+        self.conf = Conference(slug='conf', name='TestConf')
+        self.conf.save()
+        self.assembly = Assembly(name='TestAssembly', slug='asmbly', conference=self.conf)
+        self.assembly.save()
+        self.room = Room(
+            conference=self.conf, assembly=self.assembly, name='Room 1',
+            room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.NEW
+        )
+        self.room.save()
+
+    def test_create_room(self):
+        with patch.object(BigBlueButton._session, 'get') as get_mock:
+            get_mock.return_value.status_code = 404
+            with self.assertRaises(IntegrationError), self.assertLogs('core.integrations.bigbluebutton'):
+                BigBlueButton.create_room(self.room)
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.ERROR)
+            get_mock.assert_called_once()
+            get_mock.reset_mock()
+
+            get_mock.return_value.status_code = 200
+            get_mock.return_value.text = (
+                    '<response>'
+                    '<returncode>SUCCESS</returncode>'
+                    '<meetingID>Test</meetingID>'
+                    '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
+                    '<parentMeetingID>bbb-none</parentMeetingID>'
+                    '<attendeePW>ap</attendeePW>'
+                    '<moderatorPW>mp</moderatorPW>'
+                    '<createTime>1531155809613</createTime>'
+                    '<voiceBridge>70757</voiceBridge>'
+                    '<dialNumber>613-555-1234</dialNumber>'
+                    '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
+                    '<hasUserJoined>false</hasUserJoined>'
+                    '<duration>0</duration>'
+                    '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
+                    '<messageKey>duplicateWarning</messageKey>'
+                    '<message>This conference was already in existence and may currently be in progress.</message>'
+                    '</response>'
+            )
+            self.room.backend_status = Room.BackendStatus.NEW
+            self.room.save()
+            with self.assertLogs('core.integrations.bigbluebutton'):
+                BigBlueButton.create_room(self.room)
+            get_mock.assert_called_once()
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.ACTIVE)
+            self.assertEqual(self.room.backend_link, 'Test')
+            self.assertEqual(self.room.backend_data, {
+                'attendeePW': 'ap',
+                'moderatorPW': 'mp',
+                'createTime': '1531155809613',
+                'close_secret': Any(),
+            })
+
+    def test_remove_room(self):
+        with patch.object(BigBlueButton._session, 'get') as get_mock:
+            get_mock.return_value.status_code = 404
+            self.room.backend_status = Room.BackendStatus.ACTIVE
+            self.room.backend_link = 'Test'
+            self.room.backend_data = {
+                'attendeePW': 'ap',
+                'moderatorPW': 'mp',
+                'createTime': '1531155809613',
+            }
+            self.room.save()
+            with self.assertRaises(IntegrationError), self.assertLogs('core.integrations.bigbluebutton'):
+                BigBlueButton.remove_room(self.room)
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.ACTIVE)
+            get_mock.assert_called_once()
+            get_mock.reset_mock()
+
+            get_mock.return_value.status_code = 200
+            get_mock.return_value.text = (
+                '<response>'
+                '<returncode>SUCCESS</returncode>'
+                '<messageKey>sentEndMeetingRequest</messageKey>'
+                '<message>'
+                'A request to end the meeting was sent.  Please wait a few seconds, and then use the getMeetingInfo or'
+                ' isMeetingRunning API calls to verify that it was ended'
+                '</message>'
+                '</response>'
+            )
+            self.room.backend_status = Room.BackendStatus.ACTIVE
+            self.room.save()
+            BigBlueButton.remove_room(self.room)
+            get_mock.assert_called_once()
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.INACTIVE)
+
+    def test_room_status(self):
+        self.room.backend_status = Room.BackendStatus.NEW
+        self.room.backend_link = 'Demo Meeting'
+        self.room.backend_data = {
+            'attendeePW': 'ap',
+            'moderatorPW': 'mp',
+            'createTime': '1531155809613',
+        }
+        self.room.capacity = None
+        self.room.occupants = None
+        self.room.save()
+
+        with patch.object(BigBlueButton._session, 'get') as get_mock:
+            BigBlueButton.room_status(self.room)
+            get_mock.assert_not_called()
+
+            self.room.backend_status = Room.BackendStatus.ACTIVE
+            get_mock.return_value.status_code = 200
+            get_mock.return_value.text = (
+                '<response>'
+                '<returncode>SUCCESS</returncode>'
+                '<meetingName>Demo Meeting</meetingName>'
+                '<meetingID>Demo Meeting</meetingID>'
+                '<internalMeetingID>183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1531155809613</internalMeetingID>'
+                '<createTime>1531155809613</createTime>'
+                '<createDate>Tue Jul 10 16:36:25 UTC 2018</createDate>'
+                '<voiceBridge>70066</voiceBridge>'
+                '<dialNumber>613-555-1234</dialNumber>'
+                '<attendeePW>ap</attendeePW>'
+                '<moderatorPW>mp</moderatorPW>'
+                '<running>true</running>'
+                '<duration>0</duration>'
+                '<hasUserJoined>true</hasUserJoined>'
+                '<recording>false</recording>'
+                '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
+                '<startTime>1531240585239</startTime>'
+                '<endTime>0</endTime>'
+                '<participantCount>2</participantCount>'
+                '<listenerCount>1</listenerCount>'
+                '<voiceParticipantCount>1</voiceParticipantCount>'
+                '<videoCount>1</videoCount>'
+                '<maxUsers>20</maxUsers>'
+                '<moderatorCount>1</moderatorCount>'
+                '<attendees>'
+                '<attendee>'
+                '<userID>w_2wzzszfaptsp</userID>'
+                '<fullName>stu</fullName>'
+                '<role>VIEWER</role>'
+                '<isPresenter>false</isPresenter>'
+                '<isListeningOnly>true</isListeningOnly>'
+                '<hasJoinedVoice>false</hasJoinedVoice>'
+                '<hasVideo>false</hasVideo>'
+                '<clientType>FLASH</clientType>'
+                '</attendee>'
+                '</attendees>'
+                '<metadata />'
+                '<isBreakout>false</isBreakout>'
+                '</response>'
+            )
+            BigBlueButton.room_status(self.room)
+            self.assertEqual(self.room.occupants, 2)
+            self.assertEqual(self.room.capacity, 20)
+            get_mock.reset_mock()
+
+            get_mock.return_value.status_code = 200
+            get_mock.return_value.text = (
+                '<response>'
+                '<returncode>FAILED</returncode>'
+                '<messageKey>notFound</messageKey>'
+                '<message>We could not find a meeting with that meeting ID</message>'
+                '</response>'
+            )
+            BigBlueButton.room_status(self.room)
+            self.assertEqual(self.room.occupants, None)
+            self.assertEqual(self.room.capacity, None)
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.INACTIVE)
+
+    def test_join_room(self):
+        user = PlatformUser(username='asdf')
+        user.save()
+
+        self.room.backend_status = Room.BackendStatus.ACTIVE
+        self.room.backend_link = 'Test'
+        self.room.backend_data = {
+            'attendeePW': 'ap',
+            'moderatorPW': 'mp',
+            'createTime': '1531155809613',
+        }
+        self.room.save()
+
+        with patch.object(BigBlueButton._session, 'get') as get_mock, self.assertLogs('core.integrations.bigbluebutton'):
+            get_mock.side_effect = [
+                Mock(status_code=200, text=(
+                    '<response>'
+                    '<returncode>FAILED</returncode>'
+                    '<messageKey>notFound</messageKey>'
+                    '<message>We could not find a meeting with that meeting ID</message>'
+                    '</response>'
+                )),
+                Mock(status_code=200, text=(
+                    '<response>'
+                    '<returncode>SUCCESS</returncode>'
+                    '<meetingID>Test</meetingID>'
+                    '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
+                    '<parentMeetingID>bbb-none</parentMeetingID>'
+                    '<attendeePW>ap</attendeePW>'
+                    '<moderatorPW>mp</moderatorPW>'
+                    '<createTime>1531155809613</createTime>'
+                    '<voiceBridge>70757</voiceBridge>'
+                    '<dialNumber>613-555-1234</dialNumber>'
+                    '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
+                    '<hasUserJoined>false</hasUserJoined>'
+                    '<duration>0</duration>'
+                    '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
+                    '<messageKey>duplicateWarning</messageKey>'
+                    '<message>This conference was already in existence and may currently be in progress.</message>'
+                    '</response>'
+                )),
+                Mock(status_code=302, headers={
+                    'Location': 'https://yourserver.com/client/BigBlueButton.html?sessionToken=ai1wqj8wb6s7rnk0'
+                }),
+            ]
+            url = BigBlueButton.join_room(self.room, user)
+            self.assertEqual(url, 'https://yourserver.com/client/BigBlueButton.html?sessionToken=ai1wqj8wb6s7rnk0')
+            self.assertEqual(self.room.backend_status, Room.BackendStatus.ACTIVE)
diff --git a/src/core/tests/markdown.py b/src/core/tests/markdown.py
index 5e1d4a836d5e1bd3578ca37c74e665c9d9960b3d..fc2b3f8074251f6597b26d86ed1e1b3a27d0956a 100644
--- a/src/core/tests/markdown.py
+++ b/src/core/tests/markdown.py
@@ -1,6 +1,7 @@
 from django.test import TestCase
 
 from ..utils import render_markdown
+from urllib.parse import quote
 
 
 class MarkdownTest(TestCase):
@@ -22,9 +23,15 @@ class MarkdownTest(TestCase):
         ]
         for test_url, should_be_local in tests:
             with self.subTest(test_url):
-                class_ = 'internal' if should_be_local else 'external'
-                self.assertEqual(render_markdown(f'[.]({test_url})'), f'<p><a class="{class_}" href="{test_url}">.</a></p>')
-                self.assertEqual(render_markdown(f'[.][1]\n\n[1]: {test_url}'), f'<p><a class="{class_}" href="{test_url}">.</a></p>')
+                if should_be_local:
+                    class_ = 'internal'
+                    href = test_url
+                else:
+                    href = '/rc3/dereferrer/' + quote(test_url)
+                    class_ = 'external'
+
+                self.assertEqual(render_markdown(f'[.]({test_url})'), f'<p><a class="{class_}" href="{href}">.</a></p>')
+                self.assertEqual(render_markdown(f'[.][1]\n\n[1]: {test_url}'), f'<p><a class="{class_}" href="{href}">.</a></p>')
 
         forbidden = [
             'data:text/html;charset=utf-8;base64,PGh0bWw+PGhlYWQ+PHRpdGxlPnRlc3Q8L3RpdGxlPjwvaGVhZD48Ym9keT48aDE+VGVzdDwvaDE+PC9ib2R5PjwvaHRtbD4=',
diff --git a/src/core/tests/tags.py b/src/core/tests/tags.py
index 30f196a3239ddf2bf9c0393af87b65a7447451d4..a11aaf4277e0f4b5f7ef26765df1b568ded021df 100644
--- a/src/core/tests/tags.py
+++ b/src/core/tests/tags.py
@@ -1,10 +1,11 @@
 from django.contrib.auth.models import AnonymousUser
+from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
 
 from ..models.assemblies import Assembly
 from ..models.conference import Conference
 from ..models.events import Event
-from ..models.tags import ConferenceTag, TaggedItemMixin
+from ..models.tags import ConferenceTag, TaggedItemMixin, TagItem
 
 
 class TaggingTests(TestCase):
@@ -90,3 +91,127 @@ class TaggingTests(TestCase):
     def testAssemblyTagging(self):
         assembly = Assembly.objects.filter(conference=self.conference).first()
         self._testTagManagement(assembly)
+
+
+class TagItemTests(TestCase):
+    fixtures = ['anhalter.json']
+
+    def setUp(self):
+        self.conference = Conference.objects.get(slug='vogc')
+        self.user = AnonymousUser()
+        self.event = Event.objects.filter(conference=self.conference).first()
+        self.tag_items = self._createTagItems()
+
+    def _createTagItem(self, tag_slug: str) -> TagItem:
+        tag = ConferenceTag.objects.get(conference=self.conference, slug=tag_slug)
+        return TagItem(tag=tag, target_type=ContentType.objects.get_for_model(type(self.event)), target_id=self.event.id)
+
+    def _createTagItems(self):
+        result = {}
+        for tag_slug in ['foo', 'bar', 'michelinstars', 'guter Geschmack', 'Autor des besten Gedichts']:
+            result[tag_slug] = self._createTagItem(tag_slug)
+
+        return result
+
+    def _validate_value_type(self, tag_item: TagItem):
+        value_type = tag_item.tag.value_type
+        value = tag_item.value
+        if value is None:
+            return
+        if value_type == ConferenceTag.Type.SIMPLE:
+            self.fail("Simple tag item did not return None as value")
+        if value_type == ConferenceTag.Type.STRING:
+            self.assertTrue(isinstance(value, str), "String tag item did not return None or a string as value")
+        if value_type == ConferenceTag.Type.INT:
+            self.assertTrue(isinstance(value, int), "Int tag item did not return None or an int as value")
+        if value_type == ConferenceTag.Type.BOOL:
+            self.assertTrue(isinstance(value, bool), "Bool tag item did not return None or a bool as value")
+
+    def test_get_value_check_type_uninitialized(self):
+        for tag_item in self.tag_items.values():
+            self._validate_value_type(tag_item)
+
+    def test_get_value_check_type_after_set(self):
+        for tag_item in self.tag_items.values():
+            value_type = tag_item.tag.value_type
+            if value_type == ConferenceTag.Type.STRING:
+                tag_item.value = "Test"
+            if value_type == ConferenceTag.Type.INT:
+                tag_item.value = 42
+            if value_type == ConferenceTag.Type.BOOL:
+                tag_item.value = True
+            self._validate_value_type(tag_item)
+
+    def test_value_check_get_after_set_intended(self):
+        for tag_item in self.tag_items.values():
+            value_type = tag_item.tag.value_type
+            value = None
+            if value_type == ConferenceTag.Type.STRING:
+                value = "Test"
+            if value_type == ConferenceTag.Type.INT:
+                value = 42
+            if value_type == ConferenceTag.Type.BOOL:
+                value = True
+            tag_item.value = value
+            self.assertEqual(tag_item.value, value)
+
+    def test_value_check_boolean_coerce(self):
+        tag = self.tag_items["guter Geschmack"]
+        for v in [False, 0, 'n', 'no', 'nein', 'false', 'off']:
+            tag.value = True
+            self.assertTrue(tag.value)
+            tag.value = v
+            self.assertFalse(tag.value, "Value {} did not yield false".format(v))
+
+        for v in [True, -2, -1, 1, 2, 3, 'y', 'j', 'yes', 'ja', 'true', 'on']:
+            tag.value = False
+            self.assertFalse(tag.value)
+            tag.value = v
+            self.assertTrue(tag.value, "Value {} did not yield true".format(v))
+
+    def test_value_set_check_simple_with_wrong_argument(self):
+        tag = self.tag_items["foo"]
+        with self.assertRaises(ValueError):
+            tag.value = self.user
+        with self.assertRaises(ValueError):
+            tag.value = "Test"
+        with self.assertRaises(ValueError):
+            tag.value = 42
+        with self.assertRaises(ValueError):
+            tag.value = True
+
+    def test_value_set_check_string_with_wrong_argument(self):
+        tag = self.tag_items["Autor des besten Gedichts"]
+        with self.assertRaises(ValueError):
+            tag.value = None
+        with self.assertRaises(ValueError):
+            tag.value = self.user
+        with self.assertRaises(ValueError):
+            tag.value = 42
+        with self.assertRaises(ValueError):
+            tag.value = True
+
+    def test_value_set_check_int_with_wrong_argument(self):
+        tag = self.tag_items["michelinstars"]
+        with self.assertRaises(ValueError):
+            tag.value = None
+        with self.assertRaises(ValueError):
+            tag.value = self.user
+        with self.assertRaises(ValueError):
+            tag.value = "Test"
+        # Python coerces bool to int by itself
+
+    def test_value_set_check_bool_with_wrong_argument(self):
+        tag = self.tag_items["guter Geschmack"]
+        with self.assertRaises(ValueError):
+            tag.value = None
+        with self.assertRaises(ValueError):
+            tag.value = self.user
+        with self.assertRaises(ValueError):
+            tag.value = "Test"
+        with self.assertRaises(ValueError):
+            tag.value = "foo"
+        with self.assertRaises(ValueError):
+            tag.value = ""
+        with self.assertRaises(ValueError):
+            tag.value = "bar"
diff --git a/src/core/utils.py b/src/core/utils.py
index d2621b93f12947bda69b20a8063571e87bef5a0c..98310093fc80fa25739536337c5dc53e47a62f64 100644
--- a/src/core/utils.py
+++ b/src/core/utils.py
@@ -5,7 +5,7 @@ import markdown
 from markdown.extensions import Extension as MarkdownExtension
 from markdown.inlinepatterns import LinkInlineProcessor, LINK_RE, ReferenceInlineProcessor, REFERENCE_RE
 import random
-from urllib.parse import urlparse
+from urllib.parse import urlparse, quote
 
 
 MARKDOWN_EXTENSIONS = [
@@ -20,8 +20,15 @@ TOKEN_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
 def _mark_link_tag(el):
     url = urlparse(el.get('href'))
     is_local_domain = url.netloc == 'rc3.world'
-    class_ = 'external' if (url.scheme or url.netloc) and not is_local_domain else 'internal'
-    el.set('class', class_)
+    is_external = (url.scheme or url.netloc) and not is_local_domain
+    if is_external:
+        if url.scheme and url.scheme in {'http', 'https', 'ftp', 'ftps'}:
+            href = el.get('href')
+            dereferrer_href = '/rc3/dereferrer/' + quote(href)
+            el.set('href', dereferrer_href)
+        el.set('class', 'external')
+    else:
+        el.set('class', 'internal')
 
 
 class CustomLinkInlineProcessor(LinkInlineProcessor):
diff --git a/src/plainui/forms.py b/src/plainui/forms.py
index c3b529451fe038f1b498086df0e8fe1d43e799ea..2d1834452e9f28d40e1282fe4e3f0a2aa45b339a 100644
--- a/src/plainui/forms.py
+++ b/src/plainui/forms.py
@@ -5,6 +5,7 @@ from django.contrib.sites.shortcuts import get_current_site
 from django.contrib.auth import forms as auth_forms
 from django.core.mail import send_mail
 from django.forms import ValidationError
+from django.db.models import Q
 from django.template import loader
 from django.urls import reverse
 from django.utils.formats import localize
@@ -52,7 +53,7 @@ class ExampleForm(forms.Form):
 class ProfileEditForm(forms.ModelForm):
     class Meta:
         model = PlatformUser
-        fields = ['pronouns', 'description', 'high_contrast', 'receive_audio', 'receive_video']
+        fields = ['pronouns', 'description', 'time_zone', 'high_contrast', 'receive_audio', 'receive_video']
 
 
 class InputTokenForm(forms.Form):
@@ -99,20 +100,23 @@ class RoomChoiceField(forms.ModelChoiceField):
 
 
 class SelfOrganizedSessionForm(forms.Form):
+    """
+    TODO: Will explode if the related event has no start or end date yet.
+    """
     name = forms.CharField(max_length=200, min_length=1, strip=True, widget=forms.TextInput(attrs={'placeholder': _("Please enter a name")}))
-    room = RoomChoiceField(queryset=Room.objects.none())
+    room = RoomChoiceField(queryset=Room.objects.none(), help_text=_("BBB or Workshop Rooms only."))
     description = forms.CharField(widget=forms.Textarea)
     language = forms.CharField(max_length=50)
     is_public = forms.BooleanField(required=False)
-    schedule_start = forms.DateTimeField(required=True)
-    schedule_duration = forms.DurationField(required=True)
+    schedule_start = forms.DateTimeField(required=True, help_text=_("ISO 8601 format, e.g. '2020-12-27 13:37'"))
+    schedule_duration = forms.DurationField(required=True, help_text=_("'HH:MM' or even 'DD HH:MM' e.g. 3hours ='03:00'"))
 
     def __init__(self, conf, assembly, event=None, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.conf = conf
         self.assembly = assembly
         self.event = event
-        self.fields['room'].queryset = assembly.rooms.filter(room_type=Room.RoomType.BIGBLUEBUTTON)
+        self.fields['room'].queryset = assembly.rooms.filter(Q(room_type=Room.RoomType.BIGBLUEBUTTON) | Q(room_type=Room.RoomType.WORKSHOP))
 
     def _check_schedule_end(self):
         if 'schedule_start' in self.cleaned_data and 'schedule_duration' in self.cleaned_data:
@@ -132,7 +136,7 @@ class SelfOrganizedSessionForm(forms.Form):
             if self.event is not None:
                 blocking_events = blocking_events.exclude(pk=self.event.pk)
             if blocking_events.exists():
-                self.add_error('room', ValidationError(gettext("Room is not free!"), code='invalid'))
+                self.add_error('room', ValidationError(gettext("Room is not free, choose other time or room"), code='invalid'))
 
     def clean_schedule_start(self):
         if self.cleaned_data['schedule_start'] < self.conf.start:
diff --git a/src/plainui/jinja2.py b/src/plainui/jinja2.py
index d69d5f3c046eb89d160e740cd92b9392705b100c..7470c509f996be3834214c8a01e7fcad85c2ea9e 100644
--- a/src/plainui/jinja2.py
+++ b/src/plainui/jinja2.py
@@ -6,8 +6,9 @@ from django.utils.formats import localize
 from django.utils.functional import LazyObject
 from django.utils.timezone import localdate, localtime
 from django.utils.translation import ugettext, ungettext, get_language
+from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter
 
-from jinja2 import contextfilter, contextfunction, Environment, Markup
+from jinja2 import contextfunction, Environment
 
 
 def url(name, *args, current_app=None, **kwargs):
@@ -30,23 +31,22 @@ def num_of_unread_messages(request):
     return user.received_messages.filter(was_read=False).count()
 
 
-@contextfilter
-def custom_strftime(ctx, date):
+def custom_timedelta(tdelta):
+    return NaturalTimeFormatter.string_for(tdelta)
+
+
+def custom_strftime(date):
     if not isinstance(date, datetime):
         return ''
 
-    return Markup('<script>document.write(new Date(%d).toLocaleString())</script>' % (date.timestamp() * 1000,)) + \
-        Markup('<noscript>') + localize(localtime(date)) + Markup('</noscript>')
+    return localize(localtime(date))
 
 
-@contextfilter
-def custom_strfdate(ctx, date):
+def custom_strfdate(date):
     if not isinstance(date, datetime):
         return ''
 
-    ret = Markup('<script>document.write(new Date(%d).toLocaleDateString())</script><noscript>' % (date.timestamp() * 1000,))
-    ret += localize(localdate(date)) + Markup('</noscript>')
-    return ret
+    return localize(localdate(date))
 
 
 # set up an internal represenative for an unset variable as parameter for show_vars()
@@ -104,6 +104,7 @@ def environment(**options):
         'url': url,
         'show_vars': show_vars,
     })
+    env.filters['strftdelta'] = custom_timedelta
     env.filters['strftime'] = custom_strftime
     env.filters['strfdate'] = custom_strfdate
     env.install_gettext_callables(ugettext, ungettext, newstyle=True)
diff --git a/src/plainui/jinja2/plainui/assemblies.html b/src/plainui/jinja2/plainui/assemblies.html
index 24804daf9caab92da0a7c18d91457b8de5b4e862..cf90119f6b94bf6d6c2ab61809f40b921a655c87 100644
--- a/src/plainui/jinja2/plainui/assemblies.html
+++ b/src/plainui/jinja2/plainui/assemblies.html
@@ -41,7 +41,7 @@
 
     <h2>{{ _("upcoming events") }}</h2>
     <div class="border border-tertiary p-6">
-        {{ list_events.tiles(events_upcoming, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.slider(events_upcoming, is_favorite_events, is_scheduled_events ) }}
     </div>
 
     <hr class="my-5">
@@ -49,7 +49,7 @@
     <h2>{{ _("recommended events") }}</h2>
     <div class="border border-tertiary p-6">
         TODO: implement recommendation algo. Actual displayes faved events
-        {{ list_events.tiles(events_recommended, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.grid(events_recommended, is_favorite_events, is_scheduled_events ) }}
     </div>
 
     <hr class="my-5">
diff --git a/src/plainui/jinja2/plainui/assemblies_all.html b/src/plainui/jinja2/plainui/assemblies_all.html
index 48b1e935c846a0065278780177ec571663d7354d..bca09a48550720cbc4d2b835cf7e2170e4c04736 100644
--- a/src/plainui/jinja2/plainui/assemblies_all.html
+++ b/src/plainui/jinja2/plainui/assemblies_all.html
@@ -6,13 +6,38 @@
     {{ titleMacro.title(title=_("all assemblies"),
         share_url = url('plainui:assemblies_all', conf_slug=conf.slug),
         report_url = url('plainui:assemblies_all', conf_slug=conf.slug)) }}
-    <a href="{{ url('plainui:assemblies', conf_slug=conf.slug) }}">
-        assemblies start seite
-    </a>
 
-    TODO: Filter assemblies
+    <div class="mb-2">
+        <a href="{{ url('plainui:assemblies', conf_slug=conf.slug) }}" role="button" class="btn btn btn-secondary">
+            {{ _("assemblies startseite") }}
+        </a>
+        {# actually we don't need a filter here. but maybe coming.
+        <div class="float-right">
+            {%  if qfilter != "official" %}
+                <a href="{{ url('plainui:assemblies_official', conf_slug=conf.slug) }}" role="button" class="btn btn-outline-secondary">
+                    {{ _("assemblies_official_only") }}
+                </a>
+            {%  else  %}
+                <a href=""{{ url('plainui:assemblies_all', conf_slug=conf.slug) }}"" role="button" class="btn btn-secondary">
+                    {{ _("assemblies_official_only") }}
+                </a>
+            {% endif %}
 
-    <div class="border border-tertiary p-6">
+            {%  if qfilter != "community" %}
+                <a href="{{ url('plainui:assemblies_community', conf_slug=conf.slug) }}" role="button" class="btn btn-outline-secondary">
+                    {{ _("assemblies_community_only") }}
+                </a>
+            {%  else  %}
+                <a href="{{ url('plainui:assemblies_all', conf_slug=conf.slug) }}" role="button" class="btn btn-secondary">
+                    {{ _("assemblies_community_only") }}
+                </a>
+            {% endif %}
+        </div> #}
+    </div>
+    <div class="border border-tertiary p-6 my-8">
         {{ list_assm.list(assemblies, my_favorite_assemblies) }}
     </div>
+
+    <hr class="border-top-0 my-11">
+
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/assemblies_events.html b/src/plainui/jinja2/plainui/assemblies_events.html
index cbf2f0e4c02fc6c1593b0c42da7ddf841cf7e20a..1bcc85cd3fa2933f43bcb1fcfe50edf9c6d19d07 100644
--- a/src/plainui/jinja2/plainui/assemblies_events.html
+++ b/src/plainui/jinja2/plainui/assemblies_events.html
@@ -5,22 +5,20 @@
 {% block content %}
     {{ titleMacro.title(title=_("assembly events"),
         share_url = url('plainui:assemblies_events', conf_slug=conf.slug),
-        report_url = url('plainui:assemblies_events', conf_slug=conf.slug)) }}
-    <a href="{{ url('plainui:assemblies', conf_slug=conf.slug) }}">
-        assemblies start seite
-    </a>
+        report_url = url('plainui:assemblies_events', conf_slug=conf.slug))
+    }}
 
-    <h2>{{ _("running and upcoming events") }}</h2>
+    <h2>{{ _("Running and Upcoming Events") }}</h2>
     <div class="border border-tertiary p-6">
-        {{ list_events.tiles(events_upcoming, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.slider(events_upcoming, is_favorite_events, is_scheduled_events ) }}
     </div>
 
-    <hr class="my-5 border-tertiary">
+    <hr class="my-8">
 
-    <h2>{{ _("all assemblies events") }}</h2>
+    <h2>{{ _("All Assemblies Events") }}</h2>
     <div class="border border-tertiary p-6">
         {{ list_events.list(events_from_assemblies, is_favorite_events, is_scheduled_events ) }}
     </div>
 
-
+    <hr class="border-top-0 my-11">
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/assembly.html b/src/plainui/jinja2/plainui/assembly.html
index 1b5bc5c4de1f20697ff46dee2e8f0acb3ef921a8..5ca3ce53ba0bb0cec19491455e2e880b6b2db43e 100644
--- a/src/plainui/jinja2/plainui/assembly.html
+++ b/src/plainui/jinja2/plainui/assembly.html
@@ -17,17 +17,22 @@
         , share_url = url('plainui:assembly', conf_slug=conf.slug, assembly_slug = assembly.slug)
         )
     }}
+    <div class="d-flex mb-8">
+        <a href="{{ url('plainui:assemblies', conf_slug=conf.slug) }}" role="button" class="mr-2 btn btn-secondary">
+            assemblies start seite
+        </a>
+        <a class="mr-2 col-auto btn btn-secondary" href="{{ url('plainui:assemblies_all', conf_slug=conf.slug) }}" role="button">all assemblies</a>
+    </div>
+
     {% if assembly.banner_image %}
         {{ imageMacro.image(image=assembly.banner_image.url, alt=assembly.banner_image.name, title=assembly.banner_image.name) }}
     {% endif %}
     {% if assembly.description != None and assembly.description != "" -%}
         {{- markdownMacro.markdown(markdown=assembly.description_html | safe) -}}
-        <hr>
+        <hr class="my-8 border-tertiary">
     {% endif %}
-    <a class="col-auto" href="{{ url('plainui:assemblies_all', conf_slug=conf.slug) }}">all assemblies</a>
-    <a href="{{ url('plainui:assemblies', conf_slug=conf.slug) }}">
-        assemblies start seite
-    </a>
+
+
     <div class="row">
         <div class="col">
             {{ tagboxMacro.tagbox(conf.slug, tags) }}
@@ -37,30 +42,30 @@
         </div>
     </div>
 
-    <hr class="my-5 border-tertiary">
+    <hr class="my-8 border-tertiary">
 
-    <h2>{{ _("assmebly rooms") }}</h2>
+    <h2>{{ _("assembly rooms") }}</h2>
     <div class="border border-tertiary p-6">
         {{ list_rooms.list(assembly.rooms.all() ) }}
     </div>
 
-    <hr class="my-5 border-tertiary">
+    <hr class="my-8 border-tertiary">
 
     <h2>{{ _("assembly events") }}</h2>
     <div class="border border-tertiary p-6">
-        {{ list_events.tiles(events, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.grid(events, is_favorite_events, is_scheduled_events ) }}
     </div>
 
-    <hr class="my-5 border-tertiary">
+    <hr class="my-8 border-tertiary">
 
-    <h2 class="">{{ _("selforganized sessions") }}</h2>
+    <h2 class="">{{ _("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 Selforganized Session") }}</a>
+        <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 %}
-    <div class="border border-tertiary p-6">
+    <div class="border border-tertiary p-6 mb-8">
         {{ list_events.list(sos, is_favorite_events, is_scheduled_events ) }}
         {% if sos_private %}
-            <h3 class="bg bg-info p-2 px-5 text-white text-center">non public Selforganized Sessions</h3>
+            <h3 class="mt-4 bg bg-info p-2 px-5 text-white text-center">non public Selforganized Sessions</h3>
             {{ list_events.list(sos_private, is_favorite_events, is_scheduled_events ) }}
         {% endif %}
     </div>
diff --git a/src/plainui/jinja2/plainui/base.html b/src/plainui/jinja2/plainui/base.html
index 9e64c25912c439cc80c364cef7c1ba88588f3b72..883f9922dcdb98650639f8627806347175569c89 100644
--- a/src/plainui/jinja2/plainui/base.html
+++ b/src/plainui/jinja2/plainui/base.html
@@ -1,12 +1,19 @@
 {% import "plainui/components/logo.html" as logoMacro %}
-{% import "plainui/components/title.html" as titleMacro with context%}
+{% import "plainui/components/title.html" as titleMacro with context %}
 <!DOCTYPE html>
-<html lang="{{ get_language() }}">
+<html lang="{{ get_language() }}" class="no-js">
     <head>
         <meta charset="utf-8">
         <link rel="stylesheet" href="{{ static('plainui/%s.css' % (css_scope(),)) }}">
         <title>{% block title %}{% endblock %}</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>
         <ul class="sr-only">
@@ -34,9 +41,9 @@
                     {% if get_messages(request) %}
                     <div id="messages">
                         {% for message in get_messages(request) %}
-                        <div class="alert{% if message.tags %} alert-{% if message.tags == 'error' %}danger{% else %}{{ message.tags }}{% endif %}{% endif %}" role="alert">
+                        <p class="alert my-8{% if message.tags %} alert-{% if message.tags == 'error' %}danger{% else %}{{ message.tags }}{% endif %}{% endif %}" role="alert">
                             {{ message }}
-                        </div>
+                        </p>
                         {% endfor %}
                     </div>
                     {% endif %}
diff --git a/src/plainui/jinja2/plainui/ccc_events.html b/src/plainui/jinja2/plainui/ccc_events.html
index 89538085e4cc5487165bbc23ce458c5c24018a43..b8d9d15c7c6f14245a25e341b9fb350d3afb1a36 100644
--- a/src/plainui/jinja2/plainui/ccc_events.html
+++ b/src/plainui/jinja2/plainui/ccc_events.html
@@ -3,7 +3,6 @@
 {% 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 %}
 {% block content %}
     {{ titleMacro.title(title=_("Curated Events"),
diff --git a/src/plainui/jinja2/plainui/component_gallery.html b/src/plainui/jinja2/plainui/component_gallery.html
index 44f5a06eee9cef14a2840cb7ff0af695363f8e33..2c5dec198b62d8984d1dda2a4141d50aecc9ab21 100644
--- a/src/plainui/jinja2/plainui/component_gallery.html
+++ b/src/plainui/jinja2/plainui/component_gallery.html
@@ -8,9 +8,7 @@
 {% import "plainui/components/resourcesbox.html" as resboxMacro %}
 {% import "plainui/components/tagbox.html" as tagboxMacro %}
 {% import "plainui/components/listbox.html" as listboxMacro %}
-{% import "plainui/components/tile.html" as tileMacro %}
 {% import "plainui/components/tile_board.html" as tileBoardMacro %}
-{% import "plainui/components/tilesbox.html" as tilesboxMacro %}
 {% import "plainui/components/image.html" as imageMacro %}
 {% import "plainui/components/form_elements.html" as formElementsMacro %}
 {% import "plainui/components/three_cards.html" as threeCardsMacro %}
@@ -25,11 +23,11 @@
 
 {% set event1 = {"id": "1", "name": "event example 1", "slug": "event_slug1", "banner_image": {"url": image_url}, "schedule_start": time1, "schedule_end": time2, "schedule_duration": duration, "description": "Lorem Ipsum ...", "language": "en" } %}
 {% set event2 = {"id": "2", "name": "event example 2", "slug": "event_slug2", "banner_image": {"url": image_url}, "schedule_start": time1, "schedule_end": time2, "schedule_duration": duration, "description": "Lorem Ipsum ...", "language": "de" } %}
-{% set events = [ event1, event2 ] %}
+{% set events = [ event1, event2, event1, event2 ] %}
 
 {% set assembly1 = {"id": "1", "name": "assembly example 1", "slug": "assembly_slug1", "is_official": false,"banner_image": {"url": image_url}, "description": "Lorem Ipsum ..." } %}
 {% set assembly2 = {"id": "2", "name": "assembly example 2 - official", "slug": "assembly_slug2", "is_official": true, "banner_image": {"url": image_url}, "description": "Lorem Ipsum ..." } %}
-{% set assemblies = [ assembly1, assembly2 ] %}
+{% set assemblies = [ assembly1, assembly2, assembly1, assembly2, assembly1, assembly2 ] %}
 
 {% set room1 = {"id": "8387a222-536d-4bb6-b15a-9b3688fda7d9", "name": "room example 1", "room_type": "bbb", "capacity": 42, "occupants": 23 } %}
 {% set room2 = {"id": "8387a222-536d-4bb6-b15a-9b3688fda7a5", "name": "room example 2", "room_type": "workshop", "capacity": 42 } %}
@@ -72,7 +70,7 @@
 
     <dt class="h2 pb-3 mb-3 border-bottom">Slider</dt>
     <dd class="mb-10">
-        {{ sliderMacro.slider(title="Slider") }}
+        {{ sliderMacro.slider(items=['eins', 'zwei', '3', '4']) }}
     </dd>
 
     <dt class="h2 pb-3 mb-3 border-bottom">Livestream</dt>
@@ -116,6 +114,16 @@
         {{ list_assm.list(assemblies, ['2']) }}
     </dd>
 
+    <dt class="h2 pb-3 mb-3 border-bottom">Assembly Grid</dt>
+    <dd class="mb-10">
+        {{ list_assm.grid(assemblies, ['2']) }}
+    </dd>
+
+    <dt class="h2 pb-3 mb-3 border-bottom">Assembly Slider</dt>
+    <dd class="mb-10">
+        {{ list_assm.slider(assemblies, ['2']) }}
+    </dd>
+
     <dt class="h2 pb-3 mb-3 border-bottom">Events List</dt>
     <dd class="mb-10">
         {{ list_events.list(events, ['1'], ['2'] ) }}
@@ -123,7 +131,12 @@
 
     <dt class="h2 pb-3 mb-3 border-bottom">Events List Tiles</dt>
     <dd class="mb-10">
-        {{ list_events.tiles(events, ['1'], ['2'] ) }}
+        {{ list_events.grid(events, ['1'], ['2'] ) }}
+    </dd>
+
+    <dt class="h2 pb-3 mb-3 border-bottom">Events List Slider</dt>
+    <dd class="mb-10">
+        {{ list_events.slider(events, ['1'], ['2'] ) }}
     </dd>
 
     <dt class="h2 pb-3 mb-3 border-bottom">Rooms List</dt>
@@ -148,16 +161,6 @@
         }) }}
     </dd>
 
-    <dt class="h2 pb-3 mb-3 border-bottom">TODO: Tile</dt>
-    <dd class="mb-10">
-        {{tileMacro.tile({
-            "name": "Item 1",
-            "image": {
-                "url": "https://picsum.photos/600/600"
-            }
-        }, "#") }}
-    </dd>
-
     <dt class="h2 pb-3 mb-3 border-bottom">Three Cards</dt>
     <dd class="mb-10">
         {{threeCardsMacro.three_cards(cards=[{
@@ -185,38 +188,6 @@
         ]) }}
     </dd>
 
-    <dt class="h2 pb-3 mb-3 border-bottom">TODO: Tilesbox</dt>
-    <dd class="mb-10">
-        {{ tilesboxMacro.tilesbox(conf_slug="rc3", items=[{
-            "name": "Item 1",
-            "link": "#",
-            "image": {
-                "url": "https://picsum.photos/600/600"
-            }
-        },
-        {
-            "name": "Item 2",
-            "link": "#",
-            "image": {
-                "url": "https://picsum.photos/600/600"
-            }
-        },
-        {
-            "name": "Item 3",
-            "link": "#",
-            "image": {
-                "url": "https://picsum.photos/600/600"
-            }
-        },
-        {
-            "name": "Item 4",
-            "link": "#",
-            "image": {
-                "url": "https://picsum.photos/600/600"
-            }
-        }]) }}
-    </dd>
-
     <dt class="h2 pb-3 mb-3 border-bottom">Valid Form</dt>
     <dd class="mb-10">
         {{ formElementsMacro.text(form_valid, 'text') }}
diff --git a/src/plainui/jinja2/plainui/components/calendar.html b/src/plainui/jinja2/plainui/components/calendar.html
index eff10d034620dd72e52833e17b59fa4e95161f93..fbd7af6555b8d0eb6b7f1c2b4c4d87fbfe750b99 100644
--- a/src/plainui/jinja2/plainui/components/calendar.html
+++ b/src/plainui/jinja2/plainui/components/calendar.html
@@ -7,7 +7,7 @@
 {%- else -%}
     {% set time_steps = events.calendar_time_steps -%}
     {% set step_minutes = events.calendar_step_minutes -%}
-    <div class="rc3-fahrplan text-white font-headings">
+    <div class="rc3-fahrplan text-white">
         <div class="mr-4 rc3-fahrplan__timeline">
             <h2 class="m-0 text-white rc3-fahrplan__timeline-title"></h2>
             {% for step in time_steps %}
@@ -23,17 +23,21 @@
                     {% if entry.type == 'space' %}
                         <figure class="m-0 p-0 rc3-fahrplan__room-space" style="height: {{ h(entry.minutes) }}"></figure>
                     {% else %}
-                    {% set color="primary" if entry.event.kind | safe == "official" else "secondary" %}
-                        <a class="text-decoration-none"" href="{{ url('plainui:event', conf_slug=conf.slug, event_slug=entry.event.slug) }}" title="{{entry.event.name | safe}}">
+                    {% set color="primary" if entry.event.kind == "official" else "secondary" %}
+                        <a class="text-decoration-none" href="{{ url('plainui:event', conf_slug=conf.slug, event_slug=entry.event.slug) }}" title="{{entry.event.name}}">
                             <figure class="p-1 my-0 mx-1 d-flex flex-column bg-{{color}} rc3-fahrplan__room-event" style="height: {{ h(entry.minutes) }}">
-                                <h2 class="m-0 text-white rc3-fahrplan__event_title">{{entry.event.name | safe}}
+                                <h2 class="mb-1 text-white rc3-fahrplan__event_title">{{entry.event.name}}
                                     {% if entry.event.language %}
-                                        <span class="fs-medium font-weight-normal text-lowercase"> ({{entry.event.language | safe}})</span>
+                                        <span class="fs-medium font-sans-serif font-weight-normal text-lowercase"> ({{entry.event.language}})</span>
                                     {% endif %}
                                 </h2>
-                                {% if entry.event.track_name %}<div class="fs-medium text-bold">{{entry.event.track_name | safe}}</div>{% endif %}
+                                {% 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> #}
-                                <div class="mt-auto fs-medium text-bold">{{ entry.event.speakers|join(', ', attribute='speaker_name') }}</div>
+                                {% if entry.event.speakers %}
+                                    <p class="mt-auto fs-medium font-weight-bold">{{ entry.event.speakers|join(', ', attribute='speaker_name') }}</p>
+                                {% endif %}
                             </figure>
                         </a>
                     {% endif %}
diff --git a/src/plainui/jinja2/plainui/components/event_info.html b/src/plainui/jinja2/plainui/components/event_info.html
index 41de51424a200e0f97be69a83d7ead641f792d17..1731dadb9745d8f462ac7181043d5c9545244988 100644
--- a/src/plainui/jinja2/plainui/components/event_info.html
+++ b/src/plainui/jinja2/plainui/components/event_info.html
@@ -1,10 +1,12 @@
 {% import "plainui/components/image.html" as imageMacro %}
 
+
 {% macro eventInfo(event) -%}
     <div class="rc3-event-info">
-        <h2>
-            {{ _("Event starts in") }} TODO: 00:00:00
-        </h2>
+                <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) }}
@@ -55,12 +57,6 @@
                     {% endif %}
                 </dd>
             </dl>
-            <ul class="list-unstyled row m-0">
-                <li>TODO: Report</li>
-                <li>Share</li>
-                <li>Favorites</li>
-                <li>Schedule</li>
-            </ul>
         </div>
     </div>
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/form_elements.html b/src/plainui/jinja2/plainui/components/form_elements.html
index af1b2865ace480be82815428c0ee51033b16e640..7ef15fca543b5bc22d52fd93ac9902965923149c 100644
--- a/src/plainui/jinja2/plainui/components/form_elements.html
+++ b/src/plainui/jinja2/plainui/components/form_elements.html
@@ -9,10 +9,8 @@
                 class="shadow-darkmorphism d-block font-headings p-3 text-center"
                 for="id_{{name}}"
             >
-                <b>
-                    {{ el.label }}
-                    {%- if el.required %} *{% endif -%}
-                </b>
+                {{ el.label }}
+                {%- if el.required %} *{% endif -%}
             </label>
         </div>
         <div class="col-sm-12 col-lg-8">
@@ -29,10 +27,10 @@
                 {% endfor -%}
             >
             {% for err in form.errors.get(name, []) %}
-                <p class="d-block invalid-feedback font-headings">{{_(err)}}</p>
+                <p class="d-block invalid-feedback">{{_(err)}}</p>
             {% endfor %}
             {% if el.help_text %}
-                <div class="d-block font-headings fs-medium mt-2">{{ el.help_text | safe }}</div>
+                <div class="d-block fs-medium mt-2">{{ el.help_text | safe }}</div>
             {% endif %}
         </div>
     </div>
@@ -51,10 +49,8 @@
                 class="shadow-darkmorphism font-headings d-block p-3 text-center"
                 for="id_{{name}}"
             >
-                <b>
-                    {{ el.label }}
-                    {%- if el.required %} *{% endif -%}
-                </b>
+                {{ el.label }}
+                {%- if el.required %} *{% endif -%}
             </label>
         </div>
         <div class="col-sm-12 col-lg-8">
@@ -69,9 +65,9 @@
                 {% endfor -%}
                 >{{ el.value()  or '' }}</textarea>
             {% for err in form.errors.get(name, []) %}
-                <p class="d-block invalid-feedback font-headings">{{_(err)}}</p>
+                <p class="d-block invalid-feedback">{{_(err)}}</p>
             {% endfor %}
-            {% if el.help_text %}<div class="d-block fs-medium font-headings mt-2">{{ el.help_text | safe }}</div>{% endif %}
+            {% if el.help_text %}<div class="d-block fs-medium mt-2">{{ el.help_text | safe }}</div>{% endif %}
         </div>
     </div>
 {%- endmacro %}
@@ -83,10 +79,8 @@
                 class="shadow-darkmorphism d-block font-headings p-3 text-center mb-0"
                 for="id_{{name}}"
             >
-                <b>
-                    {{ el.label }}
-                    {%- if el.required %} *{% endif -%}
-                </b>
+                {{ el.label }}
+                {%- if el.required %} *{% endif -%}
             </p>
         </div>
         <div class="col-sm-12 col-lg-8">
@@ -101,12 +95,12 @@
                         {% if el.required %}required{%endif%}
                         {% if el.disabled %}disabled{%endif%}
                     >
-                    <label for="id_{{name}}" class="form-check-label font-headings">{{ el.label }}</label>
+                    <label for="id_{{name}}" class="form-check-label">{{ el.label }}</label>
                 </div>
                 {% for err in form.errors.get(name, []) -%}
-                    <p class="d-block invalid-feedback font-headings">{{_(err)}}</p>
+                    <p class="d-block invalid-feedback">{{_(err)}}</p>
                 {% endfor %}
-                {%- if el.help_text %}<div class="d-block fs-medium font-headings mt-2">{{ el.help_text | safe }}</div>{% endif %}
+                {%- if el.help_text %}<div class="d-block fs-medium mt-2">{{ el.help_text | safe }}</div>{% endif %}
             </div>
         </div>
     </div>
@@ -118,9 +112,9 @@
             <label
                 for="id_{{name}}"
                 class="shadow-darkmorphism d-block font-headings p-3 text-center"
-            ><b>
+            >
                 {{ el.label }}
-            </b></label>
+            </label>
         </div>
         <div class="col-sm-12 col-lg-8">
             <div class="form-control-selectbox">
@@ -148,9 +142,9 @@
                 </svg>
             </div>
             {% for err in form.errors.get(name, []) %}
-                <p class="d-block invalid-feedback font-headings">{{_(err)}}</p>
+                <p class="d-block invalid-feedback">{{_(err)}}</p>
             {% endfor %}
-            {% if el.help_text %}<div class="d-block fs-medium font-headings mt-2">{{ el.help_text | safe }}</div>{% endif %}
+            {% if el.help_text %}<div class="d-block fs-medium mt-2">{{ el.help_text | safe }}</div>{% endif %}
         </div>
     </div>
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/list_assemblies.html b/src/plainui/jinja2/plainui/components/list_assemblies.html
index de0414df367788bfd0efb05cf4be6f671d459a8b..eee2141c40c167e8be572f3ea1876c802cb89ce8 100644
--- a/src/plainui/jinja2/plainui/components/list_assemblies.html
+++ b/src/plainui/jinja2/plainui/components/list_assemblies.html
@@ -8,7 +8,7 @@
 
 {% macro list(assemblies, my_favorite_assemblies) -%}
     {% if assemblies %}
-        <ul class="list-unstyled">
+        <ul class="list-unstyled mb-0">
         {% for assembly in assemblies %}
             {{ list_el(assembly, faved=true if assembly.id | safe in my_favorite_assemblies ) }}
         {% endfor %}
@@ -22,12 +22,91 @@
     {% set link = url('plainui:assembly', conf_slug=conf.slug, assembly_slug=assembly.slug ) %}
     {% set color="plattform" if assembly.is_official else "assembly" %}
 
-    <li class="mt-3 d-flex border border-{{ color }} bg-gradient-{{ color }}-horizontal p-2 align-items-center font-headings">
-        <a href="{{ link }}" title="{{ assembly.name | safe }}" class="text-white mr-auto">
-            {{ assembly.name | safe }}
+    <li class="d-flex border border-{{ color }} bg-gradient-{{ color }}-horizontal p-2 align-items-center font-headings{% if not first %} mt-3{% endif %}">
+        <a
+            href="{{ link }}"
+            title="{{ assembly.name }}"
+            class="text-white mr-auto">
+            {{ assembly.name }}
         </a>
         {{ fbtns.share(link, color=color) }}
         {{ fbtns.fav(assembly.id, "assembly", faved, color=color) }}
         {{ fbtns.report(link, color=color) }}
     </li>
 {%- endmacro %}
+
+{% macro slider(assemblies, my_favorite_assemblies) -%}
+    {% if assemblies %}
+        <div class="rc3-slider">
+            <ul class="rc3-slider__container row row-cols-1 row-cols-sm-2 row-cols-lg-3 flex-nowrap m-0 list-unstyled">
+                {% for assembly in assemblies %}
+                    <li class="rc3-slider__item col mb-2{% if loop.first %} pl-0{% endif %}{% if loop.last %} pr-0{% endif %}">
+                        {{ tile(assembly, faved=true if assembly.id | safe in my_favorite_assemblies) }}
+                    </li>
+                {% endfor %}
+            </ul>
+        </div>
+    {% else %}
+        <p>{{_("No entries available.")}}</p>
+    {% endif %}
+{%- endmacro %}
+
+{% macro grid(assemblies, my_favorite_assemblies) -%}
+    {% if assemblies %}
+        <ul class="row row-cols-1 row-cols-md-2 row-cols-xl-3 list-unstyled mb-0">
+            {% for assembly in assemblies %}
+                <li class="col mb-3">
+                    {{ tile(assembly, faved=true if assembly.id | safe in my_favorite_assemblies) }}
+                </li>
+            {% endfor %}
+        </ul>
+    {% else %}
+        <p>{{_("No entries available.")}}</p>
+    {% endif %}
+{%- endmacro %}
+
+{% macro tile(assembly, faved) -%}
+    {% set link = url('plainui:assembly', conf_slug=conf.slug, assembly_slug=assembly.slug ) %}
+    {% set color="plattform" if assembly.is_official else "assembly" %}
+    <article class="h-100 d-flex flex-column border border-{{ color }}-dark bg-gradient-{{ color }}-vertical">
+        <a
+            href="{{ link }}"
+            class="text-decoration-none text-white"
+            title="{{ assembly.name | safe }}"
+        >
+            <figure class="mb-2">
+                {% if assembly.banner_image %}
+                    <img class="w-100 d-block" src="{{ assembly.banner_image.url }}" alt="{{ assembly.name | safe }}" title="{{ assembly.name | safe}}" />
+                {% else %}
+                    <img class="w-100 d-block" src="/static/plainui/img/rc3-logo-assembly.svg" alt="{{ assembly.name | safe }}" title="{{ assembly.name | safe }}" />
+                {% endif %}
+            </figure>
+            <section class="m-2">
+                <p class="mb-2 font-headings fs-medium">
+                    {{ _("Official Page") if assembly.is_official else _("Assembly Page") }}
+                </p>
+                {% if assembly.name %}
+                    <h3 class="text-white h6 mb-2">{{ assembly.name }}</h3>
+                {% endif %}
+
+                {% if assembly.description %}
+                    <p class="fs-medium mb-2">{{ assembly.description[:120] + (assembly.description[120:] and '...') }}</p>
+                {% endif %}
+            </section>
+        </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">
+                <li>
+                    {{ fbtns.share(link, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.fav(assembly.id, "assembly", faved, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.report(link, color=color) }}
+                </li>
+            </ul>
+        </footer>
+    </article>
+{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/list_events.html b/src/plainui/jinja2/plainui/components/list_events.html
index b7e8c7ee0f46c20313237e6dd9f526fb5daffa61..40bd07bb19e7400d067830e295050e42a05c8934 100644
--- a/src/plainui/jinja2/plainui/components/list_events.html
+++ b/src/plainui/jinja2/plainui/components/list_events.html
@@ -4,15 +4,16 @@
     conf
     csrf_input
 #}
-{% import "plainui/components/function_btns.html" as fbtns with context%}
+{% import "plainui/components/function_btns.html" as fbtns with context %}
 
 {% macro list(events, my_favorite_events, my_scheduled_events, assembly_slug=None, msg_none=_("No entries available.")) -%}
     {% if events %}
-        <ul class="list-unstyled">
+        <ul class="list-unstyled mb-0">
         {% for event in events %}
             {{ list_el( event,
                 faved=true if event.id | safe in my_favorite_events,
-                scheduled=true if event.id | safe in my_scheduled_events ) }}
+                scheduled=true if event.id | safe in my_scheduled_events,
+                first=loop.first ) }}
         {% endfor %}
         </ul>
     {% else %}
@@ -20,15 +21,15 @@
     {% endif %}
 {%- endmacro %}
 
-{% macro list_el(event, faved, scheduled) -%}
+{% macro list_el(event, faved, scheduled, first) -%}
     {% set link = url('plainui:event', conf_slug=conf.slug, event_slug=event.slug ) %}
-    {% set color="plattform" if event.kind | safe == "official" else "assembly" %}
-    <li class="mt-3 d-flex border border-{{ color }} bg-gradient-{{ color }}-horizontal p-2 align-items-center font-headings">
-        <a href="{{ link }}" title="{{ event.name | safe }}" class="text-white mr-auto">
-            {{ event.name | safe }}
+    {% set color="plattform" if event.kind == "official" else "assembly" %}
+    <li class="d-flex border border-{{ color }} bg-gradient-{{ color }}-horizontal p-2 align-items-center font-headings{% if not first %} mt-3{% endif %}">
+        <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 datetime="" 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.strftime('%x') }},</time>
+        <time class="mr-4">{{ event.schedule_start.strftime('%H:%M') }} - {{ event.schedule_end.strftime('%H:%M') }}</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) }}
@@ -40,13 +41,33 @@
     </li>
 {%- endmacro %}
 
-{% macro tiles(events, my_favorite_events, my_scheduled_events, msg_none=_("No entries available.")) -%}
+{% macro slider(events, my_favorite_events, my_scheduled_events, msg_none=_("No entries available.")) -%}
     {% if events %}
-        <ul class="row list-unstyled">
+        <div class="rc3-slider">
+            <ul class="rc3-slider__container row row-cols-1 row-cols-sm-2 row-cols-lg-3 flex-nowrap m-0 list-unstyled">
+            {% for event in events %}
+                <li class="rc3-slider__item col mb-2{% if loop.first %} pl-0{% endif %}{% if loop.last %} pr-0{% endif %}">
+                    {{ tile( event,
+                        faved=true if event.id | safe in my_favorite_events,
+                        scheduled=true if event.id | safe in my_scheduled_events) }}
+                </li>
+            {% endfor %}
+            </ul>
+        </div>
+    {% else %}
+        <p>{{ msg_none }}</p>
+    {% endif %}
+{%- endmacro %}
+
+{% macro grid(events, my_favorite_events, my_scheduled_events, msg_none=_("No entries available.")) -%}
+    {% if events %}
+        <ul class="row row-cols-1 row-cols-md-2 row-cols-xl-3 list-unstyled mb-0">
         {% for event in events %}
-            {{ tile( event,
-                faved=true if event.id | safe in my_favorite_events,
-                scheduled=true if event.id | safe in my_scheduled_events) }}
+            <li class="col mb-3">
+                {{ tile( event,
+                    faved=true if event.id | safe in my_favorite_events,
+                    scheduled=true if event.id | safe in my_scheduled_events) }}
+            </li>
         {% endfor %}
         </ul>
     {% else %}
@@ -56,34 +77,79 @@
 
 {% macro tile(event, faved, scheduled) -%}
     {% set link = url('plainui:event', conf_slug=conf.slug, event_slug=event.slug ) %}
-    {% set color="plattform" if event.kind | safe == "official" else "assembly" %}
-    <li class="col-6 col-md-4 col-lg-3 mt-2 mr-2 border border-{{ color }}-dark bg-gradient-{{ color }}-vertical">
-        <figure>
-            <a href="{{ link }}"  title="{{ event.name | safe }}">
+    {% 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">
+        <a
+            href="{{ link }}"
+            class="text-decoration-none text-white"
+            title="{{ event.name }}"
+        >
+            <figure class="mb-2">
                 {% if event.banner_image %}
-                    <img class="w-100 d-block" src="{{ event.banner_image.url }}" alt="{{ event.name | safe }}" title="{{ event.name | safe}}" />
+                    <img class="w-100 d-block" src="{{ event.banner_image.url }}" alt="{{ event.name }}" title="{{ event.name }}" />
                 {% else %}
-                    <img class="w-100 d-block" src="/static/plainui/img/rc3_no_avater.png" alt="{{ event.name | safe }}" title="{{ event.name | safe}}" />
+                    <img class="w-100 d-block" src="{{ random_preview_image_url() }}" alt="{{ event.name }}" title="{{ event.name }}" />
                 {% endif %}
-                <time datetime="{{ event.schedule_start }}">{{ event.schedule_start.strftime('%x') }}</time>
-                <time datetime="{{ event.schedule_start.strftime('%H:%M') }}">{{ event.schedule_start.strftime('%H:%M') }}</time >
-                <time>{{ event.schedule_duration  }}</time>
-                <p>{{ event.name | safe }}</p>
-                <p>TODO: speakers</p>
-                <p>{{ event.description[:100] + (event.description[100:] and '...') | safe }}</p>
-            </a>
-            <p>TODO: track</p>
-            {%- 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) }}
-            {% 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') }}
+                        </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') }}
+                        </time>
+                        <span>{{ event.schedule_duration }}</span>
+                    </p>
+                {% endif %}
+                {% if event.name %}
+                    <h3 class="text-white h6 mb-2">{{ event.name }}</h3>
+                {% endif %}
+                {% if event.speakers %}
+                    <b class="f s-medium d-block mb-2">{{ event.speakers|join(', ', attribute='speaker_name') }}</b>
+                {% endif %}
+                {% if event.description %}
+                    <p class="fs-medium mb-2">{{ event.description[:120] + (event.description[120:] and '...') }}</p>
+                {% endif %}
+            </section>
+        </a>
 
-            {{ fbtns.share(link, color=color) }}
-            {{ fbtns.schedule(event.id, scheduled, color=color) }}
-            {{ fbtns.fav(event.id, "event", faved, color=color) }}
-            {{ fbtns.report(link, color=color) }}
-        </figure>
-    </li>
+        <footer class="mt-auto mr-2 mb-2 d-md-flex align-items-center">
+            {% if event.track_name %}
+                <p class="mb-2 mb-md-0 font-headings fs-medium">
+                    <small class="d-block {{ "text-tertiary" if event.kind == "official" else "text-secondary"}}">
+                        {{ _("%(kind)s Event on Track", kind="Official" if event.kind == "official" else "") }}
+                    </small>
+                    {{ event.track_name }}
+                </p>
+            {% endif %}
+            <ul class="ml-auto 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) }}
+                        {{ 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(event.id, scheduled, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.fav(event.id, "event", faved, color=color) }}
+                </li>
+                <li>
+                    {{ fbtns.report(link, color=color) }}
+                </li>
+            </ul>
+        </footer>
+    </article>
 {%- endmacro %}
 
 {% macro icon_public(is_public) -%}
@@ -101,3 +167,8 @@
         </svg>
     {% endif %}
 {%- 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/list_rooms.html b/src/plainui/jinja2/plainui/components/list_rooms.html
index d67e26c8d4dc5160940b4c8fcb3982164b02a969..7d9b3e133e4965a980021528d1c71cbcc84dc022 100644
--- a/src/plainui/jinja2/plainui/components/list_rooms.html
+++ b/src/plainui/jinja2/plainui/components/list_rooms.html
@@ -44,20 +44,20 @@
 
 {%- macro icon(type) -%}
     {% if type=="lecturehall" %}
-        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-question-square-fill" viewBox="0 0 16 16">
-            <path fill-rule="evenodd" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.496 6.033a.237.237 0 0 1-.24-.247C5.35 4.091 6.737 3.5 8.005 3.5c1.396 0 2.672.73 2.672 2.24 0 1.08-.635 1.594-1.244 2.057-.737.559-1.01.768-1.01 1.486v.105a.25.25 0 0 1-.25.25h-.81a.25.25 0 0 1-.25-.246l-.004-.217c-.038-.927.495-1.498 1.168-1.987.59-.444.965-.736.965-1.371 0-.825-.628-1.168-1.314-1.168-.803 0-1.253.478-1.342 1.134-.018.137-.128.25-.266.25h-.825zm2.325 6.443c-.584 0-1.009-.394-1.009-.927 0-.552.425-.94 1.01-.94.609 0 1.028.388 1.028.94 0 .533-.42.927-1.029.927z"/>
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-easel-fill" viewBox="0 0 16 16">
+            <path d="M8.473.337a.5.5 0 0 0-.946 0L6.954 2H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h1.85l-1.323 3.837a.5.5 0 1 0 .946.326L4.908 11H7.5v2.5a.5.5 0 0 0 1 0V11h2.592l1.435 4.163a.5.5 0 0 0 .946-.326L12.15 11H14a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H9.046L8.473.337z"/>
         </svg>
     {% elif type=="bbb" %}
-        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-question-square-fill" viewBox="0 0 16 16">
-            <path fill-rule="evenodd" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.496 6.033a.237.237 0 0 1-.24-.247C5.35 4.091 6.737 3.5 8.005 3.5c1.396 0 2.672.73 2.672 2.24 0 1.08-.635 1.594-1.244 2.057-.737.559-1.01.768-1.01 1.486v.105a.25.25 0 0 1-.25.25h-.81a.25.25 0 0 1-.25-.246l-.004-.217c-.038-.927.495-1.498 1.168-1.987.59-.444.965-.736.965-1.371 0-.825-.628-1.168-1.314-1.168-.803 0-1.253.478-1.342 1.134-.018.137-.128.25-.266.25h-.825zm2.325 6.443c-.584 0-1.009-.394-1.009-.927 0-.552.425-.94 1.01-.94.609 0 1.028.388 1.028.94 0 .533-.42.927-1.029.927z"/>
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-quote-fill" viewBox="0 0 16 16">
+            <path d="M16 8c0 3.866-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.584.296-1.925.864-4.181 1.234-.2.032-.352-.176-.273-.362.354-.836.674-1.95.77-2.966C.744 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7zM7.194 6.766a1.688 1.688 0 0 0-.227-.272 1.467 1.467 0 0 0-.469-.324l-.008-.004A1.785 1.785 0 0 0 5.734 6C4.776 6 4 6.746 4 7.667c0 .92.776 1.666 1.734 1.666.343 0 .662-.095.931-.26-.137.389-.39.804-.81 1.22a.405.405 0 0 0 .011.59c.173.16.447.155.614-.01 1.334-1.329 1.37-2.758.941-3.706a2.461 2.461 0 0 0-.227-.4zM11 9.073c-.136.389-.39.804-.81 1.22a.405.405 0 0 0 .012.59c.172.16.446.155.613-.01 1.334-1.329 1.37-2.758.942-3.706a2.466 2.466 0 0 0-.228-.4 1.686 1.686 0 0 0-.227-.273 1.466 1.466 0 0 0-.469-.324l-.008-.004A1.785 1.785 0 0 0 10.07 6c-.957 0-1.734.746-1.734 1.667 0 .92.777 1.666 1.734 1.666.343 0 .662-.095.931-.26z"/>
         </svg>
     {% elif type=="stage" %}
-        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-question-square-fill" viewBox="0 0 16 16">
-            <path fill-rule="evenodd" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.496 6.033a.237.237 0 0 1-.24-.247C5.35 4.091 6.737 3.5 8.005 3.5c1.396 0 2.672.73 2.672 2.24 0 1.08-.635 1.594-1.244 2.057-.737.559-1.01.768-1.01 1.486v.105a.25.25 0 0 1-.25.25h-.81a.25.25 0 0 1-.25-.246l-.004-.217c-.038-.927.495-1.498 1.168-1.987.59-.444.965-.736.965-1.371 0-.825-.628-1.168-1.314-1.168-.803 0-1.253.478-1.342 1.134-.018.137-.128.25-.266.25h-.825zm2.325 6.443c-.584 0-1.009-.394-1.009-.927 0-.552.425-.94 1.01-.94.609 0 1.028.388 1.028.94 0 .533-.42.927-1.029.927z"/>
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-aspect-ratio-fill" viewBox="0 0 16 16">
+            <path d="M0 12.5v-9A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5zM2.5 4a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 1 0V5h2.5a.5.5 0 0 0 0-1h-3zm11 8a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-1 0V11h-2.5a.5.5 0 0 0 0 1h3z"/>
         </svg>
     {% elif type=="workshop" %}
         <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-screwdriver bi-hammer" viewBox="0 0 16 16">
-            <path  transform="translate(16), scale(-1, 1)"" fill-rule="evenodd" d="M0 1l1-1 3.081 2.2a1 1 0 0 1 .419.815v.07a1 1 0 0 0 .293.708L10.5 9.5l.914-.305a1 1 0 0 1 1.023.242l3.356 3.356a1 1 0 0 1 0 1.414l-1.586 1.586a1 1 0 0 1-1.414 0l-3.356-3.356a1 1 0 0 1-.242-1.023L9.5 10.5 3.793 4.793a1 1 0 0 0-.707-.293h-.071a1 1 0 0 1-.814-.419L0 1zm11.354 9.646a.5.5 0 0 0-.708.708l3 3a.5.5 0 0 0 .708-.708l-3-3z"/>
+            <path transform="translate(16), scale(-1, 1)" fill-rule="evenodd" d="M0 1l1-1 3.081 2.2a1 1 0 0 1 .419.815v.07a1 1 0 0 0 .293.708L10.5 9.5l.914-.305a1 1 0 0 1 1.023.242l3.356 3.356a1 1 0 0 1 0 1.414l-1.586 1.586a1 1 0 0 1-1.414 0l-3.356-3.356a1 1 0 0 1-.242-1.023L9.5 10.5 3.793 4.793a1 1 0 0 0-.707-.293h-.071a1 1 0 0 1-.814-.419L0 1zm11.354 9.646a.5.5 0 0 0-.708.708l3 3a.5.5 0 0 0 .708-.708l-3-3z"/>
             <path d="M9.812 1.952a.5.5 0 0 1-.312.89c-1.671 0-2.852.596-3.616 1.185L4.857 5.073V6.21a.5.5 0 0 1-.146.354L3.425 7.853a.5.5 0 0 1-.708 0L.146 5.274a.5.5 0 0 1 0-.706l1.286-1.29a.5.5 0 0 1 .354-.146H2.84C4.505 1.228 6.216.862 7.557 1.04a5.009 5.009 0 0 1 2.077.782l.178.129z"/>
             <path fill-rule="evenodd" d="M6.012 3.5a.5.5 0 0 1 .359.165l9.146 8.646A.5.5 0 0 1 15.5 13L14 14.5a.5.5 0 0 1-.756-.056L4.598 5.297a.5.5 0 0 1 .048-.65l1-1a.5.5 0 0 1 .366-.147z"/>
         </svg>
@@ -70,8 +70,9 @@
             <path fill-rule="evenodd" d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1H7zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5.784 6A2.238 2.238 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.325 6.325 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1h4.216zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
         </svg>
     {% else %}
-        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-question-square-fill" viewBox="0 0 16 16">
-            <path fill-rule="evenodd" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.496 6.033a.237.237 0 0 1-.24-.247C5.35 4.091 6.737 3.5 8.005 3.5c1.396 0 2.672.73 2.672 2.24 0 1.08-.635 1.594-1.244 2.057-.737.559-1.01.768-1.01 1.486v.105a.25.25 0 0 1-.25.25h-.81a.25.25 0 0 1-.25-.246l-.004-.217c-.038-.927.495-1.498 1.168-1.987.59-.444.965-.736.965-1.371 0-.825-.628-1.168-1.314-1.168-.803 0-1.253.478-1.342 1.134-.018.137-.128.25-.266.25h-.825zm2.325 6.443c-.584 0-1.009-.394-1.009-.927 0-.552.425-.94 1.01-.94.609 0 1.028.388 1.028.94 0 .533-.42.927-1.029.927z"/>
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bug-fill" viewBox="0 0 16 16">
+            <path d="M4.978.855a.5.5 0 1 0-.956.29l.41 1.352A4.985 4.985 0 0 0 3 6h10a4.985 4.985 0 0 0-1.432-3.503l.41-1.352a.5.5 0 1 0-.956-.29l-.291.956A4.978 4.978 0 0 0 8 1a4.979 4.979 0 0 0-2.731.811l-.29-.956z"/>
+            <path d="M13 6v1H8.5v8.975A5 5 0 0 0 13 11h.5a.5.5 0 0 1 .5.5v.5a.5.5 0 1 0 1 0v-.5a1.5 1.5 0 0 0-1.5-1.5H13V9h1.5a.5.5 0 0 0 0-1H13V7h.5A1.5 1.5 0 0 0 15 5.5V5a.5.5 0 0 0-1 0v.5a.5.5 0 0 1-.5.5H13zm-5.5 9.975V7H3V6h-.5a.5.5 0 0 1-.5-.5V5a.5.5 0 0 0-1 0v.5A1.5 1.5 0 0 0 2.5 7H3v1H1.5a.5.5 0 0 0 0 1H3v1h-.5A1.5 1.5 0 0 0 1 11.5v.5a.5.5 0 1 0 1 0v-.5a.5.5 0 0 1 .5-.5H3a5 5 0 0 0 4.5 4.975z"/>
         </svg>
     {% endif %}
 {%- endmacro -%}
diff --git a/src/plainui/jinja2/plainui/components/livestream.html b/src/plainui/jinja2/plainui/components/livestream.html
index df97f213f0cc262b2ed9a0bfadd0d00f79ef6a72..1f55752913c442718f470867eedc836c04ee56b4 100644
--- a/src/plainui/jinja2/plainui/components/livestream.html
+++ b/src/plainui/jinja2/plainui/components/livestream.html
@@ -1,4 +1,3 @@
-{% import "plainui/components/slider.html" as sliderMacro %}
 {% macro livestream(title, event, upcomingEvents, recommendedEvents) -%}
 <div>
     <h2 class="mb-5">{{ title }}</h2>
@@ -15,9 +14,9 @@
             </div>
         </div>
         <div class="col-7">
-            {{ sliderMacro.slider(title="Upcoming Events") }}
+            TODO: Slider Upcoming Events
             <hr class="border-top-0 py-1" />
-            {{ sliderMacro.slider(title="Recommended Events") }}
+            TODO: Slider Recommended Events
         </div>
     </div>
 </div>
diff --git a/src/plainui/jinja2/plainui/components/slider.html b/src/plainui/jinja2/plainui/components/slider.html
index d5a3b138cb7fc9a141eed7582753e7023c8dd7ca..0558439e0bda24d99540f4cf77456b30bbad0bdd 100644
--- a/src/plainui/jinja2/plainui/components/slider.html
+++ b/src/plainui/jinja2/plainui/components/slider.html
@@ -1,8 +1,13 @@
-{% macro slider(title, items) -%}
-    <section class="rc3-slider">
-        <h2>{{ title }}</h2>
-        <div class="border border-tertiary p-6">
-            TODO: Slider
-        </div>
+{% macro slider(items) -%}
+    <section class="rc3-slider border border-tertiary p-6">
+        {% if items %}
+            <ul class="rc3-slider__container row row-cols-1 row-cols-sm-2 row-cols-lg-3 flex-nowrap mb-0 list-unstyled">
+                {% for item in items %}
+                    <li class="rc3-slider__item col">
+                        {{ item }}
+                    </li>
+                {% endfor %}
+            </ul>
+        {% endif %}
     </section>
 {%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/tagbox.html b/src/plainui/jinja2/plainui/components/tagbox.html
index 89570c45d04e71262e3c8bdfb7ac81f13d9872ed..d966409259fe34665793b3fd868b1391efa7bce2 100644
--- a/src/plainui/jinja2/plainui/components/tagbox.html
+++ b/src/plainui/jinja2/plainui/components/tagbox.html
@@ -1,9 +1,9 @@
 {% macro tagbox(conf_slug, tags) -%}
     <div>
         <h4 class="h2 mb-5">{{_("Tags")}}</h4>
-        <ul class="border border-tertiary p-6 list-unstyled mb-0 d-flex flex-row flex-wrap justify-content-center align-items-center">
+        <ul class="border border-tertiary px-6 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">
+            <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 -%}
diff --git a/src/plainui/jinja2/plainui/components/tile.html b/src/plainui/jinja2/plainui/components/tile.html
deleted file mode 100644
index 32a672ae16748fdc0a89bbd85dd068595be35af2..0000000000000000000000000000000000000000
--- a/src/plainui/jinja2/plainui/components/tile.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% macro tile(item, link) -%}
-    <figure>
-        <a href="{{ link }}" class="d-block bg-secondary text-white text-center p-2 mr-auto">
-            {{item.name}}
-        </a>
-        <img class="w-100 d-block" src="{{ item.image.url }}" alt="{{ item.name }}" title="{{ item.name }}" />
-        <a href="#" class="mr-2 btn btn-secondary">+ schedule</a>
-        <a href="#" class="mr-2 btn btn-secondary">
-            <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-heart" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
-                <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>
-        </a>
-        <a href="#" class="btn btn-secondary">
-            <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-share" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
-                <path fill-rule="evenodd" d="M13.5 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zM11 2.5a2.5 2.5 0 1 1 .603 1.628l-6.718 3.12a2.499 2.499 0 0 1 0 1.504l6.718 3.12a2.5 2.5 0 1 1-.488.876l-6.718-3.12a2.5 2.5 0 1 1 0-3.256l6.718-3.12A2.5 2.5 0 0 1 11 2.5zm-8.5 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm11 5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z"/>
-            </svg>
-        </a>
-    </figure>
-{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/components/tile_board.html b/src/plainui/jinja2/plainui/components/tile_board.html
index e2671118681544394c592f0ebdbf7fe9324b527f..539fc9c38e125d2df2838e57fc3dc695d2a6ec8a 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) | safe, "rc3-tile-board__body card-body") }}
+            {{ markdownMacro.markdown_plain(item.text | 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/components/tilesbox.html b/src/plainui/jinja2/plainui/components/tilesbox.html
deleted file mode 100644
index b5de2ec88c0b5b6ce52b447ae92fc58e53cba0d1..0000000000000000000000000000000000000000
--- a/src/plainui/jinja2/plainui/components/tilesbox.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% import "plainui/components/tile.html" as tileMacro %}
-
-{% macro tilesbox(conf_slug, items, item_type="default" ) -%}
-    {% if items %}
-        <ul class="row list-unstyled">
-            {% for item in items %}
-                {% if item_type == "event" %}
-                    {% set link = url("plainui:event", conf_slug=conf_slug, event_slug=item.slug ) %}
-                {% elif item_type == "assembly" %}
-                    {% set link = url('plainui:assembly', conf_slug=conf_slug, assembly_slug=item.slug ) %}
-                {% else %}
-                    {% set link = item.link %}
-                {% endif %}
-                <li class="col-6 col-md-4 col-lg-3 mt-2">
-                    {{tileMacro.tile(item, link) }}
-                </li>
-            {% endfor %}
-        </ul>
-    {% else %}
-        <p>{{_("No entries available.")}}</p>
-    {% endif %}
-{%- endmacro %}
diff --git a/src/plainui/jinja2/plainui/dereferrer.html b/src/plainui/jinja2/plainui/dereferrer.html
index 71f5420a740a1e8cfb4654afd6613901a5d9fe1b..e070c78871697cc631c5d8aaba933c4e6402190c 100644
--- a/src/plainui/jinja2/plainui/dereferrer.html
+++ b/src/plainui/jinja2/plainui/dereferrer.html
@@ -2,16 +2,27 @@
 {% block title %}Dereferrer{% endblock %}
 {% block content %}
 
-<article class="row justify-content-center align-items-center my-8">
-    <section class="p-5 border border-primary text-center col-lg-8 col-xl-6">
-        <h1 class="mb-3">{{ _("Hey") }}</h1>
+<article class="d-flex justify-content-center align-items-center my-11">
+    <section class="p-5 border border-primary text-center mw-810">
+        <h1>{{ _("Hey") }}</h1>
 
         <p>{{ _("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.!")}}</p>
 
-        <div class="mt-5">
-            <a href='#' class="btn btn-lg btn-secondary">Link FEHLT</a>
-            <a href='{{ dst }}' class="btn btn-lg btn-primary" rel="external,noreferrer">{{ _("External Link") }}</a>
-        </div>
+        <ul class="row mt-5 mb-0 list-unstyled">
+            <li class="col d-js-only">
+                <a
+                    href="javascript:history.back()"
+                    class="btn btn-xl btn-block btn-secondary"
+                    title="{{ _("Back") }}">
+                        {{ _("Back") }}
+                </a>
+            </li>
+            <li class="col">
+                <a href='{{ dst }}' class="btn btn-xl btn-block btn-primary external" rel="external,noreferrer">
+                    {{ _("External Link") }}
+                </a>
+            </li>
+        </ul>
     </section>
 </article>
 
diff --git a/src/plainui/jinja2/plainui/event.html b/src/plainui/jinja2/plainui/event.html
index 71d254f81cdb795f84d5913a00b67ce6b94d1944..af73b7fc8b26280b17c6e34a83042cee5ed8f017 100644
--- a/src/plainui/jinja2/plainui/event.html
+++ b/src/plainui/jinja2/plainui/event.html
@@ -1,18 +1,16 @@
 {% import "plainui/components/markdown.html" as markdownMacro %}
 {% import "plainui/components/event_info.html" as eventInfoMacro %}
-{% import "plainui/components/slider.html" as sliderMacro %}
 {% 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 %}
 
-{% block head %}
-    <script src="{{ static('plainui/js/player.js') }}" /></script>
-{% endblock %}
-
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - Event {{event.name}}{% endblock %}
+{% block head %}
+    <script src="{{ static('plainui/js/player.js') }}"></script>
+{% endblock %}
 {% block content %}
 <article class="mt-10">
     {{ titleMacro.title(event.name,
@@ -26,7 +24,7 @@
         )
     }}
     {% if assembly.slug and assembly.name %}
-        <p>hosted by: <a href="{{ url("plainui:assembly", conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ assembly.name | safe }}</a></p>
+        <p>hosted by: <a href="{{ url("plainui:assembly", conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ assembly.name }}</a></p>
     {% endif %}
     {{ eventInfoMacro.eventInfo(event=event) }}
     {% if event.kind == 'official' %}
@@ -48,7 +46,7 @@
 
     <h2>{{ _("upcoming events") }}</h2>
     <div class="border border-tertiary p-6">
-        {{ list_events.tiles(events_upcoming, is_favorite_events, is_scheduled_events ) }}
+        {{ list_events.slider(events_upcoming, is_favorite_events, is_scheduled_events ) }}
     </div>
 
 </article>
diff --git a/src/plainui/jinja2/plainui/fahrplan.html b/src/plainui/jinja2/plainui/fahrplan.html
index c31a8dea268f38d33c524facf5ae6dfa4a6c0da1..b7a5351cef6d88907915aa23bfa3d3604b1e7d8f 100644
--- a/src/plainui/jinja2/plainui/fahrplan.html
+++ b/src/plainui/jinja2/plainui/fahrplan.html
@@ -29,21 +29,21 @@
         {% 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 {{ 'acitve' if mode == 'list' }}">{{ _("view as list") }}</button>
-            <button type="submit" name="set" value="mcalendar" class="m-2 btn btn-primary {{ 'active' if mode == 'calendar' }}">{{ _("view as calendar") }}</button>
+            <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 }}">{{ _("by day") }}</button>
-            <button type="submit" name="set" value="ftrack" class="m-2 btn btn-primary {{ 'active' if show_track_filters }}">{{ _("by track") }}</button>
-            <button type="submit" name="set" value="curated" class="m-2 btn btn-primary {{ 'active' if curated }}">{{ _("curated only") }}</button>
+            <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 }}">Assembly</button> #}
+                <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 }}">{{ _("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) }}</button>
             {%- endfor %}
         </div>
         {% endif %}
@@ -60,7 +60,7 @@
         {% 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 }}">{{ t.name }}</button>
+                <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 %}
diff --git a/src/plainui/jinja2/plainui/header.html b/src/plainui/jinja2/plainui/header.html
index 2fbe837ef4f7fd77b1b190b69e43449e5dc705bb..3d421a8372f0bf6527d283cc9e9bb59ad1714dc2 100644
--- a/src/plainui/jinja2/plainui/header.html
+++ b/src/plainui/jinja2/plainui/header.html
@@ -1,6 +1,7 @@
 {% set view_name = request.resolver_match.view_name %}
+{% set scope = scope|default('plattform') %}
 <header id="header" class="rc3-header container mb-3 mt-6">
-    {{ logoMacro.logo(static('plainui/img/rc3-logo-' + scope|default('plattform') +  '.svg'), url('plainui:index', conf_slug=conf.slug), conf.name + " logo", conf.name + " logo") }}
+    {{ logoMacro.logo(static('plainui/img/rc3-logo-' + scope +  '.svg'), url('plainui:index', conf_slug=conf.slug), conf.name + " logo", conf.name + " logo") }}
     <nav class="rc3-header__main">
         <a class="btn {{ 'btn-primary' if view_name == 'plainui:world' else 'btn-secondary' }} rc3-header__main-linkbox" href="{{ url('plainui:world', conf_slug=conf.slug ) }}" title="{{ _("world") }}">
             {{ _("world") }}
@@ -24,8 +25,15 @@
         </form>
     </nav>
     <div class="rc3-header__additional">
-        <a class="btn rc3-header__additional-linkbox {{ 'btn-primary' if view_name == 'plainui:userprofile' else 'btn-secondary' }}" href="{{ url('plainui:userprofile', conf_slug=conf.slug) }}" title="{{ _("Profile") }}">
-            {{ _("Profile") }}
+        <a class="btn rc3-header__additional-linkbox {{ 'btn-primary' if view_name == 'plainui:userprofile' else 'btn-secondary' }}" href="{{ url('plainui:userprofile', conf_slug=conf.slug) }}" title="{{ _('Profile') }}">
+            {% if user.avatar_url != None %}
+                {# TODO: implement real avatar if set #}
+                {% set av_url =  '/static/plainui/img/rc3-no-avatar-' + scope + '.jpeg' %}
+            {% else  %}
+                {% set av_act = '-active' if view_name == 'plainui:userprofile' else '' %}
+                {% set av_url = '/static/plainui/img/rc3-no-avatar-' + scope + av_act + '.jpeg' %}
+            {% endif  %}
+            <img class="rc3-image__img w-100 d-block" src="{{ av_url }}" alt="{{ _('Profile') }}" title="{{ _('Profile') }}" />
         </a>
         <a class="btn rc3-header__additional-linkbox {{ 'btn-primary' if view_name == 'plainui:my_fahrplan' else 'btn-secondary' }}" href="{{ url('plainui:my_fahrplan', conf_slug=conf.slug) }}">
             {{ _("My Plan") }}
@@ -47,7 +55,7 @@
         <a class="btn btn-block rc3-header__additional-linkbox {{ 'btn-primary' if view_name == 'plainui:personal_message' else 'btn-secondary' }}" href="{{ url('plainui:personal_message', conf_slug=conf.slug) }}"  title="{{ _("Messages") }}">
             {{ _("Mess ages") -}}
             {% set num_unread = num_of_unread_messages(request) -%}
-            {% if num_unread %}<span class="rc3-header__additional-linkbox-badge badge badge-info border border-primary">{{num_unread}}</span>{% endif %}
+            {% if num_unread %}<span class="rc3-header__additional-linkbox-badge bg-info badge badge-info border border-primary">{{num_unread}}</span>{% endif %}
         </a>
         <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") }}
diff --git a/src/plainui/jinja2/plainui/landing.html b/src/plainui/jinja2/plainui/landing.html
index d63ccdc6c494d641c4b2b8a482ec6a4cb2aed525..bc38df408c224f89ac84b861e63940d808834bda 100644
--- a/src/plainui/jinja2/plainui/landing.html
+++ b/src/plainui/jinja2/plainui/landing.html
@@ -44,9 +44,9 @@
         <a
             href="https://tickets.events.ccc.de/rc3/"
             target="_blank"
-            rel="noreferrer"
+            rel="external, noreferrer"
             title="{{ _("ticket") }}"
-            class="btn btn-secondary btn-box"
+            class="btn btn-secondary btn-box external"
         >
             {{ _("ticket") }}
         </a>
diff --git a/src/plainui/jinja2/plainui/login.html b/src/plainui/jinja2/plainui/login.html
index 8bd66a0663ec151222576e9fea2bc2641a6f32c9..4792e98dd358d89c9962de135e03efdb3a263355 100644
--- a/src/plainui/jinja2/plainui/login.html
+++ b/src/plainui/jinja2/plainui/login.html
@@ -22,7 +22,7 @@
     {{ formElementsMacro.password(form, 'password') }}
     <p class="my-5 font-headings text-white text-center">{{ _("By logging in you accept our use of cookies to store your user session.") }}</p>
 
-    <ul class="row row row-cols-1 row-cols-lg-3 list-unstyled">
+    <ul class="row row-cols-1 row-cols-lg-3 list-unstyled">
         <li class="col mb-3 mb-lg-0">
             <a href="{{ url('plainui:password_reset', conf_slug=conf.slug) }}" class="btn btn-xl btn-block btn-secondary" title="{{ _("Reset Password") }}">
             {{ _("Reset Password") }}
@@ -33,7 +33,7 @@
                 href="https://tickets.events.ccc.de/rc3/"
                 target="_blank"
                 rel="noreferrer"
-                class="btn btn-xl btn-block btn-secondary"
+                class="btn btn-xl btn-block btn-secondary external"
                 title="{{ _("New Ticket") }}"
             >
                 {{ _("New Ticket") }}
diff --git a/src/plainui/jinja2/plainui/password_change.html b/src/plainui/jinja2/plainui/password_change.html
index fe5a0326fca4473ee17cc17368091b4a7af77e9f..571eedb99a46f06557d00c70ee64e3d731437ed0 100644
--- a/src/plainui/jinja2/plainui/password_change.html
+++ b/src/plainui/jinja2/plainui/password_change.html
@@ -19,13 +19,13 @@
     {{ form_elements.password(form, 'new_password1') }}
     {{ form_elements.password(form, 'new_password2') }}
 
-    {{ form_elements.errors(form) }}
-
     <ul class="row list-unstyled">
         <li class="col-12 ml-md-auto col-md-8">
             <button type="submit" class="btn btn-xl btn-block btn-primary">{{ _("Change Password") }}</button>
         </li>
     </ul>
+
+    {{ form_elements.errors(form) }}
 </form>
 
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/password_reset_confirm.html b/src/plainui/jinja2/plainui/password_reset_confirm.html
index 5081c02d96f963ff99e9165ab60673eff5744b57..0d880e319eeead2d04d74828b050e7f2b63c2b3a 100644
--- a/src/plainui/jinja2/plainui/password_reset_confirm.html
+++ b/src/plainui/jinja2/plainui/password_reset_confirm.html
@@ -18,8 +18,6 @@
     {{ form_elements.password(form, 'new_password1') }}
     {{ form_elements.password(form, 'new_password2') }}
 
-    {{ form_elements.errors(form) }}
-
     <ul class="row row-cols-md-2 list-unstyled">
         <li class="col">
             <a
@@ -34,6 +32,9 @@
             <button type="submit" class="btn btn-xl btn-block btn-primary" form="login-change">{{ _("Change Password") }}</button>
         </li>
     </ul>
+
+    {{ form_elements.errors(form) }}
+
 </form>
 
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/personal_message_list.html b/src/plainui/jinja2/plainui/personal_message_list.html
index 54e9c55c38540a1b92f3824320c5e8e475f93963..f1a697dadf7691f1e3ee70d07c375636f0d946ce 100644
--- a/src/plainui/jinja2/plainui/personal_message_list.html
+++ b/src/plainui/jinja2/plainui/personal_message_list.html
@@ -1,37 +1,84 @@
+{% import "plainui/components/function_btns.html" as fbtns with context %}
+
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - {{ _("Personal Messages") }}{% endblock %}
 {% block content %}
-<div>
-    <div class="row justify-content-center">
-        <div class="col-auto"><h1>{{ _("Personal Messages") }}</h1></div>
-        <div class="w-100"></div>
-        <div class="col-auto">
-            <a href="{{ url('plainui:personal_message', conf_slug=conf.slug) }}">{{_("Inbox")}}</a>
-            <a href="{{ url('plainui:personal_message_outbox', conf_slug=conf.slug) }}">{{_("Outbox")}}</a>
+{{ titleMacro.title(_("Personal Messages") ) }}
+
+<div class="mb-2">
+    <a role="button" class="btn btn-primary mb-1" href="{{ url('plainui:personal_message', conf_slug=conf.slug) }}">{{_("Inbox")}}</a>
+    <a role="button" class="btn btn-primary mb-1" href="{{ url('plainui:personal_message_outbox', conf_slug=conf.slug) }}">{{_("Outbox")}}</a>
+    <a role="button" class="btn btn-primary mb-1" href="{{ url('plainui:personal_message_send', conf_slug=conf.slug) }}">{{_("New PM")}}</a>
+</div>
+
+<div class="border border-tertiary p-6 text-light mx-0">
+    <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{% if not sent_mode %}{{ _("Received Messages") }} {% else %}{{ _("Sent Messages") }}{% endif %}</h2>
+    <form method="POST" action="{{ url('plainui:personal_message_delete', conf_slug=conf.slug) }}">
+        {{ csrf_input }}
+        <table class="table">
+            <thead>
+            <tr>
+                <th scope="col">{% if not sent_mode %}{{ _("messages_from") }} {% else %}{{ _("messages_to") }}{% endif %}</th>
+                <th scope="col">{{ _("messages_subject") }}</th>
+                <th scope="col">{{ _("messages_date") }}</th>
+                <th scope="col"></th>
+            </tr>
+            </thead>
+            <tbody>
+
+        {%- for msg in msgs %}
+        <tr>
+            <td>
+                {% if not sent_mode %}
+                    {% if msg.was_read %}
+                        <span class="ml-2" title="{{_("messages_was_read")}}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-open" viewBox="0 0 16 16">
+                              <path d="M8.47 1.318a1 1 0 0 0-.94 0l-6 3.2A1 1 0 0 0 1 5.4v.818l5.724 3.465L8 8.917l1.276.766L15 6.218V5.4a1 1 0 0 0-.53-.882l-6-3.2zM15 7.388l-4.754 2.877L15 13.117v-5.73zm-.035 6.874L8 10.083l-6.965 4.18A1 1 0 0 0 2 15h12a1 1 0 0 0 .965-.738zM1 13.117l4.754-2.852L1 7.387v5.73zM7.059.435a2 2 0 0 1 1.882 0l6 3.2A2 2 0 0 1 16 5.4V14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V5.4a2 2 0 0 1 1.059-1.765l6-3.2z"/>
+                            </svg>
+                        </span>
+                    {% else %}
+                        <span class="ml-2" title="{{_("messages_is_new")}}">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-fill" viewBox="0 0 16 16">
+                              <path d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414.05 3.555zM0 4.697v7.104l5.803-3.558L0 4.697zM6.761 8.83l-6.57 4.027A2 2 0 0 0 2 14h12a2 2 0 0 0 1.808-1.144l-6.57-4.027L8 9.586l-1.239-.757zm3.436-.586L16 11.801V4.697l-5.803 3.546z"/>
+                            </svg>
+                        </span>
+                    {% endif %}
+                    <a href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=msg.sender_name) }}">
+                    {{msg.sender_name}}
+                    </a>
+                {% else %}
+                    <a href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=msg.recipient_name) }}">
+                        {{msg.recipient_name}}
+                    </a>
+                {% endif %}
 
-            <a href="{{ url('plainui:personal_message_send', conf_slug=conf.slug) }}">{{_("New PM")}}</a>
-            <form method="POST" action="{{ url('plainui:personal_message_delete', conf_slug=conf.slug) }}">
-                {{ csrf_input }}
-                <ul>
-                {%- for msg in msgs %}
-                    <li>
-                        {{msg.timestamp | strftime}}
-                        <a href="{{ url('plainui:personal_message_show', conf_slug=conf.slug, msg_id=msg.id) }}">{{msg.subject}}</a>
-                        {% if not sent_mode %}
-                            by {{msg.sender_name}}
-                            {% if msg.was_read %}[Read]{% endif %}
-                            {% if msg.has_responded %}[Responded]{% endif %}
-                            {% if msg.flagged_for_abuse %}[Flagged]{% endif %}
-                        {% else %}
-                            to {{msg.recipient_name}}
-                        {%endif %}
-                        <button class="btn btn-danger" type="submit" name="id" value="{{msg.id}}">{{ _("Delete") }}</button>
-                    </li>
-                {%- endfor %}
-                </ul>
-                {{ msgs | length }} of {{ total }}
-            </form>
-        </div>
-    </div>
+            </td>
+            <td>
+                {% if not sent_mode %}
+                    {% if msg.has_responded %}
+                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reply-fill" viewBox="0 0 16 16">
+                            <path transform="translate(16), scale(-1, 1)" d="M9.079 11.9l4.568-3.281a.719.719 0 0 0 0-1.238L9.079 4.1A.716.716 0 0 0 8 4.719V6c-1.5 0-6 0-7 8 2.5-4.5 7-4 7-4v1.281c0 .56.606.898 1.079.62z"/>
+                        </svg>
+                    {% endif %}
+                {%  endif %}
+                <a href="{{ url('plainui:personal_message_show', conf_slug=conf.slug, msg_id=msg.id) }}">{{msg.subject}}</a></td>
+            <td>{{msg.timestamp | strftime}}</td></a>
+            <td>
+                {# {% not implemented? should color the flag button instead! if msg.flagged_for_abuse %}[{ _("messages_flagged") }]{% endif %} #}
+                {% if not sent_mode %}
+                    {{ fbtns.report(report_url=msg.id, kind="pn", title=_("report this message")) }}
+                {% endif %}
+                <button class="ml-2 btn-icon-big btn btn-danger" type="submit" name="id" value="{{msg.id}}" title="{{ _("messages_delete") }}">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-square-fill" viewBox="0 0 16 16">
+                      <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708z"/>
+                    </svg>
+                </button>
+            </td>
+        </tr>
+        {%- endfor %}
+            </tbody>
+        </table>
+        {{ msgs | length }} {{ _("messages_x_of_n") }} {{ total }}
+    </form>
 </div>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/personal_message_send.html b/src/plainui/jinja2/plainui/personal_message_send.html
index 78132c80e663dd957f99249601f12e58095c9853..5c1980e5a6fad60bfa204894544404f0d7f51a01 100644
--- a/src/plainui/jinja2/plainui/personal_message_send.html
+++ b/src/plainui/jinja2/plainui/personal_message_send.html
@@ -2,23 +2,22 @@
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - {{ _("Personal Messages - Send") }}{% endblock %}
 {% block content %}
-<div>
-    <div class="row justify-content-center">
-        <div class="col-auto"><h1>{{ _("Send Personal Message") }}</h1></div>
-        <div class="w-100"></div>
-        <div class="col-auto">
-            <form method="POST">
-                {{ csrf_input }}
 
-                {{ form_elements.errors(form) }}
-                {{ form_elements.hidden(form, 'in_reply_to') }}
-                {{ form_elements.text(form, 'recipient') }}
-                {{ form_elements.text(form, 'subject') }}
-                {{ form_elements.textarea(form, 'body') }}
+{{ titleMacro.title(_("Send Personal Message")) }}
+<form class="border border-tertiary p-6 text-light mx-0" method="POST">
+    <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{{ _("new message") }}</h2>
+    {{ csrf_input }}
 
-                <button type="submit" class="btn btn-primary">{{ _("Send") }}</button>
-            </form>
-        </div>
-    </div>
-</div>
+    {{ form_elements.errors(form) }}
+    {{ form_elements.hidden(form, 'in_reply_to') }}
+    {{ form_elements.text(form, 'recipient') }}
+    {{ form_elements.text(form, 'subject') }}
+    {{ form_elements.textarea(form, 'body') }}
+
+    <ul class="list-unstyled row justify-content-end ">
+        <li class="col-1 order-last">
+            <button type="submit" class="btn btn-primary order-last">{{ _("Send") }}</button>
+        </li>
+    </ul>
+</form>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/personal_message_show.html b/src/plainui/jinja2/plainui/personal_message_show.html
index 1f822bd1a01c8d18e6439d91bacf5910ef991733..91a727451238b580a44af2a8c53a8c69a54a3704 100644
--- a/src/plainui/jinja2/plainui/personal_message_show.html
+++ b/src/plainui/jinja2/plainui/personal_message_show.html
@@ -1,19 +1,32 @@
+{% import "plainui/components/function_btns.html" as fbtns with context %}
 {% from "plainui/components/markdown.html" import markdown %}
+
 {% extends "plainui/base.html" %}
 {% block title %}{{conf.name}} - {{ _("Personal Message") }}{% endblock %}
 {% block content %}
-<div>
-    <div class="row justify-content-center">
-        <div class="col-auto"><h1>{{ _("Personal Message") }}</h1></div>
-        <div class="w-100"></div>
-        <div class="col-auto">
-            <h2>{{ msg.subject }}</h2>
-            <div>
-                Sent From {{ msg.sender.username }} To {{ msg.recipient.username }}
-                At {{ msg.timestamp | strftime }}
-            </div>
-            {{ markdown(msg_body) }}
-        </div>
-    </div>
+{{ titleMacro.title(_("Personal Messages"), report_url=msg.id, report_kind="pn") }}
+
+<div class="border border-tertiary p-6 text-light mx-0">
+    <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{{ msg.subject }}</h2>
+    <h6 class="card-subtitle mb-2 text-muted">{{ _("messages_from_short") }} {{ msg.sender.username }} {{ _("messages_to_short") }} {{ msg.recipient.username }} {{ _("messages_at") }} {{ msg.timestamp | strftime }}</h6>
+    {{ markdown(msg_body) }}
+    <ul class="mt-2 list-unstyled row justify-content-end ">
+        <li class="col-1">
+            <form method="POST" action="{{ url('plainui:personal_message_delete', conf_slug=conf.slug) }}">
+                <button class="ml-2 btn-icon-big btn btn-danger" type="submit" name="id" value="{{msg.id}}" title="{{ _("messages_delete") }}">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-square-fill" viewBox="0 0 16 16">
+                      <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708z"/>
+                    </svg>
+                </button>
+            </form>
+        </li>
+        {% if user.id == msg.recipient.id %}
+            <li class="col-1">
+                <a class="btn btn-primary" href="{{ url('plainui:personal_message_send_to', conf_slug=conf.slug, recipient=msg.sender.username) ~ '?in_reply_to=' ~ msg.id | urlencode ~ '&subject=AW: ' ~ msg.subject | truncate(100) | urlencode }}">
+                    {{_("Reply")}}
+                </a>
+            </li>
+        {% endif %}
+    </ul>
 </div>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/profile.html b/src/plainui/jinja2/plainui/profile.html
index 473edab69745dcf1fab1ca9cac5df6757996bc3b..58d6ac531663c1077df9d8b2b94d278443875e3e 100644
--- a/src/plainui/jinja2/plainui/profile.html
+++ b/src/plainui/jinja2/plainui/profile.html
@@ -2,77 +2,56 @@
 {% 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/image.html" as imageMacro %}
 
 {% block title %}{{conf.name}} - {{ _("Profile") }}{% endblock %}
 {% block content %}
 
-
 {{ titleMacro.title( _("My Dashboard") ) }}
-<div class="row border my-5 p-6">
-    <div class="col mr-1">
-        <div class="row">
-            <h2 class="w-100 text-center bg-secondary p-2">{{ user.username }}</h2>
-        </div>
-        <div class="row">
-            <div class="col-4 border p-2">
-                <img class="img-fluid" src="/static/plainui/img/rc3_no_avater.png" alt="{{ _("Avatar image") }}">
-            </div>
-            <dl clasS="col-4 border 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>
-        <div class="row mt-2">
-            <h2 class="w-100 text-center bg-secondary p-2">{{ _("overview") }}</h2>
-        </div>
-        <div class="row mt-2 border p-2">
-            <div class="row w-100">
-                <div class="col-9">{{ _("total events added schedule") }}</div>
-                <div class="col-3 text-right">TODO: </div>
-            </div>
-            <div class="row w-100">
-                <div class="col-9">{{ _("total events attended") }}</div>
-                <div class="col-3 text-right">TODO: </div>
-            </div>
-            <div class="row w-100">
-                <div class="col-9">{{ _("total assemblies involved") }}</div>
-                <div class="col-3 text-right">TODO: </div>
-            </div>
-            <div class="row w-100">
-                <div class="col-9">{{ _("total tokens found") }}</div>
-                <div class="col-3 text-right">TODO: </div>
-            </div>
-        </div>
-    </div>
-    <div class="col ml-1">
-        <div class="row">
-            <h2 class="w-100 text-center bg-secondary p-2">{{ _("custom preferences") }}</h2>
-        </div>
-        <div class="row border p-2">
-            <form method="POST">
-                {{ csrf_input }}
-
-                {{ form_elements.errors(form) }}
-                {{ form_elements.textarea(form, 'description') }}
-                {{ form_elements.text(form, 'pronouns') }}
-                {{ form_elements.checkbox(form, 'high_contrast') }}
-                {{ form_elements.checkbox(form, 'receive_audio') }}
-                {{ form_elements.checkbox(form, 'receive_video') }}
-
-                <input type="submit">
-            </form>
-            <a href="{{ url('plainui:password_change', conf_slug=conf.slug) }}">{{ _("Change Password") }}</a>
+
+<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="row">
+        <div class="col-5">
+            {{ imageMacro.image(image="/static/plainui/img/rc3-no-avatar-plattform-active.jpeg", alt=_("Avatar image"), title=_("Avatar image") ) }}
         </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">
+        <h2 class="w-100 bg bg-info p-2 px-5 h3 text-white text-center">{{ _("custom preferences") }}</h2>
+        {{ csrf_input }}
+
+        {{ form_elements.textarea(form, 'description') }}
+        {{ form_elements.text(form, 'pronouns') }}
+        {{ form_elements.select(form, 'time_zone') }}
+        {{ form_elements.checkbox(form, 'high_contrast') }}
+        {{ form_elements.checkbox(form, 'receive_audio') }}
+        {{ form_elements.checkbox(form, 'receive_video') }}
+
+        <ul class="row row-cols-1 row-cols-md-2 row-cols-lg-3 list-unstyled">
+            <li class="col d-none d-lg-block">&nbsp;</li>
+            <li class="col mb-2 mb-lg-0">
+                <a class="btn btn-xl btn-block btn-secondary px-5" href="{{ url('plainui:password_change', conf_slug=conf.slug) }}">{{ _("Change Password") }}</a>
+            </li>
+            <li class="col">
+                <button type="submit" class="btn btn-xl btn-block btn-primary px-5">{{ _("save") }}</button>
+            </li>
+        </ul>
+
+        {{ form_elements.errors(form) }}
+    </form>
 </div>
 
+{# 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 }}
@@ -85,23 +64,25 @@
         <li>{{badge_link.badge.name}} {% if badge_link.hidden %}(HIDDEN){% endif %}</li>
     {% endfor %}
     </ul>
-</div>
+</div> #}
 
-<hr class="my-5 border-primary">
+<hr class="my-8">
 
 <h2>{{ _("My Favorites") }}</h2>
 <div class="border border-tertiary p-6">
     <h3 class="bg bg-info p-2 px-5 text-white text-center">{{ ("Events") }}</h3>
     {{ list_events.list(my_favorite_events, is_favorite_events, is_fahrplan_events ) }}
-    <h3 class="bg bg-info p-2 px-5 text-white text-center mt-5">{{ _("Assemblies") }}</h3>
+    <h3 class="bg bg-info p-2 px-5 text-white text-center mt-8">{{ _("Assemblies") }}</h3>
     {{ list_assm.list(my_favorite_assemblies, is_favorite_assemblies ) }}
 </div>
 
-<hr class="my-5 border-primary">
+<hr class="my-8">
 
 <h2>{{ _("My Fahrplan") }}</h2>
 <div class="border border-tertiary p-6">
     {{ list_events.list(my_fahrplan_events, is_favorite_events, is_fahrplan_events ) }}
 </div>
 
+<hr class="my-11 border-top-0">
+
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/report_content.html b/src/plainui/jinja2/plainui/report_content.html
index 4e44ee2a121cebf7699d7f11642ff28415b39a89..755a6de3202b86a6be9ee61c3a70ad0fbe14f1d2 100644
--- a/src/plainui/jinja2/plainui/report_content.html
+++ b/src/plainui/jinja2/plainui/report_content.html
@@ -14,14 +14,13 @@
         {{ formElementsMacro.hidden(form, 'kind_data') }}
         {{ formElementsMacro.hidden(form, 'next') }}
 
-        {{ formElementsMacro.errors(form) }}
         {{ formElementsMacro.select(form, 'category') }}
         {{ formElementsMacro.textarea(form, 'message') }}
         {{ formElementsMacro.textarea(form, 'message2') }}
 
         <ul class="row row-cols-1 row-cols-sm-3 list-unstyled">
             <li class="col d-none d-md-block">&nbsp;</li>
-            <li class="col mb-3 mb-sm-0">
+            <li class="col mb-3 mb-sm-0 d-js-only">
                 <a
                     href="javascript:history.back()"
                     class="btn btn-xl btn-block btn-secondary"
@@ -33,6 +32,7 @@
                 <button type="submit" class="btn btn-xl btn-block btn-primary px-5">{{ _("Send") }}</button>
             </li>
         </ul>
+
         {{ formElementsMacro.errors(form) }}
         <p>{{ _("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.") }}</p>
     </form>
diff --git a/src/plainui/jinja2/plainui/room.html b/src/plainui/jinja2/plainui/room.html
index 5b0bba0e38d02f99f95298e536cb59b90293eb1d..c09107bf8480f042aacb3b2583b7b89df143dc21 100644
--- a/src/plainui/jinja2/plainui/room.html
+++ b/src/plainui/jinja2/plainui/room.html
@@ -1,4 +1,5 @@
 {% import "plainui/components/integrations.html" as integrations %}
+{% import "plainui/components/markdown.html" as markdownMacro %}
 {% extends "plainui/base.html" %}
 {% block title %}Conference {{conf.name}} - Room {{room.name}}{% endblock %}
 {% block head %}
@@ -16,4 +17,18 @@
     {% if voc_stream %}
     {{ integrations.vocPlayer(vocStream=voc_stream) }}
     {% endif %}
+
+    {% if room.description %}
+    <h4>{{ _("Description") }}</h4>
+    <p>{{ markdownMacro.markdown(markdown=room.description | safe) }}</p>
+    {% endif %}
+
+    <div class="mt-4">
+        <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>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/sos_edit.html b/src/plainui/jinja2/plainui/sos_edit.html
index 22385c8d26d4cd4b4399fbaf83c78dc427760d7a..2fb083cd05996ddaa80f01cd305cb373c7b10082 100644
--- a/src/plainui/jinja2/plainui/sos_edit.html
+++ b/src/plainui/jinja2/plainui/sos_edit.html
@@ -1,14 +1,12 @@
 {% import "plainui/components/form_elements.html" as form_elements %}
 {% extends "plainui/base.html" %}
-{% block title %}{{conf.name}} - Edit Selforganized Session{% endblock %}
+{% block title %}{{conf.name}} - Edit Self-organized Session{% endblock %}
 {% block content %}
-    {{ titleMacro.title(_("Selforganized Session")) }}
+    <form method="POST" class="border p-6 mx-auto my-11{% if form.errors %} border-danger{% else %} border-tertiary{% endif %}">
+        <h1 class="text-center bg-info p-3 text-white h3">{{ _("Self-organized Session") }}</h1>
 
-    <form method="POST">
         {{ csrf_input }}
 
-        {{ form_elements.errors(form) }}
-
         {{ form_elements.text(form, 'name') }}
         {{ form_elements.select(form, 'room') }}
         {{ form_elements.text(form, 'schedule_start') }}
@@ -17,7 +15,17 @@
         {{ form_elements.text(form, 'language') }}
         {{ form_elements.textarea(form, 'description') }}
 
-        <a class="btn btn-secondary" href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ _("Cancel") }}</a>
-        <button type="submit" class="btn btn-primary">{{ _("Update") if edit_mode else _("Create") }}</button>
+        <ul class="row row-cols-1 row-cols-sm-3 list-unstyled">
+            <li class="col d-none d-md-block">&nbsp;</li>
+            <li class="col mb-3 mb-sm-0">
+                <a class="btn btn-xl btn-block btn-secondary" title="{{ _("Back") }}" href="{{ url('plainui:assembly', conf_slug=conf.slug, assembly_slug=assembly.slug) }}">{{ _("back") }}</a>
+            </li>
+            <li class="col">
+                <button type="submit" class="btn btn-xl btn-block btn-primary px-5">{{ _("Update") if edit_mode else _("Create") }}</button>
+            </li>
+        </ul>
+
+        {{ form_elements.errors(form) }}
+
     </form>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/static_page.html b/src/plainui/jinja2/plainui/static_page.html
index 844e2de6a31985dc32dce1a8c283da3209183f01..95cb08b0e48e6961517b1cacba8e54bfdaa7197e 100644
--- a/src/plainui/jinja2/plainui/static_page.html
+++ b/src/plainui/jinja2/plainui/static_page.html
@@ -1,5 +1,4 @@
 {% extends "plainui/base.html" %}
-{% import "plainui/components/title.html" as titleMacro %}
 {% import "plainui/components/markdown.html" as markdownMacro %}
 {% block title %}{{conf.name}} - {{ page.title }}{% endblock %}
 
diff --git a/src/plainui/jinja2/plainui/upcoming.html b/src/plainui/jinja2/plainui/upcoming.html
index a49e022d9f009d3b623d729b87ef950798dfa9b1..9b0e45e3fcba73d1c5671e1dc2f6cd171eae373a 100644
--- a/src/plainui/jinja2/plainui/upcoming.html
+++ b/src/plainui/jinja2/plainui/upcoming.html
@@ -7,6 +7,6 @@
 
     <h2>{{ _("upcoming events") }}</h2>
     <div class="border border-tertiary p-6">
-        {{ list_events.tiles(events, my_favorite_events, my_scheduled_events ) }}
+        {{ list_events.slider(events, my_favorite_events, my_scheduled_events ) }}
     </div>
 {% endblock %}
diff --git a/src/plainui/jinja2/plainui/user.html b/src/plainui/jinja2/plainui/user.html
index 8ff85bd7ebe07b6ec008c670fd0aa48a5292c9cd..395b177b6813df4a9b40d8bae3a2da50c6a19dfb 100644
--- a/src/plainui/jinja2/plainui/user.html
+++ b/src/plainui/jinja2/plainui/user.html
@@ -15,7 +15,7 @@
     </div>
     <div class="row">
         <div class="col-4 border p-2">
-            <img class="img-fluid" src="/static/plainui/img/rc3_no_avater.png" alt="{{ _("Avatar image") }}">
+            <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 }}
diff --git a/src/plainui/jinja2/plainui/world.html b/src/plainui/jinja2/plainui/world.html
index b3b48328f5a753690d1afeee09469243d6d6ac10..3d67ceae24982f1c8678bb97d7f1b282691b7cfa 100644
--- a/src/plainui/jinja2/plainui/world.html
+++ b/src/plainui/jinja2/plainui/world.html
@@ -5,7 +5,12 @@
 {% block title %}{{conf.name}} - {{ _("2D World") }}{% endblock %}
 {% block content %}
     <article>
-        <a href="https://play.at.rc3.world/" class="row my-8">
+        <a
+            href="https://play.at.rc3.world/"
+            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") }}
         </a>
 
@@ -13,7 +18,12 @@
 
         {{ markdownMacro.markdown(markdown=page.rendered_body|safe) }}
 
-        <a href="https://play.at.rc3.world" class="row my-8">
+        <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>
diff --git a/src/plainui/locale/de/LC_MESSAGES/django.po b/src/plainui/locale/de/LC_MESSAGES/django.po
index 6db239aa764d496379c83b6f11b5c8ffc144e230..f72e339edee1ed84586a4ee8865512a7e577c51b 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 11:52+0000\n"
+"POT-Creation-Date: 2020-12-23 13:29+0000\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"
@@ -75,6 +75,15 @@ msgstr ""
 msgid "all assemblies"
 msgstr ""
 
+msgid "assemblies startseite"
+msgstr ""
+
+msgid "assemblies_official_only"
+msgstr "Nur offizielle Assemblies"
+
+msgid "assemblies_community_only"
+msgstr "Nur Community-Assemblies"
+
 msgid "assembly events"
 msgstr ""
 
@@ -84,6 +93,9 @@ msgstr ""
 msgid "all assemblies events"
 msgstr ""
 
+msgid "assmebly events"
+msgstr ""
+
 msgid "New Selforganized Session"
 msgstr ""
 
@@ -372,17 +384,56 @@ msgstr ""
 msgid "New PM"
 msgstr ""
 
+msgid "Received Messages"
+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_delete"
+msgstr "Löschen"
+
+msgid "messages_was_read"
+msgstr "gelesen"
+
+msgid "messages_x_of_n"
+msgstr "von"
+
 msgid "Personal Messages - Send"
 msgstr ""
 
 msgid "Send Personal Message"
-msgstr ""
+msgstr "Nachricht senden"
 
 msgid "Send"
-msgstr ""
+msgstr "Abschicken"
 
 msgid "Personal Message"
-msgstr ""
+msgstr "Persönliche Nachricht"
+
+msgid "messages_from_short"
+msgstr "Von"
+
+msgid "messages_to_short"
+msgstr "An"
+
+msgid "messages_at"
+msgstr "am"
 
 msgid "Avatar image"
 msgstr ""
diff --git a/src/plainui/locale/en/LC_MESSAGES/django.po b/src/plainui/locale/en/LC_MESSAGES/django.po
index 49e790d108ba72564e1eab054fe01952bc499158..4ee1cb9c6c410c4eb907f77086771e27ddfee7da 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 11:52+0000\n"
+"POT-Creation-Date: 2020-12-23 13:29+0000\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"
@@ -75,6 +75,15 @@ msgstr ""
 msgid "all assemblies"
 msgstr ""
 
+msgid "assemblies startseite"
+msgstr ""
+
+msgid "assemblies_official_only"
+msgstr "Only official assemblies"
+
+msgid "assemblies_community_only"
+msgstr "only community assemblies"
+
 msgid "assembly events"
 msgstr ""
 
@@ -84,6 +93,9 @@ msgstr ""
 msgid "all assemblies events"
 msgstr ""
 
+msgid "assmebly events"
+msgstr ""
+
 msgid "New Selforganized Session"
 msgstr ""
 
@@ -360,6 +372,36 @@ msgstr ""
 msgid "New PM"
 msgstr ""
 
+msgid "Received Messages"
+msgstr "Received Messages"
+
+msgid "Sent Messages"
+msgstr "Sent Messages"
+
+msgid "messages_subject"
+msgstr "Subject"
+
+msgid "messages_from"
+msgstr "From"
+
+msgid "messages_to"
+msgstr "To"
+
+msgid "messages_date"
+msgstr "Date"
+
+msgid "messages_state"
+msgstr "State"
+
+msgid "messages_delete"
+msgstr "Delete"
+
+msgid "messages_was_read"
+msgstr "read"
+
+msgid "messages_x_of_n"
+msgstr "of"
+
 msgid "Personal Messages - Send"
 msgstr ""
 
@@ -372,6 +414,15 @@ msgstr ""
 msgid "Personal Message"
 msgstr ""
 
+msgid "messages_from_short"
+msgstr "From"
+
+msgid "messages_to_short"
+msgstr "To"
+
+msgid "messages_at"
+msgstr "At"
+
 msgid "Avatar image"
 msgstr ""
 
diff --git a/src/plainui/static/plainui/img/box-arrow-up-right.svg b/src/plainui/static/plainui/img/box-arrow-up-right.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1d93acb91a5f657008d4fe5b0f9dc5cfe517fcce
--- /dev/null
+++ b/src/plainui/static/plainui/img/box-arrow-up-right.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-up-right" viewBox="0 0 16 16">
+  <path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
+  <path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
+</svg>
\ No newline at end of file
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-01.png b/src/plainui/static/plainui/img/rc3-assembly-event-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..78449b2de9ef56bf5e500dbd3a5f044039bce9c9
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-01.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-02.png b/src/plainui/static/plainui/img/rc3-assembly-event-02.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1461f8d69c7b1fd9d49d08e37c8f74f1b71f834
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-02.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-03.png b/src/plainui/static/plainui/img/rc3-assembly-event-03.png
new file mode 100644
index 0000000000000000000000000000000000000000..97a8c6be6224fc3e8c91f63c8c6c530843e36b89
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-03.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-04.png b/src/plainui/static/plainui/img/rc3-assembly-event-04.png
new file mode 100644
index 0000000000000000000000000000000000000000..44eb1ae552b78175d33a20d116481b99faafa061
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-04.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-05.png b/src/plainui/static/plainui/img/rc3-assembly-event-05.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e1fba3a41296433a19f500ec22be3d8c7d602e4
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-05.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-06.png b/src/plainui/static/plainui/img/rc3-assembly-event-06.png
new file mode 100644
index 0000000000000000000000000000000000000000..957808c4824e96c4215c50be0d3eb0fd5a3ed6ad
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-06.png differ
diff --git a/src/plainui/static/plainui/img/rc3-assembly-event-07.png b/src/plainui/static/plainui/img/rc3-assembly-event-07.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5a6b88958c78fec211f961df552a76a0c0efc49
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-assembly-event-07.png differ
diff --git a/src/plainui/static/plainui/img/rc3-logo-assembly.svg b/src/plainui/static/plainui/img/rc3-logo-assembly.svg
index 53d9e86d2b3925e02c43a95e815a956ce30978a1..cad15be8bd91dbf19cfb1eca14c1bb67549c52cc 100644
--- a/src/plainui/static/plainui/img/rc3-logo-assembly.svg
+++ b/src/plainui/static/plainui/img/rc3-logo-assembly.svg
@@ -1,3 +1,220 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="624" height="624" viewBox="0 0 624 624">
-  <image width="624" height="623" xlink:href="data:img/png;base64,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"/>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="pink_color_bars"
+   data-name="pink  color bars"
+   width="624"
+   height="624"
+   viewBox="0 0 624 624"
+   version="1.1"
+   sodipodi:docname="rc3-logo-assembly_2.svg"
+   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+  <metadata
+     id="metadata25">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1600"
+     inkscape:window-height="1762"
+     id="namedview23"
+     showgrid="false"
+     inkscape:zoom="1.1600968"
+     inkscape:cx="281.55154"
+     inkscape:cy="432.58503"
+     inkscape:window-x="1582"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="pink_color_bars"
+     inkscape:document-rotation="0" />
+  <defs
+     id="defs4">
+    <style
+       id="style2">
+      .cls-1 {
+        fill: #240039;
+      }
+
+      .cls-2 {
+        fill: #670096;
+      }
+
+      .cls-3 {
+        fill: #450069;
+      }
+
+      .cls-4 {
+        fill: #b239ff;
+      }
+
+      .cls-5 {
+        fill: #fff;
+        fill-rule: evenodd;
+      }
+
+      .cls-6 {
+        fill: none;
+        stroke: #fff;
+        stroke-width: 7.63px;
+      }
+    </style>
+  </defs>
+  <image
+     id="Layer_0"
+     data-name="Layer 0"
+     x="1"
+     y="1"
+     width="576"
+     height="575"
+     xlink:href="data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAAkAAAAI/CAYAAACf7mYiAAAKAElEQVR4nO3YsQ0DMQDDQL1//3FTpXCQOXi3ghpCz7Y7AICQs+1rcAAg5HM8QABAzD0WBwBqBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJAjgACAHAEEAOQIIAAgRwABADkCCADIEUAAQI4AAgByBBAAkCOAAIAcAQQA5AggACBHAAEAOQIIAMgRQABAjgACAHIEEACQI4AAgBwBBADkCCAAIEcAAQA5AggAyBFAAECOAAIAcgQQAJDzD6DX7ABAyPtsuxYHADK2/QCQ7ApvODTfkAAAAABJRU5ErkJggg==" />
+  <rect
+     id="_240039"
+     data-name="240039"
+     class="cls-1"
+     x="143"
+     y="240"
+     width="97"
+     height="288"
+     style="fill:#001b18;fill-opacity:1" />
+  <rect
+     id="_240039-2"
+     data-name="240039"
+     class="cls-1"
+     x="528"
+     y="432"
+     width="96"
+     height="192"
+     style="fill:#001b18;fill-opacity:1" />
+  <rect
+     id="_670096"
+     data-name="670096"
+     class="cls-2"
+     x="528"
+     y="47"
+     width="96"
+     height="97"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_670096-2"
+     data-name="670096"
+     class="cls-2"
+     x="432"
+     y="47"
+     width="96"
+     height="193"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_670096-3"
+     data-name="670096"
+     class="cls-2"
+     x="336"
+     y="47"
+     width="96"
+     height="97"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_670096-4"
+     data-name="670096"
+     class="cls-2"
+     x="336"
+     y="432"
+     width="96"
+     height="192"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_670096-5"
+     data-name="670096"
+     class="cls-2"
+     x="240"
+     y="336"
+     width="96"
+     height="288"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_450069"
+     data-name="450069"
+     class="cls-3"
+     x="143"
+     y="528"
+     width="97"
+     height="96"
+     style="fill:#01564d;fill-opacity:1" />
+  <rect
+     id="_450069-2"
+     data-name="450069"
+     class="cls-3"
+     x="528"
+     y="143"
+     width="96"
+     height="289"
+     style="fill:#01a08f;fill-opacity:1" />
+  <rect
+     id="_450069-3"
+     data-name="450069"
+     class="cls-3"
+     x="432"
+     y="528"
+     width="96"
+     height="96"
+     style="fill:#01564d;fill-opacity:1" />
+  <rect
+     id="_450069-4"
+     data-name="450069"
+     class="cls-3"
+     x="240"
+     y="47"
+     width="96"
+     height="289"
+     style="fill:#01564d;fill-opacity:1" />
+  <rect
+     id="b239ff"
+     class="cls-4"
+     x="432"
+     y="240"
+     width="96"
+     height="288"
+     style="fill:#02fae0;fill-opacity:1" />
+  <rect
+     id="b239ff-2"
+     data-name="b239ff"
+     class="cls-4"
+     x="336"
+     y="143"
+     width="96"
+     height="290"
+     style="fill:#02fae0;fill-opacity:1" />
+  <path
+     id="RC3_copy"
+     data-name="RC3 copy"
+     class="cls-5"
+     d="M222.656,545.361v-36.5l31.856-.154,30.774,36.649h24.742v-8.2L285.9,508.712h1.083a23.238,23.238,0,0,0,23.041-22.887V457.062a23.236,23.236,0,0,0-23.041-22.887h-88.3V545.361h23.969Zm0-60.773V458.144h63.093v26.444H222.656Zm208.609,60.773V521.237H344.2V458.144h87.062V434.021H343.12a22.964,22.964,0,0,0-22.886,22.886v65.568a22.966,22.966,0,0,0,22.886,22.886h88.145Zm96.339,0a22.571,22.571,0,0,0,22.423-22.423V499.9a21.224,21.224,0,0,0-1.392-7.655,22.542,22.542,0,0,0-3.711-6.417,20.741,20.741,0,0,0,.7-3.479,30.7,30.7,0,0,0,.232-3.635V456.443a22.44,22.44,0,0,0-22.423-22.422H463.274a21.855,21.855,0,0,0-15.928,6.572,21.474,21.474,0,0,0-6.649,15.85v9.9h23.659v-8.66h57.836v19.794H458.944v23.66h67.423V521.7H464.356v-7.422H440.7v8.659a22.7,22.7,0,0,0,22.577,22.423H527.6Z" />
+  <rect
+     id="rahmen_weiss"
+     data-name="rahmen weiss"
+     class="cls-6"
+     x="1.156"
+     y="-0.188"
+     width="576.313"
+     height="576.343" />
 </svg>
diff --git a/src/plainui/static/plainui/img/rc3-no-avatar-assembly-active.jpeg b/src/plainui/static/plainui/img/rc3-no-avatar-assembly-active.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..d136d59ceb863ca0cef3709b83aae735baac8e11
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-no-avatar-assembly-active.jpeg differ
diff --git a/src/plainui/static/plainui/img/rc3-no-avatar-assembly.jpeg b/src/plainui/static/plainui/img/rc3-no-avatar-assembly.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..3eed660d12539ccd24a9b91dba4e135e0a396e45
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-no-avatar-assembly.jpeg differ
diff --git a/src/plainui/static/plainui/img/rc3-no-avatar-plattform-active.jpeg b/src/plainui/static/plainui/img/rc3-no-avatar-plattform-active.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..cd48c921abfa798e70232daad7b65f331c727c82
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-no-avatar-plattform-active.jpeg differ
diff --git a/src/plainui/static/plainui/img/rc3-no-avatar-plattform.jpeg b/src/plainui/static/plainui/img/rc3-no-avatar-plattform.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..f6c865ff57fbfbef092fb8f7fc566561604eb950
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-no-avatar-plattform.jpeg differ
diff --git a/src/plainui/static/plainui/img/rc3-no-avatar-world.jpeg b/src/plainui/static/plainui/img/rc3-no-avatar-world.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..cd48c921abfa798e70232daad7b65f331c727c82
Binary files /dev/null and b/src/plainui/static/plainui/img/rc3-no-avatar-world.jpeg differ
diff --git a/src/plainui/static/plainui/img/rc3_no_avater.png b/src/plainui/static/plainui/img/rc3_no_avater.png
deleted file mode 100644
index 6a8c9b22486a82198602e5240832a1dcd26c6326..0000000000000000000000000000000000000000
Binary files a/src/plainui/static/plainui/img/rc3_no_avater.png and /dev/null differ
diff --git a/src/plainui/styles/_button-classes.scss b/src/plainui/styles/_button-classes.scss
index 4d8a1b49dbee2d44909a7995c2d3f0d25613caea..cc6512d60b6d29597f38491829129c56c555fb92 100644
--- a/src/plainui/styles/_button-classes.scss
+++ b/src/plainui/styles/_button-classes.scss
@@ -110,19 +110,20 @@
             cursor: not-allowed;
         }
 
-        &:not(:disabled):not(.disabled):active,
         &:not(:disabled):not(.disabled).active {
             text-shadow: $btn-hover-text-shadow;
+            box-shadow: none;
+        }
+
+        &:not(:disabled):not(.disabled):active {
+            text-shadow: $btn-hover-text-shadow;
+            background: rgba($value, 0.75);
 
             &:focus,
             &.focus {
                 box-shadow: none;
             }
         }
-
-        &:not(:disabled):not(.disabled):active {
-            background: rgba($value, 0.75);
-        }
     }
 
     .btn-#{$color},
@@ -165,10 +166,15 @@
             cursor: not-allowed;
         }
 
-        &:not(:disabled):not(.disabled):active,
         &:not(:disabled):not(.disabled).active,
         .show > &.dropdown-toggle {
             text-shadow: $btn-hover-text-shadow;
+            box-shadow: none;
+        }
+
+        &:not(:disabled):not(.disabled):active {
+            background: rgba($value, 0.75);
+            text-shadow: $btn-hover-text-shadow;
 
             &:focus,
             &.focus {
@@ -176,10 +182,6 @@
             }
         }
 
-        &:not(:disabled):not(.disabled):active {
-            background: rgba($value, 0.75);
-        }
-
         &:not(:disabled):not(.disabled).active,
         .show > &.dropdown-toggle {
             @if $color == "assembly" {
diff --git a/src/plainui/styles/_import-fonts.scss b/src/plainui/styles/_import-fonts.scss
index fa238fd53d0c9a934c3157ed5a63108988b909c3..a1a1ed6ef5518765d3380a5d06962d175fc61f0d 100644
--- a/src/plainui/styles/_import-fonts.scss
+++ b/src/plainui/styles/_import-fonts.scss
@@ -1,12 +1,3 @@
-@font-face{
-    font-family:"Orbitron";
-    font-weight: 400;
-    font-style: normal;
-    font-display: swap;
-    src:url("fonts/orbitron-light-webfont.woff") format("woff"),
-    url("fonts/orbitron-light-webfont.ttf") format("truetype");
-}
-
 @font-face{
     font-family:"Orbitron";
     font-weight: 700;
diff --git a/src/plainui/styles/_util-classes.scss b/src/plainui/styles/_util-classes.scss
index f73176b0153e316066a79035eed899f76e23ce18..abdefd3aac9d0f2677ca569063a580e8e6f80e51 100644
--- a/src/plainui/styles/_util-classes.scss
+++ b/src/plainui/styles/_util-classes.scss
@@ -84,11 +84,16 @@ h6,
 
 .font-headings {
     font-family: $headings-font-family;
+    font-weight: 700;
+}
+
+.font-sans-serif {
+    font-family: $font-family-sans-serif;
 }
 
 h6,
 .h6 {
-    font-weight: 400;
+    text-transform: unset;
 }
 
 .mw-664 {
@@ -99,6 +104,26 @@ h6,
     max-width: 50.625rem;
 }
 
+.external {
+    white-space: nowrap;
+
+    &::after {
+        content: "";
+        display: inline-block;
+        width: $spacer;
+        height: $spacer;
+        margin-left: map-get($spacers, 1);
+        background: currentColor;
+        mask-size: contain;
+        mask-repeat: no-repeat;
+        mask-image: url("img/box-arrow-up-right.svg");
+    }
+}
+
+.no-js .d-js-only {
+    display: none !important;
+}
+
 // Basic Bootstrap table - only use the basic table
 // - if you need another configuration update the bootstrap variables
 .table {
diff --git a/src/plainui/styles/components/_header.scss b/src/plainui/styles/components/_header.scss
index 193ffe5525be4ed5fd420e8e0805203d49bf84eb..c1cdc10f5b4c8855473823e1b658a371cd2aa2f8 100644
--- a/src/plainui/styles/components/_header.scss
+++ b/src/plainui/styles/components/_header.scss
@@ -54,6 +54,7 @@
             position: absolute !important;
             left: -0.4rem !important;
             top: -0.4rem !important;
+            padding: 0.1rem 0.5rem !important;
         }
 
         &-box-2x1 {
diff --git a/src/plainui/styles/components/_index.scss b/src/plainui/styles/components/_index.scss
index 25aa310f3e16a969d230c5c42ff3863ff6e8ad8a..c4396267c3d62be3292f7927f5f618f1eaa2e1d8 100644
--- a/src/plainui/styles/components/_index.scss
+++ b/src/plainui/styles/components/_index.scss
@@ -7,3 +7,4 @@
 @import "syntaxhilite";
 @import "player";
 @import "tile-board";
+@import "slider";
diff --git a/src/plainui/styles/components/_markdown.scss b/src/plainui/styles/components/_markdown.scss
index def2cc48577549a3d34af6ec2230d0d78970fa2d..0286993a5234e44574016e17bfe84df7e5825087 100644
--- a/src/plainui/styles/components/_markdown.scss
+++ b/src/plainui/styles/components/_markdown.scss
@@ -23,6 +23,7 @@
     hr {
         border-width: 1px;
         margin: map-get($spacers,4) 0;
+        border-color: $markdown-border-color;
     }
 
     table {
diff --git a/src/plainui/styles/components/_slider.scss b/src/plainui/styles/components/_slider.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a54c530a61197533361b54f53c3f20f0c4522c15
--- /dev/null
+++ b/src/plainui/styles/components/_slider.scss
@@ -0,0 +1,13 @@
+.rc3-slider {
+    &__container {
+        overflow-x: auto;
+        overflow-y: hidden;
+        -webkit-overflow-scrolling: touch;
+        scroll-snap-type: y mandatory;
+        scroll-padding: map-get($spacers, 2);
+    }
+
+    &__item {
+        scroll-snap-align: start;
+    }
+}
diff --git a/src/plainui/styles/utils/_bootstrap-theme-assembly.scss b/src/plainui/styles/utils/_bootstrap-theme-assembly.scss
index 47dd4c6357af731b5a28059477331fb3c2e08a35..e020c53bb5cbdc7a331398aa9b3021cb78a573f3 100644
--- a/src/plainui/styles/utils/_bootstrap-theme-assembly.scss
+++ b/src/plainui/styles/utils/_bootstrap-theme-assembly.scss
@@ -43,6 +43,7 @@ $link-hover-color: $primary;
 
 $border-color: $secondary;
 $hr-border-color: $primary;
+$markdown-border-color: $primary;
 
 $table-head-color: $gray-100;
 
diff --git a/src/plainui/styles/utils/_bootstrap-theme-high-contrast.scss b/src/plainui/styles/utils/_bootstrap-theme-high-contrast.scss
index 5885b3b54226f9d3aa87fba4763f5ee9a7ac62d2..36724b90e02e8f71fa743b0808124b9585230226 100644
--- a/src/plainui/styles/utils/_bootstrap-theme-high-contrast.scss
+++ b/src/plainui/styles/utils/_bootstrap-theme-high-contrast.scss
@@ -42,6 +42,7 @@ $card-cap-bg: $info;
 $link-hover-color: $primary;
 
 $hr-border-color: $secondary;
+$markdown-border-color: $primary;
 $table-head-color: $primary;
 
 $input-color: $gray-100;
diff --git a/src/plainui/styles/utils/_bootstrap-theme-plattform.scss b/src/plainui/styles/utils/_bootstrap-theme-plattform.scss
index 1ba6f1353052ccd8a8c0cb6432740dbfdb4ce192..c23d459e867dd4f0bb15a36d0c250c1b87eee942 100644
--- a/src/plainui/styles/utils/_bootstrap-theme-plattform.scss
+++ b/src/plainui/styles/utils/_bootstrap-theme-plattform.scss
@@ -43,7 +43,8 @@ $link-color: map-get($plattform, "font-pink");
 $link-hover-color: $link-color;
 
 $border-color: $tertiary;
-$hr-border-color: $secondary;
+$hr-border-color: $primary;
+$markdown-border-color: $secondary;
 $table-head-color: $gray-100;
 
 $input-color: $gray-100;
diff --git a/src/plainui/styles/utils/_bootstrap-theme-world.scss b/src/plainui/styles/utils/_bootstrap-theme-world.scss
index 09582e11f5c86a011b2adfd2b7759b0998ad8cce..75f2bba70906f0dd3a82ef94507692225aa70557 100644
--- a/src/plainui/styles/utils/_bootstrap-theme-world.scss
+++ b/src/plainui/styles/utils/_bootstrap-theme-world.scss
@@ -38,7 +38,8 @@ $card-cap-bg: $info;
 $link-hover-color: $primary;
 
 $border-color: $tertiary;
-$hr-border-color: $secondary;
+$hr-border-color: $primary;
+$markdown-border-color: $primary;
 
 $input-color: $gray-100;
 $input-focus-color: $gray-100;
diff --git a/src/plainui/styles/utils/_forms.scss b/src/plainui/styles/utils/_forms.scss
index c7dfba0871f23920cea6b024911103d096e0fcd2..e57a11af84dd12af14ab34545557a390354f24ab 100644
--- a/src/plainui/styles/utils/_forms.scss
+++ b/src/plainui/styles/utils/_forms.scss
@@ -1,8 +1,7 @@
 @use "./fonts";
 @use "./button";
 
-$input-font-family: fonts.$headings-font-family;
-$input-font-weight: 400;
+$input-font-family: fonts.$font-family-sans-serif;
 
 $input-padding-y: 1rem;
 $input-padding-x: 1rem;
diff --git a/src/plainui/tests.py b/src/plainui/tests.py
index d0b81c5a75bede27199b8327990d938c2f6a92df..131b2e631b9aa11f16f06c19e11796efa9973bed 100644
--- a/src/plainui/tests.py
+++ b/src/plainui/tests.py
@@ -9,7 +9,7 @@ from django.contrib import messages
 from django.contrib.contenttypes.models import ContentType
 from django.core import mail
 from django.http import SimpleCookie
-from django.test import TestCase, override_settings
+from django.test import TestCase, override_settings, modify_settings
 from django.urls import reverse
 from django.utils import timezone
 from django.utils.formats import localize
@@ -95,7 +95,7 @@ class ViewsTest(TestCase):
         hidden_conf = Conference(name='foo_self.conf', slug='slug42', is_public=False)
         hidden_conf.save()
 
-        with override_settings(DEBUG=False):
+        with override_settings(DEBUG=False), modify_settings(MIDDLEWARE={'remove': ['debug_toolbar.middleware.DebugToolbarMiddleware']}, INSTALLED_APPS={'remove': ['debug_toolbar']}):  # noqa: E501
             resp = self.client.get(reverse('plainui:conferences'), follow=False)
             self.assertRedirects(resp, reverse('plainui:landing', kwargs={'conf_slug': self.conf.slug}))
 
@@ -144,7 +144,7 @@ class ViewsTest(TestCase):
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['event'], event)
         self.assertEqual(list(resp.context_data['tags']), [tagitem])
-        self.assertEqual(resp.context_data['scope'], '')
+        self.assertEqual(resp.context_data['scope'], 'plattform')
         self.assertEqual(list(resp.context_data['suggested']), [suggested_event2, suggested_event])
 
         event.kind = Event.Kind.ASSEMBLY
@@ -201,6 +201,8 @@ class ViewsTest(TestCase):
         self.conf.start = datetime(2020, 12, 24, 10, tzinfo=utc)
         self.conf.end = datetime(2020, 12, 27, 10, tzinfo=utc)
         self.conf.save()
+        self.user.time_zone = 'UTC'
+        self.user.save()
 
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state=Assembly.State.PLACED)
         assembly.save()
@@ -227,7 +229,7 @@ class ViewsTest(TestCase):
             'schedule_start': '25.12.2020 14:00',
             'schedule_duration': '42:00',
         })
-        self.assertRedirects(resp, reverse('plainui:assembly', kwargs={'conf_slug': self.conf.slug, 'assembly_slug': assembly.slug}))
+        self.assertRedirects(resp, reverse('plainui:sos_edit', kwargs={'conf_slug': self.conf.slug, 'assembly_slug': assembly.slug, 'event_slug': 'sos-1'}))
         sos = assembly.events.get(kind=Event.Kind.SELF_ORGANIZED)
         self.assertEqual(sos.conference, self.conf)
         self.assertEqual(sos.assembly, assembly)
@@ -552,10 +554,12 @@ class ViewsTest(TestCase):
         resp = self.client.post(reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}), {
             'description': 'new_description',
             'high_contrast': 'on',
+            'time_zone': 'Europe/Berlin',
         })
         self.assertRedirects(resp, reverse('plainui:userprofile', kwargs={'conf_slug': self.conf.slug}))
         self.user.refresh_from_db()
         self.assertEqual(self.user.description, 'new_description')
+        self.assertEqual(self.user.time_zone, 'Europe/Berlin')
         self.assertTrue(self.user.high_contrast)
         self.assertFalse(self.user.receive_audio)
         self.assertFalse(self.user.receive_video)
@@ -1375,10 +1379,13 @@ class ViewsTest(TestCase):
         self.assertRedirects(resp, reverse('plainui:board_private', kwargs={'conf_slug': self.conf.slug}))
         self.assertTrue(BulletinBoardEntry.objects.filter(pk=e2.pk).exists())
 
+    @override_settings(TIME_ZONE='UTC')
     def test_Fahrplan(self):
         self.conf.start = datetime(2020, 1, 1, 0, 0, 0, tzinfo=utc)
         self.conf.end = datetime(2020, 1, 3, 0, 0, 0, tzinfo=utc)
         self.conf.save()
+        self.user.time_zone = 'UTC'
+        self.user.save()
 
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state=Assembly.State.PLACED)
         assembly.save()
@@ -1428,10 +1435,23 @@ class ViewsTest(TestCase):
             'calendar_step_minutes': 30,
         })
 
+        # some random requests that test building the filter content
+        resp = self.client.get(reverse('plainui:fahrplan', kwargs={'conf_slug': self.conf.slug}), {'mode': 'calendar', 'show_day_filters': 'y'})
+        self.assertEqual(resp.context_data['conf'], self.conf)
+
+        resp = self.client.get(reverse('plainui:fahrplan', kwargs={'conf_slug': self.conf.slug}), {'mode': 'calendar', 'show_assembly_filters': 'y'})
+        self.assertEqual(resp.context_data['conf'], self.conf)
+
+        resp = self.client.get(reverse('plainui:fahrplan', kwargs={'conf_slug': self.conf.slug}), {'mode': 'calendar', 'show_track_filters': 'y'})
+        self.assertEqual(resp.context_data['conf'], self.conf)
+
+    @override_settings(TIME_ZONE='UTC')
     def test_MyFahrplan(self):
         self.conf.start = datetime(2020, 1, 1, 0, 0, 0, tzinfo=utc)
         self.conf.end = datetime(2020, 1, 3, 0, 0, 0, tzinfo=utc)
         self.conf.save()
+        self.user.time_zone = 'UTC'
+        self.user.save()
 
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state=Assembly.State.PLACED)
         assembly.save()
diff --git a/src/plainui/urls.py b/src/plainui/urls.py
index 6c2bff8c7cd901c3274e4a4b70276d5a9e5a8606..d496fe3fe090d9e8a9e0369ae62bd11a614ebb50 100644
--- a/src/plainui/urls.py
+++ b/src/plainui/urls.py
@@ -54,6 +54,8 @@ urlpatterns = [
     path('<slug:conf_slug>/upcoming', views.UpcomingView.as_view(), name='upcoming'),
     path('<slug:conf_slug>/assemblies', views.AssembliesView.as_view(), name='assemblies'),
     path('<slug:conf_slug>/assemblies/all', views.AssembliesAllView.as_view(), name='assemblies_all'),
+    path('<slug:conf_slug>/assemblies/all/official', views.AssembliesAllView.as_view(), {'qfilter': 'official'}, name='assemblies_official'),
+    path('<slug:conf_slug>/assemblies/all/community', views.AssembliesAllView.as_view(), {'qfilter': 'community'}, name='assemblies_community'),
     path('<slug:conf_slug>/assemblies/events', views.AssembliesEventsView.as_view(), name='assemblies_events'),
     path('<slug:conf_slug>/assembly/<slug:assembly_slug>/', views.AssemblyView.as_view(), name='assembly'),
     path('<slug:conf_slug>/assembly/<slug:assembly_slug>/sos/new', views.SelfOrganizedSessionEditView.as_view(), name='sos_new'),
diff --git a/src/plainui/views.py b/src/plainui/views.py
index a53a3d7f5cc842150aa1b288ef4d16ed253e0731..5b97ccafa5aeaf169f464760452bc37f26915b11 100644
--- a/src/plainui/views.py
+++ b/src/plainui/views.py
@@ -15,6 +15,7 @@ from django.urls import reverse
 from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.http import url_has_allowed_host_and_scheme
+from django.utils.timezone import localtime
 from django.utils.translation import gettext
 from django.views.decorators.debug import sensitive_post_parameters
 from django.views.generic.base import View, TemplateView
@@ -140,7 +141,7 @@ 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]
         ]
-        context['scope'] = '' if event.kind == Event.Kind.OFFICIAL else 'assembly'
+        context['scope'] = 'plattform' if event.kind == Event.Kind.OFFICIAL else 'assembly'
         return context
 
 
@@ -220,7 +221,9 @@ class SelfOrganizedSessionEditView(ConferenceRequiredMixin, FormView):
         return initial
 
     def get_success_url(self):
-        return reverse('plainui:assembly', kwargs={'conf_slug': self.kwargs['conf_slug'], 'assembly_slug': self.kwargs['assembly_slug']})
+        return reverse('plainui:sos_edit', kwargs={'conf_slug': self.kwargs['conf_slug'],
+                                                   'assembly_slug': self.kwargs['assembly_slug'],
+                                                   'event_slug': self.sos.slug})
 
     def form_valid(self, form):
         self.sos.name = form.cleaned_data['name']
@@ -297,9 +300,15 @@ class AssembliesAllView(ConferenceRequiredMixin, TemplateView):
 
     def get_context_data(self, conf_slug, **kwargs):
         context = super().get_context_data(conf_slug=conf_slug, **kwargs)
+        user = self.request.user
         context['conf'] = self.conf
 
-        context['assemblies'] = Assembly.objects.conference_accessible(self.conf)
+        context['qfilter'] = kwargs.get('qfilter', None)
+        context['assemblies'] = Assembly.objects.accessible_by_user(user, self.conf)
+        if context['qfilter'] == 'official':
+            context['assemblies'] = context['assemblies'].filter(is_official=True)
+        elif context['qfilter'] == 'community':
+            context['assemblies'] = context['assemblies'].filter(is_official=False)
         context['my_favorite_assemblies'] = _session_get_favorite_assemblies(self.request.session, self.request.user)
         context['scope'] = 'assembly'
         return context
@@ -365,8 +374,8 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
     def get_object(self, *args, **kwargs):
         return self.request.user
 
-    def get_context_data(self):
-        context = super().get_context_data()
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
         context['conf'] = self.conf
 
         user = self.request.user
@@ -693,6 +702,7 @@ class PersonalMessageSendView(ConferenceRequiredMixin, FormView):
     def get_initial(self):
         initial = super().get_initial()
         initial['recipient'] = self.kwargs.get('recipient', '')
+        initial['subject'] = self.request.POST.get('subject', self.request.GET.get('subject', ''))
         initial['in_reply_to'] = self.request.POST.get('in_reply_to', self.request.GET.get('in_reply_to', ''))
         return initial
 
@@ -988,11 +998,12 @@ def _organize_events_for_calendar(conf, events):
         room_events[0]['event'].schedule_start if room_events[0]['type'] == 'event' else room_events[1]['event'].schedule_start
         for (_, room_events) in rooms_with_events
     )
-    calendar_start = calendar_start.replace(minute=0, second=0, microsecond=0)
+    calendar_start = localtime(calendar_start.replace(minute=0, second=0, microsecond=0))
 
     # calendar ends at the end of the last scheduled event rounded up to the full hour
     calendar_end = max((room_events[-1]['event'].schedule_end for (_, room_events) in rooms_with_events))
     calendar_end = (calendar_end + timedelta(0, 3599)).replace(minute=0, second=0, microsecond=0)
+    calendar_end = localtime(calendar_end)
 
     # put timestamps from calendar_start to calendar_end in 30 minute steps into calendar_time_steps
     calendar_time_steps = []
@@ -1117,7 +1128,11 @@ class FahrplanView(ConferenceRequiredMixin, TemplateView):
             day = None
         context['day'] = day
 
-        curated = None if curated is None else curated == 'y'
+        # curated could be in genereal none (don't filter), false (only non curated), true (only curated)
+        # not easy to toggle three states with one button and beeing easy to display
+        # for now, the button says "curated only", so it does that.
+        # curated = None if curated is None else curated == 'y'
+        # context['curated'] = curated is not None
         context['curated'] = curated is not None
 
         if show_assembly_filters:
@@ -1130,7 +1145,7 @@ class FahrplanView(ConferenceRequiredMixin, TemplateView):
             context['assembly'] = assembly = None
 
         if show_track_filters:
-            tracks = ConferenceTrack.objects.conference_accessible(self.conf)
+            tracks = ConferenceTrack.objects.filter(conference=self.conf, is_public=True)
             context['tracks'] = tracks
             track = tracks.get(slug=track) if track else None
             context['track'] = track
@@ -1296,11 +1311,13 @@ class ReportContentView(ConferenceRequiredMixin, FormView):
         try:
             static_page = StaticPage.objects.conference_accessible(conference=self.conf).get(slug='report_content')
         except StaticPage.DoesNotExist:
-            static_page = StaticPage(conference=self.conf, title='Page Missing', rendered_body='Please configure the static page "report_content" (or change slug... ).')
+            static_page = StaticPage(
+                conference=self.conf, title='Page Missing', rendered_body='Please configure the static page "report_content" (or change slug... ).'
+            )
 
         return super().get_context_data(
             conf=self.conf,
-            page = static_page,
+            page=static_page,
             **kwargs
         )
 
@@ -1318,8 +1335,13 @@ class ReportContentView(ConferenceRequiredMixin, FormView):
 
     def form_valid(self, form):
         form.send_report_mail(self.email_template_name, self.request)
-        messages.success(self.request, gettext("Thank you for your help to make this plattform safer and better! ") \
-         + gettext("Please give us some time to find a solution and keep an eye on your Messages, we may contact you."))
+        messages.success(
+            self.request,
+            gettext(
+                "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."
+            )
+         )
 
         redirect_to = form.cleaned_data['next']
         url_is_safe = url_has_allowed_host_and_scheme(
diff --git a/src/rc3platform/settings/base.py b/src/rc3platform/settings/base.py
index b77169629854e5ac715c376c672ed60feb1f758a..4ef40a5bd8ac6d4c3c6a8be1a05980e8c602bfcf 100644
--- a/src/rc3platform/settings/base.py
+++ b/src/rc3platform/settings/base.py
@@ -67,6 +67,7 @@ MIDDLEWARE = [
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'core.middleware.TimezoneMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 ]
@@ -154,7 +155,7 @@ LANGUAGE_CODE = 'en-us'
 
 LANGUAGE_COOKIE_NAME = 'RC3_LANG'
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = 'Europe/Berlin'
 
 USE_I18N = True
 
@@ -185,10 +186,16 @@ REST_FRAMEWORK = {
     ]
 }
 
+# List of disallowed assembly slugs
+FORBIDDEN_ASSEMBLY_SLUGS = ['visit', 'maps', 'api', 'pusher']
+
 # Mail configuration
 MAIL_REPLY_TO = []
 SUPPORT_HTML_MAILS = False
 
+# API access
+API_USERS = []
+
 # SSO
 SSO_COOKIE_NAME = 'SSO_TOKEN'
 SSO_COOKIE_DOMAIN = '.localhost'
@@ -200,8 +207,18 @@ SSO_SECRET = None
 PRETIX_ISSUER = 'tickets.events.ccc.de'  # expected value in the 'iss' field of pretix
 PRETIX_SECRET_KEY = None  # the JWT shared secret with Pretix
 
+# ----------------------------------
 # External Components Integration
+# ----------------------------------
+
+# BigBlueButton
 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'
+
+# Hangar
 HANGAR_URL = None
+
+# Workadventure
 WORKADVENTURE_URL_SCHEME = 'http://play.{assembly_slug}.localhost:8080/?u={username}'