diff --git a/README.md b/README.md
index 028a48c73e8654e923592c7be5a691cf9ffb35ca..274b721aad68b3a870978d0dc091a7b44afff8e8 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Look at the [contribution guide](CONTRIBUTING.md).
 
 ## Install development environment
 
-- Install `node` and `npm`
+- Install `node` and `yarn`
 
 Installation via `nvm` is recommended for easier control of installed version:
 https://github.com/creationix/nvm
@@ -19,24 +19,18 @@ nvm install 10.13.0
 nvm use v10.13.0
 ```
 
-- Install Angular CLI
-
-```sh
-npm install -g @angular/cli@7.0.6
-```
-
 - Install JS dependencies
 
 ```sh
-cd frontend
-npm install
+cd web
+yarn
 ```
 
 - Install latest Java JDK8
 
 See latest instructions for your operating system.
 
-- (Optional) Install `docker` and `docker-compose`
+- (Optional) Install `docker`
 
 If you want to run an Elasticsearch and Kibana instance on your machine.
 You can use your favorite package manager for that
@@ -49,7 +43,7 @@ First make sure you have access to an Elasticsearch HTTP API server on `http://1
 If you want to run an Elasticsearch server on your development machine you can use the `docker`/`docker-compose` configuration like so:
 
 ```sh
-docker-compose up
+docker compose up
 ```
 
 > This will launch an Elasticsearch server (with port forwarding `9200`) and a Kibana server (with port forwarding `5601`)
@@ -57,13 +51,20 @@ docker-compose up
 > **Warning**: This repository does not automatically index data into Elasticsearch, you need to prepare your indices beforehand.
 
 
-If you just need access to API (to run the `ng serve` on top of it), you can run:
+If you just need access to API, you can run:
 
 ```sh
 ./gradlew bootRun
 ```
 
-Otherwise, for the complete server (backend APIs + frontend interface), you can run:
+If you are developing and need to work on the `web` assets (scripts, styles, etc),
+you'll need to run the application with the `dev` profile:
+
+```sh
+./gradlew bootRun --args='--spring.profiles.active=dev'
+```
+
+Otherwise, for the complete server (backend APIs + web interface), you can run:
 
 ```sh
 ./gradlew assemble && java -jar backend/build/libs/faidare.jar
@@ -71,18 +72,26 @@ Otherwise, for the complete server (backend APIs + frontend interface), you can
 
 The server should then be accessible at http://localhost:8380/faidare-dev
 
-## Run frontend development server
+## Build the JS/CSS assets for the Web module
 
-The frontend requests are redirected to the local backend API server (see instructions above to launch) via the
-Angular proxy.
+The `web` directory contains the scripts and styles used by the thymeleaf templates
+when a Germplasm, Study or Site page is rendered.
 
-You can run the development server with the following command:
+The build process for these assets can be run with the following command:
 
 ```sh
