diff --git a/.isort.cfg b/.isort.cfg
index 90a916d6291eb1d325ecd31c9258d23037dcc2f4..86199fdc497d3b3b5ce9adf3aee776a4d8ba8522 100644
--- a/.isort.cfg
+++ b/.isort.cfg
@@ -3,4 +3,4 @@
 line_length = 88
 multi_line_output = 3
 include_trailing_comma = true
-known_third_party = PyPDF2,arrow,bleach,bs4,captcha,celery,dateutil,django,environ,faker,fastjsonschema,gql,httplib2,icalendar,instaloader,markdown,modelcluster,nh3,pirates,pytest,pytz,requests,sentry_sdk,taggit,wagtail,wagtailmetadata,weasyprint,willow,yaml
+known_third_party = PyPDF2,arrow,bleach,bs4,captcha,celery,dateutil,django,django_ratelimit,environ,faker,fastjsonschema,gql,httplib2,icalendar,instaloader,markdown,modelcluster,nh3,pirates,pytest,pytz,requests,sentry_sdk,taggit,wagtail,wagtailmetadata,weasyprint,willow,yaml
diff --git a/README.md b/README.md
index ba48d873d5adaf39f9cbbf65acb675ef6b9e2f2d..dfe3d8842122d1454b76bbe164dba7fe0e6dab65 100644
--- a/README.md
+++ b/README.md
@@ -152,6 +152,8 @@ V produkci musí být navíc nastaveno:
 | `EMAIL_HOST_USER` | --||-- Username |
 | `EMAIL_HOST_PASSWORD` | --||-- Heslo |
 | `EMAIL_PORT` | --||-- Port |
+| `CLAMD_TCP_ADDR` | ClamAV host (pro skenování virů v nahraných souborech) |
+| `CLAMD_TCP_SOCKET` | ClamAV socket |
 
 Různé:
 
diff --git a/main/forms.py b/main/forms.py
index 51f890009d852cb62d2d544039b2ac227c531ba3..f01f1d9ede56e8467622bd99e9390c82263bc7d6 100644
--- a/main/forms.py
+++ b/main/forms.py
@@ -2,6 +2,7 @@ import os
 import tempfile
 
 from django import forms
+from django.core.exceptions import ValidationError
 
 from shared.forms import ArticlesPageForm as SharedArticlesPageForm
 from shared.forms import JekyllImportForm as SharedJekyllImportForm
@@ -18,15 +19,53 @@ class MultipleFileField(forms.FileField):
         kwargs.setdefault("widget", MultipleFileInput())
         super().__init__(*args, **kwargs)
 
+    TOTAL_MAX_FILE_SIZE = 25 * 1024 * 1024  # 25 MB
+
     def clean(self, data, initial=None):
         single_file_clean = super().clean
+
         if isinstance(data, (list, tuple)):
+            total_size = 0
+
+            for file in data:
+                total_size += file.size
+
+            if total_size > self.TOTAL_MAX_FILE_SIZE:
+                raise ValidationError(
+                    "Celková velikost nahraných souborů je příliš velká."
+                )
+
             result = [single_file_clean(d, initial) for d in data]
         else:
             result = [single_file_clean(data, initial)]
+
         return result
 
 
