diff --git a/.gitignore b/.gitignore
index c9f890f14444c4fa408c6df4cf67cbbd469f9efb..67212327e236469900e45d99ecdc9fc600dd6ddf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ ChangeLog
 \#*
 .\#*
 .php_cs_cache
+module/finc/tests/.phpunit.result.cache
 .vagrant
 .vscode/*
 TAGS
diff --git a/composer.json b/composer.json
index db42315cf38decd4af085c072b21261fba4f98a1..a3f99c67e9cee2b2081adda8d9c4ab220920a5b9 100644
--- a/composer.json
+++ b/composer.json
@@ -10,7 +10,7 @@
     "license": "GPL-2.0",
     "config": {
         "platform": {
-            "php": "7.2"
+            "php": "7.3"
         },
         "process-timeout": 0,
         "allow-plugins": {
@@ -30,7 +30,7 @@
         "laminas/laminas-code": "3.4.1",
         "laminas/laminas-config": "3.3.0",
         "laminas/laminas-crypt": "3.3.1",
-        "laminas/laminas-db": "2.11.3",
+        "laminas/laminas-db": "2.12",
         "laminas/laminas-dependency-plugin": "2.0.0",
         "laminas/laminas-dom": "2.7.2",
         "laminas/laminas-escaper": "2.6.1",
@@ -53,7 +53,7 @@
         "laminas/laminas-servicemanager": "3.5.2",
         "laminas/laminas-session": "2.9.3",
         "laminas/laminas-soap": "2.8.0",
-        "laminas/laminas-stdlib": "3.2.1",
+        "laminas/laminas-stdlib": "3.7.1",
         "laminas/laminas-text": "2.7.1",
         "laminas/laminas-validator": "2.13.5",
         "laminas/laminas-view": "2.11.5",
diff --git a/composer.lock b/composer.lock
index 5f1633b9f39fa8948c88cee748ad38c9ab4471d5..f0886d24470e4a4f6d53f069bcfd6e74f1168949 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "412f682a3730304eb0fc5c2e150ed228",
+    "content-hash": "67d72e23ac5ac0e70a8bf8e534878b11",
     "packages": [
         {
             "name": "ahand/mobileesp",
@@ -2425,44 +2425,41 @@
         },
         {
             "name": "laminas/laminas-db",
-            "version": "2.11.3",
+            "version": "2.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laminas/laminas-db.git",
-                "reference": "6c4238918b9204db1eb8cafae2c1940d40f4c007"
+                "reference": "80cbba4e749f9eb7d8036172acb9ad41e8b6923f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laminas/laminas-db/zipball/6c4238918b9204db1eb8cafae2c1940d40f4c007",
-                "reference": "6c4238918b9204db1eb8cafae2c1940d40f4c007",
+                "url": "https://api.github.com/repos/laminas/laminas-db/zipball/80cbba4e749f9eb7d8036172acb9ad41e8b6923f",
+                "reference": "80cbba4e749f9eb7d8036172acb9ad41e8b6923f",
                 "shasum": ""
             },
             "require": {
-                "laminas/laminas-stdlib": "^2.7 || ^3.0",
+                "laminas/laminas-stdlib": "^3.3",
                 "laminas/laminas-zendframework-bridge": "^1.0",
-                "php": "^5.6 || ^7.0"
+                "php": "^7.3 || ~8.0.0"
             },
             "replace": {
                 "zendframework/zend-db": "^2.11.0"
             },
             "require-dev": {
                 "laminas/laminas-coding-standard": "~1.0.0",
-                "laminas/laminas-eventmanager": "^2.6.2 || ^3.0",
-                "laminas/laminas-hydrator": "^1.1 || ^2.1 || ^3.0",
-                "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3",
-                "phpunit/phpunit": "^5.7.27 || ^6.5.14"
+                "laminas/laminas-eventmanager": "^3.3",
+                "laminas/laminas-hydrator": "^3.2 || ^4.0",
+                "laminas/laminas-servicemanager": "^3.3",
+                "phpspec/prophecy-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.3"
             },
             "suggest": {
                 "laminas/laminas-eventmanager": "Laminas\\EventManager component",
-                "laminas/laminas-hydrator": "Laminas\\Hydrator component for using HydratingResultSets",
+                "laminas/laminas-hydrator": "(^3.2 || ^4.0) Laminas\\Hydrator component for using HydratingResultSets",
                 "laminas/laminas-servicemanager": "Laminas\\ServiceManager component"
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "2.11.x-dev",
-                    "dev-develop": "2.12.x-dev"
-                },
                 "laminas": {
                     "component": "Laminas\\Db",
                     "config-provider": "Laminas\\Db\\ConfigProvider"
@@ -2483,15 +2480,7 @@
                 "db",
                 "laminas"
             ],
-            "support": {
-                "chat": "https://laminas.dev/chat",
-                "docs": "https://docs.laminas.dev/laminas-db/",
-                "forum": "https://discourse.laminas.dev",
-                "issues": "https://github.com/laminas/laminas-db/issues",
-                "rss": "https://github.com/laminas/laminas-db/releases.atom",
-                "source": "https://github.com/laminas/laminas-db"
-            },
-            "time": "2020-03-29T12:08:51+00:00"
+            "time": "2021-02-22T22:27:56+00:00"
         },
         {
             "name": "laminas/laminas-dependency-plugin",
@@ -3292,36 +3281,29 @@
         },
         {
             "name": "laminas/laminas-i18n-resources",
-            "version": "2.6.1",
+            "version": "2.8.0",
             "source": {
                 "type": "git",
-                "url": "git@github.com:laminas/laminas-i18n-resources.git",
-                "reference": "7585cd3a4f9656814425b35689919a220c73834b"
+                "url": "https://github.com/laminas/laminas-i18n-resources.git",
+                "reference": "7d7062849064bb89e7cdd7193c43ef95e95fbe4b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laminas/laminas-i18n-resources/zipball/7585cd3a4f9656814425b35689919a220c73834b",
-                "reference": "7585cd3a4f9656814425b35689919a220c73834b",
+                "url": "https://api.github.com/repos/laminas/laminas-i18n-resources/zipball/7d7062849064bb89e7cdd7193c43ef95e95fbe4b",
+                "reference": "7d7062849064bb89e7cdd7193c43ef95e95fbe4b",
                 "shasum": ""
             },
             "require": {
-                "laminas/laminas-zendframework-bridge": "^1.0",
-                "php": "^5.6 || ^7.0"
+                "php": "^7.3 || ~8.0.0 || ~8.1.0"
             },
-            "replace": {
-                "zendframework/zend-i18n-resources": "self.version"
+            "conflict": {
+                "zendframework/zend-i18n-resources": "*"
             },
             "require-dev": {
                 "laminas/laminas-coding-standard": "~1.0.0",
-                "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5"
+                "phpunit/phpunit": "^9.5"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.6.x-dev",
-                    "dev-develop": "2.7.x-dev"
-                }
-            },
             "autoload": {
                 "classmap": [
                     "src/Resources.php"
@@ -3331,7 +3313,7 @@
             "license": [
                 "BSD-3-Clause"
             ],
-            "description": "Provides validator translations for laminas-i18n's Translator",
+            "description": "Provides validator and captcha translations for laminas-i18n's Translator",
             "homepage": "https://laminas.dev",
             "keywords": [
                 "laminas",
@@ -3346,7 +3328,13 @@
                 "rss": "https://github.com/laminas/laminas-i18n-resources/releases.atom",
                 "source": "https://github.com/laminas/laminas-i18n-resources"
             },
-            "time": "2019-12-31T17:11:35+00:00"
+            "funding": [
+                {
+                    "url": "https://funding.communitybridge.org/projects/laminas-project",
+                    "type": "community_bridge"
+                }
+            ],
+            "time": "2021-09-14T04:16:52+00:00"
         },
         {
             "name": "laminas/laminas-inputfilter",
@@ -4771,37 +4759,32 @@
         },
         {
             "name": "laminas/laminas-stdlib",
-            "version": "3.2.1",
+            "version": "3.7.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laminas/laminas-stdlib.git",
-                "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6"
+                "reference": "bcd869e2fe88d567800057c1434f2380354fe325"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6",
-                "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6",
+                "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/bcd869e2fe88d567800057c1434f2380354fe325",
+                "reference": "bcd869e2fe88d567800057c1434f2380354fe325",
                 "shasum": ""
             },
             "require": {
-                "laminas/laminas-zendframework-bridge": "^1.0",
-                "php": "^5.6 || ^7.0"
+                "php": "^7.3 || ~8.0.0 || ~8.1.0"
             },
-            "replace": {
-                "zendframework/zend-stdlib": "self.version"
+            "conflict": {
+                "zendframework/zend-stdlib": "*"
             },
             "require-dev": {
-                "laminas/laminas-coding-standard": "~1.0.0",
-                "phpbench/phpbench": "^0.13",
-                "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2"
+                "laminas/laminas-coding-standard": "~2.3.0",
+                "phpbench/phpbench": "^1.0",
+                "phpunit/phpunit": "^9.3.7",
+                "psalm/plugin-phpunit": "^0.16.0",
+                "vimeo/psalm": "^4.7"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.2.x-dev",
-                    "dev-develop": "3.3.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Laminas\\Stdlib\\": "src/"
@@ -4817,15 +4800,7 @@
                 "laminas",
                 "stdlib"
             ],
-            "support": {
-                "chat": "https://laminas.dev/chat",
-                "docs": "https://docs.laminas.dev/laminas-stdlib/",
-                "forum": "https://discourse.laminas.dev",
-                "issues": "https://github.com/laminas/laminas-stdlib/issues",
-                "rss": "https://github.com/laminas/laminas-stdlib/releases.atom",
-                "source": "https://github.com/laminas/laminas-stdlib"
-            },
-            "time": "2019-12-31T17:51:15+00:00"
+            "time": "2022-01-21T15:50:46+00:00"
         },
         {
             "name": "laminas/laminas-text",
@@ -10123,6 +10098,7 @@
                 "issues": "https://github.com/sebastianbergmann/phploc/issues",
                 "source": "https://github.com/sebastianbergmann/phploc/tree/master"
             },
+            "abandoned": true,
             "time": "2019-03-16T10:41:19+00:00"
         },
         {
@@ -12390,7 +12366,7 @@
                 "source": "https://github.com/theseer/fDOMDocument/tree/1.6.7"
             },
             "abandoned": true,
-            "time": "2022-01-25T23:10:35+00:00"
+            "time": "2017-06-30T11:53:12+00:00"
         },
         {
             "name": "theseer/tokenizer",
@@ -12456,7 +12432,7 @@
     },
     "platform-dev": [],
     "platform-overrides": {
-        "php": "7.2"
+        "php": "7.3"
     },
     "plugin-api-version": "2.3.0"
 }
diff --git a/devops/docker/php8_0/Dockerfile b/devops/docker/php8_0/Dockerfile
index 361d18e935ce8c245e201288e6e05807cba27be3..e903bb8e82cff6c497a6e550e15568ad12bff654 100644
--- a/devops/docker/php8_0/Dockerfile
+++ b/devops/docker/php8_0/Dockerfile
@@ -17,8 +17,10 @@
 # @license https://opensource.org/licenses/GPL-3.0 GNU GPLv3
 
 FROM php:8.0-fpm-alpine
+# RUN docker-php-ext-install opcache
+RUN apk add --update linux-headers
 RUN apk add --no-cache $PHPIZE_DEPS freetype-dev libxml2-dev icu-dev libxslt-dev \
-    && pecl install xdebug-3.0.0 \
+    && pecl install xdebug-3.2.0 \
     && docker-php-ext-enable xdebug \
     && docker-php-ext-install mysqli \
     && docker-php-ext-install pdo_mysql \
@@ -36,4 +38,5 @@ RUN apk add --no-cache \
 RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-freetype
 RUN docker-php-ext-install gd
 
-COPY php.ini /usr/local/etc/php/conf.d/php.ini
\ No newline at end of file
+COPY php.ini /usr/local/etc/php/conf.d/php.ini
+COPY www.conf /usr/local/etc/php-fpm.d/www.conf
\ No newline at end of file
diff --git a/devops/docker/php8_0/php.ini b/devops/docker/php8_0/php.ini
index 36c1f7030eb4e49c4c15bf9c1582926ca3d81579..250bc3972b858d8772ba6dd3fd0b9622e8ea1a5f 100644
--- a/devops/docker/php8_0/php.ini
+++ b/devops/docker/php8_0/php.ini
@@ -19,6 +19,22 @@
 ; See https://www.php.net/manual/en/function.error-reporting.php for more details.
 error_reporting = E_ALL
 
+[opcache]
+; maximum memory that OPcache can use to store compiled PHP files, Symfony recommends 256
+opcache.memory_consumption=256
+; maximum number of files that can be stored in the cache
+opcache.max_accelerated_files=20000
+; validate on every request
+opcache.revalidate_freq=0
+; validate_timestamps should be 0 in prod
+opcache.validate_timestamps=1
+opcache.interned_strings_buffer=16
+opcache.fast_shutdown=1
+opcache.enable=1
+opcache.enable_cli=1
+opcache.jit=tracing
+opcache.jit_buffer_size=256M
+
 ; See https://xdebug.org/docs/all_settings for more details.
 xdebug.mode=debug
 xdebug.start_with_request=yes
diff --git a/devops/docker/php8_0/www.conf b/devops/docker/php8_0/www.conf
new file mode 100644
index 0000000000000000000000000000000000000000..8205b6667c0f1dcdff36fd2f2a45f0cb7d856d96
--- /dev/null
+++ b/devops/docker/php8_0/www.conf
@@ -0,0 +1,9 @@
+[www]
+user = www-data
+group = www-data
+listen = 127.0.0.1:9000
+pm = dynamic
+pm.max_children = 50
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
diff --git a/docker-compose.yml b/docker-compose.yml
index b7ac4f464b3130889c1942eef6d4cda8a8912d23..f4ca7a18170e8f077ebd8cf1b2dd9e5c95c4ae8d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -19,6 +19,7 @@ services:
       - env
     entrypoint: devops/docker/${COMPOSER_VERSION:-composer1}/entrypoint.sh
     command: ["install"]
+    image: finc/composer
 
   php:
     build: devops/docker/${PHP_VERSION:-php7_2}
@@ -30,6 +31,7 @@ services:
       - db
     entrypoint: devops/docker/${PHP_VERSION:-php7_2}/entrypoint.sh
     command: ["php-fpm"]
+    image: finc/${PHP_VERSION:-php7_2}
 
   httpd:
     build: devops/docker/httpd
@@ -41,6 +43,7 @@ services:
     depends_on:
       - php
     tty: true
+    image: finc/httpd
 
   db:
     build: devops/docker/db/${DB_VERSION:-mariadb_10_3}
@@ -59,6 +62,7 @@ services:
       "--character-set-server=utf8mb4",
       "--collation-server=utf8mb4_unicode_ci"
     ]
+    image: finc/db
 
   mail:
     image: useltmann/mailcollect:8-1
@@ -85,6 +89,7 @@ services:
     env_file: .env
     entrypoint: ["devops/docker/grunt/entrypoint.sh"]
     command: ["watch:scss"]
+    image: finc/grunt
 
   autoconfig:
     image: node:7.10.1-alpine
diff --git a/local/config/vufind/config.ini b/local/config/vufind/config.ini
index e40e2edcb2d6336de03bf49e86f3d16bafea0398..35ef9aa9cd83ff58b9f09ff9831084cc3ff8dd7b 100644
--- a/local/config/vufind/config.ini
+++ b/local/config/vufind/config.ini
@@ -1520,6 +1520,10 @@ skip_numeric = true
 ;file           = /var/log/vufind.log:alert,error,notice,debug
 ;email          = alerts@myuniversity.edu:alert-5,error-5
 
+; Log time measurement, see #24163, set file name
+; also activate finc\HttpDebug\HttpService and finc\Log\PerformanceLogger in module.config.php
+;file           = data/cache/performance.csv:debug
+
 ; Get URL from https://YOURSLACK.slack.com/apps/manage/custom-integrations
 ;slack = #channel_name:alert,error
 ;slackurl = https://hooks.slack.com/services/your-private-details
diff --git a/local/languages/de.ini b/local/languages/de.ini
index 19ccf522784f5622b8dd5c580198d3c146c4277c..783d95e6656c1c5260cc55151c151dab2ed66fb1 100644
--- a/local/languages/de.ini
+++ b/local/languages/de.ini
@@ -1848,7 +1848,7 @@ resolver_link_access_limited = "Im Campusnetz verfügbar"
 resolver_link_access_open = "verfügbar"
 resolver_link_access_unknown = "Titel ist beim Resolver-Service nicht bekannt"
 ; message to be shown upon empty resolver response
-no_resolver_links = "Keine Online-Links verfügbar."
+no_resolver_links = "Derzeit keine Zugangs-Links verfügbar."
 
 ; reset password
 reset_password_text = "Bitten füllen Sie dieses Formular aus, um Ihr Passwort zurücksetzen zu lassen. Sie erhalten an u.g. E-Mail-Adresse eine Benachrichtigung, nachdem wir das Passwort zurückgesetzt haben."
@@ -2027,6 +2027,7 @@ skip-to = "Zu"
 Search type = "Suchtyp"
 
 License = "Lizenz"
+LicenseIcon = "Lizenz Icon"
 
 fine_date_short = "Gebühr fällig"
 
diff --git a/local/languages/en.ini b/local/languages/en.ini
index cd1d79d9ad79a15933f21aa95c336c5f5078c468..475e3f954c65b69250af8f4bc130a6c4a9979bf7 100644
--- a/local/languages/en.ini
+++ b/local/languages/en.ini
@@ -1955,7 +1955,7 @@ resolver_link_access_limited = "Available in Campus network"
 resolver_link_access_open = "available"
 resolver_link_access_unknown = "Record unknown to resolver"
 ; message to be shown upon empty resolver response
-no_resolver_links = "No online links available."
+no_resolver_links = "No access link currently available."
 
 ; reset password
 reset_password_text = "Please complete the form below to reset your password. You will receive an email after we have completed resetting your password."
@@ -2120,6 +2120,7 @@ skip-to = "Skip to "
 Skip to facet = "Skip to your selected search filter '%%filter_name%%'"
 
 License = "License"
+LicenseIcon = "License icon"
 
 fine_date_short = "Fine Date"
 
diff --git a/module/finc/config/dds-form.php b/module/finc/config/dds-form.php
index ab191d6f2b64046c89de2d2075ee71173e3a5522..a035d80227a78fc8247cd562e3b44e0d718c9793 100644
--- a/module/finc/config/dds-form.php
+++ b/module/finc/config/dds-form.php
@@ -146,6 +146,7 @@ return [
                 ],
             ],
         ],
+        /* de_15: article is required */
         'article'             => [
             'spec' => [
                 'name'       => 'article',
@@ -154,11 +155,12 @@ return [
                     'label' => 'form_field_title',
                 ],
                 'attributes' => [
-                    'required' => false,
+                    'required' => true,
                     'id' => 'article',
                 ],
             ],
         ],
