diff --git a/assets/scripts/routes/catalog.js b/assets/scripts/routes/catalog.js
index e2744f6..3414188 100644
--- a/assets/scripts/routes/catalog.js
+++ b/assets/scripts/routes/catalog.js
@@ -30,7 +30,7 @@ export default {
 					while (
 						elem.nextElementSibling &&
 						elem.nextElementSibling.tagName !== 'H2'
-						) {
+					) {
 						elems.push( elem.nextElementSibling );
 						elem = elem.nextElementSibling;
 					}
@@ -100,7 +100,7 @@ export default {
 					while (
 						elem.nextElementSibling &&
 						elem.nextElementSibling.tagName !== 'H3'
-						) {
+					) {
 						elems.push( elem.nextElementSibling );
 						elem = elem.nextElementSibling;
 					}
diff --git a/assets/styles/aldine.scss b/assets/styles/aldine.scss
index 0cbde1e..4ba9ec4 100644
--- a/assets/styles/aldine.scss
+++ b/assets/styles/aldine.scss
@@ -11,6 +11,7 @@
 /** Import theme styles */
 @import "common/global";
 @import "components/book";
+@import "components/featured_book";
 @import "components/forms";
 @import "layouts/footer";
 @import "layouts/header";
diff --git a/assets/styles/components/_book.scss b/assets/styles/components/_book.scss
index fd40008..d566d55 100644
--- a/assets/styles/components/_book.scss
+++ b/assets/styles/components/_book.scss
@@ -3,16 +3,18 @@
   flex-direction: column;
   justify-content: flex-start;
   width: 100%;
-  max-width: 18rem;
-  height: 25rem;
-  border: solid 1px var(--accent);
+  max-width: 22.9375rem;
+  height: 24.125rem;
   margin: 0 0 2rem;
+  padding: 1.5rem 1rem 2rem;
+  @media #{$breakpoint-not-small} { padding: 1.5rem 1.85rem 2.1875rem; }
 
-  &__cover {
-    width: 100%;
-    height: 80%;
-    background-repeat: no-repeat;
-    background-size: cover;
+  border: solid 2px var(--accent);
+  background: var(--accent);
+
+  a, p {
+    font-family: $font-family-sans-serif;
+    color: var(--accent-fg);
   }
 
   a {
@@ -23,9 +25,62 @@
   }
 
   &__title {
-    display: flex;
-    justify-content: center;
-    align-items: center;
+    margin: 0;
+    font-size: 1.25rem;
+    @media #{$breakpoint-not-small} { font-size: 1.75rem; }
+
     font-weight: 500;
+    line-height: 1.2;
+    text-align: left;
+  }
+
+  &__subject {
+    margin: 0;
+    font-size: 0.75rem;
+    @media #{$breakpoint-not-small} {
+      font-size: 1rem;
+    }
+
+    text-align: left;
+  }
+
+  &__institutions {
+    margin: .5rem 0 0;
+    font-size: 0.875rem;
+    text-align: left;
+    display: box;
+    overflow: hidden;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+  }
+
+  &__read-more {
+    margin: auto 0 0;
+    font-size: 1rem;
+    @media #{$breakpoint-not-small} { font-size: 1.125rem; }
+
+    text-align: left;
+
+    a {
+      svg {
+        width: 1rem;
+        height: 1rem;
+        @media #{$breakpoint-not-small} {
+          width: 1.125rem;
+          height: 1.125rem;
+          margin-left: 0.5rem;
+        }
+
+        vertical-align: middle;
+      }
+    }
+  }
+
+  &:last-child {
+    margin-bottom: 0;
+
+    @media #{$breakpoint-large} {
+      margin-bottom: 2rem;
+    }
   }
 }