+# Allowed MIME types
+ALLOWED_FILE_TYPES = [
+    "application/pdf",  # PDF
+    "application/msword",  # DOC
+    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  # DOCX
+    "image/png",  # PNG
+    "application/vnd.oasis.opendocument.text",  # ODT
+]
+
+
+def validate_file_type(file):
+    if file.content_type not in ALLOWED_FILE_TYPES:
+        raise ValidationError(
+            f"Chybný formát souboru: {file.content_type}. Povolené jsou pouze PDF, DOC, DOCX, PNG, and ODT."
+        )
+
+
+def validate_file_size(file, max_size=10 * 1024 * 1024):  # Default: 10 MB
+    if file.size > max_size:
+        raise ValidationError(
+            f"Soubory mohou být max. {max_size / (1024 * 1024)} MB velké."
+        )
+
+
 class CareerSubmissionForm(forms.Form):
     name = forms.CharField(
         min_length=1,
@@ -74,22 +113,35 @@ class CareerSubmissionForm(forms.Form):
 
     cv_file = forms.FileField(
         required=True,
+        validators=[validate_file_type, validate_file_size],
         widget=forms.FileInput(
-            attrs={"class": "max-w-64 mr-auto overflow-hidden break-words"}
+            attrs={
+                "class": "max-w-64 mr-auto overflow-hidden break-words",
+                "accept": ".pdf,.doc,.docx,.png,.odt",
+            }
         ),
     )
 
     cover_letter_file = forms.FileField(
         required=True,
+        validators=[validate_file_type, validate_file_size],
         widget=forms.FileInput(
-            attrs={"class": "max-w-64 mr-auto overflow-hidden break-words"}
+            attrs={
+                "class": "max-w-64 mr-auto overflow-hidden break-words",
+                "accept": ".pdf,.doc,.docx,.png,.odt",
+            }
         ),
     )
 
     other_files = MultipleFileField(
+        required=False,
+        validators=[validate_file_type, validate_file_size],
         widget=MultipleFileInput(
-            attrs={"class": "max-w-64 mr-auto overflow-hidden break-words"}
-        )
+            attrs={
+                "class": "max-w-64 mr-auto overflow-hidden break-words",
+                "accept": ".pdf,.doc,.docx,.png,.odt",
+            }
+        ),
     )
 
     personal_data_agreement = forms.BooleanField(required=True)
diff --git a/main/middlewares.py b/main/middlewares.py
new file mode 100644
index 0000000000000000000000000000000000000000..b136e1ee3f8759dcec6aae948aae0f705ddcf7f1
--- /dev/null
+++ b/main/middlewares.py
@@ -0,0 +1,40 @@
+from io import BytesIO
+
+import clamd
+from django.conf import settings
+from django.http import HttpResponseForbidden
+
+
+class ClamAVMiddleware:
+    def __init__(self, get_response):
+        self.get_response = get_response
+        # One-time configuration and initialization.
+
+    def __call__(self, request):
+        # Code to be executed for each request before
+        # the view (and later middleware) are called.
+
+        # If there is no Clamd connection set, don't check files as we are presumably
+        # in a development environment.
+        if not settings.CLAMD_TCP_SOCKET or not settings.CLAMD_TCP_ADDR:
+            return self.get_response(request)
+
+        cd = clamd.ClamdNetworkSocket(
+            host=settings.CLAMD_TCP_ADDR, port=settings.CLAMD_TCP_SOCKET, timeout=120
+        )
+
+        if request.method == "POST" and len(request.FILES) > 0:
+            for file_ in request.FILES.values():
+                scan_result = cd.instream(BytesIO(file_.read()))
+
+                if scan_result["stream"][0] == "FOUND":
+                    return HttpResponseForbidden(
+                        "Nahraný soubor obsahuje potenciálně škodlivý obsah."
+                    )
+
+        response = self.get_response(request)
+
+        # Code to be executed for each request/response after
+        # the view is called.
+
+        return response
diff --git a/main/migrations/0139_maincareerpage_recipient_emails.py b/main/migrations/0139_maincareerpage_recipient_emails.py
index 59783e9eb756353f9255fb193f4bb6b5e9994016..7cebf5cba304f6bd10112773b35645711b77f604 100644
--- a/main/migrations/0139_maincareerpage_recipient_emails.py
+++ b/main/migrations/0139_maincareerpage_recipient_emails.py
@@ -4,16 +4,19 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
-        ('main', '0138_maincareerpage_content'),
+        ("main", "0138_maincareerpage_content"),
     ]
 
     operations = [
         migrations.AddField(
-            model_name='maincareerpage',
-            name='recipient_emails',
-            field=models.CharField(default='', help_text='Zadej buď jednu adresu, nebo víc, oddělených čárkami.', verbose_name='Příjemci emailů o nových přihláškách'),
+            model_name="maincareerpage",
+            name="recipient_emails",
+            field=models.CharField(
+                default="",
+                help_text="Zadej buď jednu adresu, nebo víc, oddělených čárkami.",
+                verbose_name="Příjemci emailů o nových přihláškách",
+            ),
             preserve_default=False,
         ),
     ]