-cd frontend
-ng serve
+cd web
+yarn watch
 ```
 
+`yarn watch` automatically picks up the changes in any files,
+and rebuild the resulting assets (thanks to Webpack).
+Make sure the backend is running with the `dev` profile if you do so (see above),
+otherwise the changes won't be shown in the browser.
+
+`yarn watch:prod` is also available to use production settings,
+while `yarn build` and `yarn build:prod` do the same but without watching the changes. 
+
 ## Harvest
 
 Before all, take care to get data locally before running any indexing script.
diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts
index 64acb331b41fea7957dcb7ea7c0d7713e561dd43..6c4f0926f442b6e83b9230f7cfdf2f6ee27c1447 100644
--- a/backend/build.gradle.kts
+++ b/backend/build.gradle.kts
@@ -11,7 +11,7 @@ plugins {
     java
     jacoco
     id("org.springframework.boot") version "2.1.2.RELEASE"
-    id("com.gorylenko.gradle-git-properties") version "1.5.2"
+    id("com.gorylenko.gradle-git-properties") version "2.3.1"
     id("io.spring.dependency-management") version "1.0.6.RELEASE"
     id("org.sonarqube")
     id("org.owasp.dependencycheck") version "6.0.3"
diff --git a/backend/src/main/resources/templates/fragments/institute.html b/backend/src/main/resources/templates/fragments/institute.html
index a79ff6cc547696db1bff202ab5ca8bb481602809..85871d5238adda8d38610de218a4b1654c4dce1a 100644
--- a/backend/src/main/resources/templates/fragments/institute.html
+++ b/backend/src/main/resources/templates/fragments/institute.html
@@ -1,34 +1,48 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-
-<!--
+  <body>
+    <!--
 Reusable fragment displaying the content of an institute popover.
 Its unique argument (institute) is an InstituteVO
 -->
 
-<th:block th:fragment="institute(institute)">
-  <div class="text-center py-2" th:if="${institute.logo}">
-    <img class="img-fluid" th:src="${institute.logo}" th:alt="${institute.instituteName}"/>
-  </div>
-  <div th:replace="fragments/row::text-row(label='Code', text=${institute.instituteCode})"></div>
-  <div th:replace="fragments/row::text-row(label='Acronym', text=${institute.acronym})"></div>
-  <div th:replace="fragments/row::text-row(label='Organization', text=${institute.organisation})"></div>
-  <div th:replace="fragments/row::text-row(label='Type', text=${institute.instituteType})"></div>
-  <div th:replace="fragments/row::text-row(label='Address', text=${institute.address})"></div>
-
-  <th:block th:if="${institute.webSite}">
-    <div th:replace="fragments/row::row(label='Website', content=~{::.institute-website})">
-      <a class="institute-website"
-         target="_blank"
-         th:href="${institute.webSite}"
-         th:text="${#strings.abbreviate(institute.webSite, 25)}"></a>
-    </div>
-  </th:block>
-</th:block>
-
-</body>
+    <th:block th:fragment="institute(institute)">
+      <div class="text-center py-2" th:if="${institute.logo}">
+        <img
+          class="img-fluid"
+          th:src="${institute.logo}"
+          th:alt="${institute.instituteName}"
+        />
+      </div>
+      <div
+        th:replace="fragments/row::text-row(label='Code', text=${institute.instituteCode})"
+      ></div>
+      <div
+        th:replace="fragments/row::text-row(label='Acronym', text=${institute.acronym})"
+      ></div>
+      <div
+        th:replace="fragments/row::text-row(label='Organization', text=${institute.organisation})"
+      ></div>
+      <div
+        th:replace="fragments/row::text-row(label='Type', text=${institute.instituteType})"
+      ></div>
+      <div
+        th:replace="fragments/row::text-row(label='Address', text=${institute.address})"
+      ></div>
 
+      <th:block th:if="${institute.webSite}">
+        <div
+          th:replace="fragments/row::row(label='Website', content=~{::.institute-website})"
+        >
+          <a
+            class="institute-website"
+            target="_blank"
+            th:href="${institute.webSite}"
+            th:text="${#strings.abbreviate(institute.webSite, 25)}"
+          ></a>
+        </div>
+      </th:block>
+    </th:block>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/fragments/link.html b/backend/src/main/resources/templates/fragments/link.html
index d7c43bbdb422c31c5bce647d302c803270672669..d82f0bd5c03a81844046044b6702e8e3e361d561 100644
--- a/backend/src/main/resources/templates/fragments/link.html
+++ b/backend/src/main/resources/templates/fragments/link.html
@@ -1,20 +1,19 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-<!--
+  <body>
+    <!--
 Reusable fragment displaying a link with a label if the provided url is not
 empty, or a span with the label if the provided url is empty.
 Both arguments are strings.
 -->
-<th:block th:fragment="link(label, url)">
-  <a th:unless="${#strings.isEmpty(url)}"
-     th:href="${url}"
-     th:text="${label}"></a>
-  <span th:if="${#strings.isEmpty(url)}" th:text="${label}"></span>
-</th:block>
-
-</body>
-
+    <th:block th:fragment="link(label, url)">
+      <a
+        th:unless="${#strings.isEmpty(url)}"
+        th:href="${url}"
+        th:text="${label}"
+      ></a>
+      <span th:if="${#strings.isEmpty(url)}" th:text="${label}"></span>
+    </th:block>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/fragments/map.html b/backend/src/main/resources/templates/fragments/map.html
index 35ebed333c5c5eaf55160f400e5ebde01313cf24..cad10ae8e4027e704be8bdc78e8308b82aa9cc59 100644
--- a/backend/src/main/resources/templates/fragments/map.html
+++ b/backend/src/main/resources/templates/fragments/map.html
@@ -1,23 +1,24 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-<!--
+  <body>
+    <!--
 Reusable fragment displaying a map and its legend.
 The map is initially hidden. The JavaScript displays it if there are locations
 to display
 -->
-<div th:fragment="map" id="map-container" class="d-none">
-  <div id="map" class="border rounded"></div>
-  <div class="map-legend mt-1 small">
-    <img th:src="@{/assets/images/marker-icon-red.png}" id="red"/>
-    <label for="red" class="me-2">Origin site</label>
-    <img th:src="@{/assets/images/marker-icon-blue.png}" id="blue"/>
-    <label for="blue" class="me-2">Collecting site</label>
-    <img th:src="@{/assets/images/marker-icon-green.png}" id="green"/>
-    <label for="green" class="me-2">Evaluation site</label>
-    <img th:src="@{/assets/images/marker-icon-purple.png}" id="purple"/>
-    <label for="purple">Multi-purpose site</label>
-  </div>
-</div>
+    <div th:fragment="map" id="map-container" class="d-none">
+      <div id="map" class="border rounded"></div>
+      <div class="map-legend mt-1 small">
+        <img th:src="@{/assets/images/marker-icon-red.png}" id="red" />
+        <label for="red" class="me-2">Origin site</label>
+        <img th:src="@{/assets/images/marker-icon-blue.png}" id="blue" />
+        <label for="blue" class="me-2">Collecting site</label>
+        <img th:src="@{/assets/images/marker-icon-green.png}" id="green" />
+        <label for="green" class="me-2">Evaluation site</label>
+        <img th:src="@{/assets/images/marker-icon-purple.png}" id="purple" />
+        <label for="purple">Multi-purpose site</label>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/backend/src/main/resources/templates/fragments/row.html b/backend/src/main/resources/templates/fragments/row.html
index 8676b5906b77ae97d6e8c2ca826839450ce28842..aebf2476a74cbb9d5cd3e1fb57fcdde5d331a462 100644
--- a/backend/src/main/resources/templates/fragments/row.html
+++ b/backend/src/main/resources/templates/fragments/row.html
@@ -1,10 +1,8 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-
-<!--
+  <body>
+    <!--
 Reusable fragment displaying a responsive row containing a label and a content.
 The label argument is a string.
 The content argument is a fragment which is displayed at the right of the label.
@@ -19,14 +17,14 @@ into a block with the condition:
   </th:block>
 -->
 
-<div th:fragment="row(label, content)" class="row f-row">
-  <div class="col-md-4 label pb-1 pb-md-0" th:text="${label}"></div>
-  <div class="col">
-    <th:block th:replace="${content}" />
-  </div>
-</div>
+    <div th:fragment="row(label, content)" class="row f-row">
+      <div class="col-md-4 label pb-1 pb-md-0" th:text="${label}"></div>
+      <div class="col">
+        <th:block th:replace="${content}" />
+      </div>
+    </div>
 
-<!--
+    <!--
 Reusable fragment displaying a responsive row containing a label and a textual content.
 The label argument is a string.
 The text argument is a string which is displayed at the right of the label.
@@ -40,11 +38,13 @@ into a block with the condition:
     <div th:replace="fragments/row::text-row(label='Some label', text=${someTextExpression})"></div>
   </th:block>
 -->
-<div th:fragment="text-row(label, text)" th:unless="${#strings.isEmpty(text)}" class="row f-row">
-  <div class="col-md-4 label pb-1 pb-md-0" th:text="${label}"></div>
-  <div class="col" th:text="${text}"></div>
-</div>
-
-</body>
-
+    <div
+      th:fragment="text-row(label, text)"
+      th:unless="${#strings.isEmpty(text)}"
+      class="row f-row"
+    >
+      <div class="col-md-4 label pb-1 pb-md-0" th:text="${label}"></div>
+      <div class="col" th:text="${text}"></div>
+    </div>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/fragments/source.html b/backend/src/main/resources/templates/fragments/source.html
index b09638810885559be62d0c5448adecd617dbe2fb..ad5e588c3d53e3ce096774e742d008f5110f44ec 100644
--- a/backend/src/main/resources/templates/fragments/source.html
+++ b/backend/src/main/resources/templates/fragments/source.html
@@ -1,10 +1,8 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-
-<!--
+  <body>
+    <!--
 Reusable fragment displaying the source and the data links of an entity (site, study or germplasm).
 The source argument is a DataSource.
 The url argument is a string, which is the URL of the entity.
@@ -12,24 +10,32 @@ The entityType argument is a string, which is used in the message
 "Link to this <entityType>".
 -->
 
-<th:block th:fragment="source(source, url, entityType)">
-  <th:block th:if="${source != null}">
-    <div th:replace="fragments/row::row(label='Source', content=~{::.source})">
-      <a class="source" target="_blank" th:href="${source.url}">
-        <img class="img-fluid" style="max-height: 60px;" th:src="${source.image}" th:alt="${source.name} + ' logo'" />
-      </a>
-    </div>
-  </th:block>
-
-  <th:block th:if="${url != null && source != null}">
-    <div th:replace="fragments/row::row(label='Data link', content=~{::.source-url})">
-      <a class="source-url" target="_blank" th:href="${url}">
-        Link to this <span th:text="${entityType}"></span> on <th:block th:text="${source.name}" />
-      </a>
-    </div>
-  </th:block>
-</th:block>
-
-</body>
+    <th:block th:fragment="source(source, url, entityType)">
+      <th:block th:if="${source != null}">
+        <div
+          th:replace="fragments/row::row(label='Source', content=~{::.source})"
+        >
+          <a class="source" target="_blank" th:href="${source.url}">
+            <img
+              class="img-fluid"
+              style="max-height: 60px"
+              th:src="${source.image}"
+              th:alt="${source.name} + ' logo'"
+            />
+          </a>
+        </div>
+      </th:block>
 
+      <th:block th:if="${url != null && source != null}">
+        <div
+          th:replace="fragments/row::row(label='Data link', content=~{::.source-url})"
+        >
+          <a class="source-url" target="_blank" th:href="${url}">
+            Link to this <span th:text="${entityType}"></span> on
+            <th:block th:text="${source.name}" />
+          </a>
+        </div>
+      </th:block>
+    </th:block>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/fragments/xrefs.html b/backend/src/main/resources/templates/fragments/xrefs.html
index c60310b69c5d852b7a636d9ab7392d0a52d2b553..938d3ea6e96aeabb26d19290b4fe0fa132645e2e 100644
--- a/backend/src/main/resources/templates/fragments/xrefs.html
+++ b/backend/src/main/resources/templates/fragments/xrefs.html
@@ -1,40 +1,53 @@
 <!DOCTYPE html>
 
 <html xmlns:th="http://www.thymeleaf.org">
-
-<body>
-
-<!--
+  <body>
+    <!--
 Reusable fragment displaying a cross references section, with its title.
 The unique argument (crossReferences) is a List<XRefDocumentVO>
 -->
 
-<div class="f-card" th:fragment="xrefs(crossReferences)" th:if="${!#lists.isEmpty(crossReferences)}">
-  <h2>Cross references</h2>
-  <div class="f-card-body">
-    <div class="scroll-table-container scroll-table-container-big">
-      <table class="table table-sm table-striped table-sticky table-responsive-sm">
-        <thead>
-          <tr>
-            <th scope="col">Name</th>
-            <th scope="col">Source</th>
-            <th scope="col">Type</th>
-            <th scope="col">Description</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr th:each="crossRef : ${crossReferences}">
-            <td><a th:href="${crossRef.url}" target="_blank" th:text="${crossRef.name}"></a></td>
-            <td th:text="${crossRef.databaseName}"></td>
-            <td th:text="${crossRef.entryType}"></td>
-            <td style="min-width: 30rem;" th:text="${#strings.abbreviate(crossRef.description, 120)}"></td>
-          </tr>
-        </tbody>
-      </table>
+    <div
+      class="f-card"
+      th:fragment="xrefs(crossReferences)"
+      th:if="${!#lists.isEmpty(crossReferences)}"
+    >
+      <h2>Cross references</h2>
+      <div class="f-card-body">
+        <div class="scroll-table-container scroll-table-container-big">
+          <table
+            class="
+              table table-sm table-striped table-sticky table-responsive-sm
+            "
+          >
+            <thead>
+              <tr>
+                <th scope="col">Name</th>
+                <th scope="col">Source</th>
+                <th scope="col">Type</th>
+                <th scope="col">Description</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr th:each="crossRef : ${crossReferences}">
+                <td>
+                  <a
+                    th:href="${crossRef.url}"
+                    target="_blank"
+                    th:text="${crossRef.name}"
+                  ></a>
+                </td>
+                <td th:text="${crossRef.databaseName}"></td>
+                <td th:text="${crossRef.entryType}"></td>
+                <td
+                  style="min-width: 30rem"
+                  th:text="${#strings.abbreviate(crossRef.description, 120)}"
+                ></td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
     </div>
-  </div>
-</div>
-
-</body>
-
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/germplasm.html b/backend/src/main/resources/templates/germplasm.html
index 26c1a85bb55893c61dc237858e1f3a1a88f66be9..b18b67bbc2e6c6e52660165403442dbb735d015e 100644
--- a/backend/src/main/resources/templates/germplasm.html
+++ b/backend/src/main/resources/templates/germplasm.html
@@ -4,449 +4,710 @@
   xmlns:th="http://www.thymeleaf.org"
   th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main}, script=~{::script})}"
 >
-<head>
-  <title>Germplasm: <th:block th:text="${model.germplasm.germplasmName}" /></title>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-</head>
-
-<body>
-<main>
-  <div class="d-flex">
-    <h1 class="flex-grow-1">Germplasm: <th:block th:text="${model.germplasm.germplasmName}" /></h1>
-    <div th:if="${model.germplasm.holdingGenbank != null && model.germplasm.holdingGenbank.logo != null}">
-      <img th:src="${model.germplasm.holdingGenbank.logo}" th:alt="${model.germplasm.holdingGenbank.instituteName}" />
-    </div>
-  </div>
-
-  <div th:replace="fragments/map::map"></div>
-
-  <div class="row align-items-center justify-content-center mt-4">
-    <div class="col-auto field" th:if="${model.germplasm.photo != null && model.germplasm.photo.thumbnailFile != null}">
-      <template id="photo-popover">
-        <div class="card">
-          <img th:src="${model.germplasm.photo.file}" class="card-img-top" alt="">
-          <div class="card-body">
-            <div th:replace="fragments/row::text-row(label='Accession name', text=${model.germplasm.germplasmName})"></div>
-            <div th:replace="fragments/row::text-row(label='Photo name', text=${model.germplasm.photo.photoName})"></div>
-            <div th:replace="fragments/row::text-row(label='Description', text=${model.germplasm.photo.description})"></div>
-            <div th:replace="fragments/row::text-row(label='Copyright', text=${model.germplasm.photo.copyright})"></div>
+  <head>
+    <title>
+      Germplasm: <th:block th:text="${model.germplasm.germplasmName}" />
+    </title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+
+  <body>
+    <main>
+      <div class="d-flex">
+        <h1 class="flex-grow-1">
+          Germplasm: <th:block th:text="${model.germplasm.germplasmName}" />
+        </h1>
+        <div
+          th:if="${model.germplasm.holdingGenbank != null && model.germplasm.holdingGenbank.logo != null}"
+        >
+          <img
+            th:src="${model.germplasm.holdingGenbank.logo}"
+            th:alt="${model.germplasm.holdingGenbank.instituteName}"
+          />
+        </div>
+      </div>
+
+      <div th:replace="fragments/map::map"></div>
+
+      <div class="row align-items-center justify-content-center mt-4">
+        <div
+          class="col-auto field"
+          th:if="${model.germplasm.photo != null && model.germplasm.photo.thumbnailFile != null}"
+        >
+          <template id="photo-popover">
+            <div class="card">
+              <img
+                th:src="${model.germplasm.photo.file}"
+                class="card-img-top"
+                alt=""
+              />
+              <div class="card-body">
+                <div
+                  th:replace="fragments/row::text-row(label='Accession name', text=${model.germplasm.germplasmName})"
+                ></div>
+                <div
+                  th:replace="fragments/row::text-row(label='Photo name', text=${model.germplasm.photo.photoName})"
+                ></div>
+                <div
+                  th:replace="fragments/row::text-row(label='Description', text=${model.germplasm.photo.description})"
+                ></div>
+                <div
+                  th:replace="fragments/row::text-row(label='Copyright', text=${model.germplasm.photo.copyright})"
+                ></div>
+              </div>
+            </div>
+          </template>
+
+          <a
+            role="button"
+            class="d-flex flex-column align-items-center"
+            data-bs-toggle="popover"
+            tabindex="0"
+            th:data-bs-title="${model.germplasm.photo.photoName}"
+            data-bs-element="#photo-popover"
+            data-bs-container="body"
+            data-bs-trigger="focus"
+          >
+            <img
+              th:src="${model.germplasm.photo.thumbnailFile}"
+              class="img-fluid"
+            />
+
+            <figcaption class="figure-caption">
+              © <span th:text="${model.germplasm.photo.copyright}"></span>
+            </figcaption>
+          </a>
+        </div>
+
+        <div class="col-12 col-lg">
+          <div class="f-card">
+            <h2>Identification</h2>
+
+            <div class="f-card-body">
+              <div
+                th:replace="fragments/row::text-row(label='Germplasm name', text=${model.germplasm.germplasmName})"
+              ></div>
+              <div
+                th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.accessionNumber})"
+              ></div>
+
+              <div
+                th:replace="fragments/source::source(source=${model.source}, url=${model.germplasm.url}, entityType='germplasm')"
+              ></div>
+
+              <th:block th:unless="${#lists.isEmpty(model.germplasm.synonyms)}">
+                <div
+                  th:replace="fragments/row::row(label='Accession synonyms', content=~{::#accession-synonyms})"
+                >
+                  <div
+                    id="accession-synonyms"
+                    class="content-overflow"
+                    th:text="${#strings.listJoin(model.germplasm.synonyms, ', ')}"
+                  ></div>
+                </div>
+              </th:block>
+
+              <th:block th:unless="${#strings.isEmpty(model.taxon)}">
+                <div
+                  th:replace="fragments/row::row(label='Taxon', content=~{::#taxon})"
+                >
+                  <div id="taxon">
+                    <template id="taxon-popover">
+                      <th:block
+                        th:unless="${#strings.isEmpty(model.germplasm.genus)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Genus', content=~{::#taxon-genus})"
+                        >
+                          <em
+                            id="taxon-genus"
+                            th:text="${model.germplasm.genus}"
+                          ></em>
+                        </div>
+                      </th:block>
+                      <th:block
+                        th:unless="${#strings.isEmpty(model.germplasm.species)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Species', content=~{::#taxon-species})"
+                        >
+                          <span id="taxon-species">
+                            <em th:text="${model.germplasm.species}"></em>
+                            <span
+                              th:unless="${#strings.isEmpty(model.germplasm.speciesAuthority)}"
+                              th:text="${'(' + model.germplasm.speciesAuthority + ')'}"
+                            ></span>
+                          </span>
+                        </div>
+                      </th:block>
+                      <th:block
+                        th:unless="${#strings.isEmpty(model.germplasm.subtaxa)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Subtaxa', content=~{::#taxon-subtaxa})"
+                        >
+                          <span id="taxon-subtaxa">
+                            <em th:text="${model.germplasm.subtaxa}"></em>
+                            <span
+                              th:unless="${#strings.isEmpty(model.germplasm.subtaxaAuthority)}"
+                              th:text="${'(' + model.germplasm.subtaxaAuthority + ')'}"
+                            ></span>
+                          </span>
+                        </div>
+                      </th:block>
+
+                      <div
+                        th:replace="fragments/row::text-row(label='Authority', text=${model.taxonAuthor})"
+                      ></div>
+
+                      <th:block
+                        th:unless="${#lists.isEmpty(model.germplasm.taxonIds)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Taxon IDs', content=~{::#taxon-ids})"
+                        >
+                          <div id="taxon-ids">
+                            <div
+                              th:each="taxonId : ${model.germplasm.taxonIds}"
+                              class="row"
+                            >
+                              <div
+                                class="col-6 text-nowrap"
+                                th:text="${taxonId.sourceName}"
+                              ></div>
+                              <div class="col-6">
+                                <span
+                                  class="taxon-id"
+                                  th:replace="fragments/link::link(label=${taxonId.taxonId}, url=${#faidare.taxonIdUrl(taxonId)})"
+                                ></span>
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                      </th:block>
+
+                      <div
+                        th:replace="fragments/row::text-row(label='Comment', text=${model.germplasm.taxonComment})"
+                      ></div>
+                      <th:block
+                        th:unless="${#lists.isEmpty(model.germplasm.taxonCommonNames)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Taxon common names', content=~{::#taxon-common-names})"
+                        >
+                          <div
+                            id="taxon-common-names"
+                            class="content-overflow"
+                            th:text="${#strings.listJoin(model.germplasm.taxonCommonNames, ', ')}"
+                          ></div>
+                        </div>
+                      </th:block>
+                      <th:block
+                        th:unless="${#lists.isEmpty(model.germplasm.taxonSynonyms)}"
+                      >
+                        <div
+                          th:replace="fragments/row::row(label='Taxon synonyms', content=~{::#taxon-synonyms})"
+                        >
+                          <div
+                            id="taxon-synonyms"
+                            class="content-overflow"
+                            th:text="${#strings.listJoin(model.germplasm.taxonSynonyms, ', ')}"
+                          ></div>
+                        </div>
+                      </th:block>
+                    </template>
+                    <a
+                      role="button"
+                      tabindex="0"
+                      data-bs-toggle="popover"
+                      th:data-bs-title="${model.taxon}"
+                      data-bs-element="#taxon-popover"
+                      data-bs-container="body"
+                      data-bs-trigger="focus"
+                    >
+                      <em th:text="${model.taxon}"></em>
+                      <th:block
+                        th:unless="${#strings.isEmpty(model.taxonAuthor)}"
+                        >(<span th:text="${model.taxonAuthor}"></span
+                        >)</th:block
+                      >
+                    </a>
+                  </div>
+                </div>
+              </th:block>
+
+              <div
+                th:replace="fragments/row::text-row(label='Biological status', text=${model.germplasm.biologicalStatusOfAccessionCode})"
+              ></div>
+              <div
+                th:replace="fragments/row::text-row(label='Genetic nature', text=${model.germplasm.geneticNature})"
+              ></div>
+              <div
+                th:replace="fragments/row::text-row(label='Seed source', text=${model.germplasm.seedSource})"
+              ></div>
+              <div
+                th:replace="fragments/row::text-row(label='Pedigree', text=${model.germplasm.pedigree})"
+              ></div>
+              <div
+                th:replace="fragments/row::text-row(label='Comments', text=${model.germplasm.comment})"
+              ></div>
+
+              <th:block
+                th:if="${model.germplasm.originSite != null && !#strings.isEmpty(model.germplasm.originSite.siteName)}"
+              >
+                <div
+                  th:replace="fragments/row::row(label='Origin site', content=~{::#origin-site})"
+                >
+                  <a
+                    id="origin-site"
+                    th:href="@{/sites/{siteId}(siteId=${#faidare.toSiteParam(model.germplasm.originSite.siteId)})}"
+                    th:text="${model.germplasm.originSite.siteName}"
+                  ></a>
+                </div>
+              </th:block>
+            </div>
           </div>
         </div>
-      </template>
-
-      <a role="button"
-         class="d-flex flex-column align-items-center"
-         data-bs-toggle="popover"
-         tabindex="0"
-         th:data-bs-title="${model.germplasm.photo.photoName}"
-         data-bs-element="#photo-popover"
-         data-bs-container="body"
-         data-bs-trigger="focus">
-        <img th:src="${model.germplasm.photo.thumbnailFile}" class="img-fluid" />
-
-        <figcaption class="figure-caption">
-          © <span th:text="${model.germplasm.photo.copyright}"></span>
-        </figcaption>
-      </a>
-    </div>
-
-    <div class="col-12 col-lg">
-      <div class="f-card">
-        <h2>Identification</h2>
+      </div>
 
+      <div class="f-card" th:if="${model.germplasm.holdingInstitute}">
+        <h2>Depositary</h2>
         <div class="f-card-body">
-          <div th:replace="fragments/row::text-row(label='Germplasm name', text=${model.germplasm.germplasmName})"></div>
-          <div th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.accessionNumber})"></div>
-
-          <div th:replace="fragments/source::source(source=${model.source}, url=${model.germplasm.url}, entityType='germplasm')"></div>
+          <template id="holding-institute-popover">
+            <div
+              th:replace="fragments/institute::institute(institute=${model.germplasm.holdingInstitute})"
+            ></div>
+          </template>
+          <div
+            th:replace="fragments/row::row(label='Institution', content=~{::#institution})"
+          >
+            <a
+              id="institution"
+              role="button"
+              tabindex="0"
+              data-bs-toggle="popover"
+              th:data-bs-title="${model.germplasm.holdingInstitute.instituteName}"
+              data-bs-element="#holding-institute-popover"
+              data-bs-container="body"
+              data-bs-trigger="focus"
+              th:text="${model.germplasm.holdingInstitute.instituteName}"
+            ></a>
+          </div>
 
-          <th:block th:unless="${#lists.isEmpty(model.germplasm.synonyms)}">
-            <div th:replace="fragments/row::row(label='Accession synonyms', content=~{::#accession-synonyms})">
-              <div id="accession-synonyms" class="content-overflow" th:text="${#strings.listJoin(model.germplasm.synonyms, ', ')}"></div>
+          <th:block
+            th:if="${model.germplasm.holdingGenbank != null && !#strings.isEmpty(model.germplasm.holdingGenbank.instituteName) && !#strings.isEmpty(model.germplasm.holdingGenbank.webSite)}"
+          >
+            <div
+              th:replace="fragments/row::row(label='Stock center name', content=~{::#stock-center-name})"
+            >
+              <a
+                id="stock-center-name"
+                target="_blank"
+                th:href="${model.germplasm.holdingGenbank.webSite}"
+                th:text="${model.germplasm.holdingGenbank.instituteName}"
+              ></a>
             </div>
           </th:block>
 
-          <th:block th:unless="${#strings.isEmpty(model.taxon)}">
-            <div th:replace="fragments/row::row(label='Taxon', content=~{::#taxon})">
-              <div id="taxon">
-                <template id="taxon-popover">
-                  <th:block th:unless="${#strings.isEmpty(model.germplasm.genus)}">
-                    <div th:replace="fragments/row::row(label='Genus', content=~{::#taxon-genus})">
-                      <em id="taxon-genus" th:text="${model.germplasm.genus}"></em>
-                    </div>
-                  </th:block>
-                  <th:block th:unless="${#strings.isEmpty(model.germplasm.species)}">
-                    <div th:replace="fragments/row::row(label='Species', content=~{::#taxon-species})">
-                      <span id="taxon-species">
-                        <em th:text="${model.germplasm.species}"></em>
-                        <span th:unless="${#strings.isEmpty(model.germplasm.speciesAuthority)}"
-                              th:text="${'(' + model.germplasm.speciesAuthority + ')'}"></span>
-                      </span>
-                    </div>
-                  </th:block>
-                  <th:block th:unless="${#strings.isEmpty(model.germplasm.subtaxa)}">
-                    <div th:replace="fragments/row::row(label='Subtaxa', content=~{::#taxon-subtaxa})">
-                      <span id="taxon-subtaxa">
-                        <em th:text="${model.germplasm.subtaxa}"></em>
-                        <span th:unless="${#strings.isEmpty(model.germplasm.subtaxaAuthority)}"
-                              th:text="${'(' + model.germplasm.subtaxaAuthority + ')'}"></span>
-                      </span>
-                    </div>
-                  </th:block>
+          <div
+            th:replace="fragments/row::text-row(label='Presence status', text=${model.germplasm.presenceStatus})"
+          ></div>
+        </div>
+      </div>
 
-                  <div th:replace="fragments/row::text-row(label='Authority', text=${model.taxonAuthor})"></div>
+      <div class="f-card" th:if="${model.collecting}">
+        <h2>Collector</h2>
+        <div class="f-card-body">
+          <th:block
+            th:if="${model.germplasm.collectingSite != null && !#strings.isEmpty(model.germplasm.collectingSite.siteName)}"
+          >
+            <div
+              th:replace="fragments/row::row(label='Collecting site', content=~{::#collecting-site})"
+            >
+              <a
+                id="collecting-site"
+                th:href="@{/sites/{siteId}(siteId=${#faidare.toSiteParam(model.germplasm.collectingSite.siteId)})}"
+                th:text="${model.germplasm.collectingSite.siteName}"
+              ></a>
+            </div>
+          </th:block>
 
-                  <th:block th:unless="${#lists.isEmpty(model.germplasm.taxonIds)}">
-                    <div th:replace="fragments/row::row(label='Taxon IDs', content=~{::#taxon-ids})">
-                      <div id="taxon-ids">
-                        <div th:each="taxonId : ${model.germplasm.taxonIds}" class="row">
-                          <div class="col-6 text-nowrap" th:text="${taxonId.sourceName}"></div>
-                          <div class="col-6">
-                            <span class="taxon-id"
-                                  th:replace="fragments/link::link(label=${taxonId.taxonId}, url=${#faidare.taxonIdUrl(taxonId)})"></span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </th:block>
+          <div
+            th:replace="fragments/row::text-row(label='Material type', text=${model.germplasm.collector.materialType})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Collectors', text=${model.germplasm.collector.collectors})"
+          ></div>
+
+          <th:block
+            th:if="${!#strings.isEmpty(model.germplasm.acquisitionDate) && model.germplasm.collector.accessionCreationDate == null}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Acquisition / Creation date', text=${model.germplasm.acquisitionDate})"
+            ></div>
+          </th:block>
 
-                  <div th:replace="fragments/row::text-row(label='Comment', text=${model.germplasm.taxonComment})"></div>
-                  <th:block th:unless="${#lists.isEmpty(model.germplasm.taxonCommonNames)}">
-                    <div th:replace="fragments/row::row(label='Taxon common names', content=~{::#taxon-common-names})">
-                      <div id="taxon-common-names" class="content-overflow" th:text="${#strings.listJoin(model.germplasm.taxonCommonNames, ', ')}"></div>
-                    </div>
-                  </th:block>
-                  <th:block th:unless="${#lists.isEmpty(model.germplasm.taxonSynonyms)}">
-                    <div th:replace="fragments/row::row(label='Taxon synonyms', content=~{::#taxon-synonyms})">
-                      <div id="taxon-synonyms" class="content-overflow" th:text="${#strings.listJoin(model.germplasm.taxonSynonyms, ', ')}"></div>
-                    </div>
-                  </th:block>
-                </template>
-                <a role="button"
-                   tabindex="0"
-                   data-bs-toggle="popover"
-                   th:data-bs-title="${model.taxon}"
-                   data-bs-element="#taxon-popover"
-                   data-bs-container="body"
-                   data-bs-trigger="focus">
-                  <em th:text="${model.taxon}"></em>
-                  <th:block th:unless="${#strings.isEmpty(model.taxonAuthor)}">(<span th:text="${model.taxonAuthor}"></span>)</th:block>
-                </a>
-              </div>
+          <th:block
+            th:if="${model.germplasm.collector.institute != null && !#strings.isEmpty(model.germplasm.collector.institute.instituteName)}"
+          >
+            <template id="collector-institute-popover">
+              <div
+                th:replace="fragments/institute::institute(institute=${model.germplasm.collector.institute})"
+              ></div>
+            </template>
+            <div
+              th:replace="fragments/row::row(label='Institution', content=~{::#collecting-institution})"
+            >
+              <a
+                id="collecting-institution"
+                role="button"
+                tabindex="0"
+                data-bs-toggle="popover"
+                th:data-bs-title="${model.germplasm.collector.institute.instituteName}"
+                data-bs-element="#collector-institute-popover"
+                data-bs-container="body"
+                data-bs-trigger="focus"
+                th:text="${model.germplasm.collector.institute.instituteName}"
+              ></a>
             </div>
           </th:block>
 
-          <div th:replace="fragments/row::text-row(label='Biological status', text=${model.germplasm.biologicalStatusOfAccessionCode})"></div>
-          <div th:replace="fragments/row::text-row(label='Genetic nature', text=${model.germplasm.geneticNature})"></div>
-          <div th:replace="fragments/row::text-row(label='Seed source', text=${model.germplasm.seedSource})"></div>
-          <div th:replace="fragments/row::text-row(label='Pedigree', text=${model.germplasm.pedigree})"></div>
-          <div th:replace="fragments/row::text-row(label='Comments', text=${model.germplasm.comment})"></div>
+          <div
+            th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.collector.accessionNumber})"
+          ></div>
+        </div>
+      </div>
 
-          <th:block th:if="${model.germplasm.originSite != null && !#strings.isEmpty(model.germplasm.originSite.siteName)}">
-            <div th:replace="fragments/row::row(label='Origin site', content=~{::#origin-site})">
-              <a id="origin-site" th:href="@{/sites/{siteId}(siteId=${#faidare.toSiteParam(model.germplasm.originSite.siteId)})}" th:text="${model.germplasm.originSite.siteName}"></a>
+      <div class="f-card" th:if="${model.breeding}">
+        <h2>Breeder</h2>
+        <div class="f-card-body">
+          <th:block
+            th:if="${model.germplasm.breeder.institute != null && !#strings.isEmpty(model.germplasm.breeder.institute.instituteName)}"
+          >
+            <template id="breeder-institute-popover">
+              <div
+                th:replace="fragments/institute::institute(institute=${model.germplasm.breeder.institute})"
+              ></div>
+            </template>
+            <div
+              th:replace="fragments/row::row(label='Institute', content=~{::#breeding-institution})"
+            >
+              <a
+                id="breeding-institution"
+                role="button"
+                tabindex="0"
+                data-bs-toggle="popover"
+                th:data-bs-title="${model.germplasm.breeder.institute.instituteName}"
+                data-bs-element="#breeder-institute-popover"
+                data-bs-container="body"
+                data-bs-trigger="focus"
+                th:text="${model.germplasm.breeder.institute.instituteName}"
+              ></a>
             </div>
           </th:block>
+
+          <div
+            th:replace="fragments/row::text-row(label='Accession creation year', text=${model.germplasm.breeder.accessionCreationDate})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.breeder.accessionNumber})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Catalog registration year', text=${model.germplasm.breeder.registrationYear})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Catalog deregistration year', text=${model.germplasm.breeder.deregistrationYear})"
+          ></div>
         </div>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:if="${model.germplasm.holdingInstitute}">
-    <h2>Depositary</h2>
-    <div class="f-card-body">
-      <template id="holding-institute-popover">
-        <div th:replace="fragments/institute::institute(institute=${model.germplasm.holdingInstitute})"></div>
-      </template>
-      <div th:replace="fragments/row::row(label='Institution', content=~{::#institution})">
-        <a id="institution"
-           role="button"
-           tabindex="0"
-           data-bs-toggle="popover"
-           th:data-bs-title="${model.germplasm.holdingInstitute.instituteName}"
-           data-bs-element="#holding-institute-popover"
-           data-bs-container="body"
-           data-bs-trigger="focus"
-           th:text="${model.germplasm.holdingInstitute.instituteName}"></a>
-      </div>
 
-      <th:block th:if="${model.germplasm.holdingGenbank != null && !#strings.isEmpty(model.germplasm.holdingGenbank.instituteName) && !#strings.isEmpty(model.germplasm.holdingGenbank.webSite)}">
-        <div th:replace="fragments/row::row(label='Stock center name', content=~{::#stock-center-name})">
-          <a id="stock-center-name"
-             target="_blank"
-             th:href="${model.germplasm.holdingGenbank.webSite}"
-             th:text="${model.germplasm.holdingGenbank.instituteName}"></a>
-        </div>
-      </th:block>
-
-      <div th:replace="fragments/row::text-row(label='Presence status', text=${model.germplasm.presenceStatus})"></div>
-    </div>
-  </div>
-
-  <div class="f-card" th:if="${model.collecting}">
-    <h2>Collector</h2>
-    <div class="f-card-body">
-      <th:block th:if="${model.germplasm.collectingSite != null && !#strings.isEmpty(model.germplasm.collectingSite.siteName)}">
-        <div th:replace="fragments/row::row(label='Collecting site', content=~{::#collecting-site})">
-          <a id="collecting-site"
-             th:href="@{/sites/{siteId}(siteId=${#faidare.toSiteParam(model.germplasm.collectingSite.siteId)})}"
-             th:text="${model.germplasm.collectingSite.siteName}"
-          ></a>
-        </div>
-      </th:block>
-
-      <div th:replace="fragments/row::text-row(label='Material type', text=${model.germplasm.collector.materialType})"></div>
-      <div th:replace="fragments/row::text-row(label='Collectors', text=${model.germplasm.collector.collectors})"></div>
-
-      <th:block th:if="${!#strings.isEmpty(model.germplasm.acquisitionDate) && model.germplasm.collector.accessionCreationDate == null}">
-        <div th:replace="fragments/row::text-row(label='Acquisition / Creation date', text=${model.germplasm.acquisitionDate})"></div>
-      </th:block>
-
-      <th:block th:if="${model.germplasm.collector.institute != null && !#strings.isEmpty(model.germplasm.collector.institute.instituteName)}">
-        <template id="collector-institute-popover">
-          <div th:replace="fragments/institute::institute(institute=${model.germplasm.collector.institute})"></div>
-        </template>
-        <div th:replace="fragments/row::row(label='Institution', content=~{::#collecting-institution})">
-          <a id="collecting-institution"
-             role="button"
-             tabindex="0"
-             data-bs-toggle="popover"
-             th:data-bs-title="${model.germplasm.collector.institute.instituteName}"
-             data-bs-element="#collector-institute-popover"
-             data-bs-container="body"
-             data-bs-trigger="focus"
-             th:text="${model.germplasm.collector.institute.instituteName}"></a>
+      <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.donors)}">
+        <h2>Donors</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Institute name</th>
+                  <th scope="col">Institute code</th>
+                  <th scope="col">Donation date</th>
+                  <th scope="col">Accession number</th>
+                  <th scope="col">Accession PUI</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="row, donorIterStat : ${model.germplasm.donors}">
+                  <td>
+                    <template
+                      th:id="${'donor-institute-popover-' + donorIterStat.index}"
+                    >
+                      <div
+                        th:replace="fragments/institute::institute(institute=${row.donorInstitute})"
+                      ></div>
+                    </template>
+                    <a
+                      role="button"
+                      tabindex="0"
+                      data-bs-toggle="popover"
+                      th:data-bs-title="${row.donorInstitute.instituteName}"
+                      th:data-bs-element="${'#donor-institute-popover-' + donorIterStat.index}"
+                      data-bs-container="body"
+                      data-bs-trigger="focus"
+                      th:text="${row.donorInstitute.instituteName}"
+                    ></a>
+                  </td>
+                  <td th:text="${row.donorInstituteCode}"></td>
+                  <td th:text="${row.donationDate}"></td>
+                  <td th:text="${row.donorAccessionNumber}"></td>
+                  <td th:text="${row.donorGermplasmPUI}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
-      </th:block>
-
-      <div th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.collector.accessionNumber})"></div>
-    </div>
-  </div>
-
-  <div class="f-card" th:if="${model.breeding}">
-    <h2>Breeder</h2>
-    <div class="f-card-body">
-      <th:block th:if="${model.germplasm.breeder.institute != null && !#strings.isEmpty(model.germplasm.breeder.institute.instituteName)}">
-        <template id="breeder-institute-popover">
-          <div th:replace="fragments/institute::institute(institute=${model.germplasm.breeder.institute})"></div>
-        </template>
-        <div th:replace="fragments/row::row(label='Institute', content=~{::#breeding-institution})">
-          <a id="breeding-institution"
-             role="button"
-             tabindex="0"
-             data-bs-toggle="popover"
-             th:data-bs-title="${model.germplasm.breeder.institute.instituteName}"
-             data-bs-element="#breeder-institute-popover"
-             data-bs-container="body"
-             data-bs-trigger="focus"
-             th:text="${model.germplasm.breeder.institute.instituteName}"></a>
+      </div>
+
+      <div
+        class="f-card"
+        th:unless="${#lists.isEmpty(model.germplasm.distributors)}"
+      >
+        <h2>Distributors</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Institute</th>
+                  <th scope="col">Accession number</th>
+                  <th scope="col">Distribution status</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr
+                  th:each="row, distributorIterStat : ${model.germplasm.distributors}"
+                >
+                  <td>
+                    <template
+                      th:id="${'distributor-institute-popover-' + distributorIterStat.index}"
+                    >
+                      <div
+                        th:replace="fragments/institute::institute(institute=${row.institute})"
+                      ></div>
+                    </template>
+                    <a
+                      role="button"
+                      tabindex="0"
+                      th:data-bs-title="${row.institute.instituteName}"
+                      th:data-bs-element="${'#distributor-institute-popover-' + distributorIterStat.index}"
+                      data-bs-container="body"
+                      data-bs-trigger="focus"
+                      th:text="${row.institute.instituteName}"
+                    ></a>
+                  </td>
+                  <td th:text="${row.accessionNumber}"></td>
+                  <td th:text="${row.distributionStatus}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
-      </th:block>
-
-      <div th:replace="fragments/row::text-row(label='Accession creation year', text=${model.germplasm.breeder.accessionCreationDate})"></div>
-      <div th:replace="fragments/row::text-row(label='Accession number', text=${model.germplasm.breeder.accessionNumber})"></div>
-      <div th:replace="fragments/row::text-row(label='Catalog registration year', text=${model.germplasm.breeder.registrationYear})"></div>
-      <div th:replace="fragments/row::text-row(label='Catalog deregistration year', text=${model.germplasm.breeder.deregistrationYear})"></div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.donors)}">
-    <h2>Donors</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Institute name</th>
-              <th scope="col">Institute code</th>
-              <th scope="col">Donation date</th>
-              <th scope="col">Accession number</th>
-              <th scope="col">Accession PUI</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row, donorIterStat : ${model.germplasm.donors}">
-              <td>
-                <template th:id="${'donor-institute-popover-' + donorIterStat.index}">
-                  <div th:replace="fragments/institute::institute(institute=${row.donorInstitute})"></div>
-                </template>
-                <a role="button"
-                   tabindex="0"
-                   data-bs-toggle="popover"
-                   th:data-bs-title="${row.donorInstitute.instituteName}"
-                   th:data-bs-element="${'#donor-institute-popover-' + donorIterStat.index}"
-                   data-bs-container="body"
-                   data-bs-trigger="focus"
-                   th:text="${row.donorInstitute.instituteName}"></a>
-              </td>
-              <td th:text="${row.donorInstituteCode}"></td>
-              <td th:text="${row.donationDate}"></td>
-              <td th:text="${row.donorAccessionNumber}"></td>
-              <td th:text="${row.donorGermplasmPUI}"></td>
-            </tr>
-          </tbody>
-        </table>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.distributors)}">
-    <h2>Distributors</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Institute</th>
-              <th scope="col">Accession number</th>
-              <th scope="col">Distribution status</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row, distributorIterStat : ${model.germplasm.distributors}">
-              <td>
-                <template th:id="${'distributor-institute-popover-' + distributorIterStat.index}">
-                  <div th:replace="fragments/institute::institute(institute=${row.institute})"></div>
-                </template>
-                <a role="button"
-                   tabindex="0"
-                   th:data-bs-title="${row.institute.instituteName}"
-                   th:data-bs-element="${'#distributor-institute-popover-' + distributorIterStat.index}"
-                   data-bs-container="body"
-                   data-bs-trigger="focus"
-                   th:text="${row.institute.instituteName}"></a>
-              </td>
-              <td th:text="${row.accessionNumber}"></td>
-              <td th:text="${row.distributionStatus}"></td>
-            </tr>
-          </tbody>
-        </table>
+
+      <div class="f-card" th:unless="${#lists.isEmpty(model.attributes)}">
+        <h2>Evaluation Data</h2>
+        <div class="f-card-body">
+          <th:block th:each="descriptor : ${model.attributes}">
+            <div
+              th:replace="fragments/row::text-row(label=${descriptor.attributeName}, text=${descriptor.value})"
+            ></div>
+          </th:block>
+        </div>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.attributes)}">
-    <h2>Evaluation Data</h2>
-    <div class="f-card-body">
-      <th:block th:each="descriptor : ${model.attributes}">
-        <div th:replace="fragments/row::text-row(label=${descriptor.attributeName}, text=${descriptor.value})"></div>
-      </th:block>
-    </div>
-  </div>
-
-  <div class="f-card" th:if="${model.genealogyPresent}">
-    <h2>Genealogy</h2>
-    <div class="f-card-body">
-      <th:block th:if="${model.pedigree != null}">
-        <div th:replace="fragments/row::text-row(label='Crossing plan', text=${model.pedigree.crossingPlan})"></div>
-        <div th:replace="fragments/row::text-row(label='Crossing year', text=${model.pedigree.crossingYear})"></div>
-        <div th:replace="fragments/row::text-row(label='Family code', text=${model.pedigree.familyCode})"></div>
-        <th:block th:unless="${#strings.isEmpty(model.pedigree.parent1Name)}">
-          <div th:replace="fragments/row::row(label='Parent accessions', content=~{::#parent-accessions})">
-            <div id="parent-accessions">
-              <th:block th:if="${model.pedigree.parent1DbId}">
-                <div th:replace="fragments/row::row(label=${model.pedigree.parent1Type}, content=~{::#parent1-link})">
-                  <a id="parent1-link" th:href="@{/germplasms/{germplasmId}(germplasmId=${model.pedigree.parent1DbId})}" th:text="${model.pedigree.parent1Name}"></a>
-                </div>
-              </th:block>
 
-              <th:block th:if="${model.pedigree.parent2DbId}">
-                <div th:replace="fragments/row::row(label=${model.pedigree.parent2Type}, content=~{::#parent2-link})">
-                  <a id="parent2-link" th:href="@{/germplasms/{germplasmId}(germplasmId=${model.pedigree.parent2DbId})}" th:text="${model.pedigree.parent2Name}"></a>
-                </div>
-              </th:block>
-            </div>
-          </div>
-        </th:block>
-
-        <th:block th:unless="${#lists.isEmpty(model.pedigree.siblings)}">
-          <div th:replace="fragments/row::row(label='Sibling accessions', content=~{::#sibling-accessions})">
-            <div id="sibling-accessions" class="content-overflow">
-              <a th:each="sibling : ${model.pedigree.siblings}"
-                 th:href="@{/germplasms/{germplasmId}(germplasmId=${sibling.germplasmDbId})}"
-                 th:text="${sibling.defaultDisplayName}"></a>
-            </div>
-          </div>
-        </th:block>
-      </th:block>
-
-      <th:block th:unless="${#lists.isEmpty(model.germplasm.children)}">
-        <div th:replace="fragments/row::row(label='Descendants', content=~{::#descendants})">
-          <div id="descendants" class="content-overflow content-overflow-big">
-            <th:block th:each="child : ${model.germplasm.children}">
-              <div th:replace="fragments/row::row(label=${#strings.isEmpty(child.secondParentName) ? ('children of ' + child.firstParentName) : ('children of ' + child.firstParentName + ' x ' + child.secondParentName) }, content=~{::.descendant-child})">
-                <div class="descendant-child">
-                  <th:block th:each="sibling, siblingIterStat : ${child.sibblings}">
-                    <a th:href="@{/germplasms(pui=${sibling.pui})}"
-                       th:text="${sibling.name}"></a><th:block th:unless="${siblingIterStat.last}">, </th:block>
+      <div class="f-card" th:if="${model.genealogyPresent}">
+        <h2>Genealogy</h2>
+        <div class="f-card-body">
+          <th:block th:if="${model.pedigree != null}">
+            <div
+              th:replace="fragments/row::text-row(label='Crossing plan', text=${model.pedigree.crossingPlan})"
+            ></div>
+            <div
+              th:replace="fragments/row::text-row(label='Crossing year', text=${model.pedigree.crossingYear})"
+            ></div>
+            <div
+              th:replace="fragments/row::text-row(label='Family code', text=${model.pedigree.familyCode})"
+            ></div>
+            <th:block
+              th:unless="${#strings.isEmpty(model.pedigree.parent1Name)}"
+            >
+              <div
+                th:replace="fragments/row::row(label='Parent accessions', content=~{::#parent-accessions})"
+              >
+                <div id="parent-accessions">
+                  <th:block th:if="${model.pedigree.parent1DbId}">
+                    <div
+                      th:replace="fragments/row::row(label=${model.pedigree.parent1Type}, content=~{::#parent1-link})"
+                    >
+                      <a
+                        id="parent1-link"
+                        th:href="@{/germplasms/{germplasmId}(germplasmId=${model.pedigree.parent1DbId})}"
+                        th:text="${model.pedigree.parent1Name}"
+                      ></a>
+                    </div>
+                  </th:block>
+
+                  <th:block th:if="${model.pedigree.parent2DbId}">
+                    <div
+                      th:replace="fragments/row::row(label=${model.pedigree.parent2Type}, content=~{::#parent2-link})"
+                    >
+                      <a
+                        id="parent2-link"
+                        th:href="@{/germplasms/{germplasmId}(germplasmId=${model.pedigree.parent2DbId})}"
+                        th:text="${model.pedigree.parent2Name}"
+                      ></a>
+                    </div>
                   </th:block>
                 </div>
               </div>
             </th:block>
-          </div>
-        </div>
-      </th:block>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.population)}">
-    <h2>Population</h2>
-    <div class="f-card-body">
-      <th:block th:each="population : ${model.germplasm.population}">
-
-        <th:block th:if="${population.germplasmRef != null}">
-          <th:block th:unless="${#strings.isEmpty(population.germplasmRef.pui)}">
-            <div th:replace="fragments/row::row(label=${#faidare.collPopTitle(population)}, content=~{::.population-1})">
-              <div class="population-1">
-                <a th:if="${population.germplasmRef.pui != model.germplasm.germplasmPUI}"
-                   th:href="@{/germplasms(pui=${population.germplasmRef.pui})}"
-                   th:text="${population.germplasmRef.name}"></a>
-                <span th:if="${population.germplasmRef.pui == model.germplasm.germplasmPUI}"
-                      th:text="${population.germplasmRef.name}"></span>
-                is composed by <span th:text="${population.germplasmCount}"></span> accession(s)
-                <!-- TODO there was a link pointing at a search here -->
+
+            <th:block th:unless="${#lists.isEmpty(model.pedigree.siblings)}">
+              <div
+                th:replace="fragments/row::row(label='Sibling accessions', content=~{::#sibling-accessions})"
+              >
+                <div id="sibling-accessions" class="content-overflow">
+                  <a
+                    th:each="sibling : ${model.pedigree.siblings}"
+                    th:href="@{/germplasms/{germplasmId}(germplasmId=${sibling.germplasmDbId})}"
+                    th:text="${sibling.defaultDisplayName}"
+                  ></a>
+                </div>
+              </div>
+            </th:block>
+          </th:block>
+
+          <th:block th:unless="${#lists.isEmpty(model.germplasm.children)}">
+            <div
+              th:replace="fragments/row::row(label='Descendants', content=~{::#descendants})"
+            >
+              <div
+                id="descendants"
+                class="content-overflow content-overflow-big"
+              >
+                <th:block th:each="child : ${model.germplasm.children}">
+                  <div
+                    th:replace="fragments/row::row(label=${#strings.isEmpty(child.secondParentName) ? ('children of ' + child.firstParentName) : ('children of ' + child.firstParentName + ' x ' + child.secondParentName) }, content=~{::.descendant-child})"
+                  >
+                    <div class="descendant-child">
+                      <th:block
+                        th:each="sibling, siblingIterStat : ${child.sibblings}"
+                      >
+                        <a
+                          th:href="@{/germplasms(pui=${sibling.pui})}"
+                          th:text="${sibling.name}"
+                        ></a
+                        ><th:block th:unless="${siblingIterStat.last}"
+                          >,
+                        </th:block>
+                      </th:block>
+                    </div>
+                  </div>
+                </th:block>
               </div>
             </div>
           </th:block>
-        </th:block>
-
-        <th:block th:if="${population.germplasmRef == null}">
-          <div th:replace="fragments/row::text-row(label=${#faidare.collPopTitle(population)}, text=${population.germplasmCount + ' accession(s)'})"></div>
-          <!-- TODO there was a link pointing at a search here -->
-        </th:block>
-      </th:block>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.collection)}">
-    <h2>Collection</h2>
-    <div class="f-card-body">
-      <th:block th:each="collection : ${model.germplasm.collection}">
-        <div th:replace="fragments/row::text-row(label=${#faidare.collPopTitle(collection)}, text=${collection.germplasmCount + ' accession(s)'})"></div>
-        <!-- TODO there was a link pointing at a search here -->
-      </th:block>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.panel)}">
-    <h2>Panel</h2>
-    <div class="f-card-body">
-      <th:block th:each="panel : ${model.germplasm.panel}">
-        <div th:replace="fragments/row::text-row(label=${#faidare.collPopTitleWithoutUnderscores(panel)}, text=${panel.germplasmCount + ' accession(s)'})"></div>
-        <!-- TODO there was a link pointing at a search here -->
-      </th:block>
-    </div>
-  </div>
-
-  <div th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"></div>
-</main>
-
-<script th:inline="javascript">
-  faidare.initializePopovers();
-  faidare.initializeMap({
-    contextPath: [[${#request.getContextPath()}]],
-    locations: /* TODO [[${model.mapLocations}]]*/ []
-  });
-</script>
-</body>
+        </div>
+      </div>
+
+      <div
+        class="f-card"
+        th:unless="${#lists.isEmpty(model.germplasm.population)}"
+      >
+        <h2>Population</h2>
+        <div class="f-card-body">
+          <th:block th:each="population : ${model.germplasm.population}">
+            <th:block th:if="${population.germplasmRef != null}">
+              <th:block
+                th:unless="${#strings.isEmpty(population.germplasmRef.pui)}"
+              >
+                <div
+                  th:replace="fragments/row::row(label=${#faidare.collPopTitle(population)}, content=~{::.population-1})"
+                >
+                  <div class="population-1">
+                    <a
+                      th:if="${population.germplasmRef.pui != model.germplasm.germplasmPUI}"
+                      th:href="@{/germplasms(pui=${population.germplasmRef.pui})}"
+                      th:text="${population.germplasmRef.name}"
+                    ></a>
+                    <span
+                      th:if="${population.germplasmRef.pui == model.germplasm.germplasmPUI}"
+                      th:text="${population.germplasmRef.name}"
+                    ></span>
+                    is composed by
+                    <span th:text="${population.germplasmCount}"></span>
+                    accession(s)
+                    <!-- TODO there was a link pointing at a search here -->
+                  </div>
+                </div>
+              </th:block>
+            </th:block>
+
+            <th:block th:if="${population.germplasmRef == null}">
+              <div
+                th:replace="fragments/row::text-row(label=${#faidare.collPopTitle(population)}, text=${population.germplasmCount + ' accession(s)'})"
+              ></div>
+              <!-- TODO there was a link pointing at a search here -->
+            </th:block>
+          </th:block>
+        </div>
+      </div>
+
+      <div
+        class="f-card"
+        th:unless="${#lists.isEmpty(model.germplasm.collection)}"
+      >
+        <h2>Collection</h2>
+        <div class="f-card-body">
+          <th:block th:each="collection : ${model.germplasm.collection}">
+            <div
+              th:replace="fragments/row::text-row(label=${#faidare.collPopTitle(collection)}, text=${collection.germplasmCount + ' accession(s)'})"
+            ></div>
+            <!-- TODO there was a link pointing at a search here -->
+          </th:block>
+        </div>
+      </div>
+
+      <div class="f-card" th:unless="${#lists.isEmpty(model.germplasm.panel)}">
+        <h2>Panel</h2>
+        <div class="f-card-body">
+          <th:block th:each="panel : ${model.germplasm.panel}">
+            <div
+              th:replace="fragments/row::text-row(label=${#faidare.collPopTitleWithoutUnderscores(panel)}, text=${panel.germplasmCount + ' accession(s)'})"
+            ></div>
+            <!-- TODO there was a link pointing at a search here -->
+          </th:block>
+        </div>
+      </div>
+
+      <div
+        th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"
+      ></div>
+    </main>
+
+    <script th:inline="javascript">
+      faidare.initializePopovers();
+      faidare.initializeMap({
+        contextPath: [[${#request.getContextPath()}]],
+        locations: /* TODO [[${model.mapLocations}]]*/ []
+      });
+    </script>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/index.html b/backend/src/main/resources/templates/index.html
index 49f401b0284647d171f51ec5643987e27d836a05..2130bf93d95928eeb7711d61e3cecfbe42a37742 100644
--- a/backend/src/main/resources/templates/index.html
+++ b/backend/src/main/resources/templates/index.html
@@ -2,16 +2,16 @@
 
 <html
   xmlns:th="http://www.thymeleaf.org"
-  th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main})}"
+  th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main}, script=~{})}"
 >
-<head>
-  <title>Faidare</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-</head>
+  <head>
+    <title>Faidare</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
 
-<body>
-<main>
-  <h1>Welcome to Faidare</h1>
-</main>
-</body>
+  <body>
+    <main>
+      <h1>Welcome to Faidare</h1>
+    </main>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/layout/main.html b/backend/src/main/resources/templates/layout/main.html
index 22c36a5efb6dfe860a20b41f520ba826d160e96b..8097134fddb189f6c038ba29aceb29f14eee972f 100644
--- a/backend/src/main/resources/templates/layout/main.html
+++ b/backend/src/main/resources/templates/layout/main.html
@@ -1,21 +1,29 @@
 <!DOCTYPE html>
-<html lang="fr" th:fragment="layout (title, content, script)" xmlns:th="http://www.thymeleaf.org">
+<html
+  lang="fr"
+  th:fragment="layout (title, content, script)"
+  xmlns:th="http://www.thymeleaf.org"
+>
   <head>
     <title th:replace="${title}">Layout Title</title>
 
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
     <meta content="width=device-width, initial-scale=1" name="viewport" />
 
-    <link th:href="@{/assets/style.css}" rel="stylesheet">
+    <link th:href="@{/assets/style.css}" rel="stylesheet" />
 
-    <link rel="shortcut icon" th:href="@{/static/assets/images/favicon.ico}" type="image/x-icon" />
+    <link
+      rel="shortcut icon"
+      th:href="@{/static/assets/images/favicon.ico}"
+      type="image/x-icon"
+    />
   </head>
 
   <body>
     <nav class="navbar navbar-expand-lg navbar-light bg-light">
       <div class="container">
         <span class="navbar-brand py-0">
-          <img th:src="@{/assets/images/logo.png}" style="height: 40px"/>
+          <img th:src="@{/assets/images/logo.png}" style="height: 40px" />
         </span>
       </div>
     </nav>
diff --git a/backend/src/main/resources/templates/site.html b/backend/src/main/resources/templates/site.html
index c3bd01b26333e960cd766f7ad3418605e2057483..b923a2e7346b9a9718b0c8bea31781c23b0bc78e 100644
--- a/backend/src/main/resources/templates/site.html
+++ b/backend/src/main/resources/templates/site.html
@@ -4,75 +4,130 @@
   xmlns:th="http://www.thymeleaf.org"
   th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main}, script=~{::script})}"
 >
-<head>
-  <title>Site <th:block th:text="${model.site.locationName}" /></title>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-</head>
+  <head>
+    <title>Site <th:block th:text="${model.site.locationName}" /></title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
 
-<body>
-<main>
-  <h1>Site <th:block th:text="${model.site.locationName}" /></h1>
+  <body>
+    <main>
+      <h1>Site <th:block th:text="${model.site.locationName}" /></h1>
 
-  <div th:replace="fragments/map::map"></div>
+      <div th:replace="fragments/map::map"></div>
 
-  <div class="f-card mt-4">
-    <h2>Details</h2>
-    <div class="f-card-body">
-      <th:block th:if="${model.site.uri != null && !model.site.uri.startsWith('urn:')}">
-        <div th:replace="fragments/row::text-row(label='Permanent unique identifier', text=${model.site.uri})"></div>
-      </th:block>
+      <div class="f-card mt-4">
+        <h2>Details</h2>
+        <div class="f-card-body">
+          <th:block
+            th:if="${model.site.uri != null && !model.site.uri.startsWith('urn:')}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Permanent unique identifier', text=${model.site.uri})"
+            ></div>
+          </th:block>
 
-      <div th:replace="fragments/source::source(source=${model.source}, url=${model.site.url}, entityType='site')"></div>
+          <div
+            th:replace="fragments/source::source(source=${model.source}, url=${model.site.url}, entityType='site')"
+          ></div>
 
-      <div th:replace="fragments/row::text-row(label='Abbreviation', text=${model.site.abbreviation})"></div>
-      <div th:replace="fragments/row::text-row(label='Type', text=${model.site.locationType})"></div>
-      <div th:replace="fragments/row::text-row(label='Status', text=${model.siteStatus})"></div>
-      <div th:replace="fragments/row::text-row(label='Institution/Landowner', text=${model.site.instituteName})"></div>
-      <div th:replace="fragments/row::text-row(label='Institution address', text=${model.site.instituteAddress})"></div>
-      <div th:replace="fragments/row::text-row(label='Coordinates precision', text=${model.coordinatesPrecision})"></div>
-      <th:block th:if="${model.site.latitude}">
-        <div th:replace="fragments/row::text-row(label='Latitude', text=${#coordinates.formatLatitude(model.site.latitude)})"></div>
-      </th:block>
-      <th:block th:if="${model.site.longitude}">
-        <div th:replace="fragments/row::text-row(label='Longitude', text=${#coordinates.formatLongitude(model.site.longitude)})"></div>
-      </th:block>
-      <div th:replace="fragments/row::text-row(label='Geographical location', text=${model.geographicalLocation})"></div>
-      <th:block th:if="${model.site.countryName != null && model.geographicalLocation == null}">
-        <div th:replace="fragments/row::text-row(label='Country name', text=${model.site.countryName})"></div>
-      </th:block>
+          <div
+            th:replace="fragments/row::text-row(label='Abbreviation', text=${model.site.abbreviation})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Type', text=${model.site.locationType})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Status', text=${model.siteStatus})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Institution/Landowner', text=${model.site.instituteName})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Institution address', text=${model.site.instituteAddress})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Coordinates precision', text=${model.coordinatesPrecision})"
+          ></div>
+          <th:block th:if="${model.site.latitude}">
+            <div
+              th:replace="fragments/row::text-row(label='Latitude', text=${#coordinates.formatLatitude(model.site.latitude)})"
+            ></div>
+          </th:block>
+          <th:block th:if="${model.site.longitude}">
+            <div
+              th:replace="fragments/row::text-row(label='Longitude', text=${#coordinates.formatLongitude(model.site.longitude)})"
+            ></div>
+          </th:block>
+          <div
+            th:replace="fragments/row::text-row(label='Geographical location', text=${model.geographicalLocation})"
+          ></div>
+          <th:block
+            th:if="${model.site.countryName != null && model.geographicalLocation == null}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Country name', text=${model.site.countryName})"
+            ></div>
+          </th:block>
 
-      <th:block th:if="${model.site.countryCode != null && model.geographicalLocation == null}">
-        <div th:replace="fragments/row::text-row(label='Country code', text=${model.site.countryName})"></div>
-      </th:block>
+          <th:block
+            th:if="${model.site.countryCode != null && model.geographicalLocation == null}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Country code', text=${model.site.countryName})"
+            ></div>
+          </th:block>
 
-      <div th:replace="fragments/row::text-row(label='Altitude', text=${model.site.altitude})"></div>
-      <div th:replace="fragments/row::text-row(label='Slope', text=${model.slope})"></div>
-      <div th:replace="fragments/row::text-row(label='Exposure', text=${model.exposure})"></div>
-      <div th:replace="fragments/row::text-row(label='Topography', text=${model.topography})"></div>
-      <div th:replace="fragments/row::text-row(label='Environment type', text=${model.environmentType})"></div>
-      <div th:replace="fragments/row::text-row(label='Distance to city', text=${model.distanceToCity})"></div>
-      <div th:replace="fragments/row::text-row(label='Direction from city', text=${model.directionFromCity})"></div>
-      <div th:replace="fragments/row::text-row(label='Comment', text=${model.comment})"></div>
-    </div>
-  </div>
+          <div
+            th:replace="fragments/row::text-row(label='Altitude', text=${model.site.altitude})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Slope', text=${model.slope})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Exposure', text=${model.exposure})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Topography', text=${model.topography})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Environment type', text=${model.environmentType})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Distance to city', text=${model.distanceToCity})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Direction from city', text=${model.directionFromCity})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Comment', text=${model.comment})"
+          ></div>
+        </div>
+      </div>
 
-  <div class="f-card" th:unless="${#lists.isEmpty(model.additionalInfoProperties)}">
-    <h2>Additional info</h2>
-    <div class="f-card-body">
-      <th:block th:each="prop : ${model.additionalInfoProperties}">
-        <div th:replace="fragments/row::text-row(label=${prop.key}, text=${prop.value})"></div>
-      </th:block>
-    </div>
-  </div>
+      <div
+        class="f-card"
+        th:unless="${#lists.isEmpty(model.additionalInfoProperties)}"
+      >
+        <h2>Additional info</h2>
+        <div class="f-card-body">
+          <th:block th:each="prop : ${model.additionalInfoProperties}">
+            <div
+              th:replace="fragments/row::text-row(label=${prop.key}, text=${prop.value})"
+            ></div>
+          </th:block>
+        </div>
+      </div>
 
-  <div th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"></div>
-</main>
+      <div
+        th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"
+      ></div>
+    </main>
 
-<script th:inline="javascript">
-  faidare.initializeMap({
-    contextPath: [[${#request.getContextPath()}]],
-    locations: /* TODO [[${model.mapLocations}]]*/ []
-  });
-</script>
-</body>
+    <script th:inline="javascript">
+      faidare.initializeMap({
+        contextPath: [[${#request.getContextPath()}]],
+        locations: /* TODO [[${model.mapLocations}]]*/ []
+      });
+    </script>
+  </body>
 </html>
diff --git a/backend/src/main/resources/templates/study.html b/backend/src/main/resources/templates/study.html
index 225b6cc8a6911b3c934157e91aab55b462e7a756..415e21edb6f466863d1de5c7efaba35d458e8855 100644
--- a/backend/src/main/resources/templates/study.html
+++ b/backend/src/main/resources/templates/study.html
@@ -4,200 +4,294 @@
   xmlns:th="http://www.thymeleaf.org"
   th:replace="~{layout/main :: layout(title=~{::title}, content=~{::main}, script=~{::script})}"
 >
-<head>
-  <title>Study <th:block th:text="${model.study.studyType}" />: <th:block th:text="${model.study.studyName}" /></title>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-</head>
-
-<body>
-<main>
-  <h1>Study <th:block th:text="${model.study.studyType}" />: <th:block th:text="${model.study.studyName}" /></h1>
-
-  <div th:replace="fragments/map::map"></div>
-
-  <div class="f-card mt-4">
-    <h2>Identification</h2>
-    <div class="f-card-body">
-      <div th:replace="fragments/row::text-row(label='Name', text=${model.study.studyName})"></div>
-      <div th:replace="fragments/row::text-row(label='Identifier', text=${model.study.studyDbId})"></div>
-
-      <div th:replace="fragments/source::source(source=${model.source}, url=${model.study.url}, entityType='study')"></div>
-
-      <div th:replace="fragments/row::text-row(label='Project name', text=${model.study.programName})"></div>
-      <div th:replace="fragments/row::text-row(label='Description', text=${model.study.studyDescription})"></div>
-      <th:block th:if="${model.study.active != null}">
-        <div th:replace="fragments/row::text-row(label='Active', text=${model.study.active ? 'Yes' : 'No'})"></div>
-      </th:block>
-
-      <th:block th:unless="${#lists.isEmpty(model.study.seasons)}">
-        <div th:replace="fragments/row::text-row(label='Seasons', text=${#strings.listJoin(model.study.seasons, ',')})"></div>
-      </th:block>
-      <th:block th:if="${model.study.startDate != null && model.study.endDate != null}">
-        <div th:replace="fragments/row::text-row(label='Date', text=${'From ' + #dates.format(model.study.startDate, 'yyyy-MM-dd') + ' to ' + #dates.format(model.study.endDate, 'yyyy-MM-dd') })"></div>
-      </th:block>
-      <th:block th:if="${model.study.startDate != null && model.study.endDate == null}">
-        <div th:replace="fragments/row::text-row(label='Date', text=${'Started on ' + #dates.format(model.study.startDate, 'yyyy-MM-dd')})"></div>
-      </th:block>
-
-      <th:block th:if="${model.study.locationDbId}">
-        <div th:replace="fragments/row::row(label='Location name', content=~{::#location})">
-          <a id="location" th:href="@{/sites/{siteId}(siteId=${model.study.locationDbId})}" th:text="${model.study.locationName}"></a>
+  <head>
+    <title>
+      Study <th:block th:text="${model.study.studyType}" />: <th:block
+      th:text="${model.study.studyName}" />
+    </title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+
+  <body>
+    <main>
+      <h1>
+        Study <th:block th:text="${model.study.studyType}" />:
+        <th:block th:text="${model.study.studyName}" />
+      </h1>
+
+      <div th:replace="fragments/map::map"></div>
+
+      <div class="f-card mt-4">
+        <h2>Identification</h2>
+        <div class="f-card-body">
+          <div
+            th:replace="fragments/row::text-row(label='Name', text=${model.study.studyName})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Identifier', text=${model.study.studyDbId})"
+          ></div>
+
+          <div
+            th:replace="fragments/source::source(source=${model.source}, url=${model.study.url}, entityType='study')"
+          ></div>
+
+          <div
+            th:replace="fragments/row::text-row(label='Project name', text=${model.study.programName})"
+          ></div>
+          <div
+            th:replace="fragments/row::text-row(label='Description', text=${model.study.studyDescription})"
+          ></div>
+          <th:block th:if="${model.study.active != null}">
+            <div
+              th:replace="fragments/row::text-row(label='Active', text=${model.study.active ? 'Yes' : 'No'})"
+            ></div>
+          </th:block>
+
+          <th:block th:unless="${#lists.isEmpty(model.study.seasons)}">
+            <div
+              th:replace="fragments/row::text-row(label='Seasons', text=${#strings.listJoin(model.study.seasons, ',')})"
+            ></div>
+          </th:block>
+          <th:block
+            th:if="${model.study.startDate != null && model.study.endDate != null}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Date', text=${'From ' + #dates.format(model.study.startDate, 'yyyy-MM-dd') + ' to ' + #dates.format(model.study.endDate, 'yyyy-MM-dd') })"
+            ></div>
+          </th:block>
+          <th:block
+            th:if="${model.study.startDate != null && model.study.endDate == null}"
+          >
+            <div
+              th:replace="fragments/row::text-row(label='Date', text=${'Started on ' + #dates.format(model.study.startDate, 'yyyy-MM-dd')})"
+            ></div>
+          </th:block>
+
+          <th:block th:if="${model.study.locationDbId}">
+            <div
+              th:replace="fragments/row::row(label='Location name', content=~{::#location})"
+            >
+              <a
+                id="location"
+                th:href="@{/sites/{siteId}(siteId=${model.study.locationDbId})}"
+                th:text="${model.study.locationName}"
+              ></a>
+            </div>
+          </th:block>
+
+          <th:block th:unless="${#lists.isEmpty(model.study.dataLinks)}">
+            <div
+              th:replace="fragments/row::row(label='Data files', content=~{::#data-files})"
+            >
+              <ul id="data-files" class="list-unstyled">
+                <li th:each="dataLink : ${model.study.dataLinks}">
+                  <a
+                    target="_blank"
+                    th:href="${dataLink.url}"
+                    th:text="${dataLink.name}"
+                  ></a>
+                </li>
+              </ul>
+            </div>
+          </th:block>
         </div>
-      </th:block>
-
-      <th:block th:unless="${#lists.isEmpty(model.study.dataLinks)}">
-        <div th:replace="fragments/row::row(label='Data files', content=~{::#data-files})">
-          <ul id="data-files" class="list-unstyled">
-            <li th:each="dataLink : ${model.study.dataLinks}">
-              <a target="_blank" th:href="${dataLink.url}" th:text="${dataLink.name}"></a>
-            </li>
-          </ul>
+      </div>
+
+      <div class="f-card" th:unles="${#lists.isEmpty(model.germplasms)}">
+        <h2>Genotype</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container scroll-table-container-big">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Accession number</th>
+                  <th scope="col">Name</th>
+                  <th scope="col">Taxon</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="row : ${model.germplasms}">
+                  <td>
+                    <a
+                      th:href="@{/germplasms/{germplasmId}(germplasmId=${row.germplasmDbId})}"
+                      th:text="${row.accessionNumber}"
+                    ></a>
+                  </td>
+                  <td th:text="${row.germplasmName}"></td>
+                  <td
+                    th:text="${(row.genus == null ? '' : row.genus) + ' ' + (row.species == null ? '' : row.species)+ ' ' + (row.subtaxa == null ? '' : row.subtaxa) }"
+                  ></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
-      </th:block>
-    </div>
-  </div>
-
-  <div class="f-card" th:unles="${#lists.isEmpty(model.germplasms)}">
-    <h2>Genotype</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container scroll-table-container-big">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Accession number</th>
-              <th scope="col">Name</th>
-              <th scope="col">Taxon</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row : ${model.germplasms}">
-              <td>
-                <a th:href="@{/germplasms/{germplasmId}(germplasmId=${row.germplasmDbId})}" th:text="${row.accessionNumber}"></a>
-              </td>
-              <td th:text="${row.germplasmName}"></td>
-              <td th:text="${(row.genus == null ? '' : row.genus) + ' ' + (row.species == null ? '' : row.species)+ ' ' + (row.subtaxa == null ? '' : row.subtaxa) }"></td>
-            </tr>
-          </tbody>
-        </table>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.variables)}">
-    <h2>Variables</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Variable ID</th>
-              <th scope="col">Variable short name</th>
-              <th scope="col">Variable long name</th>
-              <th scope="col">Ontology name</th>
-              <th scope="col">Trait description</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row : ${model.variables}">
-              <td>
-                <a th:unless="${#strings.isEmpty(row.documentationURL)}" th:href="${row.documentationURL}" th:text="${row.observationVariableDbId}" target="_blank" ></a>
-                <span th:if="${#strings.isEmpty(row.documentationURL)}" th:text="${row.observationVariableDbId}"></span>
-              </td>
-              <td th:text="${row.name}"></td>
-              <td th:text="${#lists.isEmpty(row.synonyms) ? '' : row.synonyms[0]}"></td>
-              <td th:text="${row.ontologyName}"></td>
-              <td th:text="${row.trait.description}"></td>
-            </tr>
-          </tbody>
-        </table>
+
+      <div class="f-card" th:unless="${#lists.isEmpty(model.variables)}">
+        <h2>Variables</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Variable ID</th>
+                  <th scope="col">Variable short name</th>
+                  <th scope="col">Variable long name</th>
+                  <th scope="col">Ontology name</th>
+                  <th scope="col">Trait description</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="row : ${model.variables}">
+                  <td>
+                    <a
+                      th:unless="${#strings.isEmpty(row.documentationURL)}"
+                      th:href="${row.documentationURL}"
+                      th:text="${row.observationVariableDbId}"
+                      target="_blank"
+                    ></a>
+                    <span
+                      th:if="${#strings.isEmpty(row.documentationURL)}"
+                      th:text="${row.observationVariableDbId}"
+                    ></span>
+                  </td>
+                  <td th:text="${row.name}"></td>
+                  <td
+                    th:text="${#lists.isEmpty(row.synonyms) ? '' : row.synonyms[0]}"
+                  ></td>
+                  <td th:text="${row.ontologyName}"></td>
+                  <td th:text="${row.trait.description}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.trials)}">
-    <h2>Data Set</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container scroll-table-container-big">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Name</th>
-              <th scope="col">Type</th>
-              <th scope="col">Linked studies identifier</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row : ${model.trials}">
-              <td>
-                <a th:unless="${#strings.isEmpty(row.documentationURL)}" th:href="${row.documentationURL}" th:text="${row.trialName}" target="_blank" ></a>
-                <span th:if="${#strings.isEmpty(row.documentationURL)}" th:text="${row.trialName}"></span>
-              </td>
-              <td th:text="${row.trialType}"></td>
-              <td style="width: 60%">
-                <th:block th:each="trialStudy, iterStat : ${row.studies}"
-                          th:if="${trialStudy.studyDbId != model.study.studyDbId}">
-                  <a th:href="@{/studies/{studyId}(studyId=${trialStudy.studyDbId})}"
-                     th:text="${trialStudy.studyName.trim()}">
-                  </a><th:block th:if="${iterStat.last}">; </th:block>
-                </th:block>
-              </td>
-            </tr>
-          </tbody>
-        </table>
+
+      <div class="f-card" th:unless="${#lists.isEmpty(model.trials)}">
+        <h2>Data Set</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container scroll-table-container-big">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Name</th>
+                  <th scope="col">Type</th>
+                  <th scope="col">Linked studies identifier</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="row : ${model.trials}">
+                  <td>
+                    <a
+                      th:unless="${#strings.isEmpty(row.documentationURL)}"
+                      th:href="${row.documentationURL}"
+                      th:text="${row.trialName}"
+                      target="_blank"
+                    ></a>
+                    <span
+                      th:if="${#strings.isEmpty(row.documentationURL)}"
+                      th:text="${row.trialName}"
+                    ></span>
+                  </td>
+                  <td th:text="${row.trialType}"></td>
+                  <td style="width: 60%">
+                    <th:block
+                      th:each="trialStudy, iterStat : ${row.studies}"
+                      th:if="${trialStudy.studyDbId != model.study.studyDbId}"
+                    >
+                      <a
+                        th:href="@{/studies/{studyId}(studyId=${trialStudy.studyDbId})}"
+                        th:text="${trialStudy.studyName.trim()}"
+                      >
+                      </a
+                      ><th:block th:if="${iterStat.last}">; </th:block>
+                    </th:block>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.study.contacts)}">
-    <h2>Contact</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container">
-        <table class="table table-sm table-striped table-sticky table-responsive-sm">
-          <thead>
-            <tr>
-              <th scope="col">Role</th>
-              <th scope="col">Name</th>
-              <th scope="col">Email</th>
-              <th scope="col">Institution</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr th:each="row : ${model.study.contacts}">
-              <td th:text="${row.type}"></td>
-              <td th:text="${row.name}"></td>
-              <td th:text="${row.email}"></td>
-              <td th:text="${row.institutionName}"></td>
-            </tr>
-          </tbody>
-        </table>
+
+      <div class="f-card" th:unless="${#lists.isEmpty(model.study.contacts)}">
+        <h2>Contact</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container">
+            <table
+              class="
+                table table-sm table-striped table-sticky table-responsive-sm
+              "
+            >
+              <thead>
+                <tr>
+                  <th scope="col">Role</th>
+                  <th scope="col">Name</th>
+                  <th scope="col">Email</th>
+                  <th scope="col">Institution</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="row : ${model.study.contacts}">
+                  <td th:text="${row.type}"></td>
+                  <td th:text="${row.name}"></td>
+                  <td th:text="${row.email}"></td>
+                  <td th:text="${row.institutionName}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
       </div>
-    </div>
-  </div>
-
-  <div class="f-card" th:unless="${#lists.isEmpty(model.additionalInfoProperties)}">
-    <h2>Additional information</h2>
-    <div class="f-card-body">
-      <div class="scroll-table-container">
-        <table class="table table-sm">
-          <tbody>
-            <tr th:each="row : ${model.additionalInfoProperties}">
-              <th class="label" style="width: 33.33%" th:text="${row.key}" scope="row"></th>
-              <td th:text="${row.value}"></td>
-            </tr>
-          </tbody>
-        </table>
+
+      <div
+        class="f-card"
+        th:unless="${#lists.isEmpty(model.additionalInfoProperties)}"
+      >
+        <h2>Additional information</h2>
+        <div class="f-card-body">
+          <div class="scroll-table-container">
+            <table class="table table-sm">
+              <tbody>
+                <tr th:each="row : ${model.additionalInfoProperties}">
+                  <th
+                    class="label"
+                    style="width: 33.33%"
+                    th:text="${row.key}"
+                    scope="row"
+                  ></th>
+                  <td th:text="${row.value}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
       </div>
-    </div>
-  </div>
-
-  <div th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"></div>
-</main>
-
-<script th:inline="javascript">
-  faidare.initializeMap({
-    contextPath: [[${#request.getContextPath()}]],
-    locations: /* TODO [[${model.mapLocations}]]*/ []
-  });
-</script>
-</body>
+
+      <div
+        th:replace="fragments/xrefs::xrefs(crossReferences=${model.crossReferences})"
+      ></div>
+    </main>
+
+    <script th:inline="javascript">
+      faidare.initializeMap({
+        contextPath: [[${#request.getContextPath()}]],
+        locations: /* TODO [[${model.mapLocations}]]*/ []
+      });
+    </script>
+  </body>
 </html>
diff --git a/web/package.json b/web/package.json
index 175c1ad3659043ec411679d5763c2827dfd2d158..3efc776bb3c7da2b2537fa6e7aaaebd295d57509 100644
--- a/web/package.json
+++ b/web/package.json
@@ -8,7 +8,9 @@
     "build:prod": "webpack --mode production",
     "watch": "webpack --mode development --watch",
     "watch:prod": "webpack --mode production --watch",
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "format": "prettier --write \"src/**/*.+(ts|js|json|html|scss)\"",
+    "format:backend-templates": "prettier --write ../backend/src/main/resources/templates"
   },
   "author": "",
   "license": "MIT",
@@ -20,16 +22,29 @@
   "devDependencies": {
     "@types/bootstrap": "5.1.2",
     "@types/leaflet.markercluster": "1.4.5",
+    "autoprefixer": "10.3.3",
     "clean-webpack-plugin": "4.0.0",
     "css-loader": "6.2.0",
     "css-minimizer-webpack-plugin": "3.0.2",
     "leaflet.markercluster": "1.5.1",
     "mini-css-extract-plugin": "2.2.2",
+    "postcss": "8.3.6",
+    "postcss-loader": "6.1.1",
+    "prettier": "2.3.2",
     "sass": "1.39.0",
     "sass-loader": "12.1.0",
     "ts-loader": "9.2.5",
     "typescript": "4.4.2",
     "webpack": "5.51.1",
     "webpack-cli": "4.8.0"
+  },
+  "browserslist": [
+    "defaults"
+  ],
+  "prettier": {
+    "printWidth": 140,
+    "singleQuote": true,
+    "arrowParens": "avoid",
+    "trailingComma": "none"
   }
 }
diff --git a/web/src/bootstrap/popovers.ts b/web/src/bootstrap/popovers.ts
index 83b703de77469e545f992f1ad2396706db3621bd..b19e5e75dedc37274b3a863bd1f18e448af43bdf 100644
--- a/web/src/bootstrap/popovers.ts
+++ b/web/src/bootstrap/popovers.ts
@@ -1,7 +1,7 @@
 import { Popover } from 'bootstrap';
 
 export function initializePopovers() {
-    const popoverTriggerList: Array<HTMLElement> = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
+    const popoverTriggerList: Array<HTMLElement> = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
     popoverTriggerList.forEach(popoverTriggerEl => {
         const options: Partial<Popover.Options> = {};
         const contentSelector = popoverTriggerEl.dataset.bsElement;
diff --git a/web/src/index.ts b/web/src/index.ts
index c906b28eb7d5cb4d78785bd6ea5bd01ff4b92fd2..060cf75d6a996a175e6d4b29c8fa1b067cf0610e 100644
--- a/web/src/index.ts
+++ b/web/src/index.ts
@@ -4,4 +4,4 @@ import { initializeMap } from './map/map';
 (window as any).faidare = {
     initializePopovers,
     initializeMap
-}
+};
diff --git a/web/src/map/map.ts b/web/src/map/map.ts
index 9fc2a96004bca2ea53eaf342371595c7ea3f5b29..a61002a445bc900a1aa60a97125df9a67c98c676 100644
--- a/web/src/map/map.ts
+++ b/web/src/map/map.ts
@@ -36,11 +36,12 @@ export function initializeMap(options: MapOptions) {
     }
 
     const mapContainerElement = document.querySelector('#map-container');
-    mapContainerElement!.classList.remove("d-none");
+    mapContainerElement!.classList.remove('d-none');
     const mapElement = document.querySelector('#map') as HTMLElement;
     const map = L.map(mapElement);
     L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
-        attribution: 'Tiles &copy; Esri &mdash; Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, ' +
+        attribution:
+            'Tiles &copy; Esri &mdash; Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, ' +
             'Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
     }).addTo(map);
 
@@ -52,7 +53,7 @@ export function initializeMap(options: MapOptions) {
     for (const location of options.locations) {
         const icon = L.icon({
             iconUrl: markerIconUrl(options.contextPath, location),
-            iconAnchor: [12, 41], // point of the icon which will correspond to marker's location
+            iconAnchor: [12, 41] // point of the icon which will correspond to marker's location
         });
 
         const popupElement = document.createElement('div');
@@ -74,10 +75,7 @@ export function initializeMap(options: MapOptions) {
         linkElement.href = `${options.contextPath}/sites/${location.locationDbId}`;
         popupElement.appendChild(linkElement);
 
-        const marker = L.marker(
-            [location.latitude, location.longitude],
-            { icon: icon }
-        );
+        const marker = L.marker([location.latitude, location.longitude], { icon: icon });
         markers.addLayer(marker.bindPopup(popupElement));
         mapMarkers.push(marker);
     }
diff --git a/web/src/style/style.scss b/web/src/style/style.scss
index 42aa2bb3a883a5991772d6b9cbe4eddb0eb7d267..806a0d4bec25eee291369e323e27f2c77d0d3fe1 100644
--- a/web/src/style/style.scss
+++ b/web/src/style/style.scss
@@ -12,7 +12,7 @@ $table-group-separator-color: $table-border-color;
 @import '~leaflet.markercluster/dist/MarkerCluster.css';
 @import '~leaflet.markercluster/dist/MarkerCluster.Default.css';
 
-a[role=button] {
+a[role='button'] {
   color: $link-color !important;
 }
 
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 1c7b808029f88e4107f429df70a5fb93e383a29b..a8ffae0a633e83b0de4095643e2e4ece795023c5 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -40,7 +40,15 @@ module.exports = (env, argv) => ({
                 use: [
                     MiniCssExtractPlugin.loader,
                     'css-loader',
-                    'sass-loader'
+                    'sass-loader',
+                    {
+                        loader: "postcss-loader",
+                        options: {
+                            postcssOptions: {
+                                plugins: ["autoprefixer",],
+                            },
+                        },
+                    }
                 ],
             },
         ],
diff --git a/web/yarn.lock b/web/yarn.lock
index 290a3152e5858a22535126431a6e723c3bfd0602..006da818ecb29a7986970a0c18274d4648db5611 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -2,6 +2,27 @@
 # yarn lockfile v1
 
 
+"@babel/code-frame@^7.0.0":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
+  integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
+  dependencies:
+    "@babel/highlight" "^7.14.5"
+
+"@babel/helper-validator-identifier@^7.14.5":
+  version "7.14.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
+  integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
+
+"@babel/highlight@^7.14.5":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
+  integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.14.5"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
 "@discoveryjs/json-ext@^0.5.0":
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
@@ -102,6 +123,11 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.2.tgz#0465a39b5456b61a04d98bd5545f8b34be340cb7"
   integrity sha512-TbG4TOx9hng8FKxaVrCisdaxKxqEwJ3zwHoCWXZ0Jw6mnvTInpaB99/2Cy4+XxpXtjNv9/TgfGSvZFyfV/t8Fw==
 
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
 "@types/sizzle@*":
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
@@ -285,6 +311,13 @@ alphanum-sort@^1.0.2:
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
   integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
 
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
 ansi-styles@^4.1.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
@@ -312,6 +345,18 @@ array-uniq@^1.0.1:
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
   integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
 
+autoprefixer@10.3.3:
+  version "10.3.3"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.3.tgz#4bac89c74ef98e6a40fe1c5b76c0d1c91db153ce"
+  integrity sha512-yRzjxfnggrP/+qVHlUuZz5FZzEbkT+Yt0/Df6ScEMnbbZBLzYB2W0KLxoQCW+THm1SpOsM1ZPcTHAwuvmibIsQ==
+  dependencies:
+    browserslist "^4.16.8"
+    caniuse-lite "^1.0.30001252"
+    colorette "^1.3.0"
+    fraction.js "^4.1.1"
+    normalize-range "^0.1.2"
+    postcss-value-parser "^4.1.0"
+
 balanced-match@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -347,7 +392,7 @@ braces@^3.0.1, braces@~3.0.2:
   dependencies:
     fill-range "^7.0.1"
 
-browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6:
+browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.16.8:
   version "4.16.8"
   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.8.tgz#cb868b0b554f137ba6e33de0ecff2eda403c4fb0"
   integrity sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==
@@ -363,6 +408,11 @@ buffer-from@^1.0.0:
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
   integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
 
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
 caniuse-api@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@@ -373,11 +423,20 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001251:
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001251, caniuse-lite@^1.0.30001252:
   version "1.0.30001252"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz#cb16e4e3dafe948fc4a9bb3307aea054b912019a"
   integrity sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==
 
+chalk@^2.0.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
 chalk@^4.1.0:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@@ -430,6 +489,13 @@ clone-deep@^4.0.1:
     kind-of "^6.0.2"
     shallow-clone "^3.0.0"
 
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
 color-convert@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@@ -437,6 +503,11 @@ color-convert@^2.0.1:
   dependencies:
     color-name "~1.1.4"
 
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
 color-name@~1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
@@ -479,6 +550,17 @@ core-util-is@~1.0.0:
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
   integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
 
+cosmiconfig@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
+  integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
 cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -717,6 +799,13 @@ envinfo@^7.7.3:
   resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
   integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
 
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
 es-module-lexer@^0.7.1:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
@@ -727,6 +816,11 @@ escalade@^3.1.1:
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
   integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
 eslint-scope@5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@@ -807,6 +901,11 @@ find-up@^4.0.0:
     locate-path "^5.0.0"
     path-exists "^4.0.0"
 
+fraction.js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.1.tgz#ac4e520473dae67012d618aab91eda09bcb400ff"
+  integrity sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -867,6 +966,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
   integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
 
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
 has-flag@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
@@ -900,6 +1004,14 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
   resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
   integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
 
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
 import-local@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6"
@@ -931,6 +1043,11 @@ is-absolute-url@^3.0.3:
   resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
   integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
 
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
 is-binary-path@~2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
@@ -1022,6 +1139,11 @@ jest-worker@^27.0.2:
     merge-stream "^2.0.0"
     supports-color "^8.0.0"
 
+js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
 jshint@^2.13.1:
   version "2.13.1"
   resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.13.1.tgz#16bbbecdbb4564d3758d9de4f24926f8c7f8f835"
@@ -1041,6 +1163,11 @@ json-parse-better-errors@^1.0.2:
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
 
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
 json-schema-traverse@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -1074,6 +1201,11 @@ lilconfig@^2.0.3:
   resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd"
   integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==
 
+lines-and-columns@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
+  integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
+
 loader-runner@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
@@ -1177,6 +1309,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
 normalize-url@^6.0.1:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
@@ -1251,6 +1388,23 @@ p-try@^2.0.0:
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
   integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
 
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
 path-exists@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -1276,6 +1430,11 @@ path-parse@^1.0.6:
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@@ -1355,6 +1514,15 @@ postcss-discard-overridden@^5.0.1:
   resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz#454b41f707300b98109a75005ca4ab0ff2743ac6"
   integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==
 
+postcss-loader@6.1.1:
+  version "6.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.1.1.tgz#58dd0a3accd9bc87cc52eff75244db578d11301a"
+  integrity sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==
+  dependencies:
+    cosmiconfig "^7.0.0"
+    klona "^2.0.4"
+    semver "^7.3.5"
+
 postcss-merge-longhand@^5.0.2:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz#277ada51d9a7958e8ef8cf263103c9384b322a41"
@@ -1559,7 +1727,7 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
   integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
 
-postcss@^8.2.15, postcss@^8.3.5:
+postcss@8.3.6, postcss@^8.2.15, postcss@^8.3.5:
   version "8.3.6"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
   integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
@@ -1568,6 +1736,11 @@ postcss@^8.2.15, postcss@^8.3.5:
     nanoid "^3.1.23"
     source-map-js "^0.6.2"
 
+prettier@2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d"
+  integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==
+
 punycode@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@@ -1611,6 +1784,11 @@ resolve-cwd@^3.0.0:
   dependencies:
     resolve-from "^5.0.0"
 
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
 resolve-from@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
@@ -1754,6 +1932,13 @@ stylehacks@^5.0.1:
     browserslist "^4.16.0"
     postcss-selector-parser "^6.0.4"
 
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
 supports-color@^7.1.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -1953,7 +2138,7 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yaml@^1.10.2:
+yaml@^1.10.0, yaml@^1.10.2:
   version "1.10.2"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==