diff --git a/assets/styles/components/_featured_book.scss b/assets/styles/components/_featured_book.scss
new file mode 100644
index 0000000..4b82fbf
--- /dev/null
+++ b/assets/styles/components/_featured_book.scss
@@ -0,0 +1,31 @@
+.featured_book {
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  width: 100%;
+  max-width: 18rem;
+  height: 25rem;
+  border: solid 1px var(--accent);
+  margin: 0 0 2rem;
+
+  &__cover {
+    width: 100%;
+    height: 80%;
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+
+  a {
+    margin: 0;
+    text-decoration: none;
+    text-align: center;
+    hyphens: auto;
+  }
+
+  &__title {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-weight: 500;
+  }
+}
diff --git a/assets/styles/layouts/_page-catalog.scss b/assets/styles/layouts/_page-catalog.scss
index a8213ef..54e44b2 100644
--- a/assets/styles/layouts/_page-catalog.scss
+++ b/assets/styles/layouts/_page-catalog.scss
@@ -1,6 +1,6 @@
 // stylelint-disable no-descending-specificity
 
-fieldset {
+.network-catalog fieldset {
   border-top: solid 2px var(--accent);
   font-family: $font-family-sans-serif;
 
@@ -128,7 +128,7 @@ fieldset {
     margin: 2rem 0 1rem 1rem;
   }
 
-  .book {
+  .book, .featured_book {
     height: 14.375rem;
     width: calc(50% - 1rem);
     margin: 0 1rem 1rem 0;
diff --git a/assets/styles/layouts/_page-home.scss b/assets/styles/layouts/_page-home.scss
index 8bd4201..c745873 100644
--- a/assets/styles/layouts/_page-home.scss
+++ b/assets/styles/layouts/_page-home.scss
@@ -79,7 +79,7 @@
       flex-direction: row;
       justify-content: center;
 
-      .book {
+      .book, .featured_book {
         margin-right: 1rem;
         margin-left: 1rem;
       }
diff --git a/dist/scripts/aldine.js b/dist/scripts/aldine.js
index 49485a3..659e95d 100644
--- a/dist/scripts/aldine.js
+++ b/dist/scripts/aldine.js
@@ -27,7 +27,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _rou
 /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
-eval("__webpack_require__.r(__webpack_exports__);\n/* provided dependency */ var jQuery = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nvar Isotope = __webpack_require__(/*! isotope-layout */ \"./node_modules/isotope-layout/js/isotope.js\");\nvar jQueryBridget = __webpack_require__(/*! jquery-bridget */ \"./node_modules/jquery-bridget/jquery-bridget.js\");\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  /**\n   *\n   */init: function init() {\n    // JavaScript to be fired on the catalog page\n    (function () {\n      // Get all the <h2> headings\n      var headings = document.querySelectorAll('fieldset h2');\n      Array.prototype.forEach.call(headings, function (heading) {\n        // Give each <h3> a toggle button child\n        heading.innerHTML = \"\\n\\t\\t\\t\\t<button type=\\\"button\\\" aria-expanded=\\\"false\\\">\\n\\t\\t\\t\\t\\t\".concat(heading.textContent, \"\\n\\t\\t\\t\\t\\t<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" class=\\\"arrow\\\" width=\\\"13\\\" height=\\\"8\\\" viewBox=\\\"0 0 13 8\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><path d=\\\"M6.255 8L0 0h12.51z\\\" fill=\\\"currentColor\\\" fill-rule=\\\"evenodd\\\"></path></svg>\\n\\t\\t\\t\\t</button>\\n\\t\\t\\t  \");\n\n        // Function to create a node list\n        // of the content between this <h2> and the next\n        /**\n         * @param elem\n         */\n        var getContent = function getContent(elem) {\n          var elems = [];\n          while (elem.nextElementSibling && elem.nextElementSibling.tagName !== 'H2') {\n            elems.push(elem.nextElementSibling);\n            elem = elem.nextElementSibling;\n          }\n\n          // Delete the old versions of the content nodes\n          elems.forEach(function (node) {\n            node.parentNode.removeChild(node);\n          });\n          return elems;\n        };\n\n        // Assign the contents to be expanded/collapsed (array)\n        var contents = getContent(heading);\n\n        // Create a wrapper element for `contents` and hide it\n        var wrapper = document.createElement('div');\n        wrapper.hidden = true;\n\n        // Add each element of `contents` to `wrapper`\n        contents.forEach(function (node) {\n          wrapper.appendChild(node);\n        });\n\n        // Add the wrapped content back into the DOM\n        // after the heading\n        heading.parentNode.insertBefore(wrapper, heading.nextElementSibling);\n\n        // Assign the button\n        var btn = heading.querySelector('button');\n\n        /**\n         *\n         */\n        btn.onclick = function () {\n          // Cast the state as a boolean\n          var expanded = btn.getAttribute('aria-expanded') === 'true' || false;\n\n          // Switch the state\n          btn.setAttribute('aria-expanded', !expanded);\n          // Switch the content's visibility\n          wrapper.hidden = expanded;\n        };\n      });\n    })();\n    (function () {\n      // Get all the <h3> headings\n      var headings = document.querySelectorAll('fieldset h3');\n      Array.prototype.forEach.call(headings, function (heading) {\n        // Give each <h3> a toggle button child\n        heading.innerHTML = \"\\n\\t\\t\\t\\t<button type=\\\"button\\\" aria-expanded=\\\"false\\\">\\n\\t\\t\\t\\t\\t\".concat(heading.innerHTML, \"\\n\\t\\t\\t\\t\\t<svg class=\\\"arrow\\\" width=\\\"13\\\" height=\\\"8\\\" viewBox=\\\"0 0 13 8\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><path d=\\\"M6.255 8L0 0h12.51z\\\" fill=\\\"currentColor\\\" fill-rule=\\\"evenodd\\\"></path></svg>\\n\\t\\t\\t\\t</button>\\n\\t\\t\\t  \");\n\n        // Function to create a node list\n        // of the content between this <h2> and the next\n        /**\n         * @param elem\n         */\n        var getContent = function getContent(elem) {\n          var elems = [];\n          while (elem.nextElementSibling && elem.nextElementSibling.tagName !== 'H3') {\n            elems.push(elem.nextElementSibling);\n            elem = elem.nextElementSibling;\n          }\n\n          // Delete the old versions of the content nodes\n          elems.forEach(function (node) {\n            node.parentNode.removeChild(node);\n          });\n          return elems;\n        };\n\n        // Assign the contents to be expanded/collapsed (array)\n        var contents = getContent(heading);\n\n        // Create a wrapper element for `contents` and hide it\n        var wrapper = document.createElement('div');\n        wrapper.hidden = true;\n\n        // Add each element of `contents` to `wrapper`\n        contents.forEach(function (node) {\n          wrapper.appendChild(node);\n        });\n\n        // Add the wrapped content back into the DOM\n        // after the heading\n        heading.parentNode.insertBefore(wrapper, heading.nextElementSibling);\n\n        // Assign the button\n        var btn = heading.querySelector('button');\n\n        /**\n         *\n         */\n        btn.onclick = function () {\n          // Cast the state as a boolean\n          var expanded = btn.getAttribute('aria-expanded') === 'true' || false;\n\n          // Switch the state\n          btn.setAttribute('aria-expanded', !expanded);\n          // Switch the content's visibility\n          wrapper.hidden = expanded;\n        };\n      });\n    })();\n    jQuery(function ($) {\n      jQueryBridget('isotope', Isotope, $);\n      var $grid = $('.books');\n      $grid.isotope({\n        itemSelector: '.book',\n        getSortData: {\n          title: '.book__title a',\n          subject: '[data-subject]',\n          latest: '[data-date-published]'\n        },\n        sortAscending: {\n          title: true,\n          subject: false,\n          latest: false\n        }\n      });\n      var licenses = document.querySelector('.license-filters');\n      var subjects = document.querySelector('.subject-filters');\n      var institutions = document.querySelector('.institution-filters');\n      var sorts = document.querySelector('.sorts');\n      var clearFilters = document.querySelector('.clear-filters');\n      clearFilters.hidden = false;\n      licenses.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var subject = subjects.querySelector('input[type=\"radio\"]:checked').value ? \"[data-subject=\\\"\".concat(subjects.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = institutions.querySelector('input[type=\"radio\"]:checked').value ? \"[data-institution*=\\\"\".concat(institutions.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var license = event.target.value ? \"[data-license=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      institutions.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var subject = subjects.querySelector('input[type=\"radio\"]:checked').value ? \"[data-subject=\\\"\".concat(subjects.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var license = licenses.querySelector('input[type=\"radio\"]:checked').value ? \"[data-license=\\\"\".concat(licenses.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = event.target.value ? \"[data-institution*=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      subjects.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var license = licenses.querySelector('input[type=\"radio\"]:checked').value ? \"[data-license=\\\"\".concat(licenses.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = institutions.querySelector('input[type=\"radio\"]:checked').value ? \"[data-institution*=\\\"\".concat(institutions.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var subject = event.target.value ? \"[data-subject=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      clearFilters.addEventListener('click', function () {\n        var allLicenses = document.getElementById('all-licenses');\n        var allSubjects = document.getElementById('all-subjects');\n        var allInstitutions = document.getElementById('all-institutions');\n        allLicenses.checked = true;\n        allSubjects.checked = true;\n        allInstitutions.checked = true;\n        $grid.isotope({\n          filter: '*'\n        });\n      });\n      sorts.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        $grid.isotope({\n          sortBy: event.target.value\n        });\n      });\n      // \t$('.filters > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\t$('.filters').toggleClass('is-active');\n      // \t\t$('.filter-groups > div').removeClass('is-active');\n      // \t});\n      // \t$('.filter-groups .subjects > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet id = $(e.currentTarget).attr('href');\n      // \t\t$(`.filter-groups .subjects:not(${id})`).removeClass('is-active');\n      // \t\t$(`.filter-groups ${id}`).toggleClass('is-active');\n      // \t});\n      // \t$('.licenses > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet id = $(e.currentTarget).attr('href');\n      // \t\t$(id).toggleClass('is-active');\n      // \t});\n      // \t$('.subjects .filter-list a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tif ($(e.currentTarget).hasClass('is-active')) {\n      // \t\t\t$('.subjects .filter-list a').removeClass('is-active');\n      // \t\t\t$('.subjects').removeClass('has-active-child');\n      // \t\t} else {\n      // \t\t\t$('.subjects .filter-list a').removeClass('is-active');\n      // \t\t\t$(e.currentTarget).addClass('is-active');\n      // \t\t\t$('.subjects').removeClass('has-active-child');\n      // \t\t\t$(e.currentTarget)\n      // \t\t\t\t.parent()\n      // \t\t\t\t.parent()\n      // \t\t\t\t.parent('.subjects')\n      // \t\t\t\t.addClass('has-active-child');\n      // \t\t}\n      // \t\tlet subjectValue = $('.subjects .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tlet licenseValue = $('.licenses .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tif (typeof licenseValue === 'undefined') {\n      // \t\t\tlicenseValue = '';\n      // \t\t} else {\n      // \t\t\tlicenseValue = `[data-license=\"${licenseValue}\"]`;\n      // \t\t}\n      // \t\tif (typeof subjectValue === 'undefined') {\n      // \t\t\tsubjectValue = '';\n      // \t\t} else {\n      // \t\t\tsubjectValue = `[data-subject=\"${subjectValue}\"]`;\n      // \t\t}\n      // \t\t$grid.isotope({ filter: `${subjectValue}${licenseValue}` });\n      // \t});\n      // \t$('.licenses .filter-list a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tif ($(e.currentTarget).hasClass('is-active')) {\n      // \t\t\t$('.licenses .filter-list a').removeClass('is-active');\n      // \t\t\t$('.licenses').removeClass('has-active-child');\n      // \t\t} else {\n      // \t\t\t$('.licenses .filter-list a').removeClass('is-active');\n      // \t\t\t$(e.currentTarget).addClass('is-active');\n      // \t\t\t$('.licenses').addClass('has-active-child');\n      // \t\t}\n      // \t\tlet subjectValue = $('.subjects .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tlet licenseValue = $('.licenses .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tif (typeof licenseValue === 'undefined') {\n      // \t\t\tlicenseValue = '';\n      // \t\t} else {\n      // \t\t\tlicenseValue = `[data-license=\"${licenseValue}\"]`;\n      // \t\t}\n      // \t\tif (typeof subjectValue === 'undefined') {\n      // \t\t\tsubjectValue = '';\n      // \t\t} else {\n      // \t\t\tsubjectValue = `[data-subject=\"${subjectValue}\"]`;\n      // \t\t}\n      // \t\t$grid.isotope({ filter: `${subjectValue}${licenseValue}` });\n      // \t});\n      // \t$('.sort > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\t$('.sort').toggleClass('is-active');\n      // \t});\n      // \t$('.sorts a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet sortBy = $(e.currentTarget).attr('data-sort');\n      // \t\t$('.sorts a').removeClass('is-active');\n      // \t\t$(e.currentTarget).addClass('is-active');\n      // \t\t$grid.isotope({ sortBy: sortBy });\n      // \t});\n    });\n  },\n  /**\n   *\n   */finalize: function finalize() {}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,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TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO0lBQ0QsQ0FBQyxDQUFFO0VBQ0osQ0FBQztFQUNEO0FBQ0Q7QUFDQSxLQUNDVyxRQUFRLHNCQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vQHByZXNzYm9va3MvcHJlc3Nib29rcy1hbGRpbmUvLi9hc3NldHMvc2NyaXB0cy9yb3V0ZXMvY2F0YWxvZy5qcz9mNWRmIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IElzb3RvcGUgPSByZXF1aXJlKCAnaXNvdG9wZS1sYXlvdXQnICk7XG5jb25zdCBqUXVlcnlCcmlkZ2V0ID0gcmVxdWlyZSggJ2pxdWVyeS1icmlkZ2V0JyApO1xuXG5leHBvcnQgZGVmYXVsdCB7XG5cdC8qKlxuXHQgKlxuXHQgKi9cblx0aW5pdCgpIHtcblx0XHQvLyBKYXZhU2NyaXB0IHRvIGJlIGZpcmVkIG9uIHRoZSBjYXRhbG9nIHBhZ2Vcblx0XHQoIGZ1bmN0aW9uICgpIHtcblx0XHRcdC8vIEdldCBhbGwgdGhlIDxoMj4gaGVhZGluZ3Ncblx0XHRcdGNvbnN0IGhlYWRpbmdzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJ2ZpZWxkc2V0IGgyJyApO1xuXG5cdFx0XHRBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKCBoZWFkaW5ncywgaGVhZGluZyA9PiB7XG5cdFx0XHRcdC8vIEdpdmUgZWFjaCA8aDM+IGEgdG9nZ2xlIGJ1dHRvbiBjaGlsZFxuXHRcdFx0XHRoZWFkaW5nLmlubmVySFRNTCA9IGBcblx0XHRcdFx0PGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgYXJpYS1leHBhbmRlZD1cImZhbHNlXCI+XG5cdFx0XHRcdFx0JHsgaGVhZGluZy50ZXh0Q29udGVudCB9XG5cdFx0XHRcdFx0PHN2ZyBhcmlhLWhpZGRlbj1cInRydWVcIiBmb2N1c2FibGU9XCJmYWxzZVwiIGNsYXNzPVwiYXJyb3dcIiB3aWR0aD1cIjEzXCIgaGVpZ2h0PVwiOFwiIHZpZXdCb3g9XCIwIDAgMTMgOFwiIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIj48cGF0aCBkPVwiTTYuMjU1IDhMMCAwaDEyLjUxelwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIiBmaWxsLXJ1bGU9XCJldmVub2RkXCI+PC9wYXRoPjwvc3ZnPlxuXHRcdFx0XHQ8L2J1dHRvbj5cblx0XHRcdCAgYDtcblxuXHRcdFx0XHQvLyBGdW5jdGlvbiB0byBjcmVhdGUgYSBub2RlIGxpc3Rcblx0XHRcdFx0Ly8gb2YgdGhlIGNvbnRlbnQgYmV0d2VlbiB0aGlzIDxoMj4gYW5kIHRoZSBuZXh0XG5cdFx0XHRcdC8qKlxuXHRcdFx0XHQgKiBAcGFyYW0gZWxlbVxuXHRcdFx0XHQgKi9cblx0XHRcdFx0Y29uc3QgZ2V0Q29udGVudCA9IGVsZW0gPT4ge1xuXHRcdFx0XHRcdGxldCBlbGVtcyA9IFtdO1xuXHRcdFx0XHRcdHdoaWxlIChcblx0XHRcdFx0XHRcdGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nICYmXG5cdFx0XHRcdFx0XHRlbGVtLm5leHRFbGVtZW50U2libGluZy50YWdOYW1lICE9PSAnSDInXG5cdFx0XHRcdFx0XHQpIHtcblx0XHRcdFx0XHRcdGVsZW1zLnB1c2goIGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nICk7XG5cdFx0XHRcdFx0XHRlbGVtID0gZWxlbS5uZXh0RWxlbWVudFNpYmxpbmc7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gRGVsZXRlIHRoZSBvbGQgdmVyc2lvbnMgb2YgdGhlIGNvbnRlbnQgbm9kZXNcblx0XHRcdFx0XHRlbGVtcy5mb3JFYWNoKCBub2RlID0+IHtcblx0XHRcdFx0XHRcdG5vZGUucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggbm9kZSApO1xuXHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRcdHJldHVybiBlbGVtcztcblx0XHRcdFx0fTtcblxuXHRcdFx0XHQvLyBBc3NpZ24gdGhlIGNvbnRlbnRzIHRvIGJlIGV4cGFuZGVkL2NvbGxhcHNlZCAoYXJyYXkpXG5cdFx0XHRcdGxldCBjb250ZW50cyA9IGdldENvbnRlbnQoIGhlYWRpbmcgKTtcblxuXHRcdFx0XHQvLyBDcmVhdGUgYSB3cmFwcGVyIGVsZW1lbnQgZm9yIGBjb250ZW50c2AgYW5kIGhpZGUgaXRcblx0XHRcdFx0bGV0IHdyYXBwZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xuXHRcdFx0XHR3cmFwcGVyLmhpZGRlbiA9IHRydWU7XG5cblx0XHRcdFx0Ly8gQWRkIGVhY2ggZWxlbWVudCBvZiBgY29udGVudHNgIHRvIGB3cmFwcGVyYFxuXHRcdFx0XHRjb250ZW50cy5mb3JFYWNoKCBub2RlID0+IHtcblx0XHRcdFx0XHR3cmFwcGVyLmFwcGVuZENoaWxkKCBub2RlICk7XG5cdFx0XHRcdH0gKTtcblxuXHRcdFx0XHQvLyBBZGQgdGhlIHdyYXBwZWQgY29udGVudCBiYWNrIGludG8gdGhlIERPTVxuXHRcdFx0XHQvLyBhZnRlciB0aGUgaGVhZGluZ1xuXHRcdFx0XHRoZWFkaW5nLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKCB3cmFwcGVyLCBoZWFkaW5nLm5leHRFbGVtZW50U2libGluZyApO1xuXG5cdFx0XHRcdC8vIEFzc2lnbiB0aGUgYnV0dG9uXG5cdFx0XHRcdGxldCBidG4gPSBoZWFkaW5nLnF1ZXJ5U2VsZWN0b3IoICdidXR0b24nICk7XG5cblx0XHRcdFx0LyoqXG5cdFx0XHRcdCAqXG5cdFx0XHRcdCAqL1xuXHRcdFx0XHRidG4ub25jbGljayA9ICgpID0+IHtcblx0XHRcdFx0XHQvLyBDYXN0IHRoZSBzdGF0ZSBhcyBhIGJvb2xlYW5cblx0XHRcdFx0XHRsZXQgZXhwYW5kZWQgPSBidG4uZ2V0QXR0cmlidXRlKCAnYXJpYS1leHBhbmRlZCcgKSA9PT0gJ3RydWUnIHx8IGZhbHNlO1xuXG5cdFx0XHRcdFx0Ly8gU3dpdGNoIHRoZSBzdGF0ZVxuXHRcdFx0XHRcdGJ0bi5zZXRBdHRyaWJ1dGUoICdhcmlhLWV4cGFuZGVkJywgISBleHBhbmRlZCApO1xuXHRcdFx0XHRcdC8vIFN3aXRjaCB0aGUgY29udGVudCdzIHZpc2liaWxpdHlcblx0XHRcdFx0XHR3cmFwcGVyLmhpZGRlbiA9IGV4cGFuZGVkO1xuXHRcdFx0XHR9O1xuXHRcdFx0fSApO1xuXHRcdH0gKSgpO1xuXG5cdFx0KCBmdW5jdGlvbiAoKSB7XG5cdFx0XHQvLyBHZXQgYWxsIHRoZSA8aDM+IGhlYWRpbmdzXG5cdFx0XHRjb25zdCBoZWFkaW5ncyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICdmaWVsZHNldCBoMycgKTtcblxuXHRcdFx0QXJyYXkucHJvdG90eXBlLmZvckVhY2guY2FsbCggaGVhZGluZ3MsIGhlYWRpbmcgPT4ge1xuXHRcdFx0XHQvLyBHaXZlIGVhY2ggPGgzPiBhIHRvZ2dsZSBidXR0b24gY2hpbGRcblx0XHRcdFx0aGVhZGluZy5pbm5lckhUTUwgPSBgXG5cdFx0XHRcdDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGFyaWEtZXhwYW5kZWQ9XCJmYWxzZVwiPlxuXHRcdFx0XHRcdCR7IGhlYWRpbmcuaW5uZXJIVE1MIH1cblx0XHRcdFx0XHQ8c3ZnIGNsYXNzPVwiYXJyb3dcIiB3aWR0aD1cIjEzXCIgaGVpZ2h0PVwiOFwiIHZpZXdCb3g9XCIwIDAgMTMgOFwiIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIj48cGF0aCBkPVwiTTYuMjU1IDhMMCAwaDEyLjUxelwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIiBmaWxsLXJ1bGU9XCJldmVub2RkXCI+PC9wYXRoPjwvc3ZnPlxuXHRcdFx0XHQ8L2J1dHRvbj5cblx0XHRcdCAgYDtcblxuXHRcdFx0XHQvLyBGdW5jdGlvbiB0byBjcmVhdGUgYSBub2RlIGxpc3Rcblx0XHRcdFx0Ly8gb2YgdGhlIGNvbnRlbnQgYmV0d2VlbiB0aGlzIDxoMj4gYW5kIHRoZSBuZXh0XG5cdFx0XHRcdC8qKlxuXHRcdFx0XHQgKiBAcGFyYW0gZWxlbVxuXHRcdFx0XHQgKi9cblx0XHRcdFx0Y29uc3QgZ2V0Q29udGVudCA9IGVsZW0gPT4ge1xuXHRcdFx0XHRcdGxldCBlbGVtcyA9IFtdO1xuXHRcdFx0XHRcdHdoaWxlIChcblx0XHRcdFx0XHRcdGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nICYmXG5cdFx0XHRcdFx0XHRlbGVtLm5leHRFbGVtZW50U2libGluZy50YWdOYW1lICE9PSAnSDMnXG5cdFx0XHRcdFx0XHQpIHtcblx0XHRcdFx0XHRcdGVsZW1zLnB1c2goIGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nICk7XG5cdFx0XHRcdFx0XHRlbGVtID0gZWxlbS5uZXh0RWxlbWVudFNpYmxpbmc7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gRGVsZXRlIHRoZSBvbGQgdmVyc2lvbnMgb2YgdGhlIGNvbnRlbnQgbm9kZXNcblx0XHRcdFx0XHRlbGVtcy5mb3JFYWNoKCBub2RlID0+IHtcblx0XHRcdFx0XHRcdG5vZGUucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggbm9kZSApO1xuXHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRcdHJldHVybiBlbGVtcztcblx0XHRcdFx0fTtcblxuXHRcdFx0XHQvLyBBc3NpZ24gdGhlIGNvbnRlbnRzIHRvIGJlIGV4cGFuZGVkL2NvbGxhcHNlZCAoYXJyYXkpXG5cdFx0XHRcdGxldCBjb250ZW50cyA9IGdldENvbnRlbnQoIGhlYWRpbmcgKTtcblxuXHRcdFx0XHQvLyBDcmVhdGUgYSB3cmFwcGVyIGVsZW1lbnQgZm9yIGBjb250ZW50c2AgYW5kIGhpZGUgaXRcblx0XHRcdFx0bGV0IHdyYXBwZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xuXHRcdFx0XHR3cmFwcGVyLmhpZGRlbiA9IHRydWU7XG5cblx0XHRcdFx0Ly8gQWRkIGVhY2ggZWxlbWVudCBvZiBgY29udGVudHNgIHRvIGB3cmFwcGVyYFxuXHRcdFx0XHRjb250ZW50cy5mb3JFYWNoKCBub2RlID0+IHtcblx0XHRcdFx0XHR3cmFwcGVyLmFwcGVuZENoaWxkKCBub2RlICk7XG5cdFx0XHRcdH0gKTtcblxuXHRcdFx0XHQvLyBBZGQgdGhlIHdyYXBwZWQgY29udGVudCBiYWNrIGludG8gdGhlIERPTVxuXHRcdFx0XHQvLyBhZnRlciB0aGUgaGVhZGluZ1xuXHRcdFx0XHRoZWFkaW5nLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKCB3cmFwcGVyLCBoZWFkaW5nLm5leHRFbGVtZW50U2libGluZyApO1xuXG5cdFx0XHRcdC8vIEFzc2lnbiB0aGUgYnV0dG9uXG5cdFx0XHRcdGxldCBidG4gPSBoZWFkaW5nLnF1ZXJ5U2VsZWN0b3IoICdidXR0b24nICk7XG5cblx0XHRcdFx0LyoqXG5cdFx0XHRcdCAqXG5cdFx0XHRcdCAqL1xuXHRcdFx0XHRidG4ub25jbGljayA9ICgpID0+IHtcblx0XHRcdFx0XHQvLyBDYXN0IHRoZSBzdGF0ZSBhcyBhIGJvb2xlYW5cblx0XHRcdFx0XHRsZXQgZXhwYW5kZWQgPSBidG4uZ2V0QXR0cmlidXRlKCAnYXJpYS1leHBhbmRlZCcgKSA9PT0gJ3RydWUnIHx8IGZhbHNlO1xuXG5cdFx0XHRcdFx0Ly8gU3dpdGNoIHRoZSBzdGF0ZVxuXHRcdFx0XHRcdGJ0bi5zZXRBdHRyaWJ1dGUoICdhcmlhLWV4cGFuZGVkJywgISBleHBhbmRlZCApO1xuXHRcdFx0XHRcdC8vIFN3aXRjaCB0aGUgY29udGVudCdzIHZpc2liaWxpdHlcblx0XHRcdFx0XHR3cmFwcGVyLmhpZGRlbiA9IGV4cGFuZGVkO1xuXHRcdFx0XHR9O1xuXHRcdFx0fSApO1xuXHRcdH0gKSgpO1xuXG5cdFx0alF1ZXJ5KCAkID0+IHtcblx0XHRcdGpRdWVyeUJyaWRnZXQoICdpc290b3BlJywgSXNvdG9wZSwgJCApO1xuXHRcdFx0bGV0ICRncmlkID0gJCggJy5ib29rcycgKTtcblx0XHRcdCRncmlkLmlzb3RvcGUoIHtcblx0XHRcdFx0aXRlbVNlbGVjdG9yOiAnLmJvb2snLFxuXHRcdFx0XHRnZXRTb3J0RGF0YToge1xuXHRcdFx0XHRcdHRpdGxlOiAnLmJvb2tfX3RpdGxlIGEnLFxuXHRcdFx0XHRcdHN1YmplY3Q6ICdbZGF0YS1zdWJqZWN0XScsXG5cdFx0XHRcdFx0bGF0ZXN0OiAnW2RhdGEtZGF0ZS1wdWJsaXNoZWRdJyxcblx0XHRcdFx0fSxcblx0XHRcdFx0c29ydEFzY2VuZGluZzoge1xuXHRcdFx0XHRcdHRpdGxlOiB0cnVlLFxuXHRcdFx0XHRcdHN1YmplY3Q6IGZhbHNlLFxuXHRcdFx0XHRcdGxhdGVzdDogZmFsc2UsXG5cdFx0XHRcdH0sXG5cdFx0XHR9ICk7XG5cdFx0XHRsZXQgbGljZW5zZXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCAnLmxpY2Vuc2UtZmlsdGVycycgKTtcblx0XHRcdGxldCBzdWJqZWN0cyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcuc3ViamVjdC1maWx0ZXJzJyApO1xuXHRcdFx0bGV0IGluc3RpdHV0aW9ucyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcuaW5zdGl0dXRpb24tZmlsdGVycycgKTtcblx0XHRcdGxldCBzb3J0cyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcuc29ydHMnICk7XG5cdFx0XHRsZXQgY2xlYXJGaWx0ZXJzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5jbGVhci1maWx0ZXJzJyApO1xuXHRcdFx0Y2xlYXJGaWx0ZXJzLmhpZGRlbiA9IGZhbHNlO1xuXHRcdFx0bGljZW5zZXMuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24gKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCBldmVudC50YXJnZXQudHlwZSAhPT0gJ3JhZGlvJyApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb25zdCBzdWJqZWN0ID0gc3ViamVjdHMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtc3ViamVjdD1cIiR7IHN1YmplY3RzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cdFx0XHRcdGNvbnN0IGluc3RpdHV0aW9uID0gaW5zdGl0dXRpb25zLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLWluc3RpdHV0aW9uKj1cIiR7IGluc3RpdHV0aW9ucy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXHRcdFx0XHRjb25zdCBsaWNlbnNlID0gZXZlbnQudGFyZ2V0LnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtbGljZW5zZT1cIiR7IGV2ZW50LnRhcmdldC52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cblx0XHRcdFx0Y29uc3QgZmlsdGVyVmFsdWUgPSBzdWJqZWN0IHx8IGxpY2Vuc2UgfHwgaW5zdGl0dXRpb24gPyBgJHsgc3ViamVjdCB9JHsgbGljZW5zZSB9JHsgaW5zdGl0dXRpb24gfWAgOiAnKic7XG5cblx0XHRcdFx0JGdyaWQuaXNvdG9wZSggeyBmaWx0ZXI6IGZpbHRlclZhbHVlIH0gKTtcblx0XHRcdH0gKTtcblx0XHRcdGluc3RpdHV0aW9ucy5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCBmdW5jdGlvbiAoIGV2ZW50ICkge1xuXHRcdFx0XHRpZiAoIGV2ZW50LnRhcmdldC50eXBlICE9PSAncmFkaW8nICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGNvbnN0IHN1YmplY3QgPSBzdWJqZWN0cy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWVcblx0XHRcdFx0XHQ/IGBbZGF0YS1zdWJqZWN0PVwiJHsgc3ViamVjdHMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlIH1cIl1gXG5cdFx0XHRcdFx0OiAnJztcblx0XHRcdFx0Y29uc3QgbGljZW5zZSA9IGxpY2Vuc2VzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLWxpY2Vuc2U9XCIkeyBsaWNlbnNlcy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXHRcdFx0XHRjb25zdCBpbnN0aXR1dGlvbiA9IGV2ZW50LnRhcmdldC52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLWluc3RpdHV0aW9uKj1cIiR7IGV2ZW50LnRhcmdldC52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cblx0XHRcdFx0Y29uc3QgZmlsdGVyVmFsdWUgPSBzdWJqZWN0IHx8IGxpY2Vuc2UgfHwgaW5zdGl0dXRpb24gPyBgJHsgc3ViamVjdCB9JHsgbGljZW5zZSB9JHsgaW5zdGl0dXRpb24gfWAgOiAnKic7XG5cblx0XHRcdFx0JGdyaWQuaXNvdG9wZSggeyBmaWx0ZXI6IGZpbHRlclZhbHVlIH0gKTtcblx0XHRcdH0gKTtcblx0XHRcdHN1YmplY3RzLmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIGZ1bmN0aW9uICggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggZXZlbnQudGFyZ2V0LnR5cGUgIT09ICdyYWRpbycgKSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Y29uc3QgbGljZW5zZSA9IGxpY2Vuc2VzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLWxpY2Vuc2U9XCIkeyBsaWNlbnNlcy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXHRcdFx0XHRjb25zdCBpbnN0aXR1dGlvbiA9IGluc3RpdHV0aW9ucy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWVcblx0XHRcdFx0XHQ/IGBbZGF0YS1pbnN0aXR1dGlvbio9XCIkeyBpbnN0aXR1dGlvbnMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlIH1cIl1gXG5cdFx0XHRcdFx0OiAnJztcblx0XHRcdFx0Y29uc3Qgc3ViamVjdCA9IGV2ZW50LnRhcmdldC52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLXN1YmplY3Q9XCIkeyBldmVudC50YXJnZXQudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXG5cdFx0XHRcdGNvbnN0IGZpbHRlclZhbHVlID0gc3ViamVjdCB8fCBsaWNlbnNlIHx8IGluc3RpdHV0aW9uID8gYCR7IHN1YmplY3QgfSR7IGxpY2Vuc2UgfSR7IGluc3RpdHV0aW9uIH1gIDogJyonO1xuXG5cdFx0XHRcdCRncmlkLmlzb3RvcGUoIHsgZmlsdGVyOiBmaWx0ZXJWYWx1ZSB9ICk7XG5cdFx0XHR9ICk7XG5cdFx0XHRjbGVhckZpbHRlcnMuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRsZXQgYWxsTGljZW5zZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ2FsbC1saWNlbnNlcycgKTtcblx0XHRcdFx0bGV0IGFsbFN1YmplY3RzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoICdhbGwtc3ViamVjdHMnICk7XG5cdFx0XHRcdGxldCBhbGxJbnN0aXR1dGlvbnMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ2FsbC1pbnN0aXR1dGlvbnMnICk7XG5cdFx0XHRcdGFsbExpY2Vuc2VzLmNoZWNrZWQgPSB0cnVlO1xuXHRcdFx0XHRhbGxTdWJqZWN0cy5jaGVja2VkID0gdHJ1ZTtcblx0XHRcdFx0YWxsSW5zdGl0dXRpb25zLmNoZWNrZWQgPSB0cnVlO1xuXHRcdFx0XHQkZ3JpZC5pc290b3BlKCB7IGZpbHRlcjogJyonIH0gKTtcblx0XHRcdH0gKTtcblx0XHRcdHNvcnRzLmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIGZ1bmN0aW9uICggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggZXZlbnQudGFyZ2V0LnR5cGUgIT09ICdyYWRpbycgKSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHRcdCRncmlkLmlzb3RvcGUoIHsgc29ydEJ5OiBldmVudC50YXJnZXQudmFsdWUgfSApO1xuXHRcdFx0fSApO1xuXHRcdFx0Ly8gXHQkKCcuZmlsdGVycyA+IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0JCgnLmZpbHRlcnMnKS50b2dnbGVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0JCgnLmZpbHRlci1ncm91cHMgPiBkaXYnKS5yZW1vdmVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcuZmlsdGVyLWdyb3VwcyAuc3ViamVjdHMgPiBhJykuY2xpY2soZSA9PiB7XG5cdFx0XHQvLyBcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0Ly8gXHRcdGxldCBpZCA9ICQoZS5jdXJyZW50VGFyZ2V0KS5hdHRyKCdocmVmJyk7XG5cdFx0XHQvLyBcdFx0JChgLmZpbHRlci1ncm91cHMgLnN1YmplY3RzOm5vdCgke2lkfSlgKS5yZW1vdmVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0JChgLmZpbHRlci1ncm91cHMgJHtpZH1gKS50b2dnbGVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcubGljZW5zZXMgPiBhJykuY2xpY2soZSA9PiB7XG5cdFx0XHQvLyBcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0Ly8gXHRcdGxldCBpZCA9ICQoZS5jdXJyZW50VGFyZ2V0KS5hdHRyKCdocmVmJyk7XG5cdFx0XHQvLyBcdFx0JChpZCkudG9nZ2xlQ2xhc3MoJ2lzLWFjdGl2ZScpO1xuXHRcdFx0Ly8gXHR9KTtcblx0XHRcdC8vIFx0JCgnLnN1YmplY3RzIC5maWx0ZXItbGlzdCBhJykuY2xpY2soZSA9PiB7XG5cdFx0XHQvLyBcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0Ly8gXHRcdGlmICgkKGUuY3VycmVudFRhcmdldCkuaGFzQ2xhc3MoJ2lzLWFjdGl2ZScpKSB7XG5cdFx0XHQvLyBcdFx0XHQkKCcuc3ViamVjdHMgLmZpbHRlci1saXN0IGEnKS5yZW1vdmVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0XHQkKCcuc3ViamVjdHMnKS5yZW1vdmVDbGFzcygnaGFzLWFjdGl2ZS1jaGlsZCcpO1xuXHRcdFx0Ly8gXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBcdFx0XHQkKCcuc3ViamVjdHMgLmZpbHRlci1saXN0IGEnKS5yZW1vdmVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0XHQkKGUuY3VycmVudFRhcmdldCkuYWRkQ2xhc3MoJ2lzLWFjdGl2ZScpO1xuXHRcdFx0Ly8gXHRcdFx0JCgnLnN1YmplY3RzJykucmVtb3ZlQ2xhc3MoJ2hhcy1hY3RpdmUtY2hpbGQnKTtcblx0XHRcdC8vIFx0XHRcdCQoZS5jdXJyZW50VGFyZ2V0KVxuXHRcdFx0Ly8gXHRcdFx0XHQucGFyZW50KClcblx0XHRcdC8vIFx0XHRcdFx0LnBhcmVudCgpXG5cdFx0XHQvLyBcdFx0XHRcdC5wYXJlbnQoJy5zdWJqZWN0cycpXG5cdFx0XHQvLyBcdFx0XHRcdC5hZGRDbGFzcygnaGFzLWFjdGl2ZS1jaGlsZCcpO1xuXHRcdFx0Ly8gXHRcdH1cblx0XHRcdC8vIFx0XHRsZXQgc3ViamVjdFZhbHVlID0gJCgnLnN1YmplY3RzIC5maWx0ZXItbGlzdCBhLmlzLWFjdGl2ZScpLmF0dHIoXG5cdFx0XHQvLyBcdFx0XHQnZGF0YS1maWx0ZXInXG5cdFx0XHQvLyBcdFx0KTtcblx0XHRcdC8vIFx0XHRsZXQgbGljZW5zZVZhbHVlID0gJCgnLmxpY2Vuc2VzIC5maWx0ZXItbGlzdCBhLmlzLWFjdGl2ZScpLmF0dHIoXG5cdFx0XHQvLyBcdFx0XHQnZGF0YS1maWx0ZXInXG5cdFx0XHQvLyBcdFx0KTtcblx0XHRcdC8vIFx0XHRpZiAodHlwZW9mIGxpY2Vuc2VWYWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdC8vIFx0XHRcdGxpY2Vuc2VWYWx1ZSA9ICcnO1xuXHRcdFx0Ly8gXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBcdFx0XHRsaWNlbnNlVmFsdWUgPSBgW2RhdGEtbGljZW5zZT1cIiR7bGljZW5zZVZhbHVlfVwiXWA7XG5cdFx0XHQvLyBcdFx0fVxuXHRcdFx0Ly8gXHRcdGlmICh0eXBlb2Ygc3ViamVjdFZhbHVlID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0Ly8gXHRcdFx0c3ViamVjdFZhbHVlID0gJyc7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdHN1YmplY3RWYWx1ZSA9IGBbZGF0YS1zdWJqZWN0PVwiJHtzdWJqZWN0VmFsdWV9XCJdYDtcblx0XHRcdC8vIFx0XHR9XG5cdFx0XHQvLyBcdFx0JGdyaWQuaXNvdG9wZSh7IGZpbHRlcjogYCR7c3ViamVjdFZhbHVlfSR7bGljZW5zZVZhbHVlfWAgfSk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcubGljZW5zZXMgLmZpbHRlci1saXN0IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0aWYgKCQoZS5jdXJyZW50VGFyZ2V0KS5oYXNDbGFzcygnaXMtYWN0aXZlJykpIHtcblx0XHRcdC8vIFx0XHRcdCQoJy5saWNlbnNlcyAuZmlsdGVyLWxpc3QgYScpLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHRcdCQoJy5saWNlbnNlcycpLnJlbW92ZUNsYXNzKCdoYXMtYWN0aXZlLWNoaWxkJyk7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdCQoJy5saWNlbnNlcyAuZmlsdGVyLWxpc3QgYScpLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHRcdCQoZS5jdXJyZW50VGFyZ2V0KS5hZGRDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0XHQkKCcubGljZW5zZXMnKS5hZGRDbGFzcygnaGFzLWFjdGl2ZS1jaGlsZCcpO1xuXHRcdFx0Ly8gXHRcdH1cblx0XHRcdC8vIFx0XHRsZXQgc3ViamVjdFZhbHVlID0gJCgnLnN1YmplY3RzIC5maWx0ZXItbGlzdCBhLmlzLWFjdGl2ZScpLmF0dHIoXG5cdFx0XHQvLyBcdFx0XHQnZGF0YS1maWx0ZXInXG5cdFx0XHQvLyBcdFx0KTtcblx0XHRcdC8vIFx0XHRsZXQgbGljZW5zZVZhbHVlID0gJCgnLmxpY2Vuc2VzIC5maWx0ZXItbGlzdCBhLmlzLWFjdGl2ZScpLmF0dHIoXG5cdFx0XHQvLyBcdFx0XHQnZGF0YS1maWx0ZXInXG5cdFx0XHQvLyBcdFx0KTtcblx0XHRcdC8vIFx0XHRpZiAodHlwZW9mIGxpY2Vuc2VWYWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdC8vIFx0XHRcdGxpY2Vuc2VWYWx1ZSA9ICcnO1xuXHRcdFx0Ly8gXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBcdFx0XHRsaWNlbnNlVmFsdWUgPSBgW2RhdGEtbGljZW5zZT1cIiR7bGljZW5zZVZhbHVlfVwiXWA7XG5cdFx0XHQvLyBcdFx0fVxuXHRcdFx0Ly8gXHRcdGlmICh0eXBlb2Ygc3ViamVjdFZhbHVlID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0Ly8gXHRcdFx0c3ViamVjdFZhbHVlID0gJyc7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdHN1YmplY3RWYWx1ZSA9IGBbZGF0YS1zdWJqZWN0PVwiJHtzdWJqZWN0VmFsdWV9XCJdYDtcblx0XHRcdC8vIFx0XHR9XG5cdFx0XHQvLyBcdFx0JGdyaWQuaXNvdG9wZSh7IGZpbHRlcjogYCR7c3ViamVjdFZhbHVlfSR7bGljZW5zZVZhbHVlfWAgfSk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcuc29ydCA+IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0JCgnLnNvcnQnKS50b2dnbGVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcuc29ydHMgYScpLmNsaWNrKGUgPT4ge1xuXHRcdFx0Ly8gXHRcdGUucHJldmVudERlZmF1bHQoKTtcblx0XHRcdC8vIFx0XHRsZXQgc29ydEJ5ID0gJChlLmN1cnJlbnRUYXJnZXQpLmF0dHIoJ2RhdGEtc29ydCcpO1xuXHRcdFx0Ly8gXHRcdCQoJy5zb3J0cyBhJykucmVtb3ZlQ2xhc3MoJ2lzLWFjdGl2ZScpO1xuXHRcdFx0Ly8gXHRcdCQoZS5jdXJyZW50VGFyZ2V0KS5hZGRDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0JGdyaWQuaXNvdG9wZSh7IHNvcnRCeTogc29ydEJ5IH0pO1xuXHRcdFx0Ly8gXHR9KTtcblx0XHR9ICk7XG5cdH0sXG5cdC8qKlxuXHQgKlxuXHQgKi9cblx0ZmluYWxpemUoKSB7fSxcbn07XG4iXSwibmFtZXMiOlsiSXNvdG9wZSIsInJlcXVpcmUiLCJqUXVlcnlCcmlkZ2V0IiwiaW5pdCIsImhlYWRpbmdzIiwiZG9jdW1lbnQiLCJxdWVyeVNlbGVjdG9yQWxsIiwiQXJyYXkiLCJwcm90b3R5cGUiLCJmb3JFYWNoIiwiY2FsbCIsImhlYWRpbmciLCJpbm5lckhUTUwiLCJ0ZXh0Q29udGVudCIsImdldENvbnRlbnQiLCJlbGVtIiwiZWxlbXMiLCJuZXh0RWxlbWVudFNpYmxpbmciLCJ0YWdOYW1lIiwicHVzaCIsIm5vZGUiLCJwYXJlbnROb2RlIiwicmVtb3ZlQ2hpbGQiLCJjb250ZW50cyIsIndyYXBwZXIiLCJjcmVhdGVFbGVtZW50IiwiaGlkZGVuIiwiYXBwZW5kQ2hpbGQiLCJpbnNlcnRCZWZvcmUiLCJidG4iLCJxdWVyeVNlbGVjdG9yIiwib25jbGljayIsImV4cGFuZGVkIiwiZ2V0QXR0cmlidXRlIiwic2V0QXR0cmlidXRlIiwialF1ZXJ5IiwiJCIsIiRncmlkIiwiaXNvdG9wZSIsIml0ZW1TZWxlY3RvciIsImdldFNvcnREYXRhIiwidGl0bGUiLCJzdWJqZWN0IiwibGF0ZXN0Iiwic29ydEFzY2VuZGluZyIsImxpY2Vuc2VzIiwic3ViamVjdHMiLCJpbnN0aXR1dGlvbnMiLCJzb3J0cyIsImNsZWFyRmlsdGVycyIsImFkZEV2ZW50TGlzdGVuZXIiLCJldmVudCIsInRhcmdldCIsInR5cGUiLCJ2YWx1ZSIsImluc3RpdHV0aW9uIiwibGljZW5zZSIsImZpbHRlclZhbHVlIiwiZmlsdGVyIiwiYWxsTGljZW5zZXMiLCJnZXRFbGVtZW50QnlJZCIsImFsbFN1YmplY3RzIiwiYWxsSW5zdGl0dXRpb25zIiwiY2hlY2tlZCIsInNvcnRCeSIsImZpbmFsaXplIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./assets/scripts/routes/catalog.js\n");
+eval("__webpack_require__.r(__webpack_exports__);\n/* provided dependency */ var jQuery = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nvar Isotope = __webpack_require__(/*! isotope-layout */ \"./node_modules/isotope-layout/js/isotope.js\");\nvar jQueryBridget = __webpack_require__(/*! jquery-bridget */ \"./node_modules/jquery-bridget/jquery-bridget.js\");\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  /**\n   *\n   */init: function init() {\n    // JavaScript to be fired on the catalog page\n    (function () {\n      // Get all the <h2> headings\n      var headings = document.querySelectorAll('fieldset h2');\n      Array.prototype.forEach.call(headings, function (heading) {\n        // Give each <h3> a toggle button child\n        heading.innerHTML = \"\\n\\t\\t\\t\\t<button type=\\\"button\\\" aria-expanded=\\\"false\\\">\\n\\t\\t\\t\\t\\t\".concat(heading.textContent, \"\\n\\t\\t\\t\\t\\t<svg aria-hidden=\\\"true\\\" focusable=\\\"false\\\" class=\\\"arrow\\\" width=\\\"13\\\" height=\\\"8\\\" viewBox=\\\"0 0 13 8\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><path d=\\\"M6.255 8L0 0h12.51z\\\" fill=\\\"currentColor\\\" fill-rule=\\\"evenodd\\\"></path></svg>\\n\\t\\t\\t\\t</button>\\n\\t\\t\\t  \");\n\n        // Function to create a node list\n        // of the content between this <h2> and the next\n        /**\n         * @param elem\n         */\n        var getContent = function getContent(elem) {\n          var elems = [];\n          while (elem.nextElementSibling && elem.nextElementSibling.tagName !== 'H2') {\n            elems.push(elem.nextElementSibling);\n            elem = elem.nextElementSibling;\n          }\n\n          // Delete the old versions of the content nodes\n          elems.forEach(function (node) {\n            node.parentNode.removeChild(node);\n          });\n          return elems;\n        };\n\n        // Assign the contents to be expanded/collapsed (array)\n        var contents = getContent(heading);\n\n        // Create a wrapper element for `contents` and hide it\n        var wrapper = document.createElement('div');\n        wrapper.hidden = true;\n\n        // Add each element of `contents` to `wrapper`\n        contents.forEach(function (node) {\n          wrapper.appendChild(node);\n        });\n\n        // Add the wrapped content back into the DOM\n        // after the heading\n        heading.parentNode.insertBefore(wrapper, heading.nextElementSibling);\n\n        // Assign the button\n        var btn = heading.querySelector('button');\n\n        /**\n         *\n         */\n        btn.onclick = function () {\n          // Cast the state as a boolean\n          var expanded = btn.getAttribute('aria-expanded') === 'true' || false;\n\n          // Switch the state\n          btn.setAttribute('aria-expanded', !expanded);\n          // Switch the content's visibility\n          wrapper.hidden = expanded;\n        };\n      });\n    })();\n    (function () {\n      // Get all the <h3> headings\n      var headings = document.querySelectorAll('fieldset h3');\n      Array.prototype.forEach.call(headings, function (heading) {\n        // Give each <h3> a toggle button child\n        heading.innerHTML = \"\\n\\t\\t\\t\\t<button type=\\\"button\\\" aria-expanded=\\\"false\\\">\\n\\t\\t\\t\\t\\t\".concat(heading.innerHTML, \"\\n\\t\\t\\t\\t\\t<svg class=\\\"arrow\\\" width=\\\"13\\\" height=\\\"8\\\" viewBox=\\\"0 0 13 8\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><path d=\\\"M6.255 8L0 0h12.51z\\\" fill=\\\"currentColor\\\" fill-rule=\\\"evenodd\\\"></path></svg>\\n\\t\\t\\t\\t</button>\\n\\t\\t\\t  \");\n\n        // Function to create a node list\n        // of the content between this <h2> and the next\n        /**\n         * @param elem\n         */\n        var getContent = function getContent(elem) {\n          var elems = [];\n          while (elem.nextElementSibling && elem.nextElementSibling.tagName !== 'H3') {\n            elems.push(elem.nextElementSibling);\n            elem = elem.nextElementSibling;\n          }\n\n          // Delete the old versions of the content nodes\n          elems.forEach(function (node) {\n            node.parentNode.removeChild(node);\n          });\n          return elems;\n        };\n\n        // Assign the contents to be expanded/collapsed (array)\n        var contents = getContent(heading);\n\n        // Create a wrapper element for `contents` and hide it\n        var wrapper = document.createElement('div');\n        wrapper.hidden = true;\n\n        // Add each element of `contents` to `wrapper`\n        contents.forEach(function (node) {\n          wrapper.appendChild(node);\n        });\n\n        // Add the wrapped content back into the DOM\n        // after the heading\n        heading.parentNode.insertBefore(wrapper, heading.nextElementSibling);\n\n        // Assign the button\n        var btn = heading.querySelector('button');\n\n        /**\n         *\n         */\n        btn.onclick = function () {\n          // Cast the state as a boolean\n          var expanded = btn.getAttribute('aria-expanded') === 'true' || false;\n\n          // Switch the state\n          btn.setAttribute('aria-expanded', !expanded);\n          // Switch the content's visibility\n          wrapper.hidden = expanded;\n        };\n      });\n    })();\n    jQuery(function ($) {\n      jQueryBridget('isotope', Isotope, $);\n      var $grid = $('.books');\n      $grid.isotope({\n        itemSelector: '.book',\n        getSortData: {\n          title: '.book__title a',\n          subject: '[data-subject]',\n          latest: '[data-date-published]'\n        },\n        sortAscending: {\n          title: true,\n          subject: false,\n          latest: false\n        }\n      });\n      var licenses = document.querySelector('.license-filters');\n      var subjects = document.querySelector('.subject-filters');\n      var institutions = document.querySelector('.institution-filters');\n      var sorts = document.querySelector('.sorts');\n      var clearFilters = document.querySelector('.clear-filters');\n      clearFilters.hidden = false;\n      licenses.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var subject = subjects.querySelector('input[type=\"radio\"]:checked').value ? \"[data-subject=\\\"\".concat(subjects.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = institutions.querySelector('input[type=\"radio\"]:checked').value ? \"[data-institution*=\\\"\".concat(institutions.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var license = event.target.value ? \"[data-license=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      institutions.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var subject = subjects.querySelector('input[type=\"radio\"]:checked').value ? \"[data-subject=\\\"\".concat(subjects.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var license = licenses.querySelector('input[type=\"radio\"]:checked').value ? \"[data-license=\\\"\".concat(licenses.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = event.target.value ? \"[data-institution*=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      subjects.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        var license = licenses.querySelector('input[type=\"radio\"]:checked').value ? \"[data-license=\\\"\".concat(licenses.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var institution = institutions.querySelector('input[type=\"radio\"]:checked').value ? \"[data-institution*=\\\"\".concat(institutions.querySelector('input[type=\"radio\"]:checked').value, \"\\\"]\") : '';\n        var subject = event.target.value ? \"[data-subject=\\\"\".concat(event.target.value, \"\\\"]\") : '';\n        var filterValue = subject || license || institution ? \"\".concat(subject).concat(license).concat(institution) : '*';\n        $grid.isotope({\n          filter: filterValue\n        });\n      });\n      clearFilters.addEventListener('click', function () {\n        var allLicenses = document.getElementById('all-licenses');\n        var allSubjects = document.getElementById('all-subjects');\n        var allInstitutions = document.getElementById('all-institutions');\n        allLicenses.checked = true;\n        allSubjects.checked = true;\n        allInstitutions.checked = true;\n        $grid.isotope({\n          filter: '*'\n        });\n      });\n      sorts.addEventListener('click', function (event) {\n        if (event.target.type !== 'radio') {\n          return;\n        }\n        $grid.isotope({\n          sortBy: event.target.value\n        });\n      });\n      // \t$('.filters > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\t$('.filters').toggleClass('is-active');\n      // \t\t$('.filter-groups > div').removeClass('is-active');\n      // \t});\n      // \t$('.filter-groups .subjects > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet id = $(e.currentTarget).attr('href');\n      // \t\t$(`.filter-groups .subjects:not(${id})`).removeClass('is-active');\n      // \t\t$(`.filter-groups ${id}`).toggleClass('is-active');\n      // \t});\n      // \t$('.licenses > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet id = $(e.currentTarget).attr('href');\n      // \t\t$(id).toggleClass('is-active');\n      // \t});\n      // \t$('.subjects .filter-list a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tif ($(e.currentTarget).hasClass('is-active')) {\n      // \t\t\t$('.subjects .filter-list a').removeClass('is-active');\n      // \t\t\t$('.subjects').removeClass('has-active-child');\n      // \t\t} else {\n      // \t\t\t$('.subjects .filter-list a').removeClass('is-active');\n      // \t\t\t$(e.currentTarget).addClass('is-active');\n      // \t\t\t$('.subjects').removeClass('has-active-child');\n      // \t\t\t$(e.currentTarget)\n      // \t\t\t\t.parent()\n      // \t\t\t\t.parent()\n      // \t\t\t\t.parent('.subjects')\n      // \t\t\t\t.addClass('has-active-child');\n      // \t\t}\n      // \t\tlet subjectValue = $('.subjects .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tlet licenseValue = $('.licenses .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tif (typeof licenseValue === 'undefined') {\n      // \t\t\tlicenseValue = '';\n      // \t\t} else {\n      // \t\t\tlicenseValue = `[data-license=\"${licenseValue}\"]`;\n      // \t\t}\n      // \t\tif (typeof subjectValue === 'undefined') {\n      // \t\t\tsubjectValue = '';\n      // \t\t} else {\n      // \t\t\tsubjectValue = `[data-subject=\"${subjectValue}\"]`;\n      // \t\t}\n      // \t\t$grid.isotope({ filter: `${subjectValue}${licenseValue}` });\n      // \t});\n      // \t$('.licenses .filter-list a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tif ($(e.currentTarget).hasClass('is-active')) {\n      // \t\t\t$('.licenses .filter-list a').removeClass('is-active');\n      // \t\t\t$('.licenses').removeClass('has-active-child');\n      // \t\t} else {\n      // \t\t\t$('.licenses .filter-list a').removeClass('is-active');\n      // \t\t\t$(e.currentTarget).addClass('is-active');\n      // \t\t\t$('.licenses').addClass('has-active-child');\n      // \t\t}\n      // \t\tlet subjectValue = $('.subjects .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tlet licenseValue = $('.licenses .filter-list a.is-active').attr(\n      // \t\t\t'data-filter'\n      // \t\t);\n      // \t\tif (typeof licenseValue === 'undefined') {\n      // \t\t\tlicenseValue = '';\n      // \t\t} else {\n      // \t\t\tlicenseValue = `[data-license=\"${licenseValue}\"]`;\n      // \t\t}\n      // \t\tif (typeof subjectValue === 'undefined') {\n      // \t\t\tsubjectValue = '';\n      // \t\t} else {\n      // \t\t\tsubjectValue = `[data-subject=\"${subjectValue}\"]`;\n      // \t\t}\n      // \t\t$grid.isotope({ filter: `${subjectValue}${licenseValue}` });\n      // \t});\n      // \t$('.sort > a').click(e => {\n      // \t\te.preventDefault();\n      // \t\t$('.sort').toggleClass('is-active');\n      // \t});\n      // \t$('.sorts a').click(e => {\n      // \t\te.preventDefault();\n      // \t\tlet sortBy = $(e.currentTarget).attr('data-sort');\n      // \t\t$('.sorts a').removeClass('is-active');\n      // \t\t$(e.currentTarget).addClass('is-active');\n      // \t\t$grid.isotope({ sortBy: sortBy });\n      // \t});\n    });\n  },\n  /**\n   *\n   */finalize: function finalize() {}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,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TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO0lBQ0QsQ0FBQyxDQUFFO0VBQ0osQ0FBQztFQUNEO0FBQ0Q7QUFDQSxLQUNDVyxRQUFRLHNCQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vQHByZXNzYm9va3MvcHJlc3Nib29rcy1hbGRpbmUvLi9hc3NldHMvc2NyaXB0cy9yb3V0ZXMvY2F0YWxvZy5qcz9mNWRmIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IElzb3RvcGUgPSByZXF1aXJlKCAnaXNvdG9wZS1sYXlvdXQnICk7XG5jb25zdCBqUXVlcnlCcmlkZ2V0ID0gcmVxdWlyZSggJ2pxdWVyeS1icmlkZ2V0JyApO1xuXG5leHBvcnQgZGVmYXVsdCB7XG5cdC8qKlxuXHQgKlxuXHQgKi9cblx0aW5pdCgpIHtcblx0XHQvLyBKYXZhU2NyaXB0IHRvIGJlIGZpcmVkIG9uIHRoZSBjYXRhbG9nIHBhZ2Vcblx0XHQoIGZ1bmN0aW9uICgpIHtcblx0XHRcdC8vIEdldCBhbGwgdGhlIDxoMj4gaGVhZGluZ3Ncblx0XHRcdGNvbnN0IGhlYWRpbmdzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJ2ZpZWxkc2V0IGgyJyApO1xuXG5cdFx0XHRBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKCBoZWFkaW5ncywgaGVhZGluZyA9PiB7XG5cdFx0XHRcdC8vIEdpdmUgZWFjaCA8aDM+IGEgdG9nZ2xlIGJ1dHRvbiBjaGlsZFxuXHRcdFx0XHRoZWFkaW5nLmlubmVySFRNTCA9IGBcblx0XHRcdFx0PGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgYXJpYS1leHBhbmRlZD1cImZhbHNlXCI+XG5cdFx0XHRcdFx0JHsgaGVhZGluZy50ZXh0Q29udGVudCB9XG5cdFx0XHRcdFx0PHN2ZyBhcmlhLWhpZGRlbj1cInRydWVcIiBmb2N1c2FibGU9XCJmYWxzZVwiIGNsYXNzPVwiYXJyb3dcIiB3aWR0aD1cIjEzXCIgaGVpZ2h0PVwiOFwiIHZpZXdCb3g9XCIwIDAgMTMgOFwiIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIj48cGF0aCBkPVwiTTYuMjU1IDhMMCAwaDEyLjUxelwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIiBmaWxsLXJ1bGU9XCJldmVub2RkXCI+PC9wYXRoPjwvc3ZnPlxuXHRcdFx0XHQ8L2J1dHRvbj5cblx0XHRcdCAgYDtcblxuXHRcdFx0XHQvLyBGdW5jdGlvbiB0byBjcmVhdGUgYSBub2RlIGxpc3Rcblx0XHRcdFx0Ly8gb2YgdGhlIGNvbnRlbnQgYmV0d2VlbiB0aGlzIDxoMj4gYW5kIHRoZSBuZXh0XG5cdFx0XHRcdC8qKlxuXHRcdFx0XHQgKiBAcGFyYW0gZWxlbVxuXHRcdFx0XHQgKi9cblx0XHRcdFx0Y29uc3QgZ2V0Q29udGVudCA9IGVsZW0gPT4ge1xuXHRcdFx0XHRcdGxldCBlbGVtcyA9IFtdO1xuXHRcdFx0XHRcdHdoaWxlIChcblx0XHRcdFx0XHRcdGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nICYmXG5cdFx0XHRcdFx0XHRlbGVtLm5leHRFbGVtZW50U2libGluZy50YWdOYW1lICE9PSAnSDInXG5cdFx0XHRcdFx0KSB7XG5cdFx0XHRcdFx0XHRlbGVtcy5wdXNoKCBlbGVtLm5leHRFbGVtZW50U2libGluZyApO1xuXHRcdFx0XHRcdFx0ZWxlbSA9IGVsZW0ubmV4dEVsZW1lbnRTaWJsaW5nO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIERlbGV0ZSB0aGUgb2xkIHZlcnNpb25zIG9mIHRoZSBjb250ZW50IG5vZGVzXG5cdFx0XHRcdFx0ZWxlbXMuZm9yRWFjaCggbm9kZSA9PiB7XG5cdFx0XHRcdFx0XHRub2RlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIG5vZGUgKTtcblx0XHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0XHRyZXR1cm4gZWxlbXM7XG5cdFx0XHRcdH07XG5cblx0XHRcdFx0Ly8gQXNzaWduIHRoZSBjb250ZW50cyB0byBiZSBleHBhbmRlZC9jb2xsYXBzZWQgKGFycmF5KVxuXHRcdFx0XHRsZXQgY29udGVudHMgPSBnZXRDb250ZW50KCBoZWFkaW5nICk7XG5cblx0XHRcdFx0Ly8gQ3JlYXRlIGEgd3JhcHBlciBlbGVtZW50IGZvciBgY29udGVudHNgIGFuZCBoaWRlIGl0XG5cdFx0XHRcdGxldCB3cmFwcGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblx0XHRcdFx0d3JhcHBlci5oaWRkZW4gPSB0cnVlO1xuXG5cdFx0XHRcdC8vIEFkZCBlYWNoIGVsZW1lbnQgb2YgYGNvbnRlbnRzYCB0byBgd3JhcHBlcmBcblx0XHRcdFx0Y29udGVudHMuZm9yRWFjaCggbm9kZSA9PiB7XG5cdFx0XHRcdFx0d3JhcHBlci5hcHBlbmRDaGlsZCggbm9kZSApO1xuXHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0Ly8gQWRkIHRoZSB3cmFwcGVkIGNvbnRlbnQgYmFjayBpbnRvIHRoZSBET01cblx0XHRcdFx0Ly8gYWZ0ZXIgdGhlIGhlYWRpbmdcblx0XHRcdFx0aGVhZGluZy5wYXJlbnROb2RlLmluc2VydEJlZm9yZSggd3JhcHBlciwgaGVhZGluZy5uZXh0RWxlbWVudFNpYmxpbmcgKTtcblxuXHRcdFx0XHQvLyBBc3NpZ24gdGhlIGJ1dHRvblxuXHRcdFx0XHRsZXQgYnRuID0gaGVhZGluZy5xdWVyeVNlbGVjdG9yKCAnYnV0dG9uJyApO1xuXG5cdFx0XHRcdC8qKlxuXHRcdFx0XHQgKlxuXHRcdFx0XHQgKi9cblx0XHRcdFx0YnRuLm9uY2xpY2sgPSAoKSA9PiB7XG5cdFx0XHRcdFx0Ly8gQ2FzdCB0aGUgc3RhdGUgYXMgYSBib29sZWFuXG5cdFx0XHRcdFx0bGV0IGV4cGFuZGVkID0gYnRuLmdldEF0dHJpYnV0ZSggJ2FyaWEtZXhwYW5kZWQnICkgPT09ICd0cnVlJyB8fCBmYWxzZTtcblxuXHRcdFx0XHRcdC8vIFN3aXRjaCB0aGUgc3RhdGVcblx0XHRcdFx0XHRidG4uc2V0QXR0cmlidXRlKCAnYXJpYS1leHBhbmRlZCcsICEgZXhwYW5kZWQgKTtcblx0XHRcdFx0XHQvLyBTd2l0Y2ggdGhlIGNvbnRlbnQncyB2aXNpYmlsaXR5XG5cdFx0XHRcdFx0d3JhcHBlci5oaWRkZW4gPSBleHBhbmRlZDtcblx0XHRcdFx0fTtcblx0XHRcdH0gKTtcblx0XHR9ICkoKTtcblxuXHRcdCggZnVuY3Rpb24gKCkge1xuXHRcdFx0Ly8gR2V0IGFsbCB0aGUgPGgzPiBoZWFkaW5nc1xuXHRcdFx0Y29uc3QgaGVhZGluZ3MgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnZmllbGRzZXQgaDMnICk7XG5cblx0XHRcdEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoIGhlYWRpbmdzLCBoZWFkaW5nID0+IHtcblx0XHRcdFx0Ly8gR2l2ZSBlYWNoIDxoMz4gYSB0b2dnbGUgYnV0dG9uIGNoaWxkXG5cdFx0XHRcdGhlYWRpbmcuaW5uZXJIVE1MID0gYFxuXHRcdFx0XHQ8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBhcmlhLWV4cGFuZGVkPVwiZmFsc2VcIj5cblx0XHRcdFx0XHQkeyBoZWFkaW5nLmlubmVySFRNTCB9XG5cdFx0XHRcdFx0PHN2ZyBjbGFzcz1cImFycm93XCIgd2lkdGg9XCIxM1wiIGhlaWdodD1cIjhcIiB2aWV3Qm94PVwiMCAwIDEzIDhcIiB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCI+PHBhdGggZD1cIk02LjI1NSA4TDAgMGgxMi41MXpcIiBmaWxsPVwiY3VycmVudENvbG9yXCIgZmlsbC1ydWxlPVwiZXZlbm9kZFwiPjwvcGF0aD48L3N2Zz5cblx0XHRcdFx0PC9idXR0b24+XG5cdFx0XHQgIGA7XG5cblx0XHRcdFx0Ly8gRnVuY3Rpb24gdG8gY3JlYXRlIGEgbm9kZSBsaXN0XG5cdFx0XHRcdC8vIG9mIHRoZSBjb250ZW50IGJldHdlZW4gdGhpcyA8aDI+IGFuZCB0aGUgbmV4dFxuXHRcdFx0XHQvKipcblx0XHRcdFx0ICogQHBhcmFtIGVsZW1cblx0XHRcdFx0ICovXG5cdFx0XHRcdGNvbnN0IGdldENvbnRlbnQgPSBlbGVtID0+IHtcblx0XHRcdFx0XHRsZXQgZWxlbXMgPSBbXTtcblx0XHRcdFx0XHR3aGlsZSAoXG5cdFx0XHRcdFx0XHRlbGVtLm5leHRFbGVtZW50U2libGluZyAmJlxuXHRcdFx0XHRcdFx0ZWxlbS5uZXh0RWxlbWVudFNpYmxpbmcudGFnTmFtZSAhPT0gJ0gzJ1xuXHRcdFx0XHRcdCkge1xuXHRcdFx0XHRcdFx0ZWxlbXMucHVzaCggZWxlbS5uZXh0RWxlbWVudFNpYmxpbmcgKTtcblx0XHRcdFx0XHRcdGVsZW0gPSBlbGVtLm5leHRFbGVtZW50U2libGluZztcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBEZWxldGUgdGhlIG9sZCB2ZXJzaW9ucyBvZiB0aGUgY29udGVudCBub2Rlc1xuXHRcdFx0XHRcdGVsZW1zLmZvckVhY2goIG5vZGUgPT4ge1xuXHRcdFx0XHRcdFx0bm9kZS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKCBub2RlICk7XG5cdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdFx0cmV0dXJuIGVsZW1zO1xuXHRcdFx0XHR9O1xuXG5cdFx0XHRcdC8vIEFzc2lnbiB0aGUgY29udGVudHMgdG8gYmUgZXhwYW5kZWQvY29sbGFwc2VkIChhcnJheSlcblx0XHRcdFx0bGV0IGNvbnRlbnRzID0gZ2V0Q29udGVudCggaGVhZGluZyApO1xuXG5cdFx0XHRcdC8vIENyZWF0ZSBhIHdyYXBwZXIgZWxlbWVudCBmb3IgYGNvbnRlbnRzYCBhbmQgaGlkZSBpdFxuXHRcdFx0XHRsZXQgd3JhcHBlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XG5cdFx0XHRcdHdyYXBwZXIuaGlkZGVuID0gdHJ1ZTtcblxuXHRcdFx0XHQvLyBBZGQgZWFjaCBlbGVtZW50IG9mIGBjb250ZW50c2AgdG8gYHdyYXBwZXJgXG5cdFx0XHRcdGNvbnRlbnRzLmZvckVhY2goIG5vZGUgPT4ge1xuXHRcdFx0XHRcdHdyYXBwZXIuYXBwZW5kQ2hpbGQoIG5vZGUgKTtcblx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdC8vIEFkZCB0aGUgd3JhcHBlZCBjb250ZW50IGJhY2sgaW50byB0aGUgRE9NXG5cdFx0XHRcdC8vIGFmdGVyIHRoZSBoZWFkaW5nXG5cdFx0XHRcdGhlYWRpbmcucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoIHdyYXBwZXIsIGhlYWRpbmcubmV4dEVsZW1lbnRTaWJsaW5nICk7XG5cblx0XHRcdFx0Ly8gQXNzaWduIHRoZSBidXR0b25cblx0XHRcdFx0bGV0IGJ0biA9IGhlYWRpbmcucXVlcnlTZWxlY3RvciggJ2J1dHRvbicgKTtcblxuXHRcdFx0XHQvKipcblx0XHRcdFx0ICpcblx0XHRcdFx0ICovXG5cdFx0XHRcdGJ0bi5vbmNsaWNrID0gKCkgPT4ge1xuXHRcdFx0XHRcdC8vIENhc3QgdGhlIHN0YXRlIGFzIGEgYm9vbGVhblxuXHRcdFx0XHRcdGxldCBleHBhbmRlZCA9IGJ0bi5nZXRBdHRyaWJ1dGUoICdhcmlhLWV4cGFuZGVkJyApID09PSAndHJ1ZScgfHwgZmFsc2U7XG5cblx0XHRcdFx0XHQvLyBTd2l0Y2ggdGhlIHN0YXRlXG5cdFx0XHRcdFx0YnRuLnNldEF0dHJpYnV0ZSggJ2FyaWEtZXhwYW5kZWQnLCAhIGV4cGFuZGVkICk7XG5cdFx0XHRcdFx0Ly8gU3dpdGNoIHRoZSBjb250ZW50J3MgdmlzaWJpbGl0eVxuXHRcdFx0XHRcdHdyYXBwZXIuaGlkZGVuID0gZXhwYW5kZWQ7XG5cdFx0XHRcdH07XG5cdFx0XHR9ICk7XG5cdFx0fSApKCk7XG5cblx0XHRqUXVlcnkoICQgPT4ge1xuXHRcdFx0alF1ZXJ5QnJpZGdldCggJ2lzb3RvcGUnLCBJc290b3BlLCAkICk7XG5cdFx0XHRsZXQgJGdyaWQgPSAkKCAnLmJvb2tzJyApO1xuXHRcdFx0JGdyaWQuaXNvdG9wZSgge1xuXHRcdFx0XHRpdGVtU2VsZWN0b3I6ICcuYm9vaycsXG5cdFx0XHRcdGdldFNvcnREYXRhOiB7XG5cdFx0XHRcdFx0dGl0bGU6ICcuYm9va19fdGl0bGUgYScsXG5cdFx0XHRcdFx0c3ViamVjdDogJ1tkYXRhLXN1YmplY3RdJyxcblx0XHRcdFx0XHRsYXRlc3Q6ICdbZGF0YS1kYXRlLXB1Ymxpc2hlZF0nLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHRzb3J0QXNjZW5kaW5nOiB7XG5cdFx0XHRcdFx0dGl0bGU6IHRydWUsXG5cdFx0XHRcdFx0c3ViamVjdDogZmFsc2UsXG5cdFx0XHRcdFx0bGF0ZXN0OiBmYWxzZSxcblx0XHRcdFx0fSxcblx0XHRcdH0gKTtcblx0XHRcdGxldCBsaWNlbnNlcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcubGljZW5zZS1maWx0ZXJzJyApO1xuXHRcdFx0bGV0IHN1YmplY3RzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5zdWJqZWN0LWZpbHRlcnMnICk7XG5cdFx0XHRsZXQgaW5zdGl0dXRpb25zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5pbnN0aXR1dGlvbi1maWx0ZXJzJyApO1xuXHRcdFx0bGV0IHNvcnRzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5zb3J0cycgKTtcblx0XHRcdGxldCBjbGVhckZpbHRlcnMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCAnLmNsZWFyLWZpbHRlcnMnICk7XG5cdFx0XHRjbGVhckZpbHRlcnMuaGlkZGVuID0gZmFsc2U7XG5cdFx0XHRsaWNlbnNlcy5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCBmdW5jdGlvbiAoIGV2ZW50ICkge1xuXHRcdFx0XHRpZiAoIGV2ZW50LnRhcmdldC50eXBlICE9PSAncmFkaW8nICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGNvbnN0IHN1YmplY3QgPSBzdWJqZWN0cy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWVcblx0XHRcdFx0XHQ/IGBbZGF0YS1zdWJqZWN0PVwiJHsgc3ViamVjdHMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlIH1cIl1gXG5cdFx0XHRcdFx0OiAnJztcblx0XHRcdFx0Y29uc3QgaW5zdGl0dXRpb24gPSBpbnN0aXR1dGlvbnMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtaW5zdGl0dXRpb24qPVwiJHsgaW5zdGl0dXRpb25zLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cdFx0XHRcdGNvbnN0IGxpY2Vuc2UgPSBldmVudC50YXJnZXQudmFsdWVcblx0XHRcdFx0XHQ/IGBbZGF0YS1saWNlbnNlPVwiJHsgZXZlbnQudGFyZ2V0LnZhbHVlIH1cIl1gXG5cdFx0XHRcdFx0OiAnJztcblxuXHRcdFx0XHRjb25zdCBmaWx0ZXJWYWx1ZSA9IHN1YmplY3QgfHwgbGljZW5zZSB8fCBpbnN0aXR1dGlvbiA/IGAkeyBzdWJqZWN0IH0keyBsaWNlbnNlIH0keyBpbnN0aXR1dGlvbiB9YCA6ICcqJztcblxuXHRcdFx0XHQkZ3JpZC5pc290b3BlKCB7IGZpbHRlcjogZmlsdGVyVmFsdWUgfSApO1xuXHRcdFx0fSApO1xuXHRcdFx0aW5zdGl0dXRpb25zLmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIGZ1bmN0aW9uICggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggZXZlbnQudGFyZ2V0LnR5cGUgIT09ICdyYWRpbycgKSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Y29uc3Qgc3ViamVjdCA9IHN1YmplY3RzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLXN1YmplY3Q9XCIkeyBzdWJqZWN0cy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXHRcdFx0XHRjb25zdCBsaWNlbnNlID0gbGljZW5zZXMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtbGljZW5zZT1cIiR7IGxpY2Vuc2VzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cdFx0XHRcdGNvbnN0IGluc3RpdHV0aW9uID0gZXZlbnQudGFyZ2V0LnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtaW5zdGl0dXRpb24qPVwiJHsgZXZlbnQudGFyZ2V0LnZhbHVlIH1cIl1gXG5cdFx0XHRcdFx0OiAnJztcblxuXHRcdFx0XHRjb25zdCBmaWx0ZXJWYWx1ZSA9IHN1YmplY3QgfHwgbGljZW5zZSB8fCBpbnN0aXR1dGlvbiA/IGAkeyBzdWJqZWN0IH0keyBsaWNlbnNlIH0keyBpbnN0aXR1dGlvbiB9YCA6ICcqJztcblxuXHRcdFx0XHQkZ3JpZC5pc290b3BlKCB7IGZpbHRlcjogZmlsdGVyVmFsdWUgfSApO1xuXHRcdFx0fSApO1xuXHRcdFx0c3ViamVjdHMuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24gKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCBldmVudC50YXJnZXQudHlwZSAhPT0gJ3JhZGlvJyApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb25zdCBsaWNlbnNlID0gbGljZW5zZXMucXVlcnlTZWxlY3RvciggJ2lucHV0W3R5cGU9XCJyYWRpb1wiXTpjaGVja2VkJyApLnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtbGljZW5zZT1cIiR7IGxpY2Vuc2VzLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cdFx0XHRcdGNvbnN0IGluc3RpdHV0aW9uID0gaW5zdGl0dXRpb25zLnF1ZXJ5U2VsZWN0b3IoICdpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCcgKS52YWx1ZVxuXHRcdFx0XHRcdD8gYFtkYXRhLWluc3RpdHV0aW9uKj1cIiR7IGluc3RpdHV0aW9ucy5xdWVyeVNlbGVjdG9yKCAnaW5wdXRbdHlwZT1cInJhZGlvXCJdOmNoZWNrZWQnICkudmFsdWUgfVwiXWBcblx0XHRcdFx0XHQ6ICcnO1xuXHRcdFx0XHRjb25zdCBzdWJqZWN0ID0gZXZlbnQudGFyZ2V0LnZhbHVlXG5cdFx0XHRcdFx0PyBgW2RhdGEtc3ViamVjdD1cIiR7IGV2ZW50LnRhcmdldC52YWx1ZSB9XCJdYFxuXHRcdFx0XHRcdDogJyc7XG5cblx0XHRcdFx0Y29uc3QgZmlsdGVyVmFsdWUgPSBzdWJqZWN0IHx8IGxpY2Vuc2UgfHwgaW5zdGl0dXRpb24gPyBgJHsgc3ViamVjdCB9JHsgbGljZW5zZSB9JHsgaW5zdGl0dXRpb24gfWAgOiAnKic7XG5cblx0XHRcdFx0JGdyaWQuaXNvdG9wZSggeyBmaWx0ZXI6IGZpbHRlclZhbHVlIH0gKTtcblx0XHRcdH0gKTtcblx0XHRcdGNsZWFyRmlsdGVycy5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdGxldCBhbGxMaWNlbnNlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnYWxsLWxpY2Vuc2VzJyApO1xuXHRcdFx0XHRsZXQgYWxsU3ViamVjdHMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggJ2FsbC1zdWJqZWN0cycgKTtcblx0XHRcdFx0bGV0IGFsbEluc3RpdHV0aW9ucyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnYWxsLWluc3RpdHV0aW9ucycgKTtcblx0XHRcdFx0YWxsTGljZW5zZXMuY2hlY2tlZCA9IHRydWU7XG5cdFx0XHRcdGFsbFN1YmplY3RzLmNoZWNrZWQgPSB0cnVlO1xuXHRcdFx0XHRhbGxJbnN0aXR1dGlvbnMuY2hlY2tlZCA9IHRydWU7XG5cdFx0XHRcdCRncmlkLmlzb3RvcGUoIHsgZmlsdGVyOiAnKicgfSApO1xuXHRcdFx0fSApO1xuXHRcdFx0c29ydHMuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24gKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCBldmVudC50YXJnZXQudHlwZSAhPT0gJ3JhZGlvJyApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblx0XHRcdFx0JGdyaWQuaXNvdG9wZSggeyBzb3J0Qnk6IGV2ZW50LnRhcmdldC52YWx1ZSB9ICk7XG5cdFx0XHR9ICk7XG5cdFx0XHQvLyBcdCQoJy5maWx0ZXJzID4gYScpLmNsaWNrKGUgPT4ge1xuXHRcdFx0Ly8gXHRcdGUucHJldmVudERlZmF1bHQoKTtcblx0XHRcdC8vIFx0XHQkKCcuZmlsdGVycycpLnRvZ2dsZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHQkKCcuZmlsdGVyLWdyb3VwcyA+IGRpdicpLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0fSk7XG5cdFx0XHQvLyBcdCQoJy5maWx0ZXItZ3JvdXBzIC5zdWJqZWN0cyA+IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0bGV0IGlkID0gJChlLmN1cnJlbnRUYXJnZXQpLmF0dHIoJ2hyZWYnKTtcblx0XHRcdC8vIFx0XHQkKGAuZmlsdGVyLWdyb3VwcyAuc3ViamVjdHM6bm90KCR7aWR9KWApLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHQkKGAuZmlsdGVyLWdyb3VwcyAke2lkfWApLnRvZ2dsZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0fSk7XG5cdFx0XHQvLyBcdCQoJy5saWNlbnNlcyA+IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0bGV0IGlkID0gJChlLmN1cnJlbnRUYXJnZXQpLmF0dHIoJ2hyZWYnKTtcblx0XHRcdC8vIFx0XHQkKGlkKS50b2dnbGVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdFx0Ly8gXHQkKCcuc3ViamVjdHMgLmZpbHRlci1saXN0IGEnKS5jbGljayhlID0+IHtcblx0XHRcdC8vIFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHQvLyBcdFx0aWYgKCQoZS5jdXJyZW50VGFyZ2V0KS5oYXNDbGFzcygnaXMtYWN0aXZlJykpIHtcblx0XHRcdC8vIFx0XHRcdCQoJy5zdWJqZWN0cyAuZmlsdGVyLWxpc3QgYScpLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHRcdCQoJy5zdWJqZWN0cycpLnJlbW92ZUNsYXNzKCdoYXMtYWN0aXZlLWNoaWxkJyk7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdCQoJy5zdWJqZWN0cyAuZmlsdGVyLWxpc3QgYScpLnJlbW92ZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHRcdCQoZS5jdXJyZW50VGFyZ2V0KS5hZGRDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0XHQkKCcuc3ViamVjdHMnKS5yZW1vdmVDbGFzcygnaGFzLWFjdGl2ZS1jaGlsZCcpO1xuXHRcdFx0Ly8gXHRcdFx0JChlLmN1cnJlbnRUYXJnZXQpXG5cdFx0XHQvLyBcdFx0XHRcdC5wYXJlbnQoKVxuXHRcdFx0Ly8gXHRcdFx0XHQucGFyZW50KClcblx0XHRcdC8vIFx0XHRcdFx0LnBhcmVudCgnLnN1YmplY3RzJylcblx0XHRcdC8vIFx0XHRcdFx0LmFkZENsYXNzKCdoYXMtYWN0aXZlLWNoaWxkJyk7XG5cdFx0XHQvLyBcdFx0fVxuXHRcdFx0Ly8gXHRcdGxldCBzdWJqZWN0VmFsdWUgPSAkKCcuc3ViamVjdHMgLmZpbHRlci1saXN0IGEuaXMtYWN0aXZlJykuYXR0cihcblx0XHRcdC8vIFx0XHRcdCdkYXRhLWZpbHRlcidcblx0XHRcdC8vIFx0XHQpO1xuXHRcdFx0Ly8gXHRcdGxldCBsaWNlbnNlVmFsdWUgPSAkKCcubGljZW5zZXMgLmZpbHRlci1saXN0IGEuaXMtYWN0aXZlJykuYXR0cihcblx0XHRcdC8vIFx0XHRcdCdkYXRhLWZpbHRlcidcblx0XHRcdC8vIFx0XHQpO1xuXHRcdFx0Ly8gXHRcdGlmICh0eXBlb2YgbGljZW5zZVZhbHVlID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0Ly8gXHRcdFx0bGljZW5zZVZhbHVlID0gJyc7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdGxpY2Vuc2VWYWx1ZSA9IGBbZGF0YS1saWNlbnNlPVwiJHtsaWNlbnNlVmFsdWV9XCJdYDtcblx0XHRcdC8vIFx0XHR9XG5cdFx0XHQvLyBcdFx0aWYgKHR5cGVvZiBzdWJqZWN0VmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XG5cdFx0XHQvLyBcdFx0XHRzdWJqZWN0VmFsdWUgPSAnJztcblx0XHRcdC8vIFx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gXHRcdFx0c3ViamVjdFZhbHVlID0gYFtkYXRhLXN1YmplY3Q9XCIke3N1YmplY3RWYWx1ZX1cIl1gO1xuXHRcdFx0Ly8gXHRcdH1cblx0XHRcdC8vIFx0XHQkZ3JpZC5pc290b3BlKHsgZmlsdGVyOiBgJHtzdWJqZWN0VmFsdWV9JHtsaWNlbnNlVmFsdWV9YCB9KTtcblx0XHRcdC8vIFx0fSk7XG5cdFx0XHQvLyBcdCQoJy5saWNlbnNlcyAuZmlsdGVyLWxpc3QgYScpLmNsaWNrKGUgPT4ge1xuXHRcdFx0Ly8gXHRcdGUucHJldmVudERlZmF1bHQoKTtcblx0XHRcdC8vIFx0XHRpZiAoJChlLmN1cnJlbnRUYXJnZXQpLmhhc0NsYXNzKCdpcy1hY3RpdmUnKSkge1xuXHRcdFx0Ly8gXHRcdFx0JCgnLmxpY2Vuc2VzIC5maWx0ZXItbGlzdCBhJykucmVtb3ZlQ2xhc3MoJ2lzLWFjdGl2ZScpO1xuXHRcdFx0Ly8gXHRcdFx0JCgnLmxpY2Vuc2VzJykucmVtb3ZlQ2xhc3MoJ2hhcy1hY3RpdmUtY2hpbGQnKTtcblx0XHRcdC8vIFx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gXHRcdFx0JCgnLmxpY2Vuc2VzIC5maWx0ZXItbGlzdCBhJykucmVtb3ZlQ2xhc3MoJ2lzLWFjdGl2ZScpO1xuXHRcdFx0Ly8gXHRcdFx0JChlLmN1cnJlbnRUYXJnZXQpLmFkZENsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHRcdCQoJy5saWNlbnNlcycpLmFkZENsYXNzKCdoYXMtYWN0aXZlLWNoaWxkJyk7XG5cdFx0XHQvLyBcdFx0fVxuXHRcdFx0Ly8gXHRcdGxldCBzdWJqZWN0VmFsdWUgPSAkKCcuc3ViamVjdHMgLmZpbHRlci1saXN0IGEuaXMtYWN0aXZlJykuYXR0cihcblx0XHRcdC8vIFx0XHRcdCdkYXRhLWZpbHRlcidcblx0XHRcdC8vIFx0XHQpO1xuXHRcdFx0Ly8gXHRcdGxldCBsaWNlbnNlVmFsdWUgPSAkKCcubGljZW5zZXMgLmZpbHRlci1saXN0IGEuaXMtYWN0aXZlJykuYXR0cihcblx0XHRcdC8vIFx0XHRcdCdkYXRhLWZpbHRlcidcblx0XHRcdC8vIFx0XHQpO1xuXHRcdFx0Ly8gXHRcdGlmICh0eXBlb2YgbGljZW5zZVZhbHVlID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0Ly8gXHRcdFx0bGljZW5zZVZhbHVlID0gJyc7XG5cdFx0XHQvLyBcdFx0fSBlbHNlIHtcblx0XHRcdC8vIFx0XHRcdGxpY2Vuc2VWYWx1ZSA9IGBbZGF0YS1saWNlbnNlPVwiJHtsaWNlbnNlVmFsdWV9XCJdYDtcblx0XHRcdC8vIFx0XHR9XG5cdFx0XHQvLyBcdFx0aWYgKHR5cGVvZiBzdWJqZWN0VmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XG5cdFx0XHQvLyBcdFx0XHRzdWJqZWN0VmFsdWUgPSAnJztcblx0XHRcdC8vIFx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gXHRcdFx0c3ViamVjdFZhbHVlID0gYFtkYXRhLXN1YmplY3Q9XCIke3N1YmplY3RWYWx1ZX1cIl1gO1xuXHRcdFx0Ly8gXHRcdH1cblx0XHRcdC8vIFx0XHQkZ3JpZC5pc290b3BlKHsgZmlsdGVyOiBgJHtzdWJqZWN0VmFsdWV9JHtsaWNlbnNlVmFsdWV9YCB9KTtcblx0XHRcdC8vIFx0fSk7XG5cdFx0XHQvLyBcdCQoJy5zb3J0ID4gYScpLmNsaWNrKGUgPT4ge1xuXHRcdFx0Ly8gXHRcdGUucHJldmVudERlZmF1bHQoKTtcblx0XHRcdC8vIFx0XHQkKCcuc29ydCcpLnRvZ2dsZUNsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0fSk7XG5cdFx0XHQvLyBcdCQoJy5zb3J0cyBhJykuY2xpY2soZSA9PiB7XG5cdFx0XHQvLyBcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0Ly8gXHRcdGxldCBzb3J0QnkgPSAkKGUuY3VycmVudFRhcmdldCkuYXR0cignZGF0YS1zb3J0Jyk7XG5cdFx0XHQvLyBcdFx0JCgnLnNvcnRzIGEnKS5yZW1vdmVDbGFzcygnaXMtYWN0aXZlJyk7XG5cdFx0XHQvLyBcdFx0JChlLmN1cnJlbnRUYXJnZXQpLmFkZENsYXNzKCdpcy1hY3RpdmUnKTtcblx0XHRcdC8vIFx0XHQkZ3JpZC5pc290b3BlKHsgc29ydEJ5OiBzb3J0QnkgfSk7XG5cdFx0XHQvLyBcdH0pO1xuXHRcdH0gKTtcblx0fSxcblx0LyoqXG5cdCAqXG5cdCAqL1xuXHRmaW5hbGl6ZSgpIHt9LFxufTtcbiJdLCJuYW1lcyI6WyJJc290b3BlIiwicmVxdWlyZSIsImpRdWVyeUJyaWRnZXQiLCJpbml0IiwiaGVhZGluZ3MiLCJkb2N1bWVudCIsInF1ZXJ5U2VsZWN0b3JBbGwiLCJBcnJheSIsInByb3RvdHlwZSIsImZvckVhY2giLCJjYWxsIiwiaGVhZGluZyIsImlubmVySFRNTCIsInRleHRDb250ZW50IiwiZ2V0Q29udGVudCIsImVsZW0iLCJlbGVtcyIsIm5leHRFbGVtZW50U2libGluZyIsInRhZ05hbWUiLCJwdXNoIiwibm9kZSIsInBhcmVudE5vZGUiLCJyZW1vdmVDaGlsZCIsImNvbnRlbnRzIiwid3JhcHBlciIsImNyZWF0ZUVsZW1lbnQiLCJoaWRkZW4iLCJhcHBlbmRDaGlsZCIsImluc2VydEJlZm9yZSIsImJ0biIsInF1ZXJ5U2VsZWN0b3IiLCJvbmNsaWNrIiwiZXhwYW5kZWQiLCJnZXRBdHRyaWJ1dGUiLCJzZXRBdHRyaWJ1dGUiLCJqUXVlcnkiLCIkIiwiJGdyaWQiLCJpc290b3BlIiwiaXRlbVNlbGVjdG9yIiwiZ2V0U29ydERhdGEiLCJ0aXRsZSIsInN1YmplY3QiLCJsYXRlc3QiLCJzb3J0QXNjZW5kaW5nIiwibGljZW5zZXMiLCJzdWJqZWN0cyIsImluc3RpdHV0aW9ucyIsInNvcnRzIiwiY2xlYXJGaWx0ZXJzIiwiYWRkRXZlbnRMaXN0ZW5lciIsImV2ZW50IiwidGFyZ2V0IiwidHlwZSIsInZhbHVlIiwiaW5zdGl0dXRpb24iLCJsaWNlbnNlIiwiZmlsdGVyVmFsdWUiLCJmaWx0ZXIiLCJhbGxMaWNlbnNlcyIsImdldEVsZW1lbnRCeUlkIiwiYWxsU3ViamVjdHMiLCJhbGxJbnN0aXR1dGlvbnMiLCJjaGVja2VkIiwic29ydEJ5IiwiZmluYWxpemUiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./assets/scripts/routes/catalog.js\n");
 
 /***/ }),
 
diff --git a/dist/scripts/aldine.js.LICENSE.txt b/dist/scripts/aldine.js.LICENSE.txt
index aa91eb9..692dd6b 100644
--- a/dist/scripts/aldine.js.LICENSE.txt
+++ b/dist/scripts/aldine.js.LICENSE.txt
@@ -15,7 +15,7 @@
  */
 
 /*!
- * Masonry v4.2.2
+ * Masonry v4.2.1
  * Cascading grid layout library
  * https://masonry.desandro.com
  * MIT License
diff --git a/dist/styles/aldine.css b/dist/styles/aldine.css
index 573f7de..cae2b0b 100644
--- a/dist/styles/aldine.css
+++ b/dist/styles/aldine.css
@@ -1840,6 +1840,104 @@ nav {
 }
 
 .book {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+  -webkit-box-pack: start;
+      -ms-flex-pack: start;
+          justify-content: flex-start;
+  width: 100%;
+  max-width: 22.9375rem;
+  height: 24.125rem;
+  margin: 0 0 2rem;
+  padding: 1.5rem 1rem 2rem;
+  border: solid 2px var(--accent);
+  background: var(--accent);
+}
+@media screen and (min-width: 30rem) {
+  .book {
+    padding: 1.5rem 1.85rem 2.1875rem;
+  }
+}
+.book a, .book p {
+  font-family: "Karla", sans-serif;
+  color: var(--accent-fg);
+}
+.book a {
+  margin: 0;
+  text-decoration: none;
+  text-align: center;
+  -webkit-hyphens: auto;
+      -ms-hyphens: auto;
+          hyphens: auto;
+}
+.book__title {
+  margin: 0;
+  font-size: 1.25rem;
+  font-weight: 500;
+  line-height: 1.2;
+  text-align: left;
+}
+@media screen and (min-width: 30rem) {
+  .book__title {
+    font-size: 1.75rem;
+  }
+}
+.book__subject {
+  margin: 0;
+  font-size: 0.75rem;
+  text-align: left;
+}
+@media screen and (min-width: 30rem) {
+  .book__subject {
+    font-size: 1rem;
+  }
+}
+.book__institutions {
+  margin: 0.5rem 0 0;
+  font-size: 0.875rem;
+  text-align: left;
+  display: box;
+  overflow: hidden;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+.book__read-more {
+  margin: auto 0 0;
+  font-size: 1rem;
+  text-align: left;
+}
+@media screen and (min-width: 30rem) {
+  .book__read-more {
+    font-size: 1.125rem;
+  }
+}
+.book__read-more a svg {
+  width: 1rem;
+  height: 1rem;
+  vertical-align: middle;
+}
+@media screen and (min-width: 30rem) {
+  .book__read-more a svg {
+    width: 1.125rem;
+    height: 1.125rem;
+    margin-left: 0.5rem;
+  }
+}
+.book:last-child {
+  margin-bottom: 0;
+}
+@media screen and (min-width: 60rem) {
+  .book:last-child {
+    margin-bottom: 2rem;
+  }
+}
+
+.featured_book {
   display: -webkit-box;
   display: -ms-flexbox;
   display: flex;
@@ -1856,13 +1954,13 @@ nav {
   border: solid 1px var(--accent);
   margin: 0 0 2rem;
 }
-.book__cover {
+.featured_book__cover {
   width: 100%;
   height: 80%;
   background-repeat: no-repeat;
   background-size: cover;
 }
-.book a {
+.featured_book a {
   margin: 0;
   text-decoration: none;
   text-align: center;
@@ -1870,7 +1968,7 @@ nav {
       -ms-hyphens: auto;
           hyphens: auto;
 }
-.book__title {
+.featured_book__title {
   display: -webkit-box;
   display: -ms-flexbox;
   display: flex;
@@ -2289,7 +2387,7 @@ body #content a.call-to-action {
         -ms-flex-pack: center;
             justify-content: center;
   }
-  .latest-books .books .book {
+  .latest-books .books .book, .latest-books .books .featured_book {
     margin-right: 1rem;
     margin-left: 1rem;
   }
@@ -2312,24 +2410,24 @@ body #content a.call-to-action {
   text-align: center;
 }
 
-fieldset {
+.network-catalog fieldset {
   border-top: solid 2px var(--accent);
   font-family: "Karla", sans-serif;
 }
-fieldset h2,
-fieldset h3 {
+.network-catalog fieldset h2,
+.network-catalog fieldset h3 {
   margin-bottom: 0;
   font-size: 1rem;
   font-weight: bold;
   text-align: left;
   text-transform: none;
 }
-fieldset h2::before,
-fieldset h3::before {
+.network-catalog fieldset h2::before,
+.network-catalog fieldset h3::before {
   display: none;
 }
-fieldset h2 button,
-fieldset h3 button {
+.network-catalog fieldset h2 button,
+.network-catalog fieldset h3 button {
   display: -webkit-box;
   display: -ms-flexbox;
   display: flex;
@@ -2346,36 +2444,36 @@ fieldset h3 button {
   margin: 0;
   border-top: 0;
 }
-fieldset h2 button svg,
-fieldset h3 button svg {
+.network-catalog fieldset h2 button svg,
+.network-catalog fieldset h3 button svg {
   display: block;
   float: right;
   margin-top: 0.5rem;
 }
-fieldset h2 button:hover, fieldset h2 button:focus,
-fieldset h3 button:hover,
-fieldset h3 button:focus {
+.network-catalog fieldset h2 button:hover, .network-catalog fieldset h2 button:focus,
+.network-catalog fieldset h3 button:hover,
+.network-catalog fieldset h3 button:focus {
   color: var(--primary);
   background: var(--bg-body);
 }
-fieldset h2 button:active,
-fieldset h3 button:active {
+.network-catalog fieldset h2 button:active,
+.network-catalog fieldset h3 button:active {
   -webkit-box-shadow: none;
           box-shadow: none;
 }
-fieldset [aria-expanded=true] {
+.network-catalog fieldset [aria-expanded=true] {
   color: var(--primary);
 }
-fieldset [aria-expanded=true] svg {
+.network-catalog fieldset [aria-expanded=true] svg {
   -webkit-transform: rotate(180deg);
        -o-transform: rotate(180deg);
           transform: rotate(180deg);
 }
-fieldset h2 [aria-expanded=true] {
+.network-catalog fieldset h2 [aria-expanded=true] {
   border-bottom: solid 2px var(--accent);
   background: var(--body-bg-alt);
 }
-fieldset [type=radio] {
+.network-catalog fieldset [type=radio] {
   position: absolute !important;
   width: 1px !important;
   height: 1px !important;
@@ -2384,28 +2482,28 @@ fieldset [type=radio] {
   overflow: hidden !important;
   clip: rect(1px, 1px, 1px, 1px);
 }
-fieldset .label {
+.network-catalog fieldset .label {
   display: inline-block;
   width: calc(100% - 2rem);
 }
-fieldset [type=radio] + label {
+.network-catalog fieldset [type=radio] + label {
   cursor: pointer;
   display: block;
   padding: 1rem 1.1875rem;
   margin-bottom: 0;
 }
-fieldset [type=radio] + label svg {
+.network-catalog fieldset [type=radio] + label svg {
   display: none;
 }
-fieldset [type=radio]:focus label {
+.network-catalog fieldset [type=radio]:focus label {
   cursor: pointer;
   display: block;
 }
-fieldset [type=radio]:checked + label {
+.network-catalog fieldset [type=radio]:checked + label {
   color: var(--primary);
   font-weight: bold;
 }
-fieldset [type=radio]:checked + label svg {
+.network-catalog fieldset [type=radio]:checked + label svg {
   display: block;
   float: right;
   margin-top: 0.5rem;
@@ -2413,7 +2511,7 @@ fieldset [type=radio]:checked + label svg {
   height: 1rem;
   fill: transparent;
 }
-fieldset:last-of-type {
+.network-catalog fieldset:last-of-type {
   border-bottom: solid 2px var(--accent);
   margin-bottom: 1rem;
 }
@@ -2432,7 +2530,7 @@ fieldset:last-of-type {
   padding: 0;
   margin: 2rem 0 1rem 1rem;
 }
-.page-template-page-catalog .book {
+.page-template-page-catalog .book, .page-template-page-catalog .featured_book {
   height: 14.375rem;
   width: calc(50% - 1rem);
   margin: 0 1rem 1rem 0;
diff --git a/inc/customizer/namespace.php b/inc/customizer/namespace.php
index 241a538..b2a7385 100644
--- a/inc/customizer/namespace.php
+++ b/inc/customizer/namespace.php
@@ -209,6 +209,7 @@ function customize_register( \WP_Customize_Manager $wp_customize ) {
 
 		$options = get_catalog_options();
 		$books = collect( $options['books'] )->pluck( 'title', 'id' )->toArray();
+		$books = [ '' => __( 'Select a book', 'pressbooks-aldine' ) ] + $books;
 
 		foreach ( range( 1, MAX_FEATURED_BOOKS ) as $i ) {
 			$wp_customize->add_setting(
diff --git a/inc/helpers/namespace.php b/inc/helpers/namespace.php
index 0c9cf3f..9533865 100644
--- a/inc/helpers/namespace.php
+++ b/inc/helpers/namespace.php
@@ -70,8 +70,13 @@ function get_featured_books(): array {
 		}
 	}
 
+	if ( empty( $featured_books ) ) {
+		return [];
+	}
+
 	$args = [
 		'site__in'      => $featured_books,
+		'sort_by_featured' => true,
 	];
 
 	return get_catalog_data( $args );
@@ -109,8 +114,114 @@ function get_catalog_data( array $args ): array {
 			$books[] = $book;
 		}
 	}
+	// Sort by featured books.
+	if ( isset( $args['sort_by_featured'] ) ) {
+		usort( $books, function ( $a, $b ) use ( $args ) {
+			return array_search( $a['id'], $args['site__in'], true ) - array_search($b['id'],
+			$args['site__in'], true);
+		} );
+	}
+
+	return [
+		'books' => $books,
+	];
+}
+
+/**
+ * Get paginated catalog data
+ *
+ * @param int $page Catalog page
+ * @param int $per_page Books per page
+ * @param string $orderby Sort order
+ * @param string $license Copyright license
+ * @param string $subject Subject
+ *
+ * @return array
+ */
+function get_paginated_catalog_data( $page = 1, $per_page = 10, $orderby = 'title', $license = '', $subject = '' ) {
+
+	if ( ! defined( 'PB_PLUGIN_VERSION' ) ) {
+		return [
+			'pages' => 0,
+			'books' => [],
+		];
+	}
+
+	$dc = BookDataCollector::init();
+
+	/**
+	 * Filter the WP_Site_Query args for the catalog display.
+	 *
+	 * @since 1.0.0
+	 */
+	$args = apply_filters(
+		'pb_aldine_catalog_query_args',
+		/**
+		 * Deprecation notice
+		 *
+		 * @deprecated 1.0.0
+		 *
+		 * @see Pressbooks Publisher
+		 */
+		apply_filters(
+			'pb_publisher_catalog_query_args',
+			[
+				'number' => 1000000,
+				'meta_key' => $dc::IN_CATALOG, // @codingStandardsIgnoreLine
+				'meta_value' => 1, // @codingStandardsIgnoreLine
+				'public' => 1,
+				'archived' => 0,
+				'spam' => 0,
+				'deleted' => 0,
+				'network_id' => get_network()->site_id,
+			]
+		)
+	);
+
+	/**
+	 * WordPress site
+	 *
+	 * @var \WP_Site $site
+	 */
+
+	$sites_in_catalog = [];
+	$sites = get_sites( $args );
+	foreach ( $sites as $site ) {
+		$site->pb_title = $dc->get( $site->blog_id, $dc::TITLE );
+		$sites_in_catalog[] = $site;
+	}
+	if ( $orderby === 'latest' ) {
+		$sites_in_catalog = wp_list_sort( $sites_in_catalog, 'last_updated', 'DESC' );
+	} else {
+		$sites_in_catalog = wp_list_sort( $sites_in_catalog, 'pb_title', 'ASC' );
+	}
+
+	$total_pages = ceil( count( $sites_in_catalog ) / $per_page );
+	$offset = ( $page - 1 ) * $per_page;
+	$books = [];
+	foreach ( $sites_in_catalog as $i => $site ) {
+		if ( $i < $offset ) {
+			continue;
+		}
+
+		$book_information = $dc->get( $site->blog_id, $dc::BOOK_INFORMATION_ARRAY );
+		if ( is_array( $book_information ) && ! empty( $book_information ) ) {
+			$schema = book_information_to_schema( $book_information );
+			$book['title'] = $schema['name'];
+			$book['date-published'] = $schema['datePublished'] ?? '';
+			$book['subject'] = $schema['about'][0]['identifier'] ?? '';
+			$book['link'] = get_blogaddress_by_id( $site->blog_id );
+			$book['metadata'] = $schema;
+			$books[] = $book;
+		}
+
+		if ( count( $books ) >= $per_page ) {
+			break;
+		}
+	}
 
 	return [
+		'pages' => $total_pages,
 		'books' => $books,
 	];
 }
diff --git a/languages/pressbooks-aldine.pot b/languages/pressbooks-aldine.pot
index 0577f8e..a27ebfb 100644
--- a/languages/pressbooks-aldine.pot
+++ b/languages/pressbooks-aldine.pot
@@ -2,16 +2,16 @@
 # This file is distributed under the GNU GPL v3 or later.
 msgid ""
 msgstr ""
-"Project-Id-Version: Aldine 1.16.1\n"
+"Project-Id-Version: Aldine 1.17.0\n"
 "Report-Msgid-Bugs-To: https://github.com/pressbooks/pressbooks-aldine/issues\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"POT-Creation-Date: 2022-10-17T15:53:25+00:00\n"
+"POT-Creation-Date: 2022-11-04T17:15:14+00:00\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"X-Generator: WP-CLI 2.7.0\n"
+"X-Generator: WP-CLI 2.7.1\n"
 "X-Domain: pressbooks-aldine\n"
 
 #. Theme Name of the theme
diff --git a/page-catalog.php b/page-catalog.php
index 2610783..d94ebc4 100644
--- a/page-catalog.php
+++ b/page-catalog.php
@@ -18,15 +18,15 @@ use function Aldine\Helpers\get_available_institutions;
 use function Aldine\Helpers\get_available_licenses;
 use function Aldine\Helpers\get_available_subjects;
 use function Aldine\Helpers\get_catalog_licenses;
-use function Aldine\Helpers\get_featured_books;
 use function Aldine\Helpers\get_institutions;
+use function Aldine\Helpers\get_paginated_catalog_data;
 
 $current_page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
 $orderby = ( get_query_var( 'orderby' ) ) ? get_query_var( 'orderby' ) : 'title';
 $subject = ( get_query_var( 'subject' ) ) ? get_query_var( 'subject' ) : '';
 $license = ( get_query_var( 'license' ) ) ? get_query_var( 'license' ) : '';
 $institution = get_query_var( 'institution' ) ?? '';
-$catalog_data = get_featured_books();
+$catalog_data = get_paginated_catalog_data();
 $previous_page = ( $current_page > 1 ) ? $current_page - 1 : 0;
 $next_page = $current_page + 1;
 $licenses = get_catalog_licenses();
diff --git a/partials/book.php b/partials/book.php
index 7dd6c58..01f05a9 100644
--- a/partials/book.php
+++ b/partials/book.php
@@ -6,10 +6,54 @@
  */
 
 use function \Aldine\Helpers\maybe_truncate_string;
+use function \Pressbooks\Metadata\is_bisac;
+
+$subject = ( isset( $book['subject'] ) && ! is_bisac( $book['subject'] ) ) ? substr( $book['subject'], 0, 2 ) : '';
+$date = ( isset( $book['metadata']['datePublished'] ) ) ? str_replace( '-', '', $book['metadata']['datePublished'] ) : '';
+$institution_codes = array_map( static function ( $item ) {
+	return $item['code'];
+}, $book['metadata']['institutions'] ?? [] );
+$institution_names = array_map( static function ( $item ) {
+	return \Pressbooks\Metadata\get_institution_name( $item['code'] );
+}, $book['metadata']['institutions'] ?? [] );
 ?>
-<div class="book">
-	<div class="book__cover" style="background-image: url('<?php echo $book['metadata']['image']; ?>' );"></div>
+<li class="book"
+		<?php
+		if ( $date ) {
+			?>
+			data-date-published="<?php echo $date; ?>"<?php } ?>
+	data-license="<?php echo ( new \Pressbooks\Licensing() )->getLicenseFromUrl( $book['metadata']['license']['url'] ); ?>"
+	data-institution="<?php echo implode( ',', $institution_codes ); ?>"
+		<?php
+		if ( ! empty( $subject ) ) {
+			?>
+			data-subject="<?php echo $subject ?>"<?php } ?>
+>
 	<p class="book__title">
 		<a href="<?php echo $book['link']; ?>"><?php echo maybe_truncate_string( $book['metadata']['name'] ); ?></a>
 	</p>
-</div>
+	<?php
+	/*
+	<?php if (isset( $book['metadata']['author'] ) ) { ?>
+	<p class="book__author">
+		<?php _e( 'By', 'pressbooks-aldine' ); ?> <?php foreach ( $book['metadata']['author'] as $author ) {
+			echo $author['name'];
+		} ?>
+	</p>
+	<?php } ?>
+	*/
+	?>
+	<?php if ( ! empty( $subject ) ) { ?>
+		<p class="book__subject">
+			<a href="<?php echo network_home_url( "/catalog/#$subject" ) ?>"><?php echo \Pressbooks\Metadata\get_subject_from_thema( $book['subject'] ); ?></a>
+		</p>
+	<?php } ?>
+	<?php if ( $institution_names ) : ?>
+		<p class="book__institutions">
+			<?php echo implode( ', ', $institution_names ); ?>
+		</p>
+	<?php endif; ?>
+	<p class="book__read-more">
+		<a href="<?php echo $book['link']; ?>"><?php _e( 'About this book', 'pressbooks-aldine' ); ?> <svg aria-hidden="true"><use xlink:href="#arrow-right" /></svg></a>
+	</p>
+</li>
diff --git a/partials/content-front-page.php b/partials/content-front-page.php
index 85bb0d6..f667307 100644
--- a/partials/content-front-page.php
+++ b/partials/content-front-page.php
@@ -56,7 +56,7 @@ if ( $catalog_page ) {
 		<div class="books">
 			<?php
 			foreach ( $catalog_data['books'] as $book ) :
-				include( locate_template( 'partials/book.php' ) );
+				include( locate_template( 'partials/featured-book.php' ) );
 			endforeach;
 			?>
 		</div>
diff --git a/partials/content-page-catalog.php b/partials/content-page-catalog.php
index 4d6b18e..97c4aa4 100644
--- a/partials/content-page-catalog.php
+++ b/partials/content-page-catalog.php
@@ -89,7 +89,7 @@
 	endforeach;
 	?>
 </ul>
-<?php if ( isset($catalog_data['pages']) && $catalog_data['pages'] > 1 ) : ?>
+<?php if ( isset( $catalog_data['pages'] ) && $catalog_data['pages'] > 1 ) : ?>
 <nav class="catalog-navigation">
 	<?php
 	if ( $previous_page ) :
diff --git a/partials/featured-book.php b/partials/featured-book.php
new file mode 100644
index 0000000..09af159
--- /dev/null
+++ b/partials/featured-book.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Template for displaying books in network catalog
+ *
+ * @package Aldine
+ */
+
+use function \Aldine\Helpers\maybe_truncate_string;
+?>
+<div class="featured_book">
+	<div class="featured_book__cover" style="background-image: url('<?php echo $book['metadata']['image']; ?>' );"></div>
+	<p class="featured_book__title">
+		<a href="<?php echo $book['link']; ?>"><?php echo maybe_truncate_string( $book['metadata']['name'] ); ?></a>
+	</p>
+</div>