diff --git a/main/models.py b/main/models.py
index e9c2f00c2a60701469284fcb7ca3e4fed6fda061..9150089492cbb03e8381ef3b5ec49dfddced6c77 100644
--- a/main/models.py
+++ b/main/models.py
@@ -1,9 +1,11 @@
 from datetime import date, datetime
 
 from django.contrib import messages
+from django.core.exceptions import PermissionDenied
 from django.core.mail import EmailMessage
 from django.db import models
 from django.shortcuts import render
+from django_ratelimit.core import is_ratelimited
 from modelcluster.contrib.taggit import ClusterTaggableManager
 from modelcluster.fields import ParentalKey, ParentalManyToManyField
 from taggit.models import TaggedItemBase
@@ -515,6 +517,11 @@ class MainCareerPage(
     parent_page_types = ["main.MainCareersPage"]
 
     def serve(self, request):
+        if is_ratelimited(
+            request, group="career_submissions", key="ip", rate="2/m", method="POST"
+        ):
+            raise PermissionDenied("Rate limit exceeded")
+
         form = None
         current_time = datetime.now()
 
@@ -573,7 +580,7 @@ Při otevírání souborů buďte opatrní, virový sken neproběhl!
                 for file in form.cleaned_data["other_files"]:
                     email.attach(file.name, file.read(), file.content_type)
 
-                sent_successfully = email.send(fail_silently=True)
+                sent_successfully = email.send()
 
                 if sent_successfully:
                     messages.add_message(
@@ -583,13 +590,21 @@ Při otevírání souborů buďte opatrní, virový sken neproběhl!
                     messages.add_message(
                         request,
                         messages.ERROR,
-                        "Chyba serveru při odesílání přihlášky.",
+                        "Odeslání přihlášky selhalo. Zkuste to znovu.",
                     )
             else:
+                errors = ""
+
+                for error_val in form.errors.values():
+                    errors += f"{error_val.as_text()}\n"
+
                 messages.add_message(
                     request,
                     messages.ERROR,
-                    "Chyba při odeslání přihlášky - prohlížeč odeslal chybná data.",
+                    f"""
+Odeslání přihlášky selhalo:
+{errors}
+""",
                 )
         else:
             form = CareerSubmissionForm()
diff --git a/main/templates/main/main_career_page.html b/main/templates/main/main_career_page.html
index 067190985335e24b2db8f5f97f0b583898411696..060e80d33951a1d6b42087395f70831d94bbcc9e 100644
--- a/main/templates/main/main_career_page.html
+++ b/main/templates/main/main_career_page.html
@@ -86,7 +86,7 @@
 
                             {{ form.cv_file }}
 
-                            <small class="text-grey-300">(Povinné)</small>
+                            <small class="text-grey-300">(Povinné, max. 10 MB)</small>
                         </section>
                         <section class="flex flex-col gap-3 lg:items-center lg:flex-row">
                             <label
@@ -97,7 +97,7 @@
 
                             {{ form.cover_letter_file }}
 
-                            <small class="text-grey-300">(PovinnĂ˝)</small>
+                            <small class="text-grey-300">(PovinnĂ˝, max. 10 MB)</small>
                         </section>
                         <section class="flex flex-col gap-3 lg:items-center lg:flex-row">
                             <label
@@ -106,7 +106,13 @@
                                 for="id_other_files"
                             >OstatnĂ­ soubory: </label>
 
-                            {{ form.other_files }}
+                            <div class="flex flex-col gap-2">
+                                {{ form.other_files }}
+
+                                <small class="text-grey-300">
+                                    (Max. 10 MB na jeden soubor, 25 MB celkem)
+                                </small>
+                            </div>
                         </section>
 
                         <section class="flex flex-row gap-3 items-start leading-none">
diff --git a/majak/settings/base.py b/majak/settings/base.py
index 3012a6e8bf6e8bdb93cd29df2a0a39978bcff5d1..9088da18173242cee83d68e006eaf594822f3cff 100644
--- a/majak/settings/base.py
+++ b/majak/settings/base.py
@@ -115,6 +115,7 @@ MIDDLEWARE = [
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
     "django.middleware.security.SecurityMiddleware",
     "wagtail.contrib.redirects.middleware.RedirectMiddleware",
+    "main.middlewares.ClamAVMiddleware",
 ]
 
 # STATIC
@@ -163,6 +164,12 @@ CSRF_COOKIE_HTTPONLY = True
 SECURE_BROWSER_XSS_FILTER = True
 X_FRAME_OPTIONS = "DENY"
 
+# ClamAV
+
+CLAMD_USE_TCP = True
+CLAMD_TCP_SOCKET = env.int("CLAMD_TCP_SOCKET", default=0)
+CLAMD_TCP_ADDR = env.str("CLAMD_TCP_ADDR", default="")
+
 # needed for editing large map collections
 DATA_UPLOAD_MAX_NUMBER_FIELDS = None
 
diff --git a/requirements/base.in b/requirements/base.in
index 57010e1715794d31b93849da83af86037d2839d5..3f2dd2a6f5d7f71d53301821e70b4c744ae242ca 100644
--- a/requirements/base.in
+++ b/requirements/base.in
@@ -1,3 +1,4 @@
+clamd
 wagtail
 wagtail-metadata
 wagtail-trash
@@ -7,6 +8,7 @@ django-extensions
 django-redis
 django-settings-export
 django-widget-tweaks
+django-ratelimit
 django-simple-captcha
 gql[all]
 numpy
diff --git a/requirements/base.txt b/requirements/base.txt
index 28152d4f9f8610c96502169602649ffa4a1e9344..56b9bb0308da28e4da9fef00456bc0123bbce634 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -2,29 +2,29 @@
 # This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
-#    pip-compile base.in
+#    pip-compile requirements/base.in
 #
-aiohappyeyeballs==2.4.3
+aiohappyeyeballs==2.4.4
     # via aiohttp
-aiohttp==3.10.10
+aiohttp==3.11.10
     # via gql
 aiosignal==1.3.1
     # via aiohttp
-amqp==5.2.0
+amqp==5.3.1
     # via kombu
 anyascii==0.3.2
     # via wagtail
-anyio==4.6.2.post1
+anyio==4.7.0
     # via
     #   gql
     #   httpx
 arrow==1.3.0
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   ics
 asgiref==3.8.1
     # via django
-asttokens==2.4.1
+asttokens==3.0.0
     # via stack-data
 attrs==24.2.0
     # via
@@ -36,20 +36,20 @@ backoff==2.2.1
     # via gql
 beautifulsoup4==4.12.3
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   wagtail
 billiard==4.2.1
     # via celery
-bleach==6.1.0
-    # via -r base.in
-botocore==1.35.50
+bleach==6.2.0
+    # via -r requirements/base.in
+botocore==1.35.78
     # via gql
 brotli==1.1.0
     # via fonttools
 cattrs==24.1.2
     # via requests-cache
 celery==5.4.0
-    # via -r base.in
+    # via -r requirements/base.in
 certifi==2024.8.30
     # via
     #   httpcore
@@ -62,6 +62,8 @@ cffi==1.17.1
     #   weasyprint
 charset-normalizer==3.4.0
     # via requests
+clamd==1.0.2
+    # via -r requirements/base.in
 click==8.1.7
     # via
     #   celery
@@ -74,7 +76,7 @@ click-plugins==1.1.1
     # via celery
 click-repl==0.3.0
     # via celery
-cryptography==43.0.3
+cryptography==44.0.0
     # via
     #   josepy
     #   mozilla-django-oidc
@@ -87,7 +89,7 @@ defusedxml==0.7.1
     # via willow
 django==5.0.7
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   django-extensions
     #   django-filter
     #   django-modelcluster
@@ -103,9 +105,9 @@ django==5.0.7
     #   mozilla-django-oidc
     #   wagtail
 django-environ==0.11.2
-    # via -r base.in
+    # via -r requirements/base.in
 django-extensions==3.2.3
-    # via -r base.in
+    # via -r requirements/base.in
 django-filter==24.3
     # via wagtail
 django-modelcluster==6.3
@@ -114,18 +116,20 @@ django-permissionedforms==0.1
     # via wagtail
 django-ranged-response==0.2.0
     # via django-simple-captcha
+django-ratelimit==4.1.0
+    # via -r requirements/base.in
 django-redis==5.4.0
-    # via -r base.in
+    # via -r requirements/base.in
 django-settings-export==1.2.1
-    # via -r base.in
+    # via -r requirements/base.in
 django-simple-captcha==0.6.0
-    # via -r base.in
-django-taggit==5.0.1
+    # via -r requirements/base.in
+django-taggit==6.1.0
     # via wagtail
 django-treebeard==4.7.1
     # via wagtail
 django-widget-tweaks==1.5.0
-    # via -r base.in
+    # via -r requirements/base.in
 djangorestframework==3.15.2
     # via wagtail
 draftjs-exporter==5.0.0
@@ -134,43 +138,41 @@ et-xmlfile==2.0.0
     # via openpyxl
 executing==2.1.0
     # via stack-data
-fastjsonschema==2.20.0
-    # via -r base.in
+fastjsonschema==2.21.1
+    # via -r requirements/base.in
 filetype==1.2.0
     # via willow
-fonttools[woff]==4.54.1
+fonttools[woff]==4.55.3
     # via weasyprint
 frozenlist==1.5.0
     # via
     #   aiohttp
     #   aiosignal
 gql[all]==3.5.0
-    # via -r base.in
+    # via -r requirements/base.in
 graphql-core==3.2.5
     # via gql
 h11==0.14.0
     # via httpcore
-html5lib==1.1
-    # via weasyprint
-httpcore==1.0.6
+httpcore==1.0.7
     # via httpx
 httplib2==0.22.0
-    # via -r base.in
-httpx==0.27.2
+    # via -r requirements/base.in
+httpx==0.28.1
     # via gql
-icalendar==6.0.1
-    # via -r base.in
+icalendar==6.1.0
+    # via -r requirements/base.in
 ics==0.7.2
-    # via -r base.in
+    # via -r requirements/base.in
 idna==3.10
     # via
     #   anyio
     #   httpx
     #   requests
     #   yarl
-ipython==8.29.0
-    # via -r base.in
-jedi==0.19.1
+ipython==8.30.0
+    # via -r requirements/base.in
+jedi==0.19.2
     # via ipython
 jmespath==1.0.1
     # via botocore
@@ -183,7 +185,7 @@ l18n==2021.3
 laces==0.1.1
     # via wagtail
 markdown==3.7
-    # via -r base.in
+    # via -r requirements/base.in
 matplotlib-inline==0.1.7
     # via ipython
 mozilla-django-oidc==3.0.0
@@ -192,44 +194,46 @@ multidict==6.1.0
     # via
     #   aiohttp
     #   yarl
-nh3==0.2.18
-    # via -r base.in
-numpy==2.1.2
+nh3==0.2.19
+    # via -r requirements/base.in
+numpy==2.2.0
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   opencv-python
 oauthlib==3.2.2
     # via
     #   requests-oauthlib
     #   tweepy
 opencv-python==4.10.0.84
-    # via -r base.in
+    # via -r requirements/base.in
 openpyxl==3.1.5
     # via wagtail
 parso==0.8.4
     # via jedi
 pexpect==4.9.0
     # via ipython
-pillow==10.4.0
+pillow==11.0.0
     # via
     #   django-simple-captcha
     #   pillow-heif
     #   wagtail
     #   weasyprint
-pillow-heif==0.20.0
+pillow-heif==0.21.0
     # via willow
 pirates==0.7.0
-    # via -r base.in
+    # via -r requirements/base.in
 platformdirs==4.3.6
     # via requests-cache
 prompt-toolkit==3.0.48
     # via
     #   click-repl
     #   ipython
-propcache==0.2.0
-    # via yarl
+propcache==0.2.1
+    # via
+    #   aiohttp
+    #   yarl
 psycopg2-binary==2.9.10
-    # via -r base.in
+    # via -r requirements/base.in
 ptyprocess==0.7.0
     # via pexpect
 pure-eval==0.2.3
@@ -240,12 +244,12 @@ pydyf==0.11.0
     # via weasyprint
 pygments==2.18.0
     # via ipython
-pyopenssl==24.2.1
+pyopenssl==24.3.0
     # via josepy
 pyparsing==3.2.0
     # via httplib2
 pypdf2==3.0.1
-    # via -r base.in
+    # via -r requirements/base.in
 pyphen==0.17.0
     # via weasyprint
 python-dateutil==2.9.0.post0
@@ -257,16 +261,16 @@ python-dateutil==2.9.0.post0
     #   ics
 pytz==2024.2
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   django-modelcluster
     #   l18n
 pyyaml==6.0.2
-    # via -r base.in
-redis==5.2.0
+    # via -r requirements/base.in
+redis==5.2.1
     # via django-redis
 requests==2.32.3
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   gql
     #   mozilla-django-oidc
     #   requests-cache
@@ -275,33 +279,28 @@ requests==2.32.3
     #   tweepy
     #   wagtail
 requests-cache==1.2.1
-    # via -r base.in
+    # via -r requirements/base.in
 requests-oauthlib==1.3.1
     # via tweepy
 requests-toolbelt==1.0.0
     # via gql
-sentry-sdk==2.17.0
-    # via -r base.in
-six==1.16.0
+sentry-sdk==2.19.2
+    # via -r requirements/base.in
+six==1.17.0
     # via
-    #   asttokens
-    #   bleach
-    #   html5lib
     #   ics
     #   l18n
     #   python-dateutil
     #   url-normalize
 sniffio==1.3.1
-    # via
-    #   anyio
-    #   httpx
+    # via anyio
 soupsieve==2.6
     # via beautifulsoup4
-sqlparse==0.5.1
+sqlparse==0.5.3
     # via django
 stack-data==0.6.3
     # via ipython
-tatsu==5.12.1
+tatsu==5.12.2
     # via ics
 telepath==0.3.1
     # via wagtail
@@ -309,16 +308,20 @@ tinycss2==1.4.0
     # via
     #   cssselect2
     #   weasyprint
+tinyhtml5==2.0.0
+    # via weasyprint
 traitlets==5.14.3
     # via
     #   ipython
     #   matplotlib-inline
 tweepy==4.14.0
-    # via -r base.in
-types-python-dateutil==2.9.0.20241003
+    # via -r requirements/base.in
+types-python-dateutil==2.9.0.20241206
     # via arrow
 typing-extensions==4.12.2
-    # via ipython
+    # via
+    #   anyio
+    #   ipython
 tzdata==2024.2
     # via
     #   celery
@@ -337,39 +340,39 @@ vine==5.1.0
     #   amqp
     #   celery
     #   kombu
-wagtail==6.2.2
+wagtail==6.3.1
     # via
-    #   -r base.in
+    #   -r requirements/base.in
     #   wagtail-metadata
     #   wagtail-modeladmin
     #   wagtail-trash
 wagtail-metadata==5.0.0
-    # via -r base.in
+    # via -r requirements/base.in
 wagtail-modeladmin==2.1.0
     # via wagtail-trash
 wagtail-trash==3.0.0
-    # via -r base.in
+    # via -r requirements/base.in
 wand==0.6.13
-    # via -r base.in
+    # via -r requirements/base.in
 wcwidth==0.2.13
     # via prompt-toolkit
-weasyprint==62.3
-    # via -r base.in
+weasyprint==63.1
+    # via -r requirements/base.in
 webencodings==0.5.1
     # via
     #   bleach
     #   cssselect2
-    #   html5lib
     #   tinycss2
+    #   tinyhtml5
 websockets==11.0.3
     # via gql
 whitenoise==5.3.0
-    # via -r base.in
+    # via -r requirements/base.in
 willow[heif]==1.9.0
     # via
     #   wagtail
     #   willow
-yarl==1.17.0
+yarl==1.18.3
     # via
     #   aiohttp
     #   gql
diff --git a/requirements/dev.txt b/requirements/dev.txt
index e5bbcebf726f99f1a6ab8aa2f4fa5563ec64a612..031722c5366655366d60322a51ebadaf8d56787e 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -2,21 +2,21 @@
 # This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
-#    pip-compile dev.in
+#    pip-compile requirements/dev.in
 #
 asgiref==3.8.1
     # via django
-coverage[toml]==7.6.4
+coverage[toml]==7.6.9
     # via pytest-cov
 django==5.0.7
     # via
-    #   -r dev.in
+    #   -r requirements/dev.in
     #   django-debug-toolbar
 django-debug-toolbar==4.4.6
-    # via -r dev.in
+    # via -r requirements/dev.in
 factory-boy==3.3.1
     # via pytest-factoryboy
-faker==30.8.1
+faker==33.1.0
     # via factory-boy
 fastdiff==0.3.0
     # via snapshottest
@@ -26,45 +26,45 @@ inflection==0.5.1
     # via pytest-factoryboy
 iniconfig==2.0.0
     # via pytest
-packaging==24.1
+packaging==24.2
     # via
     #   pytest
     #   pytest-factoryboy
     #   pytest-sugar
 pluggy==1.5.0
     # via pytest
-pytest==8.3.3
+pytest==8.3.4
     # via
-    #   -r dev.in
+    #   -r requirements/dev.in
     #   pytest-cov
     #   pytest-django
     #   pytest-factoryboy
     #   pytest-freezegun
     #   pytest-mock
     #   pytest-sugar
-pytest-cov==5.0.0
-    # via -r dev.in
+pytest-cov==6.0.0
+    # via -r requirements/dev.in
 pytest-django==4.9.0
-    # via -r dev.in
+    # via -r requirements/dev.in
 pytest-factoryboy==2.7.0
-    # via -r dev.in
+    # via -r requirements/dev.in
 pytest-freezegun==0.4.2
-    # via -r dev.in
+    # via -r requirements/dev.in
 pytest-mock==3.14.0
-    # via -r dev.in
+    # via -r requirements/dev.in
 pytest-sugar==1.0.0
-    # via -r dev.in
+    # via -r requirements/dev.in
 python-dateutil==2.9.0.post0
     # via
     #   faker
     #   freezegun
-six==1.16.0
+six==1.17.0
     # via
     #   python-dateutil
     #   snapshottest
 snapshottest==0.6.0
-    # via -r dev.in
-sqlparse==0.5.1
+    # via -r requirements/dev.in
+sqlparse==0.5.3
     # via
     #   django
     #   django-debug-toolbar
diff --git a/requirements/production.txt b/requirements/production.txt
index 8123fbd9faca598c73776b30b9fe8cca3befd123..8e4e8c16d3ea8acf3963ac8aa800d1a3c394f1a7 100644
--- a/requirements/production.txt
+++ b/requirements/production.txt
@@ -2,9 +2,9 @@
 # This file is autogenerated by pip-compile with Python 3.11
 # by the following command:
 #
-#    pip-compile production.in
+#    pip-compile requirements/production.in
 #
 gunicorn==23.0.0
-    # via -r production.in
-packaging==24.1
+    # via -r requirements/production.in
+packaging==24.2
     # via gunicorn
diff --git a/shared/templates/styleguide2/includes/organisms/layout/messages.html b/shared/templates/styleguide2/includes/organisms/layout/messages.html
index b422fc81425bc906607c865a0c77c1d31332cf96..dbe8f862f5b4f461c030feb3d60e9ef5a500fa63 100644
--- a/shared/templates/styleguide2/includes/organisms/layout/messages.html
+++ b/shared/templates/styleguide2/includes/organisms/layout/messages.html
@@ -1,6 +1,6 @@
 <ul class="flex flex-col w-full">
     {% for message in messages %}
-        <script>alert("{{ message }}");</script>
+        <script>alert(`{{ message }}`);</script>
 
         {% comment %}
             <li>