+        /* de_15: journal is required */
         'journal'             => [
             'spec' => [
                 'name'       => 'journal',
@@ -167,7 +169,7 @@ return [
                     'label' => 'form_field_journal',
                 ],
                 'attributes' => [
-                    'required' => false,
+                    'required' => true,
                     'id' => 'journal',
                 ],
             ],
diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php
index c8dec95f9ecb81a88f96fb1af5b06978b0676291..ae9d02e7047e1f50349b46f5ceb7cad06906c509 100644
--- a/module/finc/config/module.config.php
+++ b/module/finc/config/module.config.php
@@ -17,7 +17,9 @@ $config = [
             'finc\ILS\Logic\Holds' => 'VuFind\ILS\Logic\LogicFactory',
             'finc\Rewrite\EblRewrite' => 'finc\Rewrite\EblRewriteFactory',
             'finc\Listener\I18nDataDirListener' => 'Laminas\ServiceManager\Factory\InvokableFactory',
-            'finc\Cover\Loader' => 'VuFind\Cover\LoaderFactory'
+            'finc\Cover\Loader' => 'VuFind\Cover\LoaderFactory',
+            //'finc\HttpDebug\HttpService' => 'VuFind\Service\HttpServiceFactory',
+            //'finc\Log\PerformanceLogger' => 'VuFind\Log\LoggerFactory'
         ],
         'delegators' => [
             'VuFindSearch\Service' => [
@@ -33,7 +35,9 @@ $config = [
             'VuFind\Cache\Manager' => 'finc\Cache\Manager',
             'VuFind\ILS\Connection' => 'finc\ILS\Connection',
             'VuFind\ILS\Logic\Holds' => 'finc\ILS\Logic\Holds',
-            'VuFind\Cover\Loader' => 'finc\Cover\Loader'
+            'VuFind\Cover\Loader' => 'finc\Cover\Loader',
+            // 'VuFindHttp\HttpService' => 'finc\HttpDebug\HttpService',
+            // 'VuFind\Log\Logger' => 'finc\Log\PerformanceLogger'
         ]
     ],
     'controllers' => [
diff --git a/module/finc/src/finc/Controller/Admin/I18nController.php b/module/finc/src/finc/Controller/Admin/I18nController.php
index d6240cb5bf686d0e74da1e9e94dab0fa730b1f26..6d7860f2ab161c27a508b769a3f10f306d0ac776 100644
--- a/module/finc/src/finc/Controller/Admin/I18nController.php
+++ b/module/finc/src/finc/Controller/Admin/I18nController.php
@@ -103,6 +103,7 @@ class I18nController extends AbstractAdmin
         $translations = $this->getTranslations();
         $defaultTranslations = $this->getDefaultTranslations();
 
+        $domains = [];
         foreach (array_keys($translations[$this->locale]) as $name) {
             $selected = $name === $domain;
             $domains[$name] = compact('name', 'selected');
diff --git a/module/finc/src/finc/HttpDebug/HttpService.php b/module/finc/src/finc/HttpDebug/HttpService.php
new file mode 100644
index 0000000000000000000000000000000000000000..af7e88c3c46bdf6e13a274cefccb52aba8692274
--- /dev/null
+++ b/module/finc/src/finc/HttpDebug/HttpService.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Copyright (C) 2023 Leipzig University Library
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Finc
+ * @package  Http
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+namespace finc\HttpDebug;
+
+use Laminas\Http\Client;
+use Laminas\Http\Response;
+
+/**
+ * VuFind HTTP service.
+ *
+ * @category VuFind
+ * @package  Http
+ * @author   David Maus <maus@hab.de>
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+class HttpService extends \VuFindHttp\HttpService implements \Laminas\Log\LoggerAwareInterface
+{
+    use \VuFind\Log\LoggerAwareTrait;
+
+    /**
+     * Send Request
+     *
+     * @param Client $client http client
+     *
+     * @return Response
+     */
+    protected function send(\Laminas\Http\Client $client)
+    {
+        $time     = microtime(true);
+        $response = parent::send($client);
+        $time     = microtime(true) - $time;
+
+        $this->log(
+            'performance',
+            ';' . $client->getUri() . ';' . intval($time * 1000) . ';'
+        );
+
+        return $response;
+    }
+}
diff --git a/module/finc/src/finc/ILS/Driver/PAIA.php b/module/finc/src/finc/ILS/Driver/PAIA.php
index f19d32a0c96fd860e9d3d49607cb6105dd6fb337..4d1ec00d5f452ec279be8c382b79f741d2e5a2fd 100644
--- a/module/finc/src/finc/ILS/Driver/PAIA.php
+++ b/module/finc/src/finc/ILS/Driver/PAIA.php
@@ -1514,9 +1514,10 @@ class PAIA extends \VuFind\ILS\Driver\PAIA
                 ||
                 (isset($values['address']) && !$this->paiaCheckScope(self::SCOPE_UPDATE_PATRON_ADDRESS))
             ) {
-                throw new ILSException(
-                    'You are not allowed to update the desired patron information.'
-                );
+                return [
+                    'success' => false,
+                    'sysMessage' => 'Not allowed to update patron information.'
+                ];
             }
         }
 
diff --git a/module/finc/src/finc/ILS/Logic/Holds.php b/module/finc/src/finc/ILS/Logic/Holds.php
index cafdd9476246d5001e6766221cef2aa4f3404484..c2c2d1db1df090c7f58dc1cb82ec7797652724a5 100644
--- a/module/finc/src/finc/ILS/Logic/Holds.php
+++ b/module/finc/src/finc/ILS/Logic/Holds.php
@@ -158,7 +158,7 @@ class Holds extends \VuFind\ILS\Logic\Holds
                 = $patron
             && $this->catalog->checkCapability(
                 $grb,
-                compact($patron)
+                compact('patron')
             )
                 ? $this->catalog->getRequestBlocks($patron) : false;
 
diff --git a/module/finc/src/finc/Log/PerformanceLogger.php b/module/finc/src/finc/Log/PerformanceLogger.php
new file mode 100644
index 0000000000000000000000000000000000000000..8bd35e2b1b1b5cd04e0dcc6f2b320db96ea50b01
--- /dev/null
+++ b/module/finc/src/finc/Log/PerformanceLogger.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Finc Logger for time measurement
+ *
+ * PHP version 7
+ *
+ * Copyright (C) 2023 Leipzig University Library
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Finc
+ * @package  Error_Logging
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+namespace finc\Log;
+
+use VuFind\Log\Logger as BaseLogger;
+
+/**
+ * This class wraps the BaseLogger class to allow for log verbosity
+ *
+ * @category VuFind
+ * @package  Error_Logging
+ * @author   Chris Hallberg <challber@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+class PerformanceLogger extends BaseLogger
+{
+    public static $cache = [];
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param int               $priority Priority
+     * @param mixed             $message  Message
+     * @param array|Traversable $extra    Extras
+     *
+     * @return Logger
+     */
+    public function log($priority, $message, $extra = [])
+    {
+        // ignore complex messages
+        if (!is_array($message)) {
+            if (!isset($extra['qtime'])) {
+                // cache until query was finished
+                array_push(self::$cache, $message);
+            } else {
+                // flush to log
+                $message = ';' . implode(" ", self::$cache) . ';' . $extra['qtime'] . ';';
+                self::$cache = [];
+                return parent::log($priority, $message);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Log time measurement
+     *
+     * @param string $message log message
+     * @param array  $extra   additional values
+     *
+     * @return $this|PerformanceLogger
+     */
+    public function performance($message, $extra = [])
+    {
+        return parent::log(self::DEBUG, $message, $extra);
+    }
+}
diff --git a/module/finc/src/finc/View/Helper/Root/EnhancedRenderArray.php b/module/finc/src/finc/View/Helper/Root/EnhancedRenderArray.php
index 2cebac8943844ce9fcaf00447ca4d1c99732341d..526fe8ca69d562e2da4f6f4a7a49c61386513bcc 100644
--- a/module/finc/src/finc/View/Helper/Root/EnhancedRenderArray.php
+++ b/module/finc/src/finc/View/Helper/Root/EnhancedRenderArray.php
@@ -45,22 +45,25 @@ class EnhancedRenderArray extends AbstractHelper
     /**
      * Render a portion of an array.
      *
-     * @param string $tpl  A template for displaying each row.  This should
-     * include %%KEY&&, %%LABEL%%, %%VALUE%% and %%DISABLED%% placeholders
-     * @param array  $arr  An associative array of possible values to display
-     * @param array  $rows A label => profile key associative array specifying
+     * @param string $tpl1   A template for displaying each row as read-only value.
+     * This should include %%LABEL%% and %%VALUE%% placeholders
+     * @param string $tpl2   A template for displaying each row as input field.
+     * A 2-dim-array including arrays for every line with label and autocomplete
+     * This should include %%KEY&&, %%LABEL%%, %%VALUE%%, %%AUTOCOMPLETE%%
+     * @param array  $arr    An associative array of possible values to display
+     * @param array  $arrays A label => profile key associative array specifying
      * which rows of $arr to display
      *
      * @return string
      */
-    public function __invoke($tpl1, $tpl2, $arr, $rows)
+    public function __invoke($tpl1, $tpl2, $arr, $arrays)
     {
         $html = '';
-        foreach ($rows as $label => $key) {
+        foreach ($arrays as $key => $row) {
             $html .= str_replace(
-              ['%%KEY%%', '%%LABEL%%', '%%VALUE%%'],
-              [$key, $label, $this->view->escapeHtml((isset($arr[$key]) ? $arr[$key] : ''))],
-              (isset($arr['disabled'][$key]) && ($arr['disabled'][$key] === true) ? $tpl1 : $tpl2)
+                ['%%KEY%%', '%%LABEL%%', '%%VALUE%%', '%%AUTOCOMPLETE%%'],
+                [$key, $row['label'], $this->view->escapeHtml(($arr[$key] ?? '')), $row['autocomplete']],
+                (isset($arr['disabled'][$key]) && ($arr['disabled'][$key] === true) ? $tpl1 : $tpl2)
             );
         }
         return $html;
diff --git a/module/finc/tests/.phpunit.result.cache b/module/finc/tests/.phpunit.result.cache
deleted file mode 100644
index be8380b33086124bb39bd1e3e151c42b742fb527..0000000000000000000000000000000000000000
--- a/module/finc/tests/.phpunit.result.cache
+++ /dev/null
@@ -1 +0,0 @@
-C:37:"PHPUnit\Runner\DefaultTestResultCache":5538:{a:2:{s:7:"defects";a:1:{s:65:"FincTest\View\Helper\Root\OpenUrlTest::testValidateParamsEzbFalse";i:3;}s:5:"times";a:70:{s:53:"fincTest\Config\SearchSpecsReaderTest::testParentYaml";d:0.442;s:58:"fincTest\Config\SearchSpecsReaderTest::testSearchSpecsRead";d:0.171;s:58:"fincTest\Config\SearchSpecsReaderTest::testMissingFileRead";d:0.001;s:51:"fincTest\Config\SearchSpecsReaderTest::testYamlLoad";d:0.002;s:52:"fincTest\Config\SearchSpecsReaderTest::testYamlMerge";d:0.001;s:50:"VuFindTest\ILS\Driver\PAIATest::testChangePassword";d:0.03;s:40:"VuFindTest\ILS\Driver\PAIATest::testFees";d:0.005;s:41:"VuFindTest\ILS\Driver\PAIATest::testHolds";d:0.004;s:44:"VuFindTest\ILS\Driver\PAIATest::testRequests";d:0.004;s:48:"VuFindTest\ILS\Driver\PAIATest::testTransactions";d:0.008;s:43:"VuFindTest\ILS\Driver\PAIATest::testProfile";d:0.004;s:48:"VuFindTest\ILS\Driver\PAIATest::testValidRequest";d:0.003;s:48:"VuFindTest\ILS\Driver\PAIATest::testRenewDetails";d:0.001;s:45:"VuFindTest\ILS\Driver\PAIATest::testPlaceHold";d:0.004;s:64:"VuFindTest\ILS\Driver\PAIATest::testPlaceStorageRetrievalRequest";d:0.004;s:41:"VuFindTest\ILS\Driver\PAIATest::testRenew";d:0.006;s:56:"VuFindTest\ILS\Driver\PAIATest::testMissingConfiguration";d:0.001;s:66:"VuFindTest\ILS\Driver\PAIAFincTest::testMapOptionsPickupBranchTrue";d:0;s:57:"VuFindTest\ILS\Driver\PAIAFincTest::testMapOptionsNewTrue";d:0;s:79:"VuFindTest\ILS\Driver\PAIAFincTest::testMapOptionsPickupBranchFalseNoConditions";d:0.001;s:81:"VuFindTest\ILS\Driver\PAIAFincTest::testMapOptionsPickupBranchTrueUnknownOptionId";d:0;s:83:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWithoutOptionConfigurationDefined";d:0.004;s:76:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWithoutPaiaResponseOptions";d:0.006;s:69:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsAllowUnknownOptions";d:0.004;s:63:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWrongInstance";d:0.004;s:66:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWithPickUpBranch";d:0.006;s:73:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWithAnotherPickUpBranch";d:0.004;s:81:"VuFindTest\ILS\Driver\PAIAFincTest::testGetMyHoldsWithMultipleOptionsPickUpBranch";d:0.004;s:63:"VuFindTest\ILS\Driver\PAIAFincTest::testgetOptionsReturnsIDTrue";d:0.002;s:68:"VuFindTest\ILS\Driver\PAIAFincTest::testgetOptionsNotOnlyFirstButAll";d:0.002;s:66:"VuFindTest\ILS\Driver\PAIAFincTest::testgetOptionsReturnsLabelTrue";d:0.004;s:67:"VuFindTest\ILS\Driver\PAIAFincTest::testgetOptionsReturnsEmptyArray";d:0.003;s:58:"VuFindTest\ILS\Driver\PAIAFincTest::testCallMapOptionsTrue";d:0.005;s:59:"VuFindTest\ILS\Driver\PAIAFincTest::testCallMapOptionsFalse";d:0.007;s:54:"VuFindTest\ILS\Driver\PAIAFincTest::testChangePassword";d:0.004;s:44:"VuFindTest\ILS\Driver\PAIAFincTest::testFees";d:0.004;s:45:"VuFindTest\ILS\Driver\PAIAFincTest::testHolds";d:0.006;s:48:"VuFindTest\ILS\Driver\PAIAFincTest::testRequests";d:0.004;s:52:"VuFindTest\ILS\Driver\PAIAFincTest::testTransactions";d:0.008;s:47:"VuFindTest\ILS\Driver\PAIAFincTest::testProfile";d:0.005;s:52:"VuFindTest\ILS\Driver\PAIAFincTest::testValidRequest";d:0.003;s:52:"VuFindTest\ILS\Driver\PAIAFincTest::testRenewDetails";d:0.001;s:49:"VuFindTest\ILS\Driver\PAIAFincTest::testPlaceHold";d:0.004;s:68:"VuFindTest\ILS\Driver\PAIAFincTest::testPlaceStorageRetrievalRequest";d:0.009;s:45:"VuFindTest\ILS\Driver\PAIAFincTest::testRenew";d:0.004;s:60:"VuFindTest\ILS\Driver\PAIAFincTest::testMissingConfiguration";d:0;s:64:"fincTest\RecordDriver\SolrMarcNewerPreviousTest::testNewerTitles";d:0.015;s:67:"fincTest\RecordDriver\SolrMarcNewerPreviousTest::testPreviousTitles";d:0.011;s:64:"fincTest\RecordDriver\SolrMarcNewerPreviousTest::testGetUniqueId";d:0;s:65:"FincTest\View\Helper\Root\ExternalCatalogueLinkTest::testGetLinks";d:0.004;s:66:"FincTest\View\Helper\Root\OpenUrlTest::testAddCustomParamsNoParams";d:0.03;s:70:"FincTest\View\Helper\Root\OpenUrlTest::testAddCustomStaticParamsForEzb";d:0.005;s:75:"FincTest\View\Helper\Root\OpenUrlTest::testDONTOverrideExistingParamsForEzb";d:0.005;s:71:"FincTest\View\Helper\Root\OpenUrlTest::testAddCustomStaticParamsForRedi";d:0.005;s:76:"FincTest\View\Helper\Root\OpenUrlTest::testDONTOverrideExistingParamsForRedi";d:0.005;s:71:"FincTest\View\Helper\Root\OpenUrlTest::testAddCustomDynamicParamsForEzb";d:0.005;s:72:"FincTest\View\Helper\Root\OpenUrlTest::testAddCustomDynamicParamsForRedi";d:0.005;s:65:"FincTest\View\Helper\Root\OpenUrlTest::testValidateParamsEzbFalse";d:0.005;s:64:"FincTest\View\Helper\Root\OpenUrlTest::testValidateParamsEzbTrue";d:0.027;s:63:"FincTest\View\Helper\Root\OpenUrlTest::testIsActiveIsIdempotent";d:0.008;s:63:"FincTest\View\Helper\Root\OpenUrlTest::testCheckContextDefaults";d:0.028;s:68:"FincTest\View\Helper\Root\OpenUrlTest::testCheckContextWithOverrides";d:0.005;s:60:"FincTest\View\Helper\Root\OpenUrlTest::testCheckContextNoUrl";d:0.005;s:73:"FincTest\View\Helper\Root\OpenUrlTest::testCheckExcludedRecordsRulesFalse";d:0.003;s:72:"FincTest\View\Helper\Root\OpenUrlTest::testCheckExcludedRecordsRulesTrue";d:0.003;s:93:"FincTest\View\Helper\Root\OpenUrlTest::testCheckExcludedRecordsRulesFalseDueToWildcardFailure";d:0.031;s:74:"FincTest\View\Helper\Root\OpenUrlTest::testCheckSupportedRecordsRulesFalse";d:0.003;s:91:"FincTest\View\Helper\Root\OpenUrlTest::testCheckSupportedRecordsRulesWithWildcardStillFalse";d:0.003;s:73:"FincTest\View\Helper\Root\OpenUrlTest::testCheckSupportedRecordsRulesTrue";d:0.003;s:67:"FincTest\View\Helper\Root\OpenUrlTest::testRecordDriverClassInRules";d:0.005;}}}
\ No newline at end of file
diff --git a/module/finc/tests/selenium/docker-compose.yaml b/module/finc/tests/selenium/docker-compose.yaml
index fc53c11bc8a1493fa0b661d68e941c2f2e1b7197..3c4432647985df39b2640a2e87d831c614b94b5f 100755
--- a/module/finc/tests/selenium/docker-compose.yaml
+++ b/module/finc/tests/selenium/docker-compose.yaml
@@ -10,16 +10,16 @@ services:
       - "4444:4444"
     extra_hosts:
       - "host.docker.internal:10.111.0.1"
-  chrome:
-    image: selenium/node-chrome
-    depends_on:
-      - selenium-hub
-    environment:
-      - SE_EVENT_BUS_HOST=selenium-hub
-      - SE_EVENT_BUS_PUBLISH_PORT=4442
-      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
-    extra_hosts:
-      - "host.docker.internal:10.111.0.1"
+  #chrome:
+  #  image: selenium/node-chrome
+  #  depends_on:
+  #    - selenium-hub
+  #  environment:
+  #    - SE_EVENT_BUS_HOST=selenium-hub
+  #    - SE_EVENT_BUS_PUBLISH_PORT=4442
+  #    - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
+  #  extra_hosts:
+  #    - "host.docker.internal:10.111.0.1"
   firefox:
     image: selenium/node-firefox
     depends_on:
@@ -31,7 +31,7 @@ services:
     extra_hosts:
       - "host.docker.internal:10.111.0.1"
   php:
-    image: php:7.3-fpm
+    image: php:8.0-fpm
     working_dir: /usr/local/vufind
     depends_on:
       - selenium-hub
diff --git a/module/finc/tests/selenium/tests/de_15/AccountTest.php b/module/finc/tests/selenium/tests/de_15/AccountTest.php
index 202a22fd98efd627810e2ece5d4118e3c61853eb..f6f5b7e85d0941721a4e86366a846f91635439ae 100644
--- a/module/finc/tests/selenium/tests/de_15/AccountTest.php
+++ b/module/finc/tests/selenium/tests/de_15/AccountTest.php
@@ -47,8 +47,7 @@ class AccountTest extends FincBase
     #region helper methods
     protected function changeLanguage($lang = 'de')
     {
-        $this->wd->get(self::$host . self::$port);
-        $this->wd->executeScript("document.langForm.mylang.value='" . $lang . "';document.langForm.submit();");
+        $this->wd->get(self::$host . self::$port . '/?lng=' . $lang);
         $this->waitForPartialLinkText('Katalogsuche');
     }
 }
diff --git a/module/finc/tests/selenium/tests/finc/FincBase.php b/module/finc/tests/selenium/tests/finc/FincBase.php
index 96047cad735c7215f48c034174769a73c2051605..3444f950c58123b601c8a24cd75971307a67c80e 100644
--- a/module/finc/tests/selenium/tests/finc/FincBase.php
+++ b/module/finc/tests/selenium/tests/finc/FincBase.php
@@ -17,7 +17,8 @@ class FincBase extends AbstractTestCase
     public function initBaseUrl()
     {
         // Set base url according to environment
-        switch (ConfigProvider::getInstance()->env) {
+        $env = ConfigProvider::getInstance()->env;
+        switch ($env) {
             case 'staging':
                 self::$host = 'https://staging.finc.info/vufind2/local';
                 break;
@@ -25,7 +26,8 @@ class FincBase extends AbstractTestCase
                 self::$host = 'http://host.docker.internal';
                 break;
             default:
-                throw new \RuntimeException(sprintf('Unknown environment "%s"', ConfigProvider::getInstance()->env));
+                // assume alpha with syntax [instance]/[issue number]
+                self::$host = 'https://alpha.finc.info/vufind2/' . $env;
         }
 
         $this->debug('Base URL set to "%s"', self::$host);
diff --git a/module/finc/tests/selenium/tests/performance/SearchBaseTest.php b/module/finc/tests/selenium/tests/performance/SearchBaseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f3795f996ecd34eb25d88b09abd5d9f0a90ae83
--- /dev/null
+++ b/module/finc/tests/selenium/tests/performance/SearchBaseTest.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Copyright (C) 2023 Leipzig University Library
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Run for local with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run local firefox --group performance-static --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * Run for alpha with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run de_15/12345 firefox --group performance-static --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * @category Finc
+ * @package  Test
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+namespace Selenium\performance;
+
+use Lmc\Steward\ConfigProvider;
+use Selenium\finc\FincBase;
+
+class SearchBaseTest extends FincBase
+{
+    public static $words = [
+        'Antenne',
+        'Bach',
+        'Cello',
+        'Dach',
+        'Eiche',
+        'Fach',
+        'Gans',
+        'Haus',
+        'Igel',
+        'Jagd',
+        'Kabel',
+        'Lampe',
+        'Maus',
+        'Nacht',
+        'Ohr',
+        'Pferd',
+        'Quelle',
+        'Rabe',
+        'Sonne',
+        'Tisch',
+        'Uhr',
+        'Vogel',
+        'Wald',
+        'Xylophon',
+        'Yoga',
+        'Zebra'
+    ];
+
+    const STATIC_SEARCH_TERM = 'Medienkritik';
+
+    /**
+     * @before
+     */
+    public function initBaseUrl()
+    {
+        // Set base url according to environment
+        $env = ConfigProvider::getInstance()->env;
+        switch ($env) {
+            case 'staging':
+                self::$host = 'https://staging.finc.info/vufind2/local';
+                break;
+            case 'local':
+                self::$host = 'http://host.docker.internal';
+                break;
+            default:
+                // assume alpha with syntax [instance]/[issue number]
+                self::$host = 'https://alpha.finc.info/vufind2/' . $env;
+        }
+
+        $this->debug('Base URL set to "%s"', self::$host);
+
+        if (ConfigProvider::getInstance()->env === 'production') {
+            $this->warn('The tests are run against production, so be careful!');
+        }
+    }
+
+    #region helper methods
+    protected function getBasePath() : string
+    {
+        return self::$host . self::$port . '/Search/Results?lookfor=';
+    }
+
+    protected function getSearchTerm(bool $isDynamicSearch = true) : string
+    {
+        if ($isDynamicSearch) {
+            $rand_keys = array_rand(self::$words, 2);
+            $searchTerm = self::$words[$rand_keys[0]] . " " . self::$words[$rand_keys[1]];
+        } else {
+            $searchTerm = self::STATIC_SEARCH_TERM;
+        }
+
+        return urlencode($searchTerm);
+    }
+
+    #endregion
+}
diff --git a/module/finc/tests/selenium/tests/performance/SearchRandomTest.php b/module/finc/tests/selenium/tests/performance/SearchRandomTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7520fdd3ddf08055a40dcb509e1271800a326736
--- /dev/null
+++ b/module/finc/tests/selenium/tests/performance/SearchRandomTest.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (C) 2023 Leipzig University Library
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Run for local with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run local firefox --group performance-random --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * Run for alpha with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run de_15/12345 firefox --group performance-random --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * @category Finc
+ * @package  Test
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+namespace Selenium\performance;
+
+use Lmc\Steward\ConfigProvider;
+
+/**
+ * @group performance
+ * @group performance-random
+ */
+class SearchRandomTest extends SearchBaseTest
+{
+    /**
+     * @group performance
+     * @group performance-random
+     */
+    public function testRandomSearches()
+    {
+        $start = floor(microtime(true) * 1000);
+        for ($i = 1; $i <= 100; $i++) {
+            $this->debug("Executing test no. $i");
+            $this->wd->get($this->getBasePath() . $this->getSearchTerm());
+            $this->waitForClass('result-body');
+        }
+        $end = floor(microtime(true) * 1000);
+        $durationText = "Duration of random searches: " . ((intval($end)) - (intval($start)) . " ms");
+        $this->debug($durationText);
+    }
+}
diff --git a/module/finc/tests/selenium/tests/performance/SearchStaticTest.php b/module/finc/tests/selenium/tests/performance/SearchStaticTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d8c136241139e6c080e37ce1f4a400c60390a5e
--- /dev/null
+++ b/module/finc/tests/selenium/tests/performance/SearchStaticTest.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (C) 2023 Leipzig University Library
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Run for local with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run local firefox --group performance-static --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * Run for alpha with via:
+ * docker exec -it selenium_php_1 sh -c "./vendor/bin/steward run de_15/12345 firefox --group performance-static --server-url http://selenium:4444/wd/hub -vvv"
+ *
+ * @category Finc
+ * @package  Test
+ * @author   Robert Lange <lange@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development
+ */
+namespace Selenium\performance;
+
+use Lmc\Steward\ConfigProvider;
+
+/**
+ * @group performance
+ * @group performance-static
+ */
+class SearchStaticTest extends SearchBaseTest
+{
+    /**
+     * @group performance
+     * @group performance-static
+     */
+    public function testStaticSearches()
+    {
+        $start = floor(microtime(true) * 1000);
+        for ($i = 1; $i <= 100; $i++) {
+            $this->debug("Executing test no. $i");
+            $this->wd->get($this->getBasePath() . $this->getSearchTerm(false));
+            $this->waitForClass('result-body');
+        }
+        $end = floor(microtime(true) * 1000);
+        $durationText = "Duration of static searches: " . ((intval($end)) - (intval($start)) . " ms");
+        $this->debug($durationText);
+    }
+}
diff --git a/themes/finc/scss/_customVariables.scss b/themes/finc/scss/_customVariables.scss
index ecf5718bafa4876faf97f2515337d7ff60bee656..8a18c4aa6020f37255be86ae286d2daa5b109e17 100644
--- a/themes/finc/scss/_customVariables.scss
+++ b/themes/finc/scss/_customVariables.scss
@@ -465,8 +465,8 @@ $label-edit-favorites-list-padding-left: 0 !default;
 $input-border-focus: $brand-primary !default;
 // Inputs with labels -- top margin for better alignment
 $input-top-margin: .2rem !default;
-// Invalid input AND textarea elements
-$input-textarea-invalid-margin-right: 2px !default;
+// Input AND textarea elements
+$input-textarea-margin-right: 2px !default;
 // Invalid input highlighting on focus
 $input-invalid-focus-border-color: $brand-danger-transparent !default;
 $input-invalid-focus-box-shadow: 0 0 2px 1px $brand-danger-transparent !default;
diff --git a/themes/finc/scss/components/_forms.scss b/themes/finc/scss/components/_forms.scss
index 4a12349cebc9fe6cf654cbc74daa3d5274f0348f..50021e7047f40b815017853e4c57021fe3482857 100644
--- a/themes/finc/scss/components/_forms.scss
+++ b/themes/finc/scss/components/_forms.scss
@@ -139,6 +139,9 @@ label {
 // *****************************************************************
 
 input {
+  // Same variable used for textarea as well!
+  margin-right: $input-textarea-margin-right;
+
   // Set max-width to make sure boxes don't bleed over
   // the edge on XS (e.g. acquisitionpda, source_id:3) - same
   // variable used on textarea and select
@@ -190,8 +193,6 @@ input {
   // show red border only when submitted empty or when in focus
   &:invalid {
     box-shadow: inherit;
-    // Same variable used for textarea as well!
-    margin-right: $input-textarea-invalid-margin-right;
   }
 
   &:focus {
@@ -280,17 +281,15 @@ select {
 // *****************************************************************
 
 textarea {
+  // Same variable used for input as well!
+  margin-right: $input-textarea-margin-right;
+
   // Set max-width to make sure boxes don't bleed over
   // the edge on XS (e.g. acquisitionpda, source_id:3) - same
   // variable used on input and select
   @media (max-width: $screen-xs-max) {
     max-width: $input-select-textarea-xs-max-width;
   }
-
-  &:invalid {
-    // Same variable used for input as well!
-    margin-right: $input-textarea-invalid-margin-right;
-  }
 }
 
 
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/cover.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/cover.phtml
index 21d893f58b01b0894a6b6c40436df7527ba9722e..f7714d5a5d7762e5773da2e5bd051a18d3fcb1e3 100644
--- a/themes/finc/templates/RecordDriver/DefaultRecord/cover.phtml
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/cover.phtml
@@ -23,7 +23,7 @@
       aria-label="<?=$this->transEscAttr('Link-to')?> <?=$this->transEscAttr('Cover Image')?> <?=$this->transEscAttr('of')?> <?=$title?>"
     >
   <?php endif; ?>
-  <img alt="<?=$alt?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="recordcover" src="<?=$this->escapeHtmlAttr(($cover)); ?>" />
+  <img alt="<?=$alt?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="recordcover" src="<?=$this->escapeHtmlAttr(($cover)); ?>">
   <?php if ($this->link): ?>
     </a>
     <?php
@@ -34,7 +34,7 @@ JS;
   <?=$this->inlineScript(\Laminas\View\Helper\HeadScript::SCRIPT, $coverDetailScript, 'SET'); ?>
   <?php endif; ?>
 <?php elseif ($cover === false): ?>
-  <img src="<?=$this->url('cover-unavailable')?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="nocover" alt="<?=$this->transEscAttr('No Cover Image')?>" aria-hidden="true" tabindex="-1" />
+  <img src="<?=$this->url('cover-unavailable')?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="nocover" alt="<?=$this->transEscAttr('No Cover Image')?>" aria-hidden="true" tabindex="-1">
 <?php else: ?>
   <?php /* load cover by ajax */ ?>
   <div id="<?=$coverId?>" class="ajaxcover">
@@ -42,7 +42,7 @@ JS;
      <div class="cover-container">
         <?=$this->setIconText = false;?>
         <a class="coverlink hidden" href="javascript:" aria-hidden="true" tabindex="-1">
-          <img <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?> class="recordcover ajax" src="<?=$this->url('cover-unavailable')?>" alt="<?=$this->escapeHtmlAttr($alt); ?>" />
+          <img <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?> class="recordcover ajax" src="<?=$this->url('cover-unavailable')?>" alt="<?=$this->escapeHtmlAttr($alt); ?>">
           <?php
               $coverScript = <<<JS
               loadCoverByElement(
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml
index 07cc27b547f514fcc90404a656b910234dd43399..26adabc9907a1e5e9277d87307b516bd74e54541 100644
--- a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml
@@ -78,10 +78,6 @@
         </div>
         <div class="resultItemLine2">
           <?php if ($this->driver->isCollection()): ?>
-            <?php // finc: add closing div and header #23220 - was missing in #19396 ff.
-                  // just hotfix - will be improved with #24265 ?>
-              </div>
-            </header>
             <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?>
           <?php else: ?>
             <?php
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml
index 3f3e2d21a6e3e9d0da658e4627ed5de24076dfad..c5f6bf12ef84cffb980062e5e86b6b72c2b2d807 100644
--- a/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml
@@ -221,7 +221,8 @@ if ($cover):
 
       <?php if ($this->cart()->isActiveInSearch() && isset($this->params) && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?>
         <?php /* finc: remove break after bookbag #22379 */ ?>
-        <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?>
+        <?php /* finc adds callFromTemplate for setting aria-describedby if use of book bag is with checkboxes #24358 */ ?>
+        <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier(), 'callFromTemplate' => 'resultList']); ?>
       <?php endif; ?>
 
       <?php if ($this->userlist()->getMode() !== 'disabled'): ?>
diff --git a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
index 17bc7f3cac90a1c7db0286dd34061d585dc23768..d55926fcf661073a5df48c2ab9359b490ed9e3b5 100644
--- a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
+++ b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
@@ -204,7 +204,8 @@ if ($cover):
 
       <?php if ($this->cart()->isActiveInSearch() && isset($this->params) && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?>
         <?php /* finc: remove break after bookbag #22379 */ ?>
-        <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?>
+        <?php /* finc adds callFromTemplate for setting aria-describedby if use of book bag is with checkboxes #24358 */ ?>
+        <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier(), 'callFromTemplate' => 'resultList' ]); ?>
       <?php endif; ?>
 
       <?php if ($this->userlist()->getMode() !== 'disabled'): ?>
diff --git a/themes/finc/templates/RecordDriver/SolrDico/data-license.phtml b/themes/finc/templates/RecordDriver/SolrDico/data-license.phtml
index ed4bbae530e134b5eae9cf18565c6f5122b0be1b..43009e3464fdaed01dc6515a18d8736afd95c603 100644
--- a/themes/finc/templates/RecordDriver/SolrDico/data-license.phtml
+++ b/themes/finc/templates/RecordDriver/SolrDico/data-license.phtml
@@ -4,7 +4,8 @@
 <?php ob_start() ?>
 <?=$label ?>
 <?php if (isset($data['icon'])): ?>
-  <img class="collection-data collection-licence-image" src="<?=$data['icon']?>"/>
+  <?php /* finc: add alt-Tag #19697 */ ?>
+  <img class="collection-data collection-licence-image" src="<?=$data['icon']?>" alt="<?=$this->transEsc('LicenseIcon')?>"/>
 <?php endif; ?>
 <?php $label = ob_get_contents();
   ob_end_clean();
diff --git a/themes/finc/templates/RecordTab/collectionlist.phtml b/themes/finc/templates/RecordTab/collectionlist.phtml
index b44ba7c72df25decde5f1f7402a69fc8d8f36375..94cc9f912fa0f19bb5547bf4b4dd35b77b32cbe2 100644
--- a/themes/finc/templates/RecordTab/collectionlist.phtml
+++ b/themes/finc/templates/RecordTab/collectionlist.phtml
@@ -96,11 +96,12 @@
         <?php endif; ?>
       */?>
       </div>
-      <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>">
+        <?php /* de_15: do NOT render search/list-list into the <form>-element, it is causing W3C errors #19697 */ ?>
+        <?php /* <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>"> */ ?>
           <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', $searchDetails + ['idPrefix' => ''])?>
           <?=$this->render('search/list-' . $results->getParams()->getView() . '.phtml', $searchDetails)?>
           <?=$this->paginationControl($results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $results])?>
-      </form>
+      <?php /* </form> */ ?>
     <?php else: ?>
       <?php /* finc: change h4 to h3 */ ?>
       <h3><?=$this->transEsc($params->getDisplayQuery() || ($filterCount ?? 0) > 0 ? 'nohit_heading' : 'collection_empty')?></h3>
diff --git a/themes/finc/templates/cart/contents.phtml b/themes/finc/templates/cart/contents.phtml
index 7eb666ee295792e8e8b5fa4dcac5a8036aa8f44a..9c08eb074bb04d1006c0d14d99989c1d93fcb530 100644
--- a/themes/finc/templates/cart/contents.phtml
+++ b/themes/finc/templates/cart/contents.phtml
@@ -1,7 +1,7 @@
 <!-- finc: cart - contents -->
 <?php $records = $this->cart()->getRecordDetails();
 if (!empty($records)): ?>
-  <hr/>
+  <hr>
   <ul class="list-unstyled">
   <?php foreach ($records as $i => $record): ?>
     <li>
diff --git a/themes/finc/templates/layout/layout.phtml b/themes/finc/templates/layout/layout.phtml
index 4c24153662c9b61c0b1e5bb87f52755416f2c3cb..7a385760bedc49ad6cb866767c7b9bcb22ab55b7 100644
--- a/themes/finc/templates/layout/layout.phtml
+++ b/themes/finc/templates/layout/layout.phtml
@@ -285,7 +285,8 @@ if (!isset($this->layout()->searchbox)) {
 <?=$this->render('footer.phtml')?>
 
 <!-- MODAL IN CASE WE NEED ONE -->
-<?php /* finc: move X button to logical pos. in structure + use 'aria-describedby' instead of 'aria-labelledby - CK */ ?>
+  <section>
+  <?php /* finc: move X button to logical pos. in structure + use 'aria-describedby' instead of 'aria-labelledby - CK */ ?>
     <div id="modal" class="modal fade hidden-print" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true" aria-describedby="modal-description">
       <div class="modal-dialog">
         <div class="modal-content">
@@ -298,6 +299,8 @@ if (!isset($this->layout()->searchbox)) {
         </div>
       </div>
     </div>
+  </section>
+
     <div class="offcanvas-overlay" data-toggle="offcanvas"></div>
     <?=$this->googleanalytics()?>
     <?=$this->piwik()?>
diff --git a/themes/finc/templates/myresearch/acquisition.phtml b/themes/finc/templates/myresearch/acquisition.phtml
index 3c541783a3c94efcd3b711423c7cfeeb72d742b7..00c846d3b8b7d0f65f429632c7c9f03b60321235 100644
--- a/themes/finc/templates/myresearch/acquisition.phtml
+++ b/themes/finc/templates/myresearch/acquisition.phtml
@@ -14,7 +14,7 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') .
 
   <h1><?=$this->transEsc('PDA::pda_form_subtitle')?></h1>
   <?=$this->flashmessages()?>
-  <form method="post" action="" name="acquisitionForm">
+  <form method="post" name="acquisitionForm">
     <h3><?=$this->transEsc('PDA::pda_form_suggestions_limit')?></h3>
 
     <div class="form-group">
@@ -30,13 +30,13 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') .
     </div>
 
     <div class="form-group">
-      <label class="control-label" for="acquistion_reasons"><?=$this->transEsc('PDA::pda_form_statement_label')?><span class="required">*</span></label>
-      <textarea id="acquisition_reasons" type="text" cols="50" rows="5" name="reasons" class="form-control"><?=(isset($acquisitionStatement) && !empty($acquisitionStatement)) ? $acquisitionStatement : ''?></textarea>
+      <label class="control-label" for="acquisition_reasons"><?=$this->transEsc('PDA::pda_form_statement_label')?><span class="required">*</span></label>
+      <textarea id="acquisition_reasons" cols="50" rows="5" name="reasons" class="form-control"><?=(isset($acquisitionStatement) && !empty($acquisitionStatement)) ? $acquisitionStatement : ''?></textarea>
     </div>
 
     <div class="form-group">
-      <label class="control-label" for="acquistion_proposal"><?=$this->transEsc('PDA::pda_form_proposal_label')?><span class="required">*</span></label>
-      <textarea id="acquisition_proposal" class="form-control" type="text" cols="50" rows="5" name="proposal"><?=(isset($acquisitionProposal) && !empty($acquisitionProposal)) ? $acquisitionProposal : ''?></textarea>
+      <label class="control-label" for="acquisition_proposal"><?=$this->transEsc('PDA::pda_form_proposal_label')?><span class="required">*</span></label>
+      <textarea id="acquisition_proposal" class="form-control" cols="50" rows="5" name="proposal"><?=(isset($acquisitionProposal) && !empty($acquisitionProposal)) ? $acquisitionProposal : ''?></textarea>
     </div>
 
     <div class="form-group">
diff --git a/themes/finc/templates/myresearch/edit.phtml b/themes/finc/templates/myresearch/edit.phtml
index 4ad763f6e5fed000d2857d04ed400cc49c3219d2..01023cf62d8d1b583d6567a6e79fc4d03dbd28ba 100644
--- a/themes/finc/templates/myresearch/edit.phtml
+++ b/themes/finc/templates/myresearch/edit.phtml
@@ -49,7 +49,7 @@
     </div>
   <?php endif; ?>
   <?php if (count($this->lists) > 0): ?>
-    <hr/>
+    <hr>
     <div class="form-group">
       <select name="addToList" class="form-control">
         <option value="-1">- <?=$this->transEsc('Add to another list')?> -</option>
diff --git a/themes/finc/templates/myresearch/newpassword.phtml b/themes/finc/templates/myresearch/newpassword.phtml
index 334b80035ff83589128916e4a8229599a4ec12ec..f383da468b0e08987583a31affd209df50dda8a7 100644
--- a/themes/finc/templates/myresearch/newpassword.phtml
+++ b/themes/finc/templates/myresearch/newpassword.phtml
@@ -23,7 +23,7 @@
 <?php elseif (!isset($this->hash)): ?>
   <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div>
 <?php else: ?>
-  <form id="newpassword" class="form-new-password" action="<?=$this->url('myresearch-newpassword') ?>" method="post" data-toggle="validator" role="form">
+  <form id="newpassword" class="form-new-password" action="<?=$this->url('myresearch-newpassword') ?>" method="post" data-toggle="validator">
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>" name="csrf"/>
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->hash) ?>" name="hash"/>
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->username) ?>" name="username"/>
diff --git a/themes/finc/templates/myresearch/setpin.phtml b/themes/finc/templates/myresearch/setpin.phtml
index d3ec8d6401c54e2f5213ec0153490ac9bbd31b1d..e280dc9d634c5c7c8d6bfdbcd32c5fae98ed6058 100644
--- a/themes/finc/templates/myresearch/setpin.phtml
+++ b/themes/finc/templates/myresearch/setpin.phtml
@@ -23,7 +23,7 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') .
     <?php elseif (!isset($this->hash)): ?>
       <div class="error"><?=$this->transEsc('recovery_user_not_found')?></div>
     <?php else: ?>
-      <form id="setpin" class="form-set-pin" action="<?=$this->url('myresearch-setpin')?>" method="post" data-toggle="validator" role="form">
+      <form id="setpin" class="form-set-pin" action="<?=$this->url('myresearch-setpin')?>" method="post" data-toggle="validator">
         <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash(true))?>" name="csrf"/>
         <input type="hidden" value="<?=$this->escapeHtmlAttr($this->hash)?>" name="hash"/>
         <input type="hidden" value="<?=$this->escapeHtmlAttr($this->username)?>" name="username"/>
diff --git a/themes/finc/templates/record/cart-buttons.phtml b/themes/finc/templates/record/cart-buttons.phtml
index 46a57cf85e0aa640109447372384761c3e2c5e7d..abb7910f42dfad7e1b6617c451b06e85f3613827 100644
--- a/themes/finc/templates/record/cart-buttons.phtml
+++ b/themes/finc/templates/record/cart-buttons.phtml
@@ -5,10 +5,11 @@
     <?php /* finc changes span to div */ ?>
     <div class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>">
     <?php /* finc makes add-to/remove-from bookbag accessible for keyboard navigation - CK */ ?>
-    <a href="javascript:" class="cart-add hidden<?php if (!$cart->contains($cartId)): ?> correct<?php endif ?>" aria-describedby="<?=$cartId?>">
+    <?php /* finc adds callFromTemplate for setting aria-describedby if use of book bag is with checkboxes #24358 */ ?>
+    <a href="javascript:" class="cart-add hidden<?php if (!$cart->contains($cartId)): ?> correct<?php endif ?>" <?php if ($this->callFromTemplate == 'resultList'): ?>aria-describedby="<?=$cartId?>"<?php endif ?>>
       <span class="cart-link-label btn-type-add"><?=$this->transEsc('Add to Book Bag')?></span>
     </a>
-    <a href="javascript:" class="cart-remove hidden<?php if ($cart->contains($cartId)): ?> correct<?php endif ?>" aria-describedby="<?=$cartId?>">
+    <a href="javascript:" class="cart-remove hidden<?php if ($cart->contains($cartId)): ?> correct<?php endif ?>" <?php if ($this->callFromTemplate == 'resultList'): ?>aria-describedby="<?=$cartId?>"<?php endif ?>>
       <span class="cart-link-label btn-type-minus"><?=$this->transEsc('Remove from Book Bag')?></span>
     </a>
 
diff --git a/themes/finc/templates/record/save.phtml b/themes/finc/templates/record/save.phtml
index a74f3371334ba1aec7e93b4b0718af3e6c34d6aa..f75be9a59327a88ead51fe64d13aef7ff0b443ba 100644
--- a/themes/finc/templates/record/save.phtml
+++ b/themes/finc/templates/record/save.phtml
@@ -19,7 +19,7 @@
     <?php foreach ($this->containingLists as $i => $list): ?>
       <a href="<?=$this->url('userList', ['id' => $list['id']]) ?>" data-lightbox-ignore><?=$this->escapeHtml($list['title'])?></a><?php if($i < count($this->containingLists) - 1): ?>, <?php endif; ?>
     <?php endforeach; ?>
-    </p><hr/>
+    </p><hr>
   <?php endif; ?>
 
   <?php /* Only display the list drop-down if the user has lists that do not contain