« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/static
diff options
context:
space:
mode:
Diffstat (limited to 'src/static')
-rw-r--r--src/static/css/features.css2412
-rw-r--r--src/static/css/miscellany.css626
-rw-r--r--src/static/css/page.css915
-rw-r--r--src/static/css/responsive.css223
-rw-r--r--src/static/css/search.css548
-rw-r--r--src/static/css/site.css3134
-rw-r--r--src/static/css/specific-pages.css288
-rw-r--r--src/static/css/tooltips.css445
-rw-r--r--src/static/js/client-util.js68
-rw-r--r--src/static/js/client/additional-names-box.js71
-rw-r--r--src/static/js/client/album-commentary-sidebar.js2
-rw-r--r--src/static/js/client/art-tag-gallery-filter.js149
-rw-r--r--src/static/js/client/art-tag-network.js145
-rw-r--r--src/static/js/client/artist-external-link-tooltip.js2
-rw-r--r--src/static/js/client/artist-rolling-window.js571
-rw-r--r--src/static/js/client/css-compatibility-assistant.js26
-rw-r--r--src/static/js/client/datetimestamp-tooltip.js2
-rw-r--r--src/static/js/client/dragged-link.js2
-rw-r--r--src/static/js/client/expandable-grid-section.js83
-rw-r--r--src/static/js/client/gallery-style-selector.js121
-rw-r--r--src/static/js/client/hash-link.js60
-rw-r--r--src/static/js/client/hoverable-tooltip.js59
-rw-r--r--src/static/js/client/image-overlay.js55
-rw-r--r--src/static/js/client/index.js151
-rw-r--r--src/static/js/client/intrapage-dot-switcher.js2
-rw-r--r--src/static/js/client/live-mouse-position.js2
-rw-r--r--src/static/js/client/memorable-details.js62
-rw-r--r--src/static/js/client/quick-description.js2
-rw-r--r--src/static/js/client/reveal-all-grid-control.js70
-rw-r--r--src/static/js/client/scripted-link.js2
-rw-r--r--src/static/js/client/sidebar-search.js765
-rw-r--r--src/static/js/client/sticky-heading.js140
-rw-r--r--src/static/js/client/summary-nested-link.js2
-rw-r--r--src/static/js/client/text-with-tooltip.js2
-rw-r--r--src/static/js/client/wiki-search.js2
-rw-r--r--src/static/js/group-contributions-table.js2
-rw-r--r--src/static/js/info-card.js2
-rw-r--r--src/static/js/lazy-loading.js2
-rw-r--r--src/static/js/rectangles.js44
-rw-r--r--src/static/js/search-worker.js324
-rw-r--r--src/static/js/xhr-util.js2
-rw-r--r--src/static/misc/icons.svg1
-rw-r--r--src/static/misc/image.svg11
43 files changed, 8186 insertions, 3411 deletions
diff --git a/src/static/css/features.css b/src/static/css/features.css
new file mode 100644
index 00000000..410bf5b9
--- /dev/null
+++ b/src/static/css/features.css
@@ -0,0 +1,2412 @@
+/* Additional files lists */
+
+@layer layout {
+  .additional-files-list {
+    padding-left: 0;
+  }
+
+  .additional-files-list > li {
+    list-style-type: none;
+  }
+
+  .additional-files-list summary {
+    /* Sorry, Safari!
+     * https://bugs.webkit.org/show_bug.cgi?id=157323
+     */
+    list-style-position: outside;
+    margin-left: 40px;
+  }
+
+  .additional-files-list details ul {
+    margin-left: 40px;
+    margin-top: 2px;
+    margin-bottom: 10px;
+  }
+
+  .additional-files-list .entry-description {
+    list-style-type: none;
+    max-width: 540px;
+
+    /* This should be margin-bottom, but cascading rules
+     * cause some awkwardness - `#content li` takes precedence.
+     */
+    padding-bottom: 3px;
+  }
+}
+
+/* Additional names (heading and box) */
+
+@layer layout {
+  #additional-names-box {
+    --custom-scroll-offset: calc(0.5em - 2px);
+
+    margin: 1em 0 1em -10px;
+    max-width: min(60vw, 600px);
+
+    padding: 15px 20px 10px 20px;
+  }
+
+  #additional-names-box > :first-child { margin-top: 0; }
+  #additional-names-box > :last-child { margin-bottom: 0; }
+
+  #additional-names-box p {
+    padding-left: 10px;
+    padding-right: 10px;
+    margin-bottom: 0;
+  }
+
+  #additional-names-box ul {
+    padding-left: 10px;
+    margin-top: 0.5em;
+  }
+
+  #additional-names-box li .additional-name {
+    margin-right: 0.25em;
+  }
+
+  #additional-names-box li .additional-name .content-image {
+    margin-bottom: 0.25em;
+    margin-top: 0.5em;
+  }
+
+  #content.top-index #additional-names-box {
+    margin-left: auto;
+    margin-right: auto;
+
+    /* So like, these were in two separate, right-nearby
+     * rules with the same selector, lol
+     */
+    margin-bottom: 2em;
+    margin-bottom: 0.75em;
+  }
+}
+
+@layer print {
+  #additional-names-box p {
+    font-style: oblique;
+  }
+
+  #additional-names-box li .accent {
+    opacity: 0.8;
+  }
+
+  #additional-names-box li .additional-name > img {
+    vertical-align: text-bottom;
+  }
+
+  #content.top-index #additional-names-box {
+    text-align: center;
+  }
+}
+
+@layer interaction {
+  h1 a[href="#additional-names-box"] {
+    color: inherit;
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  h1 a[href="#additional-names-box"]:hover {
+    text-decoration-style: solid;
+  }
+
+  #additional-names-box:not(.always-visible) {
+    display: none;
+  }
+}
+
+/* Carousels */
+
+@layer layout {
+  .carousel-container {
+    --carousel-tile-min-width: 120px;
+    --carousel-row-count: 3;
+    --carousel-column-count: 6;
+
+    position: relative;
+    overflow: hidden;
+    margin: 20px 0 5px 0;
+    padding: 8px 0;
+  }
+
+  .carousel-container + .quick-info,
+  .carousel-container + .quick-description {
+    margin-top: 25px;
+  }
+
+  html[data-url-key="localized.home"] .carousel-container {
+    /* This is currently unused... */
+    --carousel-tile-size: 140px;
+  }
+
+  .carousel-container[data-carousel-rows="1"] { --carousel-row-count: 1; }
+  .carousel-container[data-carousel-rows="2"] { --carousel-row-count: 2; }
+  .carousel-container[data-carousel-rows="3"] { --carousel-row-count: 3; }
+  .carousel-container[data-carousel-columns="4"] { --carousel-column-count: 4; }
+  .carousel-container[data-carousel-columns="5"] { --carousel-column-count: 5; }
+  .carousel-container[data-carousel-columns="6"] { --carousel-column-count: 6; }
+
+  .carousel-grid {
+    /* Thanks to: https://css-tricks.com/an-auto-filling-css-grid-with-max-columns/ */
+    --carousel-gap-count: calc(var(--carousel-column-count) - 1);
+    --carousel-total-gap-width: calc(var(--carousel-gap-count) * 10px);
+    --carousel-calculated-tile-max-width: calc((100% - var(--carousel-total-gap-width)) / var(--carousel-column-count));
+
+    position: relative;
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(max(var(--carousel-tile-min-width), var(--carousel-calculated-tile-max-width)), 1fr));
+    grid-template-rows: repeat(var(--carousel-row-count), auto);
+    grid-auto-flow: dense;
+    grid-auto-rows: 0;
+    overflow: hidden;
+    margin: auto;
+
+    z-index: 5;
+  }
+
+  .carousel-grid:nth-child(2),
+  .carousel-grid:nth-child(3) {
+    position: absolute;
+    top: 8px;
+    left: 0;
+    right: 0;
+  }
+
+  .carousel-item {
+    display: inline-block;
+    margin: 0;
+    flex: 1 1 150px;
+    padding: 3px;
+  }
+
+  .carousel-item .image-outer-area {
+    padding: 0;
+  }
+
+  .carousel-item .image {
+    width: 100%;
+    height: 100%;
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+}
+
+@layer material {
+  .carousel-container {
+    border-radius: 4px;
+  }
+
+  .carousel-container::before,
+  .carousel-container::after {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+
+  .carousel-container::before {
+    background-color: var(--dim-color);
+    filter: brightness(0.6);
+  }
+
+  .carousel-container::after {
+    z-index: 40;
+    pointer-events: none;
+
+    border: 1px solid var(--primary-color);
+    border-radius: 4px;
+
+    box-shadow:
+      inset 20px 2px 40px var(--shadow-color),
+      inset -20px -2px 40px var(--shadow-color);
+  }
+
+  .carousel-item {
+    border-radius: 7px;
+  }
+
+  .carousel-item .image-container {
+    border: none;
+    border-radius: 6px;
+  }
+
+  .carousel-item .image-inner-area::after {
+    box-shadow: none;
+  }
+}
+
+@layer interaction {
+  .carousel-container:hover .carousel-grid {
+    animation-play-state: running;
+  }
+
+  .carousel-grid {
+    transform: translateX(0);
+    animation: carousel-marquee1 40s linear infinite;
+    animation-play-state: paused;
+  }
+
+  .carousel-grid:nth-child(2) {
+    animation-name: carousel-marquee2;
+  }
+
+  .carousel-grid:nth-child(3) {
+    animation-name: carousel-marquee3;
+  }
+
+  @keyframes carousel-marquee1 {
+    0% {
+      transform: translateX(-100%) translateX(70px);
+    }
+
+    100% {
+      transform: translateX(-200%) translateX(70px);
+    }
+  }
+
+  @keyframes carousel-marquee2 {
+    0% {
+      transform: translateX(0%) translateX(70px);
+    }
+
+    100% {
+      transform: translateX(-100%) translateX(70px);
+    }
+  }
+
+  @keyframes carousel-marquee3 {
+    0% {
+      transform: translateX(100%) translateX(70px);
+    }
+
+    100% {
+      transform: translateX(0%) translateX(70px);
+    }
+  }
+
+  .carousel-item {
+    filter: brightness(0.8);
+  }
+
+  .carousel-item:hover {
+    filter: brightness(1);
+    background: var(--dim-color);
+  }
+}
+
+/* Content entries (at large) */
+
+@layer layout {
+  #content blockquote {
+    margin-left: 40px;
+    margin-right: min(8vw, 75px);
+    width: auto;
+  }
+
+  #content blockquote blockquote {
+    margin-left: 10px;
+    padding-left: 10px;
+    margin-right: 20px;
+    padding-top: 6px;
+    padding-bottom: 6px;
+  }
+
+  #content blockquote blockquote > :first-child {
+    margin-top: 0;
+  }
+
+  #content blockquote blockquote > :last-child {
+    margin-bottom: 0;
+  }
+}
+
+@layer material {
+  #content blockquote blockquote {
+    border-left: dotted 1px;
+  }
+}
+
+@layer print {
+  #content blockquote h2 {
+    font-size: 1em;
+    font-weight: 800;
+  }
+
+  #content blockquote h3 {
+    font-size: 1em;
+    font-weight: normal;
+    font-style: oblique;
+  }
+}
+
+/* Commentary-style content entries */
+
+@layer layout {
+  .inherited-commentary-section {
+    clear: right;
+    margin-top: 1.75em;
+    margin-bottom: 1.25em;
+    margin-right: min(4vw, 60px);
+  }
+
+  .commentary-art {
+    float: right;
+    width: 30%;
+    max-width: 250px;
+    margin: 15px 0 10px 20px;
+  }
+
+  .content-entry-heading {
+    display: flex;
+
+    margin-left: 15px;
+    margin-right: min(calc(8vw - 35px), 45px);
+    padding-left: calc(5px + 1.25ch);
+    padding-right: 1ch;
+    padding-bottom: 0.2em;
+  }
+
+  .content-entry-heading-inner-box {
+    flex-grow: 1;
+  }
+
+  .content-entry-accent {
+    margin-left: 0.25ch;
+  }
+
+  .content-entry-heading .float-spacer {
+    float: right;
+    display: block;
+    height: calc(100% - 1em);
+
+    /* Width here controls margin (inset) on wrapped lines above the date. */
+    width: 3ch;
+  }
+
+  .content-entry-heading .commentary-date {
+    display: inline-block;
+    float: right;
+    clear: right;
+    margin-left: 1.75ch;
+  }
+}
+
+@layer material {
+  .inherited-commentary-section {
+    border-color: var(--deep-color);
+    border-width: 2px 2px;
+    border-style: dashed solid;
+    background: #ffffff07;
+  }
+
+  .cover-artwork.commentary-art {
+    box-shadow: 0 0 4px 5px rgba(0, 0, 0, 0.25) !important;
+  }
+
+  .content-entry-heading {
+    border-bottom: 1px solid var(--dim-color);
+  }
+}
+
+@layer print {
+  .content-entry-heading {
+    text-indent: -1.25ch;
+  }
+
+  .content-entry-annotation {
+    font-style: oblique;
+  }
+
+  .content-entry-annotation .chunkwrap:first-child {
+    /* Cheat. */
+    display: inline;
+  }
+
+  .content-entry-annotation .chunkwrap:nth-child(n + 1) {
+    text-indent: 0;
+  }
+
+  .content-entry-heading .commentary-date {
+    text-indent: 0;
+  }
+
+  .content-entry-body summary {
+    list-style-position: outside;
+  }
+
+  .content-entry-body summary > span {
+    color: var(--primary-color);
+  }
+
+  .wiki-commentary s:not(.spoiler) {
+    text-decoration-color: #fff9;
+    text-decoration-thickness: 1.4px;
+    color: #fffb;
+  }
+}
+
+@layer interaction {
+  .content-entry-heading .commentary-date .hoverable {
+    box-shadow: 1px 2px 6px 5px #04040460;
+  }
+}
+
+/* Content headings */
+
+@layer layout {
+  .content-heading {
+    border-bottom: 3px double transparent;
+    margin-bottom: -3px;
+  }
+
+  dl dt.content-heading {
+    /* Basic margin-bottom for dt is 2px,
+     * so just subtract 3px from that.
+     */
+    margin-bottom: -1px;
+  }
+
+  h3.content-heading {
+    clear: both;
+  }
+}
+
+@layer construction {
+  summary.content-heading {
+    list-style-type: none;
+  }
+
+  summary.content-heading .cue {
+    display: inline-flex;
+  }
+
+  summary.content-heading .cue::after {
+    content: "";
+    padding-left: 0.5ch;
+    display: list-item;
+    list-style-position: inside;
+  }
+}
+
+@layer interaction {
+  .content-heading.highlight-hash-link {
+    animation: highlight-hash-link 4s;
+    animation-delay: 125ms;
+  }
+
+  /* This animation's name is referenced in JavaScript */
+  @keyframes highlight-hash-link {
+    0% {
+      border-bottom-color: transparent;
+    }
+
+    10% {
+      border-bottom-color: white;
+    }
+
+    25% {
+      border-bottom-color: white;
+    }
+
+    100% {
+      border-bottom-color: transparent;
+    }
+  }
+
+  summary.content-heading .cue {
+    color: var(--primary-color);
+  }
+
+  summary.content-heading .cue::after {
+    list-style-type: disclosure-closed;
+  }
+
+  details[open] > summary.content-heading .cue::after {
+    list-style-type: disclosure-open;
+  }
+
+  summary.content-heading > span:hover {
+    text-decoration: none !important;
+  }
+
+  summary.content-heading > span:hover .cue {
+    text-decoration: underline;
+    text-decoration-style: wavy;
+  }
+
+  summary.content-heading .when-open {
+    display: none;
+  }
+
+  details[open] > summary.content-heading .when-open {
+    display: unset;
+  }
+
+  details[open] > summary.content-heading .when-collapsed {
+    display: none;
+  }
+}
+
+/* Cover artworks, music videos, and album art info */
+
+@layer layout {
+  .cover-artwork .image {
+    display: block;
+    width: 100%;
+    height: 100%;
+  }
+
+  .music-video .image {
+    display: block;
+    aspect-ratio: 16 / 9;
+    width: 100%;
+    height: 100%;
+  }
+
+  #artwork-column .album-art-info {
+    margin: 10px min(15px, 1vw) 18px;
+    z-index: 2;
+  }
+
+  #artwork-column .cover-artwork:not(:first-child),
+  #artwork-column .cover-artwork-joiner {
+    margin-left: 30px;
+    margin-right: 5px;
+  }
+
+  #artwork-column .cover-artwork:first-child + .cover-artwork-joiner,
+  #artwork-column .cover-artwork.attached-artwork-is-main-artwork,
+  #artwork-column .cover-artwork.attached-artwork-is-main-artwork + .cover-artwork-joiner {
+    margin-left: 17.5px;
+    margin-right: 17.5px;
+  }
+
+  #artwork-column *:where(.cover-artwork, .music-video):where(:not(:first-child)) {
+    margin-top: 20px;
+  }
+
+  #artwork-column *:is(.cover-artwork, .music-video):last-child:not(:first-child) {
+    margin-bottom: 25px;
+  }
+
+  .cover-artwork-joiner {
+    z-index: -2;
+  }
+
+  .cover-artwork-joiner + .cover-artwork {
+    margin-top: 0 !important;
+  }
+
+  #artwork-column .music-video {
+    width: 88.888888%;
+    margin-left: auto;
+  }
+
+  #artwork-column .cover-artwork + .music-video {
+    margin-top: 25px;
+  }
+
+  #artwork-column .cover-artwork + .cover-artwork + .music-video {
+    margin-top: 30px;
+  }
+}
+
+@layer material {
+  .cover-artwork,
+  .music-video,
+  .album-art-info {
+    border: 2px solid var(--primary-color);
+    background: var(--bg-black-color);
+
+    -webkit-backdrop-filter: blur(3px);
+            backdrop-filter: blur(3px);
+  }
+
+  .cover-artwork {
+    border-radius: 0 0 4px 4px;
+  }
+
+  .cover-artwork:has(.image-details),
+  .cover-artwork.has-image-details {
+    border-radius: 0 0 6px 6px;
+  }
+
+  .music-video {
+    border-radius: 4px;
+    padding: 0 4px;
+  }
+
+  .album-art-info {
+    border-color: var(--deep-color);
+    border-radius: 5px;
+
+    background: var(--bg-black-color);
+    padding: 6px;
+  }
+
+  .album-art-info p {
+    margin: 0;
+  }
+
+  .cover-artwork .image-container,
+  .music-video .image-container {
+    border: none;
+    border-radius: 0 !important;
+  }
+
+  .music-video .image-container {
+    background: transparent;
+    border-style: dashed none;
+    border-width: 2px;
+    border-color: var(--dim-color);
+  }
+
+  .cover-artwork:not(:has(.image-details)),
+  .cover-artwork:not(.has-image-details) {
+    /* Hacky: `overflow: hidden` hides tag tooltips, so it can't be applied
+     * if we've got tags/details visible. But it's okay, because we only
+     * need to apply it if it *doesn't* - that's when the rounded border
+     * of the .cover-artwork needs to cut off its child .image-container
+     * (which has a background that otherwise causes sharp corners).
+     */
+    overflow: hidden;
+  }
+
+  #artwork-column .cover-artwork,
+  #artwork-column .music-video {
+    --normal-shadow: 0 0 12px 12px #00000080;
+
+    box-shadow:
+      0 2px 14px -6px var(--primary-color),
+      var(--normal-shadow);
+  }
+
+  #artwork-column .cover-artwork:not(:first-child) {
+    --normal-shadow: 0 0 9px 9px #00000068;
+  }
+
+  .cover-artwork .image-details {
+    border-top-color: var(--deep-color);
+  }
+
+  .cover-artwork .image-details + .image-details {
+    border-top-color: var(--primary-color);
+  }
+
+  .image-details {
+    display: block;
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .image-details:not(.image-details + .image-details) {
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 9px;
+    padding-right: 9px;
+
+    padding-top: 6px;
+    padding-bottom: 4px;
+
+    border-top: 1px dashed var(--deep-color);
+  }
+
+  .image-details + .image-details {
+    display: block;
+
+    margin-left: 6px;
+    margin-right: 6px;
+    padding-left: 3px;
+    padding-right: 3px;
+
+    padding-top: 4px;
+    padding-bottom: 4px;
+
+    border-top: 1px dotted var(--primary-color);
+  }
+
+  .image-details:last-child {
+    margin-bottom: 2px;
+  }
+}
+
+@layer print {
+  .cover-artwork,
+  .music-video,
+  .album-art-info {
+    font-size: 0.8em;
+  }
+
+  p.image-details.illustrator-details {
+    text-align: center;
+    font-style: oblique;
+  }
+
+  p.image-details.origin-details {
+    margin-bottom: 2px;
+  }
+
+  p.image-details.origin-details .origin-details-line {
+    display: block;
+    margin-top: 0.25em;
+  }
+
+  p.image-details.origin-details .filename-line {
+    display: block;
+    margin-top: 0.25em;
+  }
+
+  .music-video .music-video-label {
+    margin: 6px 12px 3px;
+    text-align: center;
+  }
+
+  .music-video .music-video-label.title-style {
+    margin-bottom: 4px;
+    font-style: oblique;
+  }
+
+  .music-video .image-container ~ p {
+    margin: 3px 5px;
+  }
+
+  .music-video .image-container + p {
+    margin-top: 5px;
+  }
+
+  .music-video .image-container ~ p:last-child {
+    margin-bottom: 6px;
+  }
+
+  .music-video .artists-line {
+    display: block;
+    padding-left: 1.2ch;
+    text-indent: -1.2ch;
+  }
+
+  .music-video .artists-line > * {
+    text-indent: 0;
+  }
+
+  .music-video .artists-line + br {
+    display: none;
+  }
+}
+
+@layer construction {
+  ul.image-details.art-tag-details {
+    padding-bottom: 0;
+  }
+
+  ul.image-details.art-tag-details li {
+    display: inline-block;
+  }
+
+  ul.image-details.art-tag-details li:not(:last-child)::after {
+    content: " \00b7 ";
+  }
+
+  .cover-artwork-joiner::after {
+    content: "";
+    display: block;
+    width: 0;
+    height: 15px;
+    margin-left: auto;
+    margin-right: auto;
+    border-right: 3px solid var(--primary-color);
+  }
+}
+
+@layer interaction {
+  .music-video .image-link::after {
+    content: "▶︎";
+    background: #0008;
+    color: white;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .music-video .image-link:hover::after {
+    font-size: 1.4em;
+    background: #0006;
+  }
+
+  .music-video .image-inner-area::after {
+    /* We're overriding normal image-inner-area stuff. */
+    opacity: 0.8;
+    box-shadow: 0 0 4px inset black;
+  }
+
+  .music-video .image-link:hover .image {
+    transform: scale(1.02);
+  }
+}
+
+/* Dot switchers */
+
+@layer print {
+  .dot-switcher > span {
+    color: #ffffffcc;
+  }
+
+  .dot-switcher > span.current {
+    font-weight: normal;
+    color: #ffffffff;
+  }
+
+  .dot-switcher > span.current a {
+    font-weight: 800;
+  }
+
+  .group-view-switcher {
+    margin-left: 1ch;
+  }
+}
+
+@layer construction {
+  .dot-switcher > span:not(:first-child) {
+    display: inline-block;
+    white-space: nowrap;
+  }
+
+  .dot-switcher > span:not(:first-child)::before {
+    content: "\0020\00b7\0020";
+    white-space: pre;
+    font-weight: 800;
+    color: white;
+  }
+
+  /* Yeah, all this stuff only applies to elements of the dot switcher
+   * besides the first, which will necessarily have a bullet point at left.
+   */
+  .dot-switcher *:where(.dot-switcher > span:not(:first-child) > *) {
+    display: inline-block;
+    white-space: wrap;
+    text-align: left;
+    vertical-align: top;
+  }
+}
+
+@layer interaction {
+  .dot-switcher.intrapage > span:not(.current) a {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  .dot-switcher.intrapage > span.current a {
+    /* Keeping cursor: pointer (the default) is intentional here. */
+    text-decoration: none !important;
+  }
+}
+
+/* External icons */
+
+@layer print {
+  .external-icon {
+    display: inline-block;
+    padding: 0 3px;
+    width: 24px;
+    height: 1em;
+    position: relative;
+  }
+
+  .external-icon svg {
+    width: 24px;
+    height: 24px;
+    top: -0.25em;
+    position: absolute;
+    fill: var(--primary-color);
+  }
+}
+
+/* Gallery switchers (of various sorts) */
+
+@layer layout {
+  .gallery-set-switcher {
+    text-align: center;
+  }
+
+  .gallery-view-switcher,
+  .gallery-style-selector {
+    margin-left: auto;
+    margin-right: auto;
+    text-align: center;
+    line-height: 1.4;
+  }
+
+  .gallery-style-selector .styles {
+    display: inline-flex;
+    justify-content: center;
+  }
+
+  .gallery-style-selector .styles label:not(:last-child) {
+    margin-right: 1.25ch;
+  }
+
+  .gallery-style-selector .count {
+    position: relative;
+    bottom: -0.25em;
+  }
+}
+
+@layer print {
+  .gallery-view-switcher,
+  .gallery-style-selector {
+    line-height: 1.4;
+  }
+
+  .gallery-style-selector .count {
+    font-size: 0.85em;
+    opacity: 0.9;
+  }
+}
+
+/* Grid listings and grid items */
+
+@layer layout {
+  .grid-listing {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+    align-items: flex-start;
+    padding: 5px 15px;
+    box-sizing: border-box;
+    margin-top: 1em;
+  }
+
+  .grid-listing .reveal-all-container {
+    flex-basis: 100%;
+  }
+
+  .grid-listing .reveal-all-container.has-nearby-tab {
+    margin-bottom: 0.6em;
+  }
+
+  .grid-listing .reveal-all {
+    max-width: 400px;
+    margin: 0.20em auto 0;
+    text-align: center;
+  }
+
+  .grid-listing .reveal-all a {
+    display: inline-block;
+    margin-bottom: 0.15em;
+  }
+
+  .grid-listing .reveal-all b {
+    white-space: nowrap;
+  }
+
+  .grid-item {
+    --tab-lift: 0px;
+    --tabnt-offset: 0px;
+
+    margin: 10px;
+    margin-top:
+      calc(
+         10px
+       - var(--tab-lift)
+       + var(--tabnt-offset));
+
+    padding: 5px;
+  }
+
+  .grid-item.has-tab {
+    border-radius: 8px 8px 3px 3px;
+  }
+
+  .grid-item:not(.has-tab) {
+    --tabnt-offset: calc(1.2em - 4px);
+  }
+
+  .grid-item .image-container {
+    width: 100%;
+  }
+
+  .grid-item .image {
+    width: 100%;
+    height: 100% !important;
+    margin-top: auto;
+    margin-bottom: auto;
+    object-fit: contain;
+  }
+
+  .grid-listing > .grid-item {
+    flex: 1 25%;
+    max-width: 200px;
+  }
+
+  .grid-actions {
+    margin: 15px;
+    align-self: center;
+
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+
+  .grid-actions > .grid-item {
+    flex-basis: unset !important;
+    margin: 5px;
+    width: 120px;
+    --primary-color: inherit !important;
+    --dim-color: inherit !important;
+  }
+
+  .grid-listing > .grid-caption,
+  .grid-listing > .grid-expando {
+    flex-basis: 100%;
+  }
+
+  .grid-expando {
+    margin-top: 1em;
+    margin-bottom: 2em;
+
+    display: flex;
+    flex-direction: row;
+    justify-content: space-around;
+  }
+
+  .grid-expando-content {
+    margin: 0;
+  }
+}
+
+@layer material {
+  .grid-listing:not(:has(.grid-item:not([class*="hidden-by-"]))) {
+    padding-bottom: 140px;
+    background: #cccccc07;
+    border-radius: 10px;
+    border: 1px dashed #fff3;
+  }
+
+  .grid-listing:not(:has(.grid-item:not([class*="hidden-by-"]))) .reveal-all-container {
+    display: none;
+  }
+
+  .grid-item {
+    background-color: #111111;
+    border: 1px dotted var(--primary-color);
+    border-radius: 2px;
+  }
+
+  .grid-item .image-inner-area {
+    border-radius: 0;
+    box-shadow: none;
+  }
+
+  .grid-item .image-inner-area::after {
+    box-shadow: none;
+  }
+
+  .grid-item .image {
+    --shadow-filter:
+      drop-shadow(0 3px 2px #0004)
+      drop-shadow(0 1px 5px #0001)
+      drop-shadow(0 3px 4px #0001);
+  }
+}
+
+@layer print {
+  .grid-item {
+    text-align: center;
+    line-height: 1.2;
+    font-size: 0.9em;
+  }
+
+  .grid-item > span {
+    display: block;
+    overflow-wrap: break-word;
+    hyphens: auto;
+  }
+
+  /* tab */
+  .grid-item > span:first-child {
+    margin-bottom: calc(3px + var(--tab-lift));
+
+    font-style: oblique;
+  }
+
+  /* title */
+  .grid-item > .image-container + span {
+    margin-top: 6px;
+  }
+
+  .grid-name-marker {
+    color: white;
+  }
+
+  /* info */
+  .grid-item > .image-container + span ~ span {
+    margin-top: 2px;
+
+    font-size: 0.9em;
+    opacity: 0.8;
+  }
+
+  .grid-caption {
+    text-align: center;
+    line-height: 1.5;
+  }
+
+  .grid-expando-content {
+    text-align: center;
+    line-height: 1.5;
+  }
+}
+
+@layer interaction {
+  .grid-listing .reveal-all a {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  .grid-listing .reveal-all .warnings {
+    opacity: 0.4;
+  }
+
+  .grid-listing .reveal-all:hover .warnings {
+    opacity: 1;
+  }
+
+  .grid-item.has-tab:hover {
+    --tab-lift: 3px;
+  }
+
+  .grid-item[class*="hidden-by-"] {
+    display: none;
+  }
+
+  .grid-item:hover {
+    text-decoration: none;
+  }
+
+  .grid-actions .grid-item:hover {
+    text-decoration: underline;
+  }
+
+  /* title */
+  .grid-item:hover > .image-container + span {
+    text-decoration: underline;
+  }
+
+  .grid-expando-toggle {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  .grid-item.shown-by-expandable-cut {
+    animation: expand-cover-grid 0.8s forwards;
+  }
+
+  @keyframes expand-cover-grid {
+    from {
+      opacity: 0;
+    }
+
+    to {
+      opacity: 1;
+    }
+  }
+}
+
+/* Group contributions tables */
+
+@layer layout {
+  .group-contributions-table {
+    margin-left: 40px;
+    border-collapse: collapse;
+  }
+
+  .group-contributions-table td {
+    padding: 0 0 4px 0;
+  }
+
+  .group-contributions-table .group-contributions-link-cell {
+    display: list-item;
+  }
+
+  .group-contributions-table .group-contributions-metrics-cell {
+    padding-left: 1.5ch;
+    white-space: nowrap;
+    text-align: right;
+  }
+}
+
+@layer interaction {
+  .group-contributions-sorted-by-count:not(.visible),
+  .group-contributions-sorted-by-duration:not(.visible) {
+    display: none;
+  }
+
+  .group-contributions-sort-button {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+}
+
+/* Image and media containers */
+
+@layer layout {
+  .image-container {
+    display: block;
+    box-sizing: border-box;
+    position: relative;
+    height: 100%;
+    overflow: hidden;
+  }
+
+  .content-image-container,
+  .content-video-container,
+  .content-audio-container {
+    margin-top: 1em;
+    margin-bottom: 1em;
+  }
+
+  .content-video-container,
+  .content-audio-container {
+    width: fit-content;
+    max-width: 100%;
+  }
+
+  .content-video-container video,
+  .content-audio-container audio {
+    display: block;
+    max-width: 100%;
+  }
+
+  .image-text-area {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: center;
+    padding: 5px 15px;
+  }
+
+  .image-outer-area {
+    width: 100%;
+    height: 100%;
+    padding: 5px;
+    box-sizing: border-box;
+  }
+
+  .image-link {
+    display: block;
+    overflow: hidden;
+  }
+
+  .square .image-link {
+    width: 100%;
+    height: 100%;
+  }
+
+  .image-inner-area {
+    position: relative;
+    width: 100%;
+    height: 100%;
+  }
+
+  .image-link .image-inner-area {
+    /* Jankily fix a rendering issue with border-radius on Safari.
+     * The `-webkit-` prefix is only to keep this from applying on
+     * other browsers (well, Firefox), where it doesn't *break*
+     * anything, but also isn't necessary.
+     */
+    -webkit-transform: translateZ(0);
+  }
+
+  img {
+    object-fit: cover;
+  }
+
+  .square .image {
+    width: 100%;
+    height: 100%;
+  }
+
+  .image-link .image {
+    display: block;
+    max-width: 100%;
+    height: auto;
+  }
+
+  .reveal-text-container {
+    position: absolute;
+    top: 15px;
+    left: 10px;
+    right: 10px;
+    bottom: 10px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+  }
+
+  .reveal-text {
+    padding-bottom: 0.5em;
+  }
+
+  .reveal .image.reveal-thumbnail {
+    position: absolute;
+    top: 0;
+    left: 0;
+  }
+
+  .reveal.has-reveal-thumbnail:not(.revealed) .image:not(.reveal-thumbnail) {
+    /* Keep the main image as part of the box model.
+     * It's what actually defines the dimensions of the
+     * image-container, so those dimensions never shift
+     * once the image is actually revealed.
+     */
+    visibility: hidden;
+  }
+}
+
+@layer material {
+  .image-container {
+    background-color: var(--dim-color);
+    border: 2px solid var(--primary-color);
+    border-radius: 0;
+    box-shadow: 0 2px 4px -2px var(--bg-black-color) inset;
+  }
+
+  .content-video-container,
+  .content-audio-container {
+    background-color: var(--dark-color);
+    border: 2px solid var(--primary-color);
+    border-radius: 2.5px 2.5px 3px 3px;
+    padding: 5px;
+  }
+
+  .image-text-area {
+    background: rgba(0, 0, 0, 0.65);
+    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5) inset;
+
+    line-height: 1.35em;
+    color: var(--primary-color);
+    font-style: oblique;
+    text-shadow: 0 2px 5px rgba(0, 0, 0, 0.75);
+  }
+
+  .image-link {
+    border-bottom: 1px solid #ffffff03;
+    border-radius: 2.5px 2.5px 3px 3px;
+    box-shadow:
+      0 1px 8px -3px var(--bg-black-color);
+  }
+
+  .image-inner-area::after {
+    content: "";
+    display: block;
+    position: absolute;
+    pointer-events: none;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    border-bottom-left-radius: 0.5px;
+    opacity: 0.035;
+    box-shadow:
+      6px -6px 2px -4px white inset;
+  }
+
+  .image {
+    --reveal-filter: ;
+    --shadow-filter: ;
+
+    backdrop-filter: blur(0);
+    filter:
+      var(--reveal-filter)
+      var(--shadow-filter);
+  }
+
+  .reveal .image.reveal-thumbnail {
+    image-rendering: pixelated;
+  }
+}
+
+@layer print {
+  .image-container {
+    text-align: left;
+    color: white;
+  }
+
+  .content-image-container.align-center {
+    text-align: center;
+    margin-top: 1.5em;
+    margin-bottom: 1.5em;
+  }
+
+  .content-video-container.align-center,
+  .content-audio-container.align-center {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .content-image-container.align-full,
+  .content-video-container.align-full,
+  .content-audio-container.align-full {
+    width: 100%;
+  }
+
+  .content-audio-container .filename {
+    color: white;
+    font-family: monospace;
+    display: block;
+    font-size: 0.9em;
+    padding-left: 1ch;
+    padding-right: 1ch;
+    padding-bottom: 0.25em;
+    margin-bottom: 0.5em;
+    border-bottom: 1px solid #fff4;
+  }
+
+  .reveal-text {
+    color: white;
+    text-align: center;
+    font-weight: bold;
+    font-size: 0.8rem;
+  }
+
+  .grid-item .reveal-text {
+    font-size: 0.9em;
+  }
+
+  .reveal-symbol {
+    display: inline-block;
+    width: 1em;
+    height: 1em;
+    margin-bottom: 0.1em;
+
+    font-size: 1.6em;
+    opacity: 0.8;
+  }
+
+  .sidebar .image-container {
+    max-width: 350px;
+  }
+}
+
+@layer interaction {
+  .image-link:focus {
+    outline: 3px double white;
+  }
+
+  .image-link:focus:not(:focus-visible) {
+    outline: none;
+  }
+
+  .image-container.has-link:not(.no-image-preview) {
+    background: var(--deep-color);
+    box-shadow: none;
+    border-radius: 0 0 4px 4px;
+  }
+
+  .reveal-interaction {
+    opacity: 0.8;
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  .reveal:not(.revealed) .image {
+    opacity: 0.7;
+    --reveal-filter: blur(20px) brightness(0.7);
+  }
+
+  .reveal:not(.revealed) .image-outer-area > * {
+    position: relative;
+    border-radius: 6px;
+  }
+
+  .reveal:not(.revealed) .image-outer-area > *::after {
+    content: "";
+    position: absolute;
+    box-sizing: border-box;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    border: 1px dotted var(--primary-color);
+    border-radius: 6px;
+    pointer-events: none;
+
+    /* By an awkward DOM intersection, this element might be
+     * .image-inner-area::after, which is already styled with
+     * a slight visual effect. Guarantee that the properties
+     * set to that end are overwritten, and fully co-opt it
+     * to serve as the interaction cue instead.
+     */
+    box-shadow: none;
+    opacity: 1;
+  }
+
+  .reveal:not(.revealed) .image-inner-area {
+    background: var(--deep-color);
+  }
+
+  .reveal:not(.revealed) .image-outer-area > *:hover::after {
+    border-style: solid;
+    box-shadow: 0 0 0 1.5px #00000099 inset;
+  }
+
+  .reveal:not(.revealed) .image-outer-area > *:hover .image {
+    --reveal-filter: blur(20px) brightness(0.6);
+    opacity: 0.6;
+  }
+
+  .reveal:not(.revealed) .image-outer-area > *:hover .reveal-interaction {
+    text-decoration-style: solid;
+  }
+
+  .reveal.revealed.has-reveal-thumbnail .image.reveal-thumbnail {
+    display: none !important;
+  }
+
+  .reveal.revealed .image {
+    opacity: 1;
+  }
+
+  .reveal.revealed .reveal-text-container {
+    display: none;
+  }
+}
+
+/* Image overlay */
+
+@layer layout {
+  #image-overlay-container {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 4000;
+
+    padding: 20px 40px;
+    box-sizing: border-box;
+
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+  }
+
+  #image-overlay-content-container {
+    display: flex;
+    flex-direction: column;
+
+    overflow: hidden;
+  }
+
+  #image-overlay-image-area {
+    display: block;
+    overflow: hidden;
+    width: 80vmin;
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  #image-overlay-image-layout {
+    display: block;
+    position: relative;
+    max-height: 100%;
+    margin: 4px 3px;
+  }
+
+  #image-overlay-image,
+  #image-overlay-image-thumb {
+    display: block;
+    width: 100%;
+    height: auto;
+  }
+
+  #image-overlay-image {
+    position: absolute;
+    max-height: 100%;
+    object-fit: contain;
+  }
+
+  #image-overlay-container.no-thumb #image-overlay-image {
+    position: static;
+  }
+
+  #image-overlay-action-container {
+    padding: 7px 4px 7px 4px;
+    text-align: center;
+  }
+}
+
+@layer material {
+  #image-overlay-container {
+    background: rgba(0, 0, 0, 0.8);
+    color: white;
+  }
+
+  #image-overlay-container::before {
+    content: '';
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+
+    background: var(--deep-color);
+    opacity: 0.20;
+  }
+
+  #image-overlay-content-container {
+    border-radius: 0 0 8px 8px;
+    border: 2px solid var(--primary-color);
+    background: var(--deep-ghost-color);
+
+    box-shadow:
+      0 0 90px 30px #00000060,
+      0 0 20px 10px #00000040,
+      0 0 10px 3px #00000080;
+
+    -webkit-backdrop-filter: blur(3px);
+            backdrop-filter: blur(3px);
+  }
+
+  #image-overlay-image-layout {
+    background: rgba(0, 0, 0, 0.65);
+  }
+
+  #image-overlay-image-thumb {
+    filter: blur(16px);
+    transform: scale(1.5);
+  }
+
+  #image-overlay-action-container {
+    border-radius: 0 0 5px 5px;
+    background: var(--bg-black-color);
+    box-shadow:
+      0 3px 8px -5px var(--primary-color) inset;
+  }
+}
+
+@layer print {
+  #image-overlay-action-container {
+    color: white;
+    font-style: oblique;
+    text-align: center;
+  }
+
+  #image-overlay-file-size-warning {
+    opacity: 0.8;
+    font-size: 0.9em;
+  }
+}
+
+@layer construction {
+  #image-overlay-image-area::after {
+    content: "";
+    display: block;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 4px;
+    width: var(--download-progress);
+    background: var(--primary-color);
+    box-shadow: 0 -3px 12px 4px var(--primary-color);
+    transition: 0.25s;
+  }
+
+  #image-overlay-container.loaded #image-overlay-image-area::after {
+    width: 100%;
+    background: white;
+    opacity: 0;
+  }
+
+  #image-overlay-container.errored #image-overlay-image-area::after {
+    width: 100%;
+    background: red;
+  }
+
+  #image-overlay-container:not(.visible) #image-overlay-image-area::after {
+    width: 0 !important;
+  }
+
+  #image-overlay-container #image-overlay-action-content-without-size:not(.visible),
+  #image-overlay-container #image-overlay-action-content-with-size:not(.visible),
+  #image-overlay-container #image-overlay-file-size-warning:not(.visible),
+  #image-overlay-container #image-overlay-file-size-kilobytes:not(.visible),
+  #image-overlay-container #image-overlay-file-size-megabytes:not(.visible) {
+    display: none;
+  }
+}
+
+@layer interaction {
+  #image-overlay-container {
+    opacity: 0;
+    pointer-events: none;
+    transition: opacity 0.4s;
+  }
+
+  #image-overlay-container.visible {
+    opacity: 1;
+    pointer-events: auto;
+  }
+
+  #image-overlay-container.loaded #image-overlay-image-thumb {
+    opacity: 0;
+    pointer-events: none;
+    transition: opacity 0.25s;
+  }
+}
+
+/* Info card */
+
+@layer interactivity {
+  #info-card-container {
+    pointer-events: none; /* Padding area shouldn't 8e interactive. */
+  }
+
+  .info-card {
+    pointer-events: none;
+  }
+
+  #info-card-container.show .info-card {
+    animation: 0.01s linear 0.2s forwards info-card-become-interactive;
+  }
+
+  @keyframes info-card-become-interactive {
+    to {
+      pointer-events: auto;
+    }
+  }
+}
+
+@layer layout {
+  #info-card-container {
+    position: absolute;
+    left: 0;
+    right: 10px;
+
+    display: none;
+  }
+
+  #info-card-container.show,
+  #info-card-container.hide {
+    display: flex;
+  }
+
+  #info-card-container > * {
+    flex-basis: 400px;
+  }
+
+  .info-card {
+    padding: 5px;
+  }
+
+  .info-card-art-container {
+    float: right;
+    width: 40%;
+    margin: 5px;
+
+    /* Dynamically shown. */
+    display: none;
+  }
+
+  .info-card-art-container .image-container {
+    padding: 2px;
+  }
+
+  .info-card-art {
+    display: block;
+    width: 100%;
+    height: 100%;
+  }
+
+  .info-card::after {
+    content: "";
+    display: block;
+    clear: both;
+  }
+}
+
+@layer material {
+  .info-card-decor {
+    padding-left: 3ch;
+    border-top: 1px solid white;
+  }
+
+  .info-card {
+    background-color: black;
+    color: white;
+
+    border: 1px dotted var(--primary-color);
+    border-radius: 3px;
+    box-shadow: 0 5px 5px black;
+  }
+
+  .info-card-name {
+    font-size: 1em;
+    border-bottom: 1px dotted;
+    margin: 0;
+  }
+
+  .info-card p {
+    margin-top: 0.25em;
+    margin-bottom: 0.25em;
+  }
+
+  .info-card p:last-child {
+    margin-bottom: 0;
+  }
+}
+
+@layer print {
+  .info-card {
+    font-size: 0.9em;
+  }
+
+  .info-card-art-container {
+    font-size: 0.8em;
+  }
+}
+
+@layer interaction {
+  #info-card-container.show {
+    animation: 0.2s linear forwards info-card-show;
+    transition: top 0.1s, left 0.1s;
+  }
+
+  #info-card-container.hide {
+    animation: 0.2s linear forwards info-card-hide;
+  }
+
+  @keyframes info-card-show {
+    0% {
+      opacity: 0;
+      margin-top: -5px;
+    }
+
+    100% {
+      opacity: 1;
+      margin-top: 0;
+    }
+  }
+
+  @keyframes info-card-hide {
+    0% {
+      opacity: 1;
+      margin-top: 0;
+    }
+
+    100% {
+      opacity: 0;
+      margin-top: 5px;
+      display: none !important;
+    }
+  }
+}
+
+/* Lyrics */
+
+@layer layout {
+  .lyrics-switcher {
+    padding-left: 20px;
+  }
+}
+
+@layer print {
+  .lyrics-entry .lyrics-details,
+  .lyrics-entry .origin-details {
+    font-size: 0.9em;
+    font-style: oblique;
+  }
+
+  .lyrics-entry .lyrics-details {
+    margin-bottom: 0;
+  }
+
+  .lyrics-entry .origin-details {
+    margin-top: 0.25em;
+  }
+
+  .lyrics-entry sup {
+    vertical-align: text-top;
+    opacity: 0.8;
+    cursor: default;
+  }
+}
+
+/*
+@layer interaction {
+  .lyrics-entry.long-lyrics {
+    clip-path: inset(-15px -20px);
+  }
+
+  .lyrics-entry.long-lyrics::after {
+    content: "";
+    pointer-events: none;
+    display: block;
+
+    /* Slight stretching past the bottom of the screen seems
+     * to make resizing the window (and "revealing" that area)
+     * a bit smoother.
+     *....../
+    position: fixed;
+    bottom: -20px;
+    left: 0;
+    right: 0;
+
+    height: calc(20px + min(90px, 13.5vh));
+    background: linear-gradient(to bottom, transparent, black 70%, black);
+    opacity: 0.6;
+  }
+}
+*/
+
+/* Quick description */
+
+@layer layout {
+  .quick-description:not(.has-external-links-only) {
+    --clamped-padding-ratio: max(var(--responsive-padding-ratio), 0.06);
+    margin-left: auto;
+    margin-right: auto;
+    padding-left: calc(0.40 * var(--clamped-padding-ratio) * 100%);
+    padding-right: calc(0.40 * var(--clamped-padding-ratio) * 100%);
+    padding-top: 0.25em;
+    padding-bottom: 0.75em;
+    max-width: 500px;
+  }
+
+  .quick-description.has-external-links-only {
+    padding-left: 12%;
+    padding-right: 12%;
+  }
+
+  .quick-description.has-content-only {
+    padding-bottom: 0.5em;
+  }
+}
+
+@layer material {
+  .quick-description:not(.has-external-links-only) {
+    border-left: 1px solid var(--dim-color);
+    border-right: 1px solid var(--dim-color);
+  }
+}
+
+@layer print {
+  .quick-description:not(.has-external-links-only) {
+    line-height: 1.25em;
+  }
+
+  .quick-description p {
+    text-align: center;
+  }
+
+  .quick-description > blockquote {
+    margin-left: 0 !important;
+    margin-right: 0 !important;
+  }
+
+  .quick-description .description-content.long hr ~ p {
+    text-align: left;
+  }
+
+  .quick-description .description-content :first-child {
+    margin-top: 0;
+  }
+
+  .quick-description .quick-description-actions,
+  .quick-description.has-content-only .description-content :last-child {
+    margin-bottom: 0;
+  }
+}
+
+@layer interaction {
+  .quick-description:not(.collapsed) .description-content.short,
+  .quick-description:not(.collapsed) .quick-description-actions.when-collapsed,
+  .quick-description:not(.expanded) .description-content.long,
+  .quick-description:not(.expanded) .quick-description-actions.when-expanded {
+    display: none;
+  }
+
+  .quick-description .quick-description-actions .expand-link,
+  .quick-description .quick-description-actions .collapse-link {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+}
+
+/* Quick info */
+
+@layer layout {
+  .quick-info {
+    padding-left: calc(var(--responsive-padding-ratio) * 100%);
+    padding-right: calc(var(--responsive-padding-ratio) * 100%);
+  }
+}
+
+@layer print {
+  .quick-info {
+    text-align: center;
+    line-height: 1.25em;
+  }
+}
+
+@layer construction {
+  ul.quick-info {
+    list-style: none;
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  ul.quick-info li {
+    display: inline-block;
+  }
+
+  ul.quick-info li:not(:last-child)::after {
+    content: " \00b7 ";
+    font-weight: 800;
+  }
+
+  ul.quick-info li:has(+ br)::after {
+    content: unset !important;
+  }
+}
+
+/* Sticky heading */
+
+@layer layout {
+  :where([id]) {
+    --custom-scroll-offset: 0px;
+  }
+
+  :where(#content [id]) {
+    /* Adjust scroll margin. */
+    scroll-margin-top: calc(
+        74px /* Sticky heading */
+      + 33px /* Sticky subheading */
+      - 1em  /* One line of text (align bottom) */
+      - 12px /* Padding for hanging letters & focus ring */
+      + var(--custom-scroll-offset) /* Customizable offset */
+    ) !important;
+  }
+
+  .content-sticky-heading-root {
+    width: calc(100% + 2 * var(--content-padding));
+    margin: calc(-1 * var(--content-padding));
+    margin-bottom: 0;
+  }
+
+  .content-sticky-heading-anchor,
+  .content-sticky-heading-container {
+    width: 100%;
+  }
+
+  .content-sticky-heading-root:not([inert]) {
+    position: sticky;
+    top: 0;
+  }
+
+  .content-sticky-heading-anchor:not(:where(.content-sticky-heading-root[inert]) *) {
+    position: relative;
+  }
+
+  .content-sticky-heading-container:not(:where(.content-sticky-heading-root[inert]) *) {
+    position: absolute;
+  }
+
+  .content-sticky-heading-root[inert] {
+    visibility: hidden;
+  }
+
+  main.long-content .content-sticky-heading-container {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  main.long-content .content-sticky-heading-container .content-sticky-heading-row,
+  main.long-content .content-sticky-heading-container .content-sticky-subheading-row {
+    padding-left: calc(var(--long-content-padding-ratio) * (100% - 2 * var(--content-padding)) + var(--content-padding));
+    padding-right: calc(var(--long-content-padding-ratio) * (100% - 2 * var(--content-padding)) + var(--content-padding));
+  }
+
+  .content-sticky-heading-row {
+    box-sizing: border-box;
+    padding:
+      calc(1.25 * var(--content-padding) + 5px)
+      20px
+      calc(0.75 * var(--content-padding))
+      20px;
+
+    width: 100%;
+    margin: 0;
+  }
+
+  .content-sticky-heading-container.has-cover .content-sticky-heading-row,
+  .content-sticky-heading-container.has-cover .content-sticky-subheading-row {
+    display: grid;
+    grid-template-areas:
+      "title cover";
+    grid-template-columns: 1fr min(40%, 400px);
+  }
+
+  .content-sticky-heading-container.cover-visible .content-sticky-heading-row {
+    grid-template-columns: 1fr min(40%, 90px);
+  }
+
+  .content-sticky-heading-row h1 {
+    position: relative;
+    margin: 0;
+    padding-right: 20px;
+    overflow-x: hidden;
+  }
+
+  .content-sticky-heading-row h1 .reference-collapsed-heading {
+    position: absolute;
+    white-space: nowrap;
+    visibility: hidden;
+  }
+
+  .content-sticky-heading-cover-container {
+    position: relative;
+    height: 0;
+    margin: -15px 0px -5px -5px;
+  }
+
+  .content-sticky-heading-cover {
+    position: absolute;
+    top: 0;
+    width: 80px;
+    right: 10px;
+  }
+
+  .content-sticky-heading-container .image-outer-area {
+    padding: 3px;
+  }
+
+  .content-sticky-heading-cover .image {
+    display: block;
+    width: 100%;
+    height: 100%;
+  }
+
+  .content-sticky-subheading-row {
+    position: absolute;
+    width: 100%;
+    box-sizing: border-box;
+    padding: 10px 20px 5px 20px;
+    margin-top: 0;
+    z-index: -1;
+  }
+
+  .content-sticky-subheading-row h2 {
+    margin: 0;
+  }
+
+  .content-sticky-subheading {
+    padding-right: 20px;
+  }
+}
+
+@layer material {
+  .content-sticky-heading-row {
+    background: var(--bg-black-color);
+    border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
+
+    box-shadow:
+      inset 0 10px 10px -5px var(--shadow-color),
+      0 4px 8px -4px #000000b0;
+
+    -webkit-backdrop-filter: blur(6px);
+            backdrop-filter: blur(6px);
+  }
+
+  .content-sticky-heading-cover {
+    box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.25);
+  }
+
+  .content-sticky-heading-cover .cover-artwork {
+    border-width: 1px;
+    border-radius: 1.25px;
+    box-shadow: none;
+  }
+
+  .content-sticky-heading-container .image-inner-area {
+    border-radius: 1.75px;
+
+    /* Editor's note: I don't know what this overflow: hidden is for. */
+    overflow: hidden;
+  }
+
+  .content-sticky-subheading-row {
+    background: var(--bg-black-color);
+    border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
+
+    box-shadow:
+      0 2px 2px -1px #00000060,
+      0 4px 12px -4px #00000090;
+
+    -webkit-backdrop-filter: blur(4px);
+            backdrop-filter: blur(4px);
+  }
+}
+
+@layer print {
+  .content-sticky-subheading-row h2 {
+    font-size: 0.9em !important;
+    font-weight: normal;
+    font-style: oblique;
+    color: #eee;
+  }
+}
+
+@layer interaction {
+  .content-sticky-heading-container.collapse h1 {
+    white-space: nowrap;
+    overflow-wrap: normal;
+
+    animation: collapse-sticky-heading 0.35s forwards;
+    text-overflow: ellipsis;
+    overflow-x: hidden;
+  }
+
+  @keyframes collapse-sticky-heading {
+    from {
+      height: var(--uncollapsed-heading-height);
+    }
+
+    99.9% {
+      height: var(--collapsed-heading-height);
+    }
+
+    to {
+      height: auto;
+    }
+  }
+
+  .content-sticky-heading-container h1 a {
+    transition: text-decoration-color 0.35s;
+  }
+
+  .content-sticky-heading-container h1 a:not([href]) {
+    color: inherit;
+    cursor: text;
+    text-decoration: underline;
+    text-decoration-style: dotted;
+    text-decoration-color: transparent;
+  }
+
+  .content-sticky-heading-cover-needs-reveal {
+    display: none;
+  }
+
+  .content-sticky-heading-cover {
+    transition: transform 0.35s, opacity 0.25s;
+  }
+
+  .content-sticky-heading-cover-container:not(.visible) .content-sticky-heading-cover {
+    opacity: 0;
+    transform: translateY(15px);
+    transition: transform 0.35s, opacity 0.30s;
+  }
+
+  .content-sticky-subheading-row {
+    transition: margin-top 0.35s, opacity 0.25s;
+  }
+
+  .content-sticky-subheading-row:not(.visible) {
+    margin-top: -20px;
+    opacity: 0;
+  }
+}
+
+/* Sticky sidebar */
+
+@layer layout {
+  .sidebar-column:not(.sticky-column) {
+    align-self: stretch;
+  }
+
+  .sidebar-column.sticky-column {
+    align-self: flex-start;
+
+    position: sticky;
+    top: 10px;
+    max-height: calc(100vh - 20px);
+
+    display: flex;
+    flex-direction: column;
+  }
+
+  .sidebar-multiple.sticky-column .sidebar:last-child {
+    flex-shrink: 1;
+    overflow-y: scroll;
+  }
+
+  .wiki-search-sidebar-box .wiki-search-results-container {
+    overflow-y: scroll;
+  }
+
+  .sidebar-column.sidebar.sticky-column > h1 {
+    position: sticky;
+    top: 0;
+    z-index: 2;
+
+    margin: 0 calc(-1 * var(--content-padding));
+    margin-bottom: 10px;
+
+    padding: 10px 5px;
+  }
+}
+
+@layer material {
+  .sidebar-multiple.sticky-column .sidebar:last-child {
+    scrollbar-width: thin;
+    scrollbar-color: var(--dim-color) var(--dark-color);
+  }
+
+  .wiki-search-sidebar-box .wiki-search-results-container {
+    overflow-y: scroll;
+    scrollbar-width: thin;
+    scrollbar-color: var(--dim-color) var(--dark-color);
+  }
+
+  .sidebar-column.sticky-column .sidebar:last-child::-webkit-scrollbar,
+  .wiki-search-sidebar-box .wiki-search-results-container::-webkit-scrollbar {
+    background: var(--dark-color);
+    width: 12px;
+  }
+
+  .sidebar-column.sticky-column .sidebar:last-child::-webkit-scrollbar-thumb,
+  .wiki-search-sidebar-box .wiki-search-results-container::-webkit-scrollbar-thumb {
+    transition: background 0.2s;
+    background: rgba(255, 255, 255, 0.2);
+    border: 3px solid transparent;
+    border-radius: 10px;
+    background-clip: content-box;
+  }
+
+  .sidebar-column.sidebar.sticky-column > h1 {
+    border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
+
+    background: var(--bg-black-color);
+
+    -webkit-backdrop-filter: blur(4px);
+            backdrop-filter: blur(4px);
+
+    box-shadow:
+      0 2px 3px -1px #0006,
+      0 4px 8px -2px #0009;
+  }
+}
diff --git a/src/static/css/miscellany.css b/src/static/css/miscellany.css
new file mode 100644
index 00000000..dcdd72ca
--- /dev/null
+++ b/src/static/css/miscellany.css
@@ -0,0 +1,626 @@
+/* Squares */
+
+@layer layout {
+  .square {
+    position: relative;
+    width: 100%;
+  }
+
+  .square::after {
+    content: "";
+    display: block;
+    padding-bottom: 100%;
+  }
+
+  .square-content {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
+}
+
+/* Utility spans */
+
+@layer print {
+  .nowrap {
+    white-space: nowrap;
+  }
+
+  .blockwrap, .chunkwrap {
+    display: inline-block;
+  }
+
+  p .current {
+    font-weight: 800;
+  }
+
+  .js-hide,
+  .js-show-once-data,
+  .js-hide-once-data {
+    display: none;
+  }
+}
+
+/* Totally miscellaneous typography */
+
+@layer layout {
+  li .origin-details {
+    display: block;
+    margin-left: 2ch;
+  }
+
+  li > ul {
+    margin-top: 5px;
+  }
+}
+
+@layer print {
+  center {
+    margin-top: 1em;
+    margin-bottom: 1em;
+  }
+
+  .flash-act-title {
+    display: inline-block;
+    text-decoration: inherit;
+  }
+
+  a .flash-act-title {
+    color: var(--primary-color);
+  }
+
+  .other-group-accent,
+  .rerelease-line {
+    opacity: 0.7;
+    font-style: oblique;
+  }
+
+  .other-group-accent {
+    white-space: nowrap;
+  }
+
+  .other-group-accent a {
+    color: var(--page-primary-color);
+  }
+
+  dt .by, li .by {
+    font-style: oblique;
+  }
+
+  dt .by a, li .by a {
+    display: inline-block;
+  }
+
+  p code {
+    font-size: 0.95em;
+    font-family: "courier new", monospace;
+    font-weight: 800;
+    line-height: 1.1;
+  }
+
+  /* "has-details" means a "has a <details> element" here. */
+  ul > li.has-details {
+    list-style-type: none;
+    margin-left: -17px;
+  }
+
+  li .origin-details {
+    font-size: 0.9em;
+    font-style: oblique;
+  }
+}
+
+/* Definition lists and division lists */
+
+@layer layout {
+  dl dt {
+    padding-left: 40px;
+  }
+
+  dl dt {
+    /* Heads up, this affects the measurement
+     * for dl dt which are .content-heading!
+     */
+    margin-bottom: 2px;
+  }
+
+  dl dt[id]:not(.content-heading) {
+    --custom-scroll-offset: calc(2.5em - 2px);
+  }
+
+  dl dd {
+    margin-bottom: 1em;
+  }
+
+  dl ul,
+  dl ol {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  dl.division-list dd ul {
+    padding-left: 0;
+  }
+
+  dl.division-list dt {
+    font-size: 0.88em;
+  }
+
+  dl.division-list dd {
+    margin-bottom: 0.4em;
+  }
+
+  dl.division-list dd + dt::before {
+    content: "";
+    display: block;
+    width: 220px;
+    border-bottom: 1px solid var(--dim-color);
+    margin-bottom: 0.6em;
+  }
+}
+
+/* Ordinary links */
+
+@layer print {
+  a {
+    color: var(--primary-color);
+    text-decoration: none;
+  }
+
+  a.current {
+    font-weight: 800;
+  }
+
+  a.series {
+    font-style: oblique;
+  }
+}
+
+@Layer interaction {
+  /* This is an !important rule because it's really applicable
+   * pretty much anywhere, but the selector specificity is lower
+   * than most selectors which, say, add a dotted underline to
+   * begin with - those would take cascading priority.
+   *
+   * Similarly, this rule has to live in the interaction layer,
+   * because that's where other underline hover cues are set.
+   * If we set set it any higher (e.g. in print) the !important
+   * takes universal priority over !important's set in deeper
+   * layers - like interaction.
+   */
+  a:hover {
+    text-decoration: underline;
+    text-decoration-style: solid !important;
+  }
+
+  a:not([href]) {
+    cursor: default;
+  }
+
+  a:not([href]):hover {
+    text-decoration: none;
+  }
+}
+
+/* Links with symbols */
+
+@layer print {
+  a .normal-content {
+    color: white;
+  }
+
+  .external-link:not(.from-content) {
+    white-space: nowrap;
+  }
+
+  .external-link.indicate-external::after {
+    content: '\00a0➚';
+    font-style: normal;
+  }
+
+  .external-link.indicate-external:hover::after {
+    color: white;
+  }
+
+  .image-media-link::after {
+    /* Thanks to Jay Freestone for being awesome:
+     * https://www.jayfreestone.com/writing/wrapping-and-inline-pseudo-elements/
+     */
+
+    pointer-events: none;
+    content: '\200b';
+    padding-left: 22px;
+
+    background-color: var(--primary-color);
+
+    /* mask-image is set in content JavaScript,
+     * because we can't identify the correct nor
+     * absolute path to the file from CSS.
+     */
+
+    mask-repeat: no-repeat;
+    mask-position: calc(100% - 2px);
+  }
+
+  .image-media-link:hover::after {
+    background-color: white;
+  }
+}
+
+/* Label elements */
+
+@layer interaction {
+  label:hover span {
+    text-decoration: underline;
+    text-decoration-style: solid;
+  }
+
+  label > input[type=checkbox]:not(:checked) + span {
+    opacity: 0.8;
+  }
+}
+
+/* Progress elements */
+
+@layer material {
+  progress {
+    accent-color: var(--primary-color);
+  }
+}
+
+/* Code blocks */
+
+@layer print {
+  pre.content-code {
+    position: relative;
+    white-space: nowrap;
+
+    max-width: calc(100vw - 180px);
+
+    /* Welcome to heck. */
+    font-family: inherit;
+
+    border: 1px dashed var(--primary-color);
+  }
+
+  pre.content-code span {
+    display: block;
+    overflow-x: scroll;
+    padding: 5px 20px 5px 5px;
+    background: black;
+    color: white;
+  }
+
+  pre.content-code::before {
+    content: "";
+    display: block;
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 100%;
+    height: 100%;
+    box-shadow: -15px 0 24px -8px black inset;
+    pointer-events: none;
+  }
+
+  pre.content-code code {
+    font-family: "courier new", monospace;
+    font-weight: 800;
+    font-size: 0.8em;
+  }
+}
+
+/* HR elements, of various flavors */
+
+@layer print {
+  #content hr {
+    border: 1px inset #808080;
+  }
+
+  #content hr.split {
+    color: #808080;
+  }
+
+  #content hr.split::before {
+    content: "(split)";
+  }
+
+  #content hr.main-separator {
+    color: var(--dim-color);
+    clear: none;
+    margin-top: -0.25em;
+    margin-bottom: 1.75em;
+  }
+
+  #content hr.main-separator::before {
+    content: "♦";
+    font-size: 1.2em;
+  }
+
+  #content hr.split,
+  #content hr.main-separator {
+    position: relative;
+    overflow: hidden;
+    border: none;
+  }
+
+  #content hr.split::after,
+  #content hr.main-separator::after {
+    display: inline-block;
+    content: "";
+    width: calc(100% - min(calc(8vw - 35px), 45px));
+    position: absolute;
+    top: 50%;
+    margin-left: 10px;
+  }
+
+  #content hr.split::after {
+    border: 1px inset currentColor;
+    margin-top: -2px;
+  }
+
+  #content hr.main-separator::after {
+    border-bottom: 1px solid currentColor;
+  }
+
+  hr.cute,
+  #content hr.cute,
+  .sidebar hr.cute {
+    border-color: var(--primary-color);
+    border-style: none none dotted none;
+  }
+}
+
+/* Images and media embedded right into content, effects */
+
+@layer layout {
+  a.align-center, img.align-center,
+  audio.align-center, video.align-center {
+    display: block;
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  a.align-full, a.align-full img,
+  img.align-full, video.align-full {
+    width: 100%;
+  }
+
+  .content-image {
+    display: inline-block !important;
+  }
+}
+
+@layer material {
+  img.pixelate, .pixelate img,
+  video.pixelate, .pixelate video {
+    image-rendering: crisp-edges;
+  }
+}
+
+@layer print {
+  p > img, li > img {
+    max-width: 100%;
+    object-fit: contain;
+    height: auto;
+    vertical-align: text-bottom;
+  }
+}
+
+/* Paths and filenames */
+
+@layer print {
+  span.path, code.filename {
+    font-size: 0.95em;
+    font-family: "courier new", monospace;
+    font-weight: 800;
+    background: #ccc3;
+
+    padding: 0.05em 0.5ch;
+    border: 1px solid #ccce;
+    border-radius: 2px;
+    line-height: 1.4;
+  }
+
+  blockquote :is(span.path, code.filename) {
+    font-size: 0.9em;
+  }
+
+  .image-details code.filename {
+    margin-left: -0.4ch;
+    opacity: 0.8;
+  }
+
+  .image-details code.filename:hover {
+    opacity: 1;
+    cursor: text;
+  }
+
+  span.path i {
+    display: inline-block;
+    font-style: normal;
+  }
+
+  span.path i::before {
+    content: "\0020/\0020";
+    color: #ccc;
+  }
+}
+
+/* Spoiler elements */
+
+@layer print {
+  s.spoiler {
+    display: inline-block;
+    color: transparent;
+    text-decoration: underline;
+    text-decoration-color: white;
+    text-decoration-style: dashed;
+    text-decoration-skip: none;
+    text-decoration-skip-ink: none;
+  }
+
+  s.spoiler::selection {
+    color: black;
+    background: white;
+  }
+
+  s.spoiler::-moz-selection {
+    color: black;
+    background: white;
+  }
+}
+
+/* Summary elements */
+
+@layer print {
+  summary > span b {
+    font-weight: normal;
+    color: var(--primary-color);
+  }
+}
+
+@layer interaction {
+  summary > span:hover {
+    cursor: pointer;
+    text-decoration: underline;
+    text-decoration-color: var(--primary-color);
+  }
+
+  summary > span:hover a {
+    text-decoration: none !important;
+  }
+
+  summary > span:hover:has(a:hover),
+  summary > span:hover:has(a.nested-hover),
+  summary.has-nested-hover > span {
+    text-decoration: none !important;
+  }
+
+  summary > span:hover:has(a:hover) a,
+  summary > span:hover:has(a.nested-hover) a,
+  summary.has-nested-hover > span a {
+    text-decoration: underline !important;
+  }
+
+  summary.underline-white > span:hover {
+    text-decoration-color: white;
+  }
+
+  /* This link isn't supposed to be underlined *at all*
+   * when the summary (and not link) is hovered, but
+   * for some reason Safari is still applying its colored
+   * and dotted(!) underline. Get around the apparent
+   * effect by just making it white.
+   */
+  summary.underline-white > span:hover a:not(:hover) {
+    text-decoration-color: white;
+  }
+}
+
+/* "Drops" */
+
+@layer layout {
+  .drop {
+    padding: 15px 20px;
+    width: max-content;
+    max-width: min(60vw, 600px);
+  }
+
+  .commentary-drop {
+    margin-top: 25px;
+    margin-bottom: 15px;
+    margin-left: 20px;
+    padding: 10px 20px;
+    max-width: min(60vw, 300px);
+  }
+}
+
+@layer material {
+  .drop {
+    border: 1px dotted var(--primary-color);
+    border-radius: 6px;
+
+    background:
+      linear-gradient(var(--bg-color), var(--bg-color)),
+      linear-gradient(#000000bb, #000000bb),
+      var(--primary-color);
+
+    --drop-shadow: 0 -2px 6px -1px var(--dim-color) inset;
+    box-shadow: var(--drop-shadow);
+  }
+}
+
+@layer interaction {
+  .drop.shiny {
+    cursor: default;
+  }
+
+  @supports (box-shadow: 1px 1px 1px color-mix(in srgb, blue, 40% red)) {
+    @property --drop-shine {
+      syntax: '<percentage>';
+      initial-value: 0%;
+      inherits: false;
+    }
+
+    .drop.shiny {
+      cursor: default;
+      transition: --drop-shine 0.2s;
+    }
+
+    .drop.shiny:hover {
+      --drop-shine: 100%;
+
+      box-shadow:
+        var(--drop-shadow),
+        0 2px 4px -0.5px color-mix(in srgb, var(--primary-color), calc(100% - var(--drop-shine)) transparent);
+    }
+  }
+}
+
+/* Track sections, and similar */
+
+@layer layout {
+  .album-group-list dt,
+  .group-series-list dt {
+    padding-left: 0;
+  }
+
+  .album-group-list dd,
+  .group-series-list dd {
+    margin-left: 0;
+  }
+
+  .album-group-list li {
+    padding-left: 1.5ch;
+    text-indent: -1.5ch;
+  }
+
+  .album-group-list li > * {
+    text-indent: 0;
+  }
+
+  .album-group-list blockquote,
+  .group-series-list blockquote {
+    max-width: 540px;
+    margin-bottom: 9px;
+    margin-top: 3px;
+  }
+
+  .album-group-list blockquote p:first-child,
+  .group-series-list blockquote p:first-child {
+    margin-top: 0;
+  }
+
+  .album-group-list blockquote p:last-child,
+  .group-series-list blockquote p:last-child {
+    margin-bottom: 0;
+  }
+}
+
+@layer print {
+  .album-group-list dt,
+  .group-series-list dt {
+    font-style: oblique;
+  }
+}
diff --git a/src/static/css/page.css b/src/static/css/page.css
new file mode 100644
index 00000000..fdd15358
--- /dev/null
+++ b/src/static/css/page.css
@@ -0,0 +1,915 @@
+/* Document root */
+
+@property --box-opacity {
+  /* This property rule doesn't actually do anything in practice.
+   * It just lets us see the effective value of --box-opacity
+   * in browser CSS inspectors.
+   */
+  syntax: "<number>";
+  inherits: true;
+  initial-value: 0.6;
+}
+
+:root {
+  color-scheme: dark;
+
+  --initial-wallpaper-opacity: 0.5;
+  --wallpaper-brightness: var(--initial-wallpaper-opacity);
+  --box-opacity: calc(0.6 + 0.3 * (clamp(0.75, var(--wallpaper-brightness), 0.95) - 0.75) / (0.95 - 0.75));
+}
+
+/* Body */
+
+@layer layout {
+  body {
+    position: relative;
+    margin: 0;
+    padding: 10px;
+    overflow-y: scroll;
+  }
+
+  body::before {
+    content: "";
+  }
+}
+
+/* Wallpaper */
+
+@layer layout {
+  body::before, .wallpaper-part {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: -1;
+
+    /* NB: these are 100 LVW, "largest view width", etc.
+     * Stabilizes background on viewports with modal dimensions,
+     * e.g. expanding/shrinking tab bar or collapsible find bar.
+     * 100% dimensions are kept above for browser compatibility.
+     */
+    width: 100lvw;
+    height: 100lvh;
+  }
+}
+
+@layer material {
+  body {
+    background: black;
+  }
+
+  body::before {
+    /* This is where the basic background-image rule
+     * gets applied... but the path *to* that media file
+     * isn't part of the CSS itself anymore!
+     */
+  }
+
+  body::before, .wallpaper-part {
+    background-position: center;
+    background-size: cover;
+    opacity: var(--initial-wallpaper-opacity);
+  }
+}
+
+/* Page container */
+
+@layer layout {
+  #page-container {
+    max-width: 1100px;
+
+    margin: 0 auto 40px;
+    padding: calc(15px - var(--page-border-width)) 0;
+
+    --page-border-width: 0;
+    border: var(--page-border-width) solid transparent;
+    box-sizing: border-box;
+  }
+
+  #page-container > * {
+    margin-left: calc(15px - var(--page-border-width));
+    margin-right: calc(15px - var(--page-border-width));
+  }
+
+  .layout-columns {
+    display: flex;
+    align-items: stretch;
+  }
+}
+
+@layer material {
+  #page-container {
+    background-color: var(--bg-color, rgba(35, 35, 35, 0.8));
+
+    --adjust-page-opacity: calc(alpha * (1.0 + 0.55 * (var(--box-opacity, 0.6) - 0.6)));
+
+    background-color:
+      color-mix(in oklab,
+        rgb(from var(--bg-color, rgba(35, 35, 35, 0.8))
+          r g b / var(--adjust-page-opacity)),
+        rgb(from var(--bg-color, rgba(35, 35, 35, 0.8))
+          0 0 0 / var(--adjust-page-opacity))
+        calc((var(--box-opacity, 0.6) - 0.6) * 40%));
+
+    color: #ffffff;
+
+    --page-border-width: 0.5px;
+    border-bottom-width: 1px;
+    border-radius: 4px 4px 5px 5px;
+
+    --page-border-color: rgba(
+      144, 144, 144,
+      calc(0.4 + 0.5 * (var(--box-opacity) - 0.6) / 0.4));
+    border-color: var(--page-border-color);
+
+    box-shadow:
+      0 0 40px #0008,
+      0 20px 15px -4px #0002,
+      0 6px 15px -3px #2221,
+      0 4px 6px 2px #1113,
+      0 1px 4px 1px #1114;
+  }
+}
+
+/* Skippers */
+
+@layer layout {
+  #skippers > * {
+    display: inline-block;
+  }
+}
+
+@layer construction {
+  #skippers > .skipper-list:not(:last-child)::after {
+    display: inline-block;
+    content: "\00a0";
+    margin-left: 2px;
+    margin-right: -2px;
+    border-left: 1px dotted;
+  }
+
+  #skippers .skipper-list > .skipper:not(:last-child)::after {
+    content: " \00b7 ";
+    font-weight: 800;
+  }
+}
+
+@layer interaction {
+  #skippers {
+    position: absolute;
+    left: -10000px;
+    top: auto;
+    width: 1px;
+    height: 1px;
+  }
+
+  #skippers:focus-within {
+    position: static;
+    width: unset;
+    height: unset;
+  }
+
+  #page-container:not(.showing-sidebar-left) #skippers .skipper[data-for=sidebar-left],
+  #page-container:not(.showing-sidebar-right) #skippers .skipper[data-for=sidebar-right] {
+    display: none;
+  }
+}
+
+/* Banner */
+
+@property --banner-shine {
+  syntax: '<percentage>';
+  initial-value: 0%;
+  inherits: false;
+}
+
+@layer interactivity {
+  #banner::before, #banner::after {
+    pointer-events: none;
+  }
+}
+
+@layer layout {
+  #banner {
+    position: relative;
+  }
+
+  /* Used for top border */
+  #banner::before {
+    content: "";
+    position: absolute;
+    top: -1px;
+    left: var(--page-border-width);
+    right: var(--page-border-width);
+    bottom: 0;
+  }
+
+  /* Used for inset shadow */
+  #banner::after {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+
+  #banner {
+    margin: 10px calc(-1 * var(--page-border-width));
+  }
+
+  #banner img {
+    display: block;
+    width: 100%;
+    height: auto;
+  }
+
+  #banner.short, #banner.short img {
+    /* 1100px -> 72.5px, 900px -> 60px
+     * 12.5px height per 200px width
+     * Viewport maxes at 1124px, so:
+     *   Start from 72.5px
+     *   Get width below maximum:
+     *     (1124px - min(1124px, 100vw))
+     *   Multiply by rate of change:
+     *     (12.5 / 200)
+     */
+    max-height: calc(72.5px - (1124px - min(1124px, 100vw)) * (12.5 / 200));
+  }
+
+  #banner.short img {
+    object-position: 0 var(--short-banner-alignment, 50%);
+  }
+}
+
+@layer material {
+  #banner {
+    background-color: var(--abyss-color);
+    border-bottom: 1px solid var(--primary-color);
+  }
+
+  #banner::before {
+    border-top: 1px dashed var(--page-border-color);
+  }
+
+  #banner::after {
+    box-shadow:
+      inset 0 -2px 3px rgba(0, 0, 0, 0.35),
+      inset 0 1px 4px 2px rgba(0, 0, 0, 0.15);
+  }
+
+  #banner.dim img {
+    opacity: 0.32;
+  }
+}
+
+/* Banner - shine effect */
+
+@supports (-webkit-box-reflect: below) {
+  @layer material {
+    #banner {
+      -webkit-box-reflect: below -12px linear-gradient(transparent, color-mix(in srgb, transparent, var(--banner-shine, 0%) white));
+    }
+  }
+
+  @layer interaction {
+    #banner {
+      transition: --banner-shine 0.8s;
+    }
+
+    #banner:not(.dim, .collapsed) {
+      --banner-shine: 4%;
+    }
+
+    #banner:not(.dim, .collapsed):hover {
+      --banner-shine: 35%;
+      transition-delay: 0.3s;
+    }
+  }
+}
+
+/* Boxes (in general) */
+
+@layer layout {
+  #skippers,
+  #header,
+  #secondary-nav,
+  #footer {
+    padding: 5px;
+  }
+
+  #skippers,
+  #header,
+  #secondary-nav {
+    margin-bottom: 10px;
+  }
+
+  #footer {
+    margin-top: 10px;
+  }
+}
+
+@layer material {
+  #skippers,
+  #header,
+  #secondary-nav,
+  #content,
+  #footer,
+  .sidebar {
+    background-color: rgba(0, 0, 0, var(--box-opacity, 0.6));
+    border: 1px dotted var(--primary-color);
+    border-radius: 3px;
+  }
+}
+
+@layer print {
+  #skippers,
+  #header,
+  #secondary-nav,
+  #footer,
+  .sidebar {
+    font-size: 0.85em;
+  }
+}
+
+/* Header and navigation links */
+
+@layer layout {
+  #header { display: grid; }
+
+  .nav-main-links { grid-area: main-links; }
+  .nav-content { grid-area: content; }
+  .nav-bottom-row { grid-area: bottom-row; }
+
+  #header.nav-has-main-links.nav-has-content {
+    grid-template-columns: 2.5fr 3fr;
+    grid-template-rows: min-content 1fr;
+    grid-template-areas:
+      "main-links content"
+      "bottom-row content";
+  }
+
+  #header.nav-has-main-links:not(.nav-has-content) {
+    grid-template-columns: 1fr;
+    grid-template-areas:
+      "main-links"
+      "bottom-row";
+  }
+
+  .nav-main-links {
+    margin-right: 20px;
+  }
+
+  .nav-bottom-row {
+    align-self: start;
+  }
+}
+
+@layer print {
+  .nav-link {
+    display: inline-block;
+  }
+
+  .nav-main-links .nav-link.current > span.nav-link-content > a {
+    font-weight: 800;
+  }
+
+  .nav-main-links .nav-link-accent {
+    display: inline-block;
+  }
+
+  .series-nav-links {
+    display: inline-block;
+  }
+}
+
+@layer construction {
+  .nav-links-index .nav-link:not(:first-child)::before,
+  .nav-links-groups .nav-link:not(:first-child)::before {
+    content: "\0020\00b7\0020";
+    font-weight: 800;
+  }
+
+  .nav-links-hierarchical .nav-link + .nav-link::before,
+  .nav-links-hierarchical .nav-link + .blockwrap .nav-link::before {
+    content: "\0020/\0020";
+  }
+
+  .group-with-series .group-nav-links + .series-nav-links::before {
+    content: "\00a0»\00a0";
+    font-weight: normal;
+  }
+
+  .group-with-series .series-nav-links:not(:last-child)::after {
+    content: ",\00a0";
+    font-weight: normal;
+  }
+
+  .group-with-series .series-nav-links + .series-nav-links::before {
+    content: "";
+  }
+}
+
+@layer interaction {
+  .inert-previous-next-link {
+    opacity: 0.7;
+  }
+}
+
+/* Secondary nav */
+
+@layer layout {
+  #secondary-nav {
+    text-align: center;
+  }
+
+  #secondary-nav.album-secondary-nav {
+    display: flex;
+    justify-content: space-around;
+    padding-left: 7.5% !important;
+    padding-right: 7.5% !important;
+    flex-wrap: wrap;
+  }
+
+  #secondary-nav.album-secondary-nav.with-previous-next .group-with-series {
+    width: 100%;
+  }
+
+  #secondary-nav.album-secondary-nav.with-previous-next > * {
+    margin-left: 5px;
+    margin-right: 5px;
+  }
+}
+
+@layer print {
+  #secondary-nav.album-secondary-nav {
+    line-height: 1.4;
+  }
+
+  #secondary-nav.album-secondary-nav .group-nav-links .dot-switcher,
+  #secondary-nav.album-secondary-nav .series-nav-links .dot-switcher {
+    white-space: nowrap;
+  }
+}
+
+/* Sidebar column */
+
+@layer layout {
+  .sidebar-column {
+    flex: 1 1 35%;
+    min-width: 150px;
+    max-width: 250px;
+    align-self: flex-start;
+  }
+
+  .sidebar-column.wide {
+    max-width: 350px;
+    flex-basis: 300px;
+    flex-shrink: 0;
+    flex-grow: 1;
+  }
+
+  .sidebar-column.always-content-column {
+    /* duplicated in thin & medium media query */
+    position: static !important;
+    max-width: unset !important;
+    flex-basis: unset !important;
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+    width: 100%;
+  }
+
+  .sidebar-multiple {
+    display: flex;
+    flex-direction: column;
+  }
+
+  .sidebar-multiple .sidebar:not(:first-child) {
+    margin-top: 15px;
+  }
+}
+
+@layer interaction {
+  .sidebar-column.initially-hidden {
+    display: none;
+  }
+
+  .sidebar-column.search-showing-results {
+    position: sticky;
+    top: 5px;
+    align-self: flex-start !important; /* pls */
+  }
+}
+
+/* Sidebar boxes */
+
+@layer layout {
+  .sidebar {
+    --content-padding: 5px;
+    padding: var(--content-padding);
+  }
+
+  #sidebar-left {
+    margin-right: 10px;
+  }
+
+  #sidebar-right {
+    margin-left: 10px;
+  }
+
+  .sidebar-box-joiner {
+    height: 10px;
+  }
+
+  .sidebar-box-joiner + .sidebar {
+    margin-top: 0 !important;
+  }
+}
+
+@layer material {
+  .sidebar-box-joiner {
+    width: 0;
+    margin-left: auto;
+    margin-right: auto;
+    border-right: 1px dashed var(--primary-color);
+  }
+}
+
+@layer print {
+  .sidebar > h1,
+  .sidebar > h2,
+  .sidebar > h3,
+  .sidebar > p {
+    text-align: center;
+    padding-left: 4px;
+    padding-right: 4px;
+  }
+
+  .sidebar h1 {
+    font-size: 1.25em;
+  }
+
+  .sidebar h2 {
+    font-size: 1.1em;
+    margin: 0;
+  }
+
+  .sidebar h2:first-child {
+    margin-top: 0.5em;
+    margin-bottom: 0.5em;
+  }
+
+  .sidebar h3 {
+    font-size: 1.1em;
+    font-style: oblique;
+    font-variant: small-caps;
+    margin-top: 0.3em;
+    margin-bottom: 0em;
+  }
+
+  .sidebar > p {
+    margin: 0.5em 0;
+    padding: 0 5px;
+  }
+
+  .sidebar hr {
+    color: #555;
+    margin: 10px 5px;
+  }
+
+  .sidebar > ol,
+  .sidebar > ul {
+    padding-left: 30px;
+    padding-right: 15px;
+  }
+
+  .sidebar > dl {
+    padding-right: 15px;
+    padding-left: 0;
+  }
+
+  .sidebar > dl dt {
+    padding-left: 10px;
+    margin-top: 0.5em;
+  }
+
+  .sidebar > dl dt.current {
+    font-weight: 800;
+  }
+
+  .sidebar > dl dd {
+    margin-left: 0;
+  }
+
+  .sidebar > dl dd ul {
+    padding-left: 30px;
+    margin-left: 0;
+  }
+
+  .sidebar > dl .side {
+    padding-left: 10px;
+  }
+
+  .sidebar details.has-tree-list[open] summary {
+    font-weight: 800;
+  }
+
+  .sidebar dl.tree-list {
+    margin-top: 0.25em;
+    line-height: 1.25em;
+    padding-left: 15px;
+  }
+
+  .sidebar dl.tree-list dt {
+    display: list-item;
+    list-style-type: disc;
+    padding-left: 0;
+    margin-left: 20px;
+  }
+
+  .sidebar dl.tree-list dl {
+    padding-left: 15px;
+  }
+
+  .sidebar dl.tree-list dd {
+    margin-left: 0;
+  }
+
+  .sidebar dl.tree-list dt.current a {
+    font-weight: 800;
+    border-bottom: 1px solid;
+  }
+
+  .sidebar .times-used {
+    opacity: 0.7;
+    font-size: 0.9em;
+    cursor: default;
+  }
+
+  .sidebar li.current {
+    font-weight: 800;
+  }
+
+  .sidebar li {
+    overflow-wrap: break-word;
+    padding-right: 3px;
+  }
+
+  .sidebar li.structured {
+    margin-bottom: 3px;
+  }
+
+  .sidebar > details.current summary {
+    font-weight: 800;
+  }
+
+  .sidebar > details summary {
+    margin-top: 0.5em;
+    padding-left: 5px;
+  }
+
+  .sidebar > details.current summary span b {
+    font-weight: 800;
+  }
+
+  .sidebar > details ul,
+  .sidebar > details ol {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .sidebar > details:last-child {
+    margin-bottom: 10px;
+  }
+
+  .sidebar > details[open] {
+    margin-bottom: 1em;
+  }
+
+  .sidebar article {
+    text-align: left;
+    margin: 5px 5px 15px 5px;
+  }
+
+  .sidebar article:last-child {
+    margin-bottom: 5px;
+  }
+
+  .sidebar article h2,
+  .news-index h2 {
+    border-bottom: 1px dotted;
+  }
+
+  .sidebar article h2 time,
+  .news-index time {
+    float: right;
+    font-weight: normal;
+  }
+
+  .group-chronology-link,
+  .series-chronology-link {
+    font-style: oblique;
+  }
+
+  .group-chronology-link a,
+  .series-chronology-link a {
+    font-style: normal;
+  }
+}
+
+/* Track release sidebar box */
+
+@layer layout {
+  .track-release-sidebar-box {
+    --content-padding: 3px;
+  }
+
+  .track-release-sidebar-box h1 {
+    margin: 0;
+  }
+
+  .track-release-sidebar-box + .track-release-sidebar-box,
+  .track-release-sidebar-box + .track-list-sidebar-box,
+  .track-list-sidebar-box + .track-release-sidebar-box {
+    margin-top: 5px !important;
+    border-top-left-radius: 0 !important;
+    border-top-right-radius: 0 !important;
+  }
+
+  .track-release-sidebar-box:has(+ .track-list-sidebar-box),
+  .track-list-sidebar-box:has(+ .track-release-sidebar-box) {
+    border-bottom-right-radius: 0 !important;
+    border-bottom-left-radius: 0 !important;
+  }
+}
+
+@layer print {
+  .track-release-sidebar-box h1 {
+    font-weight: normal;
+    font-size: 0.9em;
+    font-style: oblique;
+  }
+}
+
+/* Track list sidebar box */
+
+@layer print {
+  .track-list-sidebar-box summary {
+    padding-left: 20px !important;
+    text-indent: -15px !important;
+  }
+
+  .track-list-sidebar-box .track-section-range {
+    white-space: nowrap;
+  }
+}
+
+/* Content area */
+
+@layer layout {
+  #content {
+    flex-grow: 1;
+    flex-shrink: 3;
+
+    position: relative;
+    box-sizing: border-box;
+
+    --content-padding: 20px;
+    padding: var(--content-padding);
+  }
+}
+
+@layer print {
+  #content {
+    overflow-wrap: anywhere;
+  }
+
+  #content h1 {
+    font-size: 1.5em;
+  }
+
+  #content li {
+    margin-bottom: 4px;
+  }
+
+  #content li i {
+    white-space: nowrap;
+  }
+
+  #content li.divider {
+    list-style-type: none;
+    max-width: 220px;
+    margin-top: 0.6em;
+    margin-bottom: 0.6em;
+  }
+
+  #content li.divider hr {
+    color: #888;
+    border: none;
+    border-bottom: 1px solid;
+  }
+
+  #content details {
+    margin-top: 0.25em;
+    margin-bottom: 0.25em;
+  }
+
+  #content.top-index h1,
+  #content.flash-index h1 {
+    text-align: center;
+    font-size: 2em;
+  }
+
+  #content.flash-index h2 {
+    text-align: center;
+    font-size: 2.5em;
+    font-variant: small-caps;
+    font-style: oblique;
+    margin-bottom: 0;
+    text-align: center;
+    width: 100%;
+  }
+
+  #content.flash-index h2 .flash-act-title,
+  #content.flash-index h1 .flash-act-title {
+    display: block;
+  }
+
+  #content.flash-index h2 .flash-act-title {
+    text-transform: uppercase;
+  }
+
+  #content.top-index h2 {
+    text-align: center;
+    font-size: 2em;
+    font-weight: normal;
+    margin-bottom: 0.25em;
+  }
+
+  #content.top-index.has-subtitle h1 {
+    margin-bottom: 0.35em;
+  }
+
+  #content.top-index h2.page-subtitle {
+    font-size: 1.8em;
+    margin-top: 0.35em;
+    margin-bottom: 0.5em;
+  }
+}
+
+/* Footer */
+
+@layer layout {
+  .footer-content {
+    margin: 5px 12%;
+  }
+
+  .footer-content > :first-child {
+    margin-top: 0;
+  }
+
+  .footer-content > :last-child {
+    margin-bottom: 0;
+  }
+
+  .footer-localization-links {
+    margin: 5px 12%;
+  }
+}
+
+@layer print {
+  footer {
+    text-align: center;
+    font-style: oblique;
+  }
+}
+
+@layer construction {
+  .footer-localization-links > span:not(:last-child)::after {
+    content: " \00b7 ";
+    font-weight: 800;
+  }
+}
+
+/* Content (main box) */
+
+@layer layout {
+  main {
+    --responsive-padding-ratio: 0.10;
+  }
+
+  main.long-content {
+    --long-content-padding-ratio: var(--responsive-padding-ratio);
+  }
+
+  main.long-content .main-content-container,
+  main.long-content > h1 {
+    padding-left: calc(var(--long-content-padding-ratio) * 100%);
+    padding-right: calc(var(--long-content-padding-ratio) * 100%);
+  }
+
+  #content.top-index section {
+    margin-bottom: 1.5em;
+  }
+}
diff --git a/src/static/css/responsive.css b/src/static/css/responsive.css
new file mode 100644
index 00000000..38e4188a
--- /dev/null
+++ b/src/static/css/responsive.css
@@ -0,0 +1,223 @@
+/* Layout - Wide (most computers) */
+
+@media (min-width: 850px) {
+  #page-container.showing-sidebar-left:not(.sidebars-in-content-column) #secondary-nav:not(.always-visible),
+  #page-container.showing-sidebar-right:not(.sidebars-in-content-column) #secondary-nav:not(.always-visible) {
+    display: none;
+  }
+}
+
+/* Layout - Medium (tablets, some landscape mobiles)
+ *
+ * Note: Rules defined here are exclusive to "medium" width, i.e. they don't
+ * additionally apply to "thin". Use the later section which applies to both
+ * if so desired.
+ */
+
+@media (min-width: 600px) and (max-width: 849.98px) {
+  /* Medium layout is mainly defined (to the user) by hiding the sidebar, so
+   * don't apply the similar layout change of widening the long-content area
+   * if this page doesn't have a sidebar to hide in the first place.
+   */
+  #page-container.showing-sidebar-left main,
+  #page-container.showing-sidebar-right main {
+    --responsive-padding-ratio: 0.06;
+  }
+}
+
+/* Layout - Wide or Medium */
+
+@media (min-width: 600px) {
+  .content-sticky-heading-root {
+    /* Safari doesn't always play nicely with position: sticky,
+     * this seems to fix images sometimes displaying above the
+     * position: absolute subheading (h2) child
+     *
+     * See also: https://stackoverflow.com/questions/50224855/
+     */
+    transform: translate3d(0, 0, 0);
+    z-index: 1;
+  }
+
+  /* Cover art floats to the right. It's positioned in HTML beneath the
+   * heading, so pull it up a little to "float" on top.
+   */
+  #artwork-column {
+    float: right;
+    width: 40%;
+    min-width: 220px;
+    max-width: 280px;
+    margin: -60px 0 10px 20px;
+
+    position: relative;
+    z-index: 2;
+  }
+
+  /* ...Except on top-indexes, where cover art is displayed prominently
+   * between the heading and subheading.
+   */
+  #content.top-index #artwork-column {
+    float: none;
+    margin: 2em auto 2.5em auto;
+    max-width: 375px;
+  }
+
+  html[data-url-key="localized.home"] #page-container.showing-sidebar-left .grid-listing > .grid-item:not(:nth-child(n+7)) {
+    flex-basis: 23%;
+    margin: 15px;
+  }
+
+  html[data-url-key="localized.home"] #page-container.showing-sidebar-left .grid-listing > .grid-item:nth-child(n+7) {
+    flex-basis: 18%;
+    margin: 10px;
+  }
+}
+
+/* Layout - Medium or Thin */
+
+@media (max-width: 849.98px) {
+  .sidebar.collapsible,
+  .sidebar-box-joiner.collapsible,
+  .sidebar-column.all-boxes-collapsible {
+    display: none;
+  }
+
+  /* Duplicated for "sidebars in content column" */
+
+  .layout-columns {
+    flex-direction: column;
+  }
+
+  .layout-columns > *:not(:last-child) {
+    margin-bottom: 10px;
+  }
+
+  .sidebar-column {
+    position: static !important;
+    max-width: unset !important;
+    flex-basis: unset !important;
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+    width: 100%;
+  }
+
+  .sidebar .news-entry:not(.first-news-entry) {
+    display: none;
+  }
+
+  .wiki-search-sidebar-box {
+    --keep-viewport-visible: 205px;
+  }
+
+  /* End duplicated for "sidebars in content column" */
+
+  .grid-listing > .grid-item {
+    flex-basis: 40%;
+  }
+}
+
+/* Layout - "sidebars in content column"
+ * This is the same code as immediately above, for medium and
+ * thin layouts, but can be opted into by the page itself
+ * instead of through a media query.
+ */
+
+#page-container.sidebars-in-content-column
+.layout-columns {
+  flex-direction: column;
+}
+
+#page-container.sidebars-in-content-column
+.layout-columns > *:not(:last-child) {
+  margin-bottom: 10px;
+}
+
+#page-container.sidebars-in-content-column
+.sidebar-column {
+  position: static !important;
+  max-width: unset !important;
+  flex-basis: unset !important;
+  margin-right: 0 !important;
+  margin-left: 0 !important;
+  width: 100%;
+}
+
+#page-container.sidebars-in-content-column
+.sidebar .news-entry:not(.first-news-entry) {
+  display: none;
+}
+
+#page-container.sidebars-in-content-column
+.wiki-search-sidebar-box {
+  max-height: max(245px, 60vh, calc(100vh - 205px));
+}
+
+/* Layout - Thin (phones) */
+
+@media (max-width: 600px) {
+  main {
+    --responsive-padding-ratio: 0.02;
+  }
+
+  #artwork-column {
+    margin: 25px 0 5px 0;
+    width: 100%;
+    max-width: unset;
+  }
+
+  #artwork-column .cover-artwork {
+    --normal-shadow: 0 0 transparent;
+  }
+
+  #artwork-column .cover-artwork:not(:first-child),
+  #artwork-column .cover-artwork-joiner {
+    margin-left: 30px;
+    margin-right: 30px;
+  }
+
+  .music-video {
+    width: 70%;
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .music-video .image {
+    aspect-ratio: 32 / 9;
+  }
+
+  #additional-names-box {
+    width: unset;
+    max-width: unset;
+  }
+
+  .nav-has-content .nav-main-links .nav-link-accent {
+    display: block;
+  }
+
+  /* Show sticky heading above cover art */
+
+  .content-sticky-heading-root {
+    z-index: 2;
+  }
+
+  .content-sticky-heading-row h1 {
+    padding-right: 10px;
+  }
+
+  /* Let sticky heading text span past lower-index cover art */
+
+  .content-sticky-heading-container.has-cover .content-sticky-heading-row,
+  .content-sticky-heading-container.has-cover .content-sticky-subheading-row {
+    grid-template-columns: 1fr 90px;
+  }
+
+  /* Disable grid features, just line header children up vertically */
+
+  #header {
+    display: block;
+  }
+
+  #header > div:not(:first-child) {
+    margin-top: 0.5em;
+  }
+}
diff --git a/src/static/css/search.css b/src/static/css/search.css
new file mode 100644
index 00000000..409e12df
--- /dev/null
+++ b/src/static/css/search.css
@@ -0,0 +1,548 @@
+/* Sidebar box */
+
+@layer layout {
+  .wiki-search-sidebar-box {
+    padding: 1px 0 0 0;
+
+    z-index: 100;
+
+    --keep-viewport-visible: 125px;
+    max-height: max(245px, 60vh, calc(100vh - var(--keep-viewport-visible)));
+
+    display: flex;
+    flex-direction: column;
+  }
+
+  #banner.short ~ * .wiki-search-sidebar-box {
+    --keep-viewport-visible: 180px;
+  }
+}
+
+@layer material {
+  .wiki-search-sidebar-box {
+    background-color: #000000c0;
+
+    -webkit-backdrop-filter:
+      brightness(1.2) blur(4px);
+            backdrop-filter:
+      brightness(1.2) blur(4px);
+  }
+
+  .wiki-search-sidebar-box.showing-results {
+    box-shadow:
+      0 4px 16px -8px var(--primary-color),
+      0 10px 6px var(--bg-black-color),
+      0 6px 4px #00000040;
+  }
+}
+
+/* Interactions with other sidebar boxes */
+
+@layer layout {
+  /* This is to say, any sidebar that's *not* the first sidebar
+   * after the search box, effectively squishing the rest of the
+   * boxes a bit tighter together.
+   */
+  .wiki-search-sidebar-box.showing-results + .sidebar ~ .sidebar {
+    margin-top: 8px;
+  }
+}
+
+@layer interaction {
+  .wiki-search-sidebar-box.showing-results ~ .sidebar:not(:hover),
+  .wiki-search-sidebar-box.showing-results ~ .sidebar-box-joiner {
+    opacity: 0.8;
+    filter: brightness(0.85);
+  }
+
+  .wiki-search-sidebar-box.showing-results ~ .sidebar:hover + .sidebar-box-joiner,
+  .wiki-search-sidebar-box.showing-results ~ .sidebar-box-joiner:has(+ .sidebar:hover) {
+    opacity: revert-layer;
+    filter: revert-layer;
+  }
+}
+
+/* Label and input elements */
+
+@layer layout {
+  .wiki-search-label {
+    width: calc(100% - 4px);
+    padding: 2px 4px;
+    margin: 2px 2px 3px 2px;
+    box-sizing: border-box;
+
+    display: flex;
+    flex-direction: row;
+  }
+}
+
+@layer material {
+  .wiki-search-label {
+    background: transparent;
+    border: 1px solid var(--dim-color);
+    border-radius: 3px;
+  }
+
+  .wiki-search-label::before {
+    display: inline-block;
+    padding-left: 3px;
+    padding-right: 3px;
+    margin-right: 3px;
+    width: 1.8em;
+    text-align: center;
+    content: '\1f50d\fe0e';
+  }
+
+  .wiki-search-input {
+    background: transparent;
+    border: transparent;
+    color: inherit;
+    flex-grow: 1;
+  }
+
+  .wiki-search-input::-webkit-search-cancel-button {
+    filter: grayscale(1) invert(1);
+  }
+
+  .wiki-search-input::placeholder {
+    color: var(--primary-color);
+    font-style: oblique;
+  }
+}
+
+@layer interaction {
+  .wiki-search-label.disabled {
+    opacity: 0.6;
+  }
+
+  .wiki-search-label.disabled,
+  .wiki-search-input[disabled] {
+    cursor: not-allowed;
+  }
+
+  .wiki-search-label:not(.disabled):hover,
+  .wiki-search-label:focus-within {
+    background: var(--light-ghost-color);
+  }
+
+  .wiki-search-label:focus-within {
+    border-color: var(--primary-color);
+  }
+
+  .wiki-search-label:focus-within::before {
+    opacity: 0.7;
+  }
+
+  .wiki-search-input:focus {
+    border: none;
+    outline: none;
+  }
+
+  /* This is always the "Search for anything" text,
+   * if any placeholder is visible while focused.
+   */
+  .wiki-search-input:focus::placeholder {
+    color: var(--dim-color);
+  }
+}
+
+/* Progress area ("Loading data..." etc) */
+
+@layer layout {
+  .wiki-search-progress-container {
+    padding: 2px 6px 4px 6px;
+    display: flex;
+    flex-direction: row;
+  }
+
+  .wiki-search-progress-label {
+    margin-right: 1ch;
+  }
+
+  .wiki-search-progress-bar {
+    flex-grow: 1;
+  }
+}
+
+@layer print {
+  .wiki-search-progress-label {
+    font-size: 0.9em;
+    font-style: oblique;
+  }
+}
+
+@layer interaction {
+  .wiki-search-progress-label {
+    cursor: default;
+  }
+}
+
+/* "Failed to search" and "no results" info */
+
+@layer layout {
+  .wiki-search-failed-container,
+  .wiki-search-no-results {
+    padding: 2px 3px 4px 6px;
+  }
+
+  .wiki-search-failed-container p {
+    margin: 0;
+  }
+}
+
+@layer print {
+  .wiki-search-no-results {
+    font-size: 0.9em;
+  }
+}
+
+@layer interaction {
+  .wiki-search-no-results {
+    cursor: default;
+  }
+}
+
+/* Context area ("Return to...") */
+
+@layer layout {
+  .wiki-search-context-container {
+    padding: 2px 12px 4px;
+    padding-left: calc(12px + 1.2ch);
+    text-indent: -1.2ch;
+  }
+}
+
+@layer material {
+  .wiki-search-context-container {
+    border-bottom: 1px solid var(--dim-color);
+  }
+}
+
+@layer print {
+  .wiki-search-context-container {
+    font-size: 0.9em;
+  }
+}
+
+/* Filters */
+
+@layer layout {
+  .wiki-search-filter-container {
+    padding: 4px;
+  }
+
+  .wiki-search-filter-link {
+    display: inline-block;
+    margin: 2px;
+    padding: 2px 4px;
+    border: 2px solid transparent;
+    border-radius: 4px;
+  }
+}
+
+@layer interaction {
+  .wiki-search-filter-link:where(.active.shown) {
+    animation:
+      0.15s ease   0.00s forwards normal    show-filter,
+      0.60s linear 0.15s infinite alternate blink-filter;
+  }
+
+  .wiki-search-filter-link:where(.active:not(.shown)) {
+    animation:
+      0.00s linear 0.00s forwards normal    show-filter,
+      0.60s linear 0.00s infinite alternate blink-filter;
+  }
+
+  .wiki-search-filter-link:where(:not(.active).hidden) {
+    /* We can't just reverse the show-filter animation,
+     * because that won't actually start it over again.
+     */
+    animation:
+      0.15s ease   0.00s forwards reverse   show-filter-the-sequel;
+  }
+
+  .wiki-search-filter-link.active-from-query {
+    background: var(--primary-color);
+    border-color: var(--primary-color);
+    color: #000a;
+    animation: none;
+  }
+
+  .wiki-search-filter-link.active-from-query::after {
+    content: "I";
+    color: black;
+    font-family: monospace;
+    font-weight: 800;
+    font-size: 1.2em;
+    margin-left: 0.5ch;
+    vertical-align: middle;
+    animation: 1s steps(2, jump-none) 0.6s infinite blink-caret;
+  }
+
+  @keyframes show-filter {
+    from {
+      background: transparent;
+      border-color: transparent;
+      color: var(--primary-color);
+    }
+
+    to {
+      background: var(--primary-color);
+      border-color: var(--primary-color);
+      color: black;
+    }
+  }
+
+  /* Exactly the same as show-filter above. */
+  @keyframes show-filter-the-sequel {
+    from {
+      background: transparent;
+      border-color: transparent;
+      color: var(--primary-color);
+    }
+
+    to {
+      background: var(--primary-color);
+      border-color: var(--primary-color);
+      color: black;
+    }
+  }
+
+  @keyframes blink-filter {
+    to {
+      background: color-mix(in srgb, var(--primary-color) 90%, transparent);
+    }
+  }
+
+  @keyframes blink-caret {
+    from { opacity: 0; }
+    to { opacity: 1; }
+  }
+}
+
+/* Divider above search result list */
+
+@layer material {
+  .wiki-search-sidebar-box hr {
+    margin-top: 3px !important;
+    margin-bottom: 3px !important;
+  }
+}
+
+@layer layout {
+  .wiki-search-sidebar-box hr {
+    border-color: var(--primary-color);
+    border-style: none none dotted none;
+  }
+}
+
+/* Result list */
+
+@layer layout {
+  .wiki-search-results-container {
+    position: relative;
+    margin-bottom: 0;
+    padding: 2px;
+  }
+}
+
+@layer interaction {
+  .wiki-search-results-container::before,
+  .wiki-search-results-container::after {
+    content: "";
+    display: block;
+    position: sticky;
+    pointer-events: none;
+    z-index: 1;
+  }
+
+  .wiki-search-result:hover {
+    z-index: 2;
+  }
+
+  /* Shadow along top edge */
+
+  .wiki-search-results-container > :first-child {
+    margin-top: -4px;
+  }
+
+  .wiki-search-results-container::before {
+    height: 8px; top: -2px;
+    background: linear-gradient(to bottom, black, black 50%, transparent);
+    opacity: 0.4;
+  }
+
+  /* Shadow along bottom edge */
+
+  .wiki-search-results-container > :last-child {
+    margin-bottom: -10px;
+  }
+
+  .wiki-search-results-container::after {
+    height: 16px; bottom: -2px;
+    background: linear-gradient(to top, black, black 30%, transparent);
+    opacity: 0.4;
+  }
+}
+
+/* Basic result styling, including interactions */
+
+@layer layout {
+  .wiki-search-result {
+    position: relative;
+    display: flex;
+    padding: 4px 3px 4px 6px;
+  }
+
+  .wiki-search-result::before {
+    content: '';
+    position: absolute;
+    top: -2px;
+    bottom: -2px;
+    left: 0;
+    right: 0;
+  }
+}
+
+@layer material {
+  .wiki-search-result::before {
+    border: 1.5px solid var(--primary-color);
+    border-radius: 4px;
+  }
+}
+
+@layer interaction {
+  .wiki-search-result:hover {
+    text-decoration: none !important;
+  }
+
+  .wiki-search-result::before {
+    display: none;
+  }
+
+  .wiki-search-result:hover::before,
+  .wiki-search-result:focus::before {
+    display: block;
+    background: var(--light-ghost-color);
+  }
+
+  .wiki-search-result.current-result {
+    background: var(--light-ghost-color);
+    border-top: 1px solid var(--dim-color);
+    border-bottom: 1px solid var(--dim-color);
+  }
+
+  .wiki-search-result.current-result:hover {
+    background: none;
+    border-color: transparent;
+  }
+}
+
+/* Stuff inside results */
+
+@layer layout {
+  .wiki-search-result-text-area {
+    align-self: center;
+    flex-grow: 1;
+    min-width: 0;
+    padding-bottom: 2px;
+  }
+
+  .wiki-search-result-name {
+    margin-right: 0.25em;
+  }
+
+  .wiki-search-result-image-container {
+    align-self: flex-start;
+    flex-shrink: 0;
+    margin-right: 6px;
+    border-radius: 2px;
+    overflow: hidden;
+  }
+
+  .wiki-search-result-image,
+  .wiki-search-result-image-placeholder {
+    display: block;
+    width: 1.8em;
+    height: 1.8em;
+    aspect-ratio: 1 / 1;
+    border-radius: 1.5px;
+  }
+}
+
+@layer material {
+  .wiki-search-result-image-container {
+    background-color: var(--deep-color);
+    border: 2px solid var(--deep-color);
+  }
+
+  .wiki-search-result-image-placeholder {
+    background-color: #0004;
+    box-shadow: 0 1px 3px -1px #0008 inset;
+  }
+
+  .wiki-search-result-image.has-warning {
+    filter: blur(2px) brightness(0.8);
+  }
+}
+
+@layer print {
+  .wiki-search-result-text-area {
+    overflow-wrap: break-word;
+  }
+
+  .wiki-search-result:hover .wiki-search-result-name {
+    text-decoration: underline;
+  }
+
+  .wiki-search-current-result-text,
+  .wiki-search-result-kind,
+  .wiki-search-result-disambiguator {
+    opacity: 0.9;
+    display: inline-block;
+  }
+
+  .wiki-search-current-result-text,
+  .wiki-search-result-kind {
+    font-style: oblique;
+  }
+}
+
+@layer interaction {
+  .wiki-search-result.current-result:hover .wiki-search-current-result-text {
+    filter: saturate(0.8) brightness(1.4);
+  }
+
+  .wiki-search-results:not(:has(.wiki-search-result-image)) .wiki-search-result-image-container {
+    display: none;
+  }
+}
+
+/* "I'm done searching" line */
+
+@layer layout {
+  .wiki-search-end-search-line {
+    text-align: center;
+    margin-top: 6px;
+    margin-bottom: 2px;
+  }
+}
+
+@layer material {
+  .wiki-search-end-search-line a {
+    display: inline-block;
+    font-style: oblique;
+    opacity: 0.9;
+    padding: 3px 6px 4px 6px;
+    border: 1.5px solid transparent;
+    border-radius: 4px;
+  }
+}
+
+@layer interaction {
+  .wiki-search-end-search-line a:hover {
+    opacity: 1;
+    background: var(--light-ghost-color);
+    border-color: var(--deep-color);
+  }
+}
diff --git a/src/static/css/site.css b/src/static/css/site.css
index 7fe6f915..b3fff34c 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -1,3120 +1,14 @@
-/* A frontend file! Wow.
- * This file is just loaded statically 8y <link>s in the HTML files, so there's
- * no need to re-run upd8.js when tweaking values here. Handy!
- */
-
-/* Squares */
-
-/* This styling is kind of awkwardly placed at the very top. Sorry!
- * We need to rework what order sets of styles get applied in to be
- * much more explicit (so that overriding isn't a headache), and
- * hopefully that can be done through @imports, but it'll take some
- * reworking and cleaning up.
- */
-
-.square {
-  position: relative;
-  width: 100%;
-}
-
-.square::after {
-  content: "";
-  display: block;
-  padding-bottom: 100%;
-}
-
-.square-content {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-}
-
-/* Layout - Common */
-
-body {
-  position: relative;
-  margin: 0;
-  padding: 10px;
-  overflow-y: scroll;
-}
-
-body::before {
-  content: "";
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100vw;
-  height: 100vh;
-  z-index: -1;
-
-  /* NB: these are 100 LVW, "largest view width", etc.
-   * Stabilizes background on viewports with modal dimensions,
-   * e.g. expanding/shrinking tab bar or collapsible find bar.
-   * 100% dimensions are kept above for browser compatibility.
-   */
-  width: 100lvw;
-  height: 100lvh;
-}
-
-#page-container {
-  max-width: 1100px;
-  margin: 0 auto 40px;
-  padding: 15px 0;
-}
-
-#page-container > * {
-  margin-left: 15px;
-  margin-right: 15px;
-}
-
-#skippers:focus-within {
-  position: static;
-  width: unset;
-  height: unset;
-}
-
-#banner {
-  margin: 10px 0;
-  width: 100%;
-  position: relative;
-}
-
-#banner::after {
-  content: "";
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-}
-
-#banner img {
-  display: block;
-  width: 100%;
-  height: auto;
-}
-
-#skippers {
-  position: absolute;
-  left: -10000px;
-  top: auto;
-  width: 1px;
-  height: 1px;
-}
-
-.layout-columns {
-  display: flex;
-  align-items: stretch;
-}
-
-#header,
-#secondary-nav,
-#skippers,
-#footer {
-  padding: 5px;
-}
-
-#header,
-#secondary-nav,
-#skippers {
-  margin-bottom: 10px;
-}
-
-#footer {
-  margin-top: 10px;
-}
-
-#header {
-  display: grid;
-}
-
-#header.nav-has-main-links.nav-has-content {
-  grid-template-columns: 2.5fr 3fr;
-  grid-template-rows: min-content 1fr;
-  grid-template-areas:
-    "main-links content"
-    "bottom-row content";
-}
-
-#header.nav-has-main-links:not(.nav-has-content) {
-  grid-template-columns: 1fr;
-  grid-template-areas:
-    "main-links"
-    "bottom-row";
-}
-
-.nav-main-links {
-  grid-area: main-links;
-  margin-right: 20px;
-}
-
-.nav-content {
-  grid-area: content;
-}
-
-.nav-bottom-row {
-  grid-area: bottom-row;
-  align-self: start;
-}
-
-.sidebar-column {
-  flex: 1 1 20%;
-  min-width: 150px;
-  max-width: 250px;
-  flex-basis: 250px;
-  align-self: flex-start;
-}
-
-.sidebar-column.wide {
-  max-width: 350px;
-  flex-basis: 300px;
-  flex-shrink: 0;
-  flex-grow: 1;
-}
-
-.sidebar-column.initially-hidden {
-  display: none;
-}
-
-.sidebar-multiple {
-  display: flex;
-  flex-direction: column;
-}
-
-.sidebar-multiple .sidebar:not(:first-child) {
-  margin-top: 15px;
-}
-
-.sidebar {
-  --content-padding: 5px;
-  padding: var(--content-padding);
-}
-
-#sidebar-left {
-  margin-right: 10px;
-}
-
-#sidebar-right {
-  margin-left: 10px;
-}
-
-#content {
-  position: relative;
-  --content-padding: 20px;
-  box-sizing: border-box;
-  padding: var(--content-padding);
-  flex-grow: 1;
-  flex-shrink: 3;
-}
-
-.footer-content {
-  margin: 5px 12%;
-}
-
-.footer-content > :first-child {
-  margin-top: 0;
-}
-
-.footer-content > :last-child {
-  margin-bottom: 0;
-}
-
-.footer-localization-links {
-  margin: 5px 12%;
-}
-
-/* Design & Appearance - Layout elements */
-
-body {
-  background: black;
-}
-
-body::before {
-  background-image: url("../../media/bg.jpg");
-  background-position: center;
-  background-size: cover;
-  opacity: 0.5;
-}
-
-#page-container {
-  background-color: var(--bg-color, rgba(35, 35, 35, 0.8));
-  color: #ffffff;
-  box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
-}
-
-#skippers > * {
-  display: inline-block;
-}
-
-#skippers > .skipper-list:not(:last-child)::after {
-  display: inline-block;
-  content: "\00a0";
-  margin-left: 2px;
-  margin-right: -2px;
-  border-left: 1px dotted;
-}
-
-#skippers .skipper-list > .skipper:not(:last-child)::after {
-  content: " \00b7 ";
-  font-weight: 800;
-}
-
-#page-container:not(.showing-sidebar-left) #skippers .skipper[data-for=sidebar-left],
-#page-container:not(.showing-sidebar-right) #skippers .skipper[data-for=sidebar-right] {
-  display: none;
-}
-
-#banner {
-  background: black;
-  background-color: var(--dim-color);
-  border-bottom: 1px solid var(--primary-color);
-}
-
-#banner::after {
-  box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.35);
-  pointer-events: none;
-}
-
-#banner.dim img {
-  opacity: 0.8;
-}
-
-#header,
-#secondary-nav,
-#skippers,
-#footer,
-.sidebar {
-  font-size: 0.85em;
-}
-
-.sidebar,
-#content,
-#header,
-#secondary-nav,
-#skippers,
-#footer {
-  background-color: rgba(0, 0, 0, 0.6);
-  border: 1px dotted var(--primary-color);
-  border-radius: 3px;
-  transition: background-color 0.2s;
-}
-
-/*
-.sidebar:focus-within,
-#content:focus-within,
-#header:focus-within,
-#secondary-nav:focus-within,
-#skippers:focus-within,
-#footer:focus-within {
-  background-color: rgba(0, 0, 0, 0.85);
-  border-style: solid;
-}
-*/
-
-.sidebar > h1,
-.sidebar > h2,
-.sidebar > h3,
-.sidebar > p {
-  text-align: center;
-  padding-left: 4px;
-  padding-right: 4px;
-}
-
-.sidebar h1 {
-  font-size: 1.25em;
-}
-
-.sidebar h2 {
-  font-size: 1.1em;
-  margin: 0;
-}
-
-.sidebar h3 {
-  font-size: 1.1em;
-  font-style: oblique;
-  font-variant: small-caps;
-  margin-top: 0.3em;
-  margin-bottom: 0em;
-}
-
-.sidebar > p {
-  margin: 0.5em 0;
-  padding: 0 5px;
-}
-
-.sidebar hr {
-  color: #555;
-  margin: 10px 5px;
-}
-
-.sidebar > ol,
-.sidebar > ul {
-  padding-left: 30px;
-  padding-right: 15px;
-}
-
-.sidebar > dl {
-  padding-right: 15px;
-  padding-left: 0;
-}
-
-.sidebar > dl dt {
-  padding-left: 10px;
-  margin-top: 0.5em;
-}
-
-.sidebar > dl dt.current {
-  font-weight: 800;
-}
-
-.sidebar > dl dd {
-  margin-left: 0;
-}
-
-.sidebar > dl dd ul {
-  padding-left: 30px;
-  margin-left: 0;
-}
-
-.sidebar > dl .side {
-  padding-left: 10px;
-}
-
-.sidebar li.current {
-  font-weight: 800;
-}
-
-.sidebar li {
-  overflow-wrap: break-word;
-}
-
-.sidebar > details.current summary {
-  font-weight: 800;
-}
-
-.sidebar > details summary {
-  margin-top: 0.5em;
-  padding-left: 5px;
-}
-
-.sidebar > details.current summary span b {
-  font-weight: 800;
-}
-
-summary > span b {
-  font-weight: normal;
-  color: var(--primary-color);
-}
-
-summary > span:hover {
-  cursor: pointer;
-  text-decoration: underline;
-  text-decoration-color: var(--primary-color);
-}
-
-summary > span:hover a {
-  text-decoration: none !important;
-}
-
-summary > span:hover:has(a:hover),
-summary > span:hover:has(a.nested-hover),
-summary.has-nested-hover > span {
-  text-decoration: none !important;
-}
-
-summary > span:hover:has(a:hover) a,
-summary > span:hover:has(a.nested-hover) a,
-summary.has-nested-hover > span a {
-  text-decoration: underline !important;
-}
-
-summary.underline-white > span:hover {
-  text-decoration-color: white;
-}
-
-/* This link isn't supposed to be underlined *at all*
- * when the summary (and not link) is hovered, but
- * for some reason Safari is still applying its colored
- * and dotted(!) underline. Get around the apparent
- * effect by just making it white.
- */
-summary.underline-white > span:hover a:not(:hover) {
-  text-decoration-color: white;
-}
-
-.sidebar > details ul,
-.sidebar > details ol {
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-.sidebar > details:last-child {
-  margin-bottom: 10px;
-}
-
-.sidebar > details[open] {
-  margin-bottom: 1em;
-}
-
-.sidebar article {
-  text-align: left;
-  margin: 5px 5px 15px 5px;
-}
-
-.sidebar article:last-child {
-  margin-bottom: 5px;
-}
-
-.sidebar article h2,
-.news-index h2 {
-  border-bottom: 1px dotted;
-}
-
-.sidebar article h2 time,
-.news-index time {
-  float: right;
-  font-weight: normal;
-}
-
-.sidebar-column.search-showing-results {
-  position: sticky;
-  top: 5px;
-  align-self: flex-start !important; /* pls */
-}
-
-.sidebar-box-joiner {
-  width: 0;
-  margin-left: auto;
-  margin-right: auto;
-  border-right: 1px dashed var(--primary-color);
-  height: 10px;
-}
-
-.sidebar-box-joiner + .sidebar {
-  margin-top: 0 !important;
-}
-
-.wiki-search-sidebar-box {
-  padding: 1px 0 0 0;
-
-  z-index: 100;
-  max-height: calc(100vh - 20px);
-
-  display: flex;
-  flex-direction: column;
-
-  background-color: #000000c0;
-
-  -webkit-backdrop-filter:
-    brightness(1.2) blur(4px);
-
-          backdrop-filter:
-    brightness(1.2) blur(4px);
-}
-
-.wiki-search-sidebar-box.showing-results {
-  box-shadow:
-    0 4px 16px -8px var(--primary-color),
-    0 10px 6px var(--bg-black-color),
-    0 6px 4px #00000040;
-}
-
-/* This is to say, any sidebar that's *not*
- * the first sidebar after the search box.
- */
-.wiki-search-sidebar-box.showing-results + .sidebar ~ .sidebar {
-  margin-top: 5px;
-}
-
-.wiki-search-sidebar-box.showing-results ~ .sidebar:not(:hover) {
-  opacity: 0.8;
-  filter: brightness(0.7);
-}
-
-.wiki-search-label {
-  width: calc(100% - 4px);
-  padding: 2px 4px;
-  margin: 2px 2px 3px 2px;
-  box-sizing: border-box;
-
-  display: flex;
-  flex-direction: row;
-
-  background: transparent;
-  border: 1px solid var(--dim-color);
-  border-radius: 3px;
-}
-
-.wiki-search-label::before {
-  display: inline-block;
-  padding-left: 3px;
-  padding-right: 3px;
-  margin-right: 3px;
-  width: 1.8em;
-  text-align: center;
-  content: '\1f50d\fe0e';
-}
-
-.wiki-search-input {
-  background: transparent;
-  border: transparent;
-  color: inherit;
-  flex-grow: 1;
-}
-
-.wiki-search-input::-webkit-search-cancel-button {
-  filter: grayscale(1) invert(1);
-}
-
-.wiki-search-label.disabled {
-  opacity: 0.6;
-}
-
-.wiki-search-label.disabled,
-.wiki-search-input[disabled] {
-  cursor: not-allowed;
-}
-
-.wiki-search-label:not(.disabled):hover,
-.wiki-search-label:focus-within {
-  background: var(--light-ghost-color);
-}
-
-.wiki-search-label:focus-within {
-  border-color: var(--primary-color);
-}
-
-.wiki-search-label:focus-within::before {
-  opacity: 0.7;
-}
-
-.wiki-search-input:focus {
-  border: none;
-  outline: none;
-}
-
-.wiki-search-input::placeholder {
-  color: var(--primary-color);
-  font-style: oblique;
-}
-
-.wiki-search-input:focus::placeholder {
-  color: var(--dim-color);
-}
-
-.wiki-search-sidebar-box hr {
-  border-color: var(--primary-color);
-  border-style: none none dotted none;
-  margin-top: 3px;
-  margin-bottom: 3px;
-}
-
-.wiki-search-progress-container {
-  padding: 2px 6px 4px 6px;
-  display: flex;
-  flex-direction: row;
-}
-
-.wiki-search-progress-label {
-  font-size: 0.9em;
-  font-style: oblique;
-  cursor: default;
-  margin-right: 1ch;
-}
-
-.wiki-search-progress-bar {
-  flex-grow: 1;
-}
-
-.wiki-search-failed-container {
-  padding: 2px 3px 4px 6px;
-}
-
-.wiki-search-failed-container p {
-  margin: 0;
-}
-
-.wiki-search-results-container {
-  margin-bottom: 0;
-  padding: 2px;
-}
-
-.wiki-search-no-results {
-  font-size: 0.9em;
-  padding: 2px 3px 4px 6px;
-  cursor: default;
-}
-
-.wiki-search-result {
-  position: relative;
-  display: flex;
-  padding: 4px 3px 4px 6px;
-}
-
-.wiki-search-result:hover {
-  text-decoration: none !important;
-}
-
-.wiki-search-result::before {
-  content: '';
-  position: absolute;
-  top: -2px;
-  bottom: -2px;
-  left: 0;
-  right: 0;
-
-  border: 1.5px solid var(--primary-color);
-  border-radius: 4px;
-  display: none;
-}
-
-.wiki-search-result.current-result {
-  background: var(--light-ghost-color);
-  border-top: 1px solid var(--dim-color);
-  border-bottom: 1px solid var(--dim-color);
-}
-
-.wiki-search-result:hover::before {
-  display: block;
-  background: var(--light-ghost-color);
-}
-
-.wiki-search-result.current-result:hover {
-  background: none;
-  border-color: transparent;
-}
-
-.wiki-search-result.current-result:hover .wiki-search-current-result-text {
-  filter: saturate(0.8) brightness(1.4);
-}
-
-.wiki-search-result-text-area {
-  align-self: center;
-  flex-grow: 1;
-  min-width: 0;
-  overflow-wrap: break-word;
-  padding-bottom: 2px;
-}
-
-.wiki-search-result-name {
-  margin-right: 0.25em;
-}
-
-.wiki-search-result:hover .wiki-search-result-name {
-  text-decoration: underline;
-}
-
-.wiki-search-current-result-text,
-.wiki-search-result-kind {
-  font-style: oblique;
-  opacity: 0.9;
-  display: inline-block;
-}
-
-.wiki-search-result-image-container {
-  align-self: flex-start;
-  flex-shrink: 0;
-  margin-right: 6px;
-  border-radius: 2px;
-  overflow: hidden;
-
-  background-color: var(--deep-color);
-  border: 2px solid var(--deep-color);
-}
-
-.wiki-search-results:not(:has(.wiki-search-result-image)) .wiki-search-result-image-container {
-  display: none;
-}
-
-.wiki-search-result-image,
-.wiki-search-result-image-placeholder {
-  display: block;
-  width: 1.8em;
-  height: 1.8em;
-  aspect-ratio: 1 / 1;
-  border-radius: 1.5px;
-}
-
-.wiki-search-result-image-placeholder {
-  background-color: #0004;
-  box-shadow: 0 1px 3px -1px #0008 inset;
-}
-
-.wiki-search-result-image.has-warning {
-  filter: blur(2px) brightness(0.8);
-}
-
-.wiki-search-end-search-line {
-  text-align: center;
-  margin-top: 6px;
-  margin-bottom: 2px;
-}
-
-.wiki-search-end-search-line a {
-  display: inline-block;
-  font-style: oblique;
-  opacity: 0.9;
-  padding: 3px 6px 4px 6px;
-  border-radius: 4px;
-  border: 1.5px solid transparent;
-}
-
-.wiki-search-end-search-line a:hover {
-  opacity: 1;
-  background: var(--light-ghost-color);
-  border-color: var(--deep-color);
-}
-
-#content {
-  overflow-wrap: anywhere;
-}
-
-footer {
-  text-align: center;
-  font-style: oblique;
-}
-
-.footer-localization-links > span:not(:last-child)::after {
-  content: " \00b7 ";
-  font-weight: 800;
-}
-
-/* Design & Appearance - Content elements */
-
-a {
-  color: var(--primary-color);
-  text-decoration: none;
-}
-
-a:hover {
-  text-decoration: underline;
-  text-decoration-style: solid !important;
-}
-
-a.current {
-  font-weight: 800;
-}
-
-a.series {
-  font-style: oblique;
-}
-
-a:not([href]) {
-  cursor: default;
-}
-
-a:not([href]):hover {
-  text-decoration: none;
-}
-
-.external-link:not(.from-content) {
-  white-space: nowrap;
-}
-
-.external-link.indicate-external::after {
-  content: '\00a0➚';
-  font-style: normal;
-}
-
-.external-link.indicate-external:hover::after {
-  color: white;
-}
-
-.external-link .normal-content {
-  color: white;
-}
-
-.nav-link {
-  display: inline-block;
-}
-
-.nav-main-links .nav-link.current > span.nav-link-content > a {
-  font-weight: 800;
-}
-
-.nav-main-links .nav-link-accent {
-  display: inline-block;
-}
-
-.nav-links-index .nav-link:not(:first-child)::before,
-.nav-links-groups .nav-link:not(:first-child)::before {
-  content: "\0020\00b7\0020";
-  font-weight: 800;
-}
-
-.nav-links-hierarchical .nav-link:not(:first-child)::before {
-  content: "\0020/\0020";
-}
-
-.series-nav-link {
-  display: inline-block;
-}
-
-.series-nav-link:not(:first-child)::before {
-  content: "\0020»\0020";
-  font-weight: normal;
-}
-
-.dot-switcher > span:not(:first-child)::before {
-  content: "\0020\00b7\0020";
-  font-weight: 800;
-}
-
-.dot-switcher > span.current {
-  font-weight: 800;
-}
-
-.dot-switcher.intrapage > span:not(.current) a {
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-.dot-switcher.intrapage > span.current a {
-  /* Keeping cursor: pointer (the default) is intentional here. */
-  text-decoration: none !important;
-}
-
-#secondary-nav {
-  text-align: center;
-
-  /* Default to visible. It'll automatically be hidden
-   * in layouts where the sidebar is visible.
-   */
-  display: block;
-}
-
-#secondary-nav.album-secondary-nav.with-previous-next {
-  display: flex;
-  justify-content: space-around;
-  padding-left: 7.5% !important;
-  padding-right: 7.5% !important;
-  flex-wrap: wrap;
-  line-height: 1.4;
-}
-
-#secondary-nav.album-secondary-nav.with-previous-next .group-with-series {
-  width: 100%;
-}
-
-#secondary-nav.album-secondary-nav.with-previous-next > * {
-  margin-left: 5px;
-  margin-right: 5px;
-}
-
-#secondary-nav.album-secondary-nav .dot-switcher {
-  white-space: nowrap;
-}
-
-.inert-previous-next-link {
-  opacity: 0.7;
-}
-
-.nowrap {
-  white-space: nowrap;
-}
-
-.blockwrap, .chunkwrap {
-  display: inline-block;
-}
-
-.text-with-tooltip {
-  position: relative;
-}
-
-.text-with-tooltip .text-with-tooltip-interaction-cue {
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-.text-with-tooltip > .hoverable:hover .text-with-tooltip-interaction-cue,
-.text-with-tooltip > .hoverable.has-visible-tooltip .text-with-tooltip-interaction-cue {
-  text-decoration-style: wavy !important;
-}
-
-.text-with-tooltip.datetimestamp .text-with-tooltip-interaction-cue,
-.text-with-tooltip.missing-duration .text-with-tooltip-interaction-cue,
-.text-with-tooltip.commentary-date .text-with-tooltip-interaction-cue,
-.text-with-tooltip.wiki-edits .text-with-tooltip-interaction-cue {
-  cursor: default;
-}
-
-.text-with-tooltip.missing-duration > .hoverable {
-  opacity: 0.5;
-}
-
-.text-with-tooltip.missing-duration > .hoverable:hover,
-.text-with-tooltip.missing-duration > .hoverable.has-visible-tooltip {
-  opacity: 1;
-}
-
-.text-with-tooltip.missing-duration .text-with-tooltip-interaction-cue {
-  text-decoration: none !important;
-}
-
-.tooltip {
-  position: absolute;
-  z-index: 3;
-  left: -10px;
-  top: calc(1em + 1px);
-  display: none;
-}
-
-li:not(:first-child:last-child) .tooltip,
-.offset-tooltips > :not(:first-child:last-child) .tooltip {
-  left: 14px;
-}
-
-.tooltip-content {
-  display: block;
-
-  background: var(--bg-black-color);
-  border: 1px dotted var(--primary-color);
-  border-radius: 6px;
-
-  -webkit-backdrop-filter:
-    brightness(1.5) saturate(1.4) blur(4px);
-
-          backdrop-filter:
-    brightness(1.5) saturate(1.4) blur(4px);
-
-  box-shadow:
-    0 3px 4px 4px #000000aa,
-    0 -2px 4px -2px var(--primary-color) inset;
-
-  text-indent: 0;
-}
-
-.contribution-tooltip {
-  padding: 3px 6px 6px 6px;
-  left: -34px;
-}
-
-.datetimestamp-tooltip,
-.missing-duration-tooltip,
-.commentary-date-tooltip {
-  padding: 3px 4px 2px 2px;
-  left: -10px;
-}
-
-.thing-name-tooltip,
-.wiki-edits-tooltip {
-  padding: 3px 4px 2px 2px;
-  left: -6px !important;
-}
-
-.wiki-edits-tooltip {
-  font-size: 0.85em;
-}
-
-/* Terrifying?
- * https://stackoverflow.com/a/64424759/4633828
- */
-.thing-name-tooltip { margin-right: -120px; }
-.wiki-edits-tooltip { margin-right: -200px; }
-
-.contribution-tooltip .tooltip-content {
-  padding: 6px 2px 2px 2px;
-
-  -webkit-user-select: none;
-          user-select: none;
-
-  cursor: default;
-
-  display: grid;
-
-  grid-template-columns:
-    [icon-start] 26px [icon-end handle-start] auto [handle-end platform-start] auto [platform-end];
-}
-
-.contribution-tooltip .external-link {
-  display: grid;
-  grid-column-start: icon-start;
-  grid-column-end: handle-end;
-  grid-template-columns: subgrid;
-
-  height: 1.4em;
-}
-
-.contribution-tooltip .chronology-link {
-  display: grid;
-  grid-column-start: icon-start;
-  grid-column-end: handle-end;
-  grid-template-columns: subgrid;
-
-  height: 1.2em;
-}
-
-.contribution-tooltip .external-icon,
-.contribution-tooltip .chronology-symbol {
-  grid-column-start: icon-start;
-  grid-column-end: icon-end;
-}
-
-.contribution-tooltip .external-icon svg {
-  width: 18px;
-  height: 18px;
-  top: -0.1em;
-}
-
-.contribution-tooltip .chronology-symbol {
-  text-align: center;
-}
-
-.contribution-tooltip .external-handle,
-.contribution-tooltip .chronology-text {
-  grid-column-start: handle-start;
-  grid-column-end: handle-end;
-
-  width: max-content;
-  max-width: 200px;
-
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-}
-
-.contribution-tooltip .external-handle {
-  padding-right: 8px;
-}
-
-.contribution-tooltip .chronology-text {
-  padding-right: 6px;
-}
-
-.contribution-tooltip .chronology-text,
-.contribution-tooltip .chronology-info {
-  font-size: 0.85em;
-}
-
-.contribution-tooltip .tooltip-divider {
-  grid-column-start: icon-start;
-  grid-column-end: platform-end;
-
-  border-top: 1px dotted var(--primary-color);
-  margin-top: 3px;
-  margin-bottom: 4px;
-}
-
-.contribution-tooltip .external-platform,
-.contribution-tooltip .chronology-info {
-  display: none;
-
-  grid-column-start: platform-start;
-  grid-column-end: platform-end;
-
-  --external-platform-opacity: 0.8;
-  opacity: 0.8;
-  padding-right: 4px;
-
-  white-space: nowrap;
-}
-
-.contribution-tooltip.show-info .external-platform,
-.contribution-tooltip.show-info .chronology-info {
-  display: inline;
-  animation: external-platform 0.2s forwards linear;
-}
-
-@keyframes external-platform {
-  from {
-    opacity: 0;
-  }
-
-  to {
-    opacity: var(--external-platform-opacity);
-  }
-}
-
-.contribution-tooltip .external-link:hover,
-.contribution-tooltip .chronology-link:hover {
-  filter: brightness(1.4);
-  text-decoration: none;
-}
-
-.contribution-tooltip .external-link:hover .external-handle,
-.contribution-tooltip .chronology-link:hover .chronology-text {
-  text-decoration: underline;
-}
-
-.contribution-tooltip .external-link:hover + .external-platform,
-.contribution-tooltip .chronology-link:hover + .chronology-info {
-  --external-platform-opacity: 1;
-  text-decoration: underline;
-  text-decoration-color: #ffffffaa;
-}
-
-.datetimestamp-tooltip .tooltip-content,
-.missing-duration-tooltip .tooltip-content,
-.commentary-date-tooltip .tooltip-content {
-  padding: 5px 6px;
-  white-space: nowrap;
-  font-size: 0.9em;
-}
-
-.thing-name-tooltip .tooltip-content,
-.wiki-edits-tooltip .tooltip-content {
-  padding: 3px 4.5px;
-}
-
-.external-icon {
-  display: inline-block;
-  padding: 0 3px;
-  width: 24px;
-  height: 1em;
-  position: relative;
-}
-
-.external-icon svg {
-  width: 24px;
-  height: 24px;
-  top: -0.25em;
-  position: absolute;
-  fill: var(--primary-color);
-}
-
-.rerelease,
-.other-group-accent {
-  opacity: 0.7;
-  font-style: oblique;
-}
-
-.other-group-accent {
-  white-space: nowrap;
-}
-
-.other-group-accent a {
-  color: var(--page-primary-color);
-}
-
-s.spoiler {
-  display: inline-block;
-  color: transparent;
-  text-decoration: underline;
-  text-decoration-color: white;
-  text-decoration-style: dashed;
-  text-decoration-skip: none;
-  text-decoration-skip-ink: none;
-}
-
-s.spoiler::selection {
-  color: black;
-  background: white;
-}
-
-s.spoiler::-moz-selection {
-  color: black;
-  background: white;
-}
-
-progress {
-  accent-color: var(--primary-color);
-}
-
-.content-columns {
-  columns: 2;
-}
-
-.content-columns .column {
-  break-inside: avoid;
-}
-
-.content-columns .column h2 {
-  margin-top: 0;
-  font-size: 1em;
-}
-
-p .current {
-  font-weight: 800;
-}
-
-#cover-art-container {
-  font-size: 0.8em;
-  border: 2px solid var(--primary-color);
-  box-shadow:
-    0 2px 14px -6px var(--primary-color),
-    0 0 12px 12px #00000080;
-
-  border-radius: 0 0 4px 4px;
-  background: var(--bg-black-color);
-
-  -webkit-backdrop-filter: blur(3px);
-          backdrop-filter: blur(3px);
-}
-
-#cover-art-container:has(.image-details),
-#cover-art-container.has-image-details {
-  border-radius: 0 0 6px 6px;
-}
-
-#cover-art-container:not(:has(.image-details)),
-#cover-art-container:not(.has-image-details) {
-  /* Hacky: `overflow: hidden` hides tag tooltips, so it can't be applied
-   * if we've got tags/details visible. But it's okay, because we only
-   * need to apply it if it *doesn't* - that's when the rounded border
-   * of #cover-art-container needs to cut off its child image-container
-   * (which has a background that otherwise causes sharp corners).
-   */
-  overflow: hidden;
-}
-
-#cover-art-container .image-container {
-  /* Border is handled on the cover-art-container. */
-  border: none;
-  border-radius: 0;
-}
-
-#cover-art-container .image-details {
-  border-top-color: var(--deep-color);
-}
-
-#cover-art-container .image-details + .image-details {
-  border-top-color: var(--primary-color);
-}
-
-#cover-art-container .image {
-  display: block;
-  width: 100%;
-  height: 100%;
-}
-
-.image-details {
-  display: block;
-
-  margin-top: 0;
-  margin-bottom: 0;
-
-  /* Styles below only apply for first image-details. */
-
-  margin-left: 0;
-  margin-right: 0;
-  padding-left: 9px;
-  padding-right: 9px;
-
-  padding-top: 6px;
-  padding-bottom: 4px;
-
-  border-top: 1px dashed var(--dim-color);
-}
-
-.image-details + .image-details {
-  display: block;
-
-  margin-left: 6px;
-  margin-right: 6px;
-  padding-left: 3px;
-  padding-right: 3px;
-
-  padding-top: 4px;
-  padding-bottom: 4px;
-
-  border-top: 1px dotted var(--primary-color);
-}
-
-.image-details:last-child {
-  margin-bottom: 2px;
-}
-
-ul.image-details.art-tag-details li {
-  display: inline-block;
-}
-
-ul.image-details.art-tag-details li:not(:last-child)::after {
-  content: " \00b7 ";
-}
-
-.image-details.non-unique-details {
-  font-style: oblique;
-}
-
-p.image-details.illustrator-details {
-  text-align: center;
-  font-style: oblique;
-}
-
-#artist-commentary.first-entry-is-dated {
-  clear: right;
-}
-
-.commentary-entry-heading {
-  display: flex;
-  flex-direction: row;
-
-  margin-left: 15px;
-  padding-left: 5px;
-  max-width: 625px;
-  padding-bottom: 0.2em;
-
-  border-bottom: 1px solid var(--dim-color);
-}
-
-.commentary-entry-heading-text {
-  flex-grow: 1;
-  padding-left: 1.25ch;
-  text-indent: -1.25ch;
-}
-
-.commentary-entry-accent {
-  font-style: oblique;
-}
-
-.commentary-entry-heading .commentary-date {
-  flex-shrink: 0;
-
-  margin-left: 0.75ch;
-  align-self: flex-end;
-
-  padding-left: 0.5ch;
-  padding-right: 0.25ch;
-}
-
-.commentary-entry-heading .hoverable {
-  box-shadow: 1px 2px 6px 5px #04040460;
-}
-
-.commentary-entry-body summary {
-  list-style-position: outside;
-}
-
-.commentary-entry-body summary > span {
-  color: var(--primary-color);
-}
-
-.commentary-art {
-  float: right;
-  width: 30%;
-  max-width: 250px;
-  margin: 15px 0 10px 20px;
-
-  /* This !important is unfortunate, but it's necessary
-   * even if the rule itself is placed lower, because this
-   * is a relatively low-priority selector compared to
-   * others that alter image shadows.
-   */
-  box-shadow: 0 0 4px 5px rgba(0, 0, 0, 0.25) !important;
-}
-
-.js-hide,
-.js-show-once-data,
-.js-hide-once-data {
-  display: none;
-}
-
-.content-image-container {
-  margin-top: 1em;
-  margin-bottom: 1em;
-}
-
-.content-image-container.align-center {
-  text-align: center;
-  margin-top: 1.5em;
-  margin-bottom: 1.5em;
-}
-
-a.align-center, img.align-center {
-  display: block;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-center {
-  margin-top: 1em;
-  margin-bottom: 1em;
-}
-
-.content-image {
-  display: inline-block !important;
-}
-
-.image-link {
-  display: block;
-  overflow: hidden;
-}
-
-.image-link:focus {
-  outline: 3px double white;
-}
-
-.image-link:focus:not(:focus-visible) {
-  outline: none;
-}
-
-.image-link .image {
-  display: block;
-  max-width: 100%;
-  height: auto;
-}
-
-.square .image-link {
-  width: 100%;
-  height: 100%;
-}
-
-.square .image {
-  width: 100%;
-  height: 100%;
-}
-
-h1 {
-  font-size: 1.5em;
-}
-
-#content li {
-  margin-bottom: 4px;
-}
-
-#content li i {
-  white-space: nowrap;
-}
-
-#content.top-index h1,
-#content.flash-index h1 {
-  text-align: center;
-  font-size: 2em;
-}
-
-html[data-url-key="localized.home"] #content h1 {
-  text-align: center;
-  font-size: 2.5em;
-}
-
-#content.flash-index h2 {
-  text-align: center;
-  font-size: 2.5em;
-  font-variant: small-caps;
-  font-style: oblique;
-  margin-bottom: 0;
-  text-align: center;
-  width: 100%;
-}
-
-#content.top-index h2 {
-  text-align: center;
-  font-size: 2em;
-  font-weight: normal;
-  margin-bottom: 0.25em;
-}
-
-#content.top-index.has-subtitle h1 {
-  margin-bottom: 0.35em;
-}
-
-#content.top-index h2.page-subtitle {
-  font-size: 1.8em;
-  margin-top: 0.35em;
-  margin-bottom: 0.5em;
-}
-
-.quick-info {
-  text-align: center;
-  padding-left: calc(var(--responsive-padding-ratio) * 100%);
-  padding-right: calc(var(--responsive-padding-ratio) * 100%);
-  line-height: 1.25em;
-}
-
-ul.quick-info {
-  list-style: none;
-  padding-left: 0;
-  padding-right: 0;
-}
-
-ul.quick-info li {
-  display: inline-block;
-}
-
-ul.quick-info li:not(:last-child)::after {
-  content: " \00b7 ";
-  font-weight: 800;
-}
-
-.carousel-container + .quick-info,
-.carousel-container + .quick-description {
-  margin-top: 25px;
-}
-
-.quick-description:not(.has-external-links-only) {
-  --clamped-padding-ratio: max(var(--responsive-padding-ratio), 0.06);
-  margin-left: auto;
-  margin-right: auto;
-  padding-left: calc(0.40 * var(--clamped-padding-ratio) * 100%);
-  padding-right: calc(0.40 * var(--clamped-padding-ratio) * 100%);
-  max-width: 500px;
-
-  padding-top: 0.25em;
-  padding-bottom: 0.75em;
-  border-left: 1px solid var(--dim-color);
-  border-right: 1px solid var(--dim-color);
-  line-height: 1.25em;
-}
-
-.quick-description.has-external-links-only {
-  padding-left: 12%;
-  padding-right: 12%;
-}
-
-.quick-description.has-content-only {
-  padding-bottom: 0.5em;
-}
-
-.quick-description p {
-  text-align: center;
-}
-
-.quick-description > blockquote {
-  margin-left: 0 !important;
-}
-
-.quick-description .description-content.long hr ~ p {
-  text-align: left;
-}
-
-.quick-description > .description-content :first-child {
-  margin-top: 0;
-}
-
-.quick-description > .quick-description-actions,
-.quick-description.has-content-only .description-content :last-child {
-  margin-bottom: 0;
-}
-
-.quick-description:not(.collapsed) .description-content.short,
-.quick-description:not(.collapsed) .quick-description-actions.when-collapsed,
-.quick-description:not(.expanded) .description-content.long,
-.quick-description:not(.expanded) .quick-description-actions.when-expanded {
-  display: none;
-}
-
-.quick-description .quick-description-actions .expand-link,
-.quick-description .quick-description-actions .collapse-link {
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-#intro-menu {
-  margin: 24px 0;
-  padding: 10px;
-  background-color: #222222;
-  text-align: center;
-  border: 1px dotted var(--primary-color);
-  border-radius: 2px;
-}
-
-#intro-menu p {
-  margin: 12px 0;
-}
-
-#intro-menu a {
-  margin: 0 6px;
-}
-
-li .by {
-  font-style: oblique;
-  max-width: 600px;
-}
-
-li .by a {
-  display: inline-block;
-}
-
-p code {
-  font-size: 0.95em;
-  font-family: "courier new", monospace;
-  font-weight: 800;
-  line-height: 1.1;
-}
-
-#content blockquote {
-  margin-left: 40px;
-  max-width: 600px;
-  margin-right: 0;
-}
-
-#content blockquote blockquote {
-  margin-left: 10px;
-  padding-left: 10px;
-  margin-right: 20px;
-  border-left: dotted 1px;
-  padding-top: 6px;
-  padding-bottom: 6px;
-}
-
-#content blockquote blockquote > :first-child {
-  margin-top: 0;
-}
-
-#content blockquote blockquote > :last-child {
-  margin-bottom: 0;
-}
-
-#content blockquote h2 {
-  font-size: 1em;
-  font-weight: 800;
-}
-
-#content blockquote h3 {
-  font-size: 1em;
-  font-weight: normal;
-  font-style: oblique;
-}
-
-main {
-  --responsive-padding-ratio: 0.10;
-}
-
-main.long-content {
-  --long-content-padding-ratio: var(--responsive-padding-ratio);
-}
-
-main.long-content .main-content-container,
-main.long-content > h1 {
-  padding-left: calc(var(--long-content-padding-ratio) * 100%);
-  padding-right: calc(var(--long-content-padding-ratio) * 100%);
-}
-
-dl dt {
-  padding-left: 40px;
-  max-width: 600px;
-}
-
-dl dt {
-  /* Heads up, this affects the measurement
-   * for dl dt which are .content-heading!
-   */
-  margin-bottom: 2px;
-}
-
-dl dd {
-  margin-bottom: 1em;
-}
-
-dl ul,
-dl ol {
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-ul > li.has-details {
-  list-style-type: none;
-  margin-left: -17px;
-}
-
-.album-group-list dt,
-.group-series-list dt {
-  font-style: oblique;
-  padding-left: 0;
-}
-
-.album-group-list dd,
-.group-series-list dd {
-  margin-left: 0;
-}
-
-.album-group-list blockquote {
-  max-width: 540px;
-  margin-bottom: 9px;
-  margin-top: 3px;
-}
-
-.album-group-list blockquote p:first-child {
-  margin-top: 0;
-}
-
-.album-group-list blockquote p:last-child {
-  margin-bottom: 0;
-}
-
-.group-chronology-link,
-.series-chronology-link {
-  font-style: oblique;
-}
-
-.group-chronology-link a,
-.series-chronology-link a {
-  font-style: normal;
-}
-
-.group-view-switcher {
-  margin-left: 1ch;
-}
-
-#content hr {
-  border: 1px inset #808080;
-  width: 100%;
-}
-
-#content hr.split::before {
-  content: "(split)";
-  color: #808080;
-}
-
-#content hr.split {
-  position: relative;
-  overflow: hidden;
-  border: none;
-}
-
-#content hr.split::after {
-  display: inline-block;
-  content: "";
-  border: 1px inset #808080;
-  width: 100%;
-  position: absolute;
-  top: 50%;
-  margin-top: -2px;
-  margin-left: 10px;
-}
-
-li > ul {
-  margin-top: 5px;
-}
-
-.additional-files-list {
-  padding-left: 0;
-}
-
-.additional-files-list > li {
-  list-style-type: none;
-}
-
-.additional-files-list summary {
-  /* Sorry, Safari!
-   * https://bugs.webkit.org/show_bug.cgi?id=157323
-   */
-  list-style-position: outside;
-  margin-left: 40px;
-}
-
-.additional-files-list details ul {
-  margin-left: 40px;
-  margin-top: 2px;
-  margin-bottom: 10px;
-}
-
-.additional-files-list .entry-description {
-  list-style-type: none;
-  max-width: 540px;
-
-  /* This should be margin-bottom, but cascading rules
-   * cause some awkwardness - `#content li` takes precedence.
-   */
-  padding-bottom: 3px;
-}
-
-.group-contributions-table {
-  display: inline-block;
-}
-
-.group-contributions-table .group-contributions-row {
-  display: flex;
-  justify-content: space-between;
-}
-
-.group-contributions-table .group-contributions-metrics {
-  margin-left: 1.5ch;
-  white-space: nowrap;
-}
-
-.group-contributions-sorted-by-count:not(.visible),
-.group-contributions-sorted-by-duration:not(.visible) {
-  display: none;
-}
-
-.group-contributions-sort-button {
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-html[data-url-key="localized.albumCommentary"] li.no-commentary {
-  opacity: 0.7;
-}
-
-html[data-url-key="localized.albumCommentary"] .content-heading-main-title {
-  margin-right: 0.25em;
-}
-
-html[data-url-key="localized.albumCommentary"] .content-heading-accent {
-  font-weight: normal;
-  font-style: oblique;
-  font-size: 0.9rem;
-  display: inline-block;
-}
-
-html[data-url-key="localized.albumCommentary"] p.track-info {
-  margin-left: 20px;
-}
-
-html[data-url-key="localized.groupInfo"] .by a {
-  color: var(--page-primary-color);
-}
-
-html[data-url-key="localized.listing"][data-url-value0="random"] #data-loading-line,
-html[data-url-key="localized.listing"][data-url-value0="random"] #data-loaded-line,
-html[data-url-key="localized.listing"][data-url-value0="random"] #data-error-line {
-  display: none;
-}
-
-html[data-url-key="localized.listing"][data-url-value0="random"] #content a:not([href]) {
-  opacity: 0.7;
-}
-
-html[data-url-key="localized.newsEntry"] .read-another-links {
-  font-style: oblique;
-  font-size: 0.9em;
-}
-
-/* Additional names (heading and box) */
-
-h1 a[href="#additional-names-box"] {
-  color: inherit;
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-h1 a[href="#additional-names-box"]:hover {
-  text-decoration-style: solid;
-}
-
-#additional-names-box {
-  --custom-scroll-offset: calc(0.5em - 2px);
-
-  margin: 1em 0 1em -10px;
-  padding: 15px 20px 10px 20px;
-  width: max-content;
-  max-width: min(60vw, 600px);
-
-  border: 1px dotted var(--primary-color);
-  border-radius: 6px;
-
-  background:
-    linear-gradient(var(--bg-color), var(--bg-color)),
-    linear-gradient(#000000bb, #000000bb),
-    var(--primary-color);
-
-  box-shadow: 0 -2px 6px -1px var(--dim-color) inset;
-
-  display: none;
-}
-
-#additional-names-box > :first-child { margin-top: 0; }
-#additional-names-box > :last-child { margin-bottom: 0; }
-
-#additional-names-box p {
-  padding-left: 10px;
-  padding-right: 10px;
-  margin-bottom: 0;
-  font-style: oblique;
-}
-
-#additional-names-box ul {
-  padding-left: 10px;
-  margin-top: 0.5em;
-}
-
-#additional-names-box li .additional-name {
-  margin-right: 0.25em;
-}
-
-#additional-names-box li .additional-name .content-image {
-  margin-bottom: 0.25em;
-  margin-top: 0.5em;
-}
-
-#additional-names-box li .accent {
-  opacity: 0.8;
-}
-
-#additional-names-box li .additional-name > img {
-  vertical-align: text-bottom;
-}
-
-/* Images */
-
-.image-container {
-  display: block;
-  box-sizing: border-box;
-  position: relative;
-  height: 100%;
-  overflow: hidden;
-
-  background-color: var(--dim-color);
-  border: 2px solid var(--primary-color);
-  border-radius: 0;
-  box-shadow: 0 2px 4px -2px var(--bg-black-color) inset;
-
-  text-align: left;
-  color: white;
-}
-
-.image-text-area {
-  position: absolute;
-  top: 0;
-  left: 0;
-  bottom: 0;
-  right: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  text-align: center;
-  padding: 5px 15px;
-
-  background: rgba(0, 0, 0, 0.65);
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.5) inset;
-
-  line-height: 1.35em;
-  color: var(--primary-color);
-  font-style: oblique;
-  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.75);
-}
-
-.image-outer-area {
-  width: 100%;
-  height: 100%;
-  padding: 5px;
-  box-sizing: border-box;
-}
-
-.image-link {
-  border-bottom: 1px solid #ffffff03;
-  border-radius: 2.5px 2.5px 3px 3px;
-  box-shadow:
-    0 1px 8px -3px var(--bg-black-color);
-}
-
-.image-inner-area {
-  position: relative;
-  width: 100%;
-  height: 100%;
-}
-
-.image-link .image-inner-area {
-  /* Jankily fix a rendering issue with border-radius on Safari.
-   * The `-webkit-` prefix is only to keep this from applying on
-   * other browsers (well, Firefox), where it doesn't *break*
-   * anything, but also isn't necessary.
-   */
-  -webkit-transform: translateZ(0);
-}
-
-img {
-  object-fit: cover;
-}
-
-.image-inner-area::after {
-  content: "";
-  display: block;
-  position: absolute;
-  pointer-events: none;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  border-bottom-left-radius: 0.5px;
-  opacity: 0.035;
-  box-shadow:
-    6px -6px 2px -4px white inset;
-}
-
-img.pixelate, .pixelate img {
-  image-rendering: crisp-edges;
-}
-
-.reveal-text-container {
-  position: absolute;
-  top: 15px;
-  left: 10px;
-  right: 10px;
-  bottom: 10px;
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-}
-
-.grid-item .reveal-text {
-  font-size: 0.9em;
-}
-
-.reveal-text {
-  color: white;
-  text-align: center;
-  font-weight: bold;
-  padding-bottom: 0.5em;
-  font-size: 0.8rem;
-}
-
-.reveal-symbol {
-  display: inline-block;
-  width: 1em;
-  height: 1em;
-  margin-bottom: 0.1em;
-
-  font-size: 1.6em;
-  opacity: 0.8;
-}
-
-.reveal-interaction {
-  opacity: 0.8;
-  text-decoration: underline;
-  text-decoration-style: dotted;
-}
-
-.reveal .image {
-  opacity: 0.7;
-  filter: blur(20px) brightness(0.7);
-}
-
-.reveal .image.reveal-thumbnail {
-  position: absolute;
-  top: 0;
-  left: 0;
-  image-rendering: pixelated;
-}
-
-.reveal.has-reveal-thumbnail:not(.revealed) .image:not(.reveal-thumbnail) {
-  /* Keep the main image as part of the box model.
-   * It's what actually defines the dimensions of the
-   * image-container, so those dimensions never shift
-   * once the image is actually revealed.
-   */
-  visibility: hidden;
-}
-
-.reveal.revealed.has-reveal-thumbnail .image.reveal-thumbnail {
-  display: none !important;
-}
-
-.reveal.revealed .image {
-  filter: none;
-  opacity: 1;
-}
-
-.reveal.revealed .reveal-text-container {
-  display: none;
-}
-
-.reveal:not(.revealed) .image-outer-area > * {
-  --reveal-border-radius: 6px;
-  position: relative;
-  overflow: hidden;
-  border-radius: var(--reveal-border-radius);
-}
-
-.reveal:not(.revealed) .image-outer-area > *::after {
-  content: "";
-  position: absolute;
-  box-sizing: border-box;
-  top: 0;
-  left: 0;
-  bottom: 0;
-  right: 0;
-  border: 1px dotted var(--primary-color);
-  border-radius: var(--reveal-border-radius);
-  pointer-events: none;
-
-  /* By an awkward DOM intersection, this element might be
-   * .image-inner-area::after, which is already styled with
-   * a slight visual effect. Guarantee that the properties
-   * set to that end are overwritten, and fully co-opt it
-   * to serve as the interaction cue instead.
-   */
-  box-shadow: none;
-  opacity: 1;
-}
-
-.reveal:not(.revealed) .image-inner-area {
-  background: var(--deep-color);
-}
-
-.reveal:not(.revealed) .image-outer-area > *:hover::after {
-  border-style: solid;
-  box-shadow: 0 0 0 1.5px #00000099 inset;
-}
-
-.reveal:not(.revealed) .image-outer-area > *:hover .image {
-  filter: blur(20px) brightness(0.6);
-  opacity: 0.6;
-}
-
-.reveal:not(.revealed) .image-outer-area > *:hover .reveal-interaction {
-  text-decoration-style: solid;
-}
-
-.image-container.has-link:not(.no-image-preview) {
-  background: var(--deep-color);
-  box-shadow: none;
-  border-radius: 0 0 4px 4px;
-}
-
-.sidebar .image-container {
-  max-width: 350px;
-}
-
-/* Grid listings */
-
-.grid-listing {
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: center;
-  align-items: flex-start;
-  padding: 5px 15px;
-}
-
-.grid-item {
-  font-size: 0.9em;
-}
-
-.grid-item {
-  display: inline-block;
-  text-align: center;
-  background-color: #111111;
-  border: 1px dotted var(--primary-color);
-  border-radius: 2px;
-  padding: 5px;
-  margin: 10px;
-}
-
-.grid-item .image-container {
-  width: 100%;
-}
-
-.grid-item .image-inner-area {
-  border-radius: 0;
-  box-shadow: none;
-}
-
-.grid-item .image-inner-area::after {
-  box-shadow: none;
-}
-
-.grid-item .image {
-  width: 100%;
-  height: 100% !important;
-  margin-top: auto;
-  margin-bottom: auto;
-}
-
-.grid-item:hover {
-  text-decoration: none;
-}
-
-.grid-actions .grid-item:hover {
-  text-decoration: underline;
-}
-
-.grid-item > span {
-  display: block;
-  overflow-wrap: break-word;
-  hyphens: auto;
-}
-
-.grid-item > span:not(:first-child) {
-  margin-top: 2px;
-}
-
-.grid-item > span:first-of-type {
-  margin-top: 6px;
-}
-
-.grid-item > span:not(:first-of-type) {
-  font-size: 0.9em;
-  opacity: 0.8;
-}
-
-.grid-item:hover > span:first-of-type {
-  text-decoration: underline;
-}
-
-.grid-listing > .grid-item {
-  flex: 1 25%;
-  max-width: 200px;
-}
-
-.grid-actions {
-  display: flex;
-  flex-direction: row;
-  margin: 15px;
-  align-self: center;
-  flex-wrap: wrap;
-  justify-content: center;
-}
-
-.grid-actions > .grid-item {
-  flex-basis: unset !important;
-  margin: 5px;
-  width: 120px;
-  --primary-color: inherit !important;
-  --dim-color: inherit !important;
-}
-
-/* Carousel */
-
-.carousel-container {
-  --carousel-tile-min-width: 120px;
-  --carousel-row-count: 3;
-  --carousel-column-count: 6;
-
-  position: relative;
-  overflow: hidden;
-  margin: 20px 0 5px 0;
-  padding: 8px 0;
-}
-
-.carousel-container::before {
-  content: "";
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  z-index: -20;
-  background-color: var(--dim-color);
-  filter: brightness(0.6);
-}
-
-.carousel-container::after {
-  content: "";
-  pointer-events: none;
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  border: 1px solid var(--primary-color);
-  border-radius: 4px;
-  z-index: 40;
-  box-shadow:
-    inset 20px 2px 40px var(--shadow-color),
-    inset -20px -2px 40px var(--shadow-color);
-}
-
-.carousel-container:hover .carousel-grid {
-  animation-play-state: running;
-}
-
-html[data-url-key="localized.home"] .carousel-container {
-  --carousel-tile-size: 140px;
-}
-
-.carousel-container[data-carousel-rows="1"] { --carousel-row-count: 1; }
-.carousel-container[data-carousel-rows="2"] { --carousel-row-count: 2; }
-.carousel-container[data-carousel-rows="3"] { --carousel-row-count: 3; }
-.carousel-container[data-carousel-columns="4"] { --carousel-column-count: 4; }
-.carousel-container[data-carousel-columns="5"] { --carousel-column-count: 5; }
-.carousel-container[data-carousel-columns="6"] { --carousel-column-count: 6; }
-
-.carousel-grid:nth-child(2),
-.carousel-grid:nth-child(3) {
-  position: absolute;
-  top: 8px;
-  left: 0;
-  right: 0;
-}
-
-.carousel-grid:nth-child(2) {
-  animation-name: carousel-marquee2;
-}
-
-.carousel-grid:nth-child(3) {
-  animation-name: carousel-marquee3;
-}
-
-@keyframes carousel-marquee1 {
-  0% {
-    transform: translateX(-100%) translateX(70px);
-  }
-
-  100% {
-    transform: translateX(-200%) translateX(70px);
-  }
-}
-
-@keyframes carousel-marquee2 {
-  0% {
-    transform: translateX(0%) translateX(70px);
-  }
-
-  100% {
-    transform: translateX(-100%) translateX(70px);
-  }
-}
-
-@keyframes carousel-marquee3 {
-  0% {
-    transform: translateX(100%) translateX(70px);
-  }
-
-  100% {
-    transform: translateX(0%) translateX(70px);
-  }
-}
-
-.carousel-grid {
-  /* Thanks to: https://css-tricks.com/an-auto-filling-css-grid-with-max-columns/ */
-  --carousel-gap-count: calc(var(--carousel-column-count) - 1);
-  --carousel-total-gap-width: calc(var(--carousel-gap-count) * 10px);
-  --carousel-calculated-tile-max-width: calc((100% - var(--carousel-total-gap-width)) / var(--carousel-column-count));
-
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(max(var(--carousel-tile-min-width), var(--carousel-calculated-tile-max-width)), 1fr));
-  grid-template-rows: repeat(var(--carousel-row-count), auto);
-  grid-auto-flow: dense;
-  grid-auto-rows: 0;
-  overflow: hidden;
-  margin: auto;
-
-  transform: translateX(0);
-  animation: carousel-marquee1 40s linear infinite;
-  animation-play-state: paused;
-  z-index: 5;
-}
-
-.carousel-item {
-  display: inline-block;
-  margin: 0;
-  flex: 1 1 150px;
-  padding: 3px;
-  border-radius: 10px;
-  filter: brightness(0.8);
-}
-
-.carousel-item .image-container {
-  border: none;
-  border-radius: 5px;
-}
-
-.carousel-item .image-outer-area {
-  padding: 0;
-}
-
-.carousel-item .image-inner-area::after {
-  box-shadow: none;
-}
-
-.carousel-item .image {
-  width: 100%;
-  height: 100%;
-  margin-top: auto;
-  margin-bottom: auto;
-}
-
-.carousel-item:hover {
-  filter: brightness(1);
-  background: var(--dim-color);
-}
-
-/* Info card */
-
-#info-card-container {
-  position: absolute;
-
-  left: 0;
-  right: 10px;
-
-  pointer-events: none; /* Padding area shouldn't 8e interactive. */
-  display: none;
-}
-
-#info-card-container.show,
-#info-card-container.hide {
-  display: flex;
-}
-
-#info-card-container > * {
-  flex-basis: 400px;
-}
-
-#info-card-container.show {
-  animation: 0.2s linear forwards info-card-show;
-  transition: top 0.1s, left 0.1s;
-}
-
-#info-card-container.hide {
-  animation: 0.2s linear forwards info-card-hide;
-}
-
-@keyframes info-card-show {
-  0% {
-    opacity: 0;
-    margin-top: -5px;
-  }
-
-  100% {
-    opacity: 1;
-    margin-top: 0;
-  }
-}
-
-@keyframes info-card-hide {
-  0% {
-    opacity: 1;
-    margin-top: 0;
-  }
-
-  100% {
-    opacity: 0;
-    margin-top: 5px;
-    display: none !important;
-  }
-}
-
-.info-card-decor {
-  padding-left: 3ch;
-  border-top: 1px solid white;
-}
-
-.info-card {
-  background-color: black;
-  color: white;
-
-  border: 1px dotted var(--primary-color);
-  border-radius: 3px;
-  box-shadow: 0 5px 5px black;
-
-  padding: 5px;
-  font-size: 0.9em;
-
-  pointer-events: none;
-}
-
-.info-card::after {
-  content: "";
-  display: block;
-  clear: both;
-}
-
-#info-card-container.show .info-card {
-  animation: 0.01s linear 0.2s forwards info-card-become-interactive;
-}
-
-@keyframes info-card-become-interactive {
-  to {
-    pointer-events: auto;
-  }
-}
-
-.info-card-art-container {
-  float: right;
-
-  width: 40%;
-  margin: 5px;
-  font-size: 0.8em;
-
-  /* Dynamically shown. */
-  display: none;
-}
-
-.info-card-art-container .image-container {
-  padding: 2px;
-}
-
-.info-card-art {
-  display: block;
-  width: 100%;
-  height: 100%;
-}
-
-.info-card-name {
-  font-size: 1em;
-  border-bottom: 1px dotted;
-  margin: 0;
-}
-
-.info-card p {
-  margin-top: 0.25em;
-  margin-bottom: 0.25em;
-}
-
-.info-card p:last-child {
-  margin-bottom: 0;
-}
-
-/* Custom hash links */
-
-.content-heading {
-  border-bottom: 3px double transparent;
-  margin-bottom: -3px;
-}
-
-.content-heading.highlight-hash-link {
-  animation: highlight-hash-link 4s;
-  animation-delay: 125ms;
-}
-
-dl dt.content-heading {
-  /* Basic margin-bottom for dt is 2px,
-   * so just subtract 3px from that.
-   */
-  margin-bottom: -1px;
-}
-
-h3.content-heading {
-  clear: both;
-}
-
-/* This animation's name is referenced in JavaScript */
-@keyframes highlight-hash-link {
-  0% {
-    border-bottom-color: transparent;
-  }
-
-  10% {
-    border-bottom-color: white;
-  }
-
-  25% {
-    border-bottom-color: white;
-  }
-
-  100% {
-    border-bottom-color: transparent;
-  }
-}
-
-/* Sticky heading */
-
-[id] {
-  --custom-scroll-offset: 0px;
-}
-
-#content [id] {
-  /* Adjust scroll margin. */
-  scroll-margin-top: calc(
-      74px /* Sticky heading */
-    + 33px /* Sticky subheading */
-    - 1em  /* One line of text (align bottom) */
-    - 12px /* Padding for hanging letters & focus ring */
-    + var(--custom-scroll-offset) /* Customizable offset */
-  );
-}
-
-.content-sticky-heading-container {
-  position: sticky;
-  top: 0;
-
-  margin: calc(-1 * var(--content-padding));
-  margin-bottom: calc(0.5 * var(--content-padding));
-
-  transform: translateY(-5px);
-}
-
-main.long-content .content-sticky-heading-container {
-  padding-left: 0;
-  padding-right: 0;
-}
-
-main.long-content .content-sticky-heading-container .content-sticky-heading-row,
-main.long-content .content-sticky-heading-container .content-sticky-subheading-row {
-  padding-left: calc(var(--long-content-padding-ratio) * (100% - 2 * var(--content-padding)) + var(--content-padding));
-  padding-right: calc(var(--long-content-padding-ratio) * (100% - 2 * var(--content-padding)) + var(--content-padding));
-}
-
-.content-sticky-heading-row {
-  box-sizing: border-box;
-  padding:
-    calc(1.25 * var(--content-padding) + 5px)
-    20px
-    calc(0.75 * var(--content-padding))
-    20px;
-
-  width: 100%;
-  margin: 0;
-
-  background: var(--bg-black-color);
-  border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
-
-  -webkit-backdrop-filter: blur(6px);
-          backdrop-filter: blur(6px);
-}
-
-.content-sticky-heading-container.has-cover .content-sticky-heading-row,
-.content-sticky-heading-container.has-cover .content-sticky-subheading-row {
-  display: grid;
-  grid-template-areas:
-    "title cover";
-  grid-template-columns: 1fr min(40%, 400px);
-}
-
-.content-sticky-heading-row h1 {
-  margin: 0;
-  padding-right: 20px;
-}
-
-.content-sticky-heading-cover-container {
-  position: relative;
-  height: 0;
-  margin: -15px 0px -5px -5px;
-}
-
-.content-sticky-heading-cover-needs-reveal {
-  display: none;
-}
-
-.content-sticky-heading-cover {
-  position: absolute;
-  top: 0;
-  width: 80px;
-  right: 10px;
-  box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.25);
-  transition: transform 0.35s, opacity 0.25s;
-}
-
-.content-sticky-heading-cover-container:not(.visible) .content-sticky-heading-cover {
-  opacity: 0;
-  transform: translateY(15px);
-  transition: transform 0.35s, opacity 0.30s;
-}
-
-.content-sticky-heading-cover .image-container {
-  border-width: 1px;
-  border-radius: 1.25px;
-  box-shadow: none;
-}
-
-.content-sticky-heading-container .image-outer-area {
-  padding: 3px;
-}
-
-.content-sticky-heading-container .image-inner-area {
-  border-radius: 1.75px;
-  overflow: hidden;
-}
-
-.content-sticky-heading-cover .image {
-  display: block;
-  width: 100%;
-  height: 100%;
-}
-
-.content-sticky-subheading-row {
-  position: absolute;
-  width: 100%;
-  box-sizing: border-box;
-  padding: 10px 20px 5px 20px;
-  margin-top: 0;
-  z-index: -1;
-
-  background: var(--bg-black-color);
-  border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
-  box-shadow:
-    0 2px 2px -1px #00000060,
-    0 4px 12px -4px #00000090;
-
-  -webkit-backdrop-filter: blur(4px);
-          backdrop-filter: blur(4px);
-
-  transition: margin-top 0.35s, opacity 0.25s;
-}
-
-.content-sticky-subheading-row h2 {
-  margin: 0;
-
-  font-size: 0.9em !important;
-  font-weight: normal;
-  font-style: oblique;
-  color: #eee;
-}
-
-.content-sticky-subheading-row:not(.visible) {
-  margin-top: -20px;
-  opacity: 0;
-}
-
-.content-sticky-subheading {
-  padding-right: 20px;
-}
-
-.content-sticky-heading-container h2.visible {
-  margin-top: 0;
-  opacity: 1;
-}
-
-.content-sticky-heading-row {
-  box-shadow:
-    inset 0 10px 10px -5px var(--shadow-color),
-    0 4px 8px -4px #000000b0;
-}
-
-#content, .sidebar {
-  contain: paint;
-}
-
-/* Sticky sidebar */
-
-.sidebar-column:not(.sticky-column) {
-  align-self: stretch;
-}
-
-.sidebar-column.sticky-column {
-  position: sticky;
-  top: 10px;
-  align-self: flex-start;
-  max-height: calc(100vh - 20px);
-  display: flex;
-  flex-direction: column;
-}
-
-.sidebar-multiple.sticky-column .sidebar:last-child {
-  flex-shrink: 1;
-  overflow-y: scroll;
-  scrollbar-width: thin;
-  scrollbar-color: var(--dim-color) var(--dark-color);
-}
-
-.wiki-search-sidebar-box .wiki-search-results-container {
-  overflow-y: scroll;
-  scrollbar-width: thin;
-  scrollbar-color: var(--dim-color) var(--dark-color);
-}
-
-.sidebar-column.sticky-column .sidebar:last-child::-webkit-scrollbar,
-.wiki-search-sidebar-box .wiki-search-results-container::-webkit-scrollbar {
-  background: var(--dark-color);
-  width: 12px;
-}
-
-.sidebar-column.sticky-column .sidebar:last-child::-webkit-scrollbar-thumb,
-.wiki-search-sidebar-box .wiki-search-results-container::-webkit-scrollbar-thumb {
-  transition: background 0.2s;
-  background: rgba(255, 255, 255, 0.2);
-  border: 3px solid transparent;
-  border-radius: 10px;
-  background-clip: content-box;
-}
-
-.sidebar-column.sidebar.sticky-column > h1 {
-  position: sticky;
-  top: 0;
-  z-index: 2;
-
-  margin: 0 calc(-1 * var(--content-padding));
-  margin-bottom: 10px;
-
-  border-bottom: 1px dotted rgba(220, 220, 220, 0.4);
-  padding: 10px 5px;
-
-  background: var(--bg-black-color);
-  -webkit-backdrop-filter: blur(4px);
-  backdrop-filter: blur(4px);
-
-  box-shadow:
-    0 2px 3px -1px #0006,
-    0 4px 8px -2px #0009;
-}
-
-/* Image overlay */
-
-#image-overlay-container {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  z-index: 4000;
-
-  background: rgba(0, 0, 0, 0.8);
-  color: white;
-  padding: 20px 40px;
-  box-sizing: border-box;
-
-  opacity: 0;
-  pointer-events: none;
-  transition: opacity 0.4s;
-
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-}
-
-#image-overlay-container::before {
-  content: '';
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-
-  background: var(--deep-color);
-  opacity: 0.20;
-}
-
-#image-overlay-container.visible {
-  opacity: 1;
-  pointer-events: auto;
-}
-
-#image-overlay-content-container {
-  border-radius: 0 0 8px 8px;
-  border: 2px solid var(--primary-color);
-  background: var(--deep-ghost-color);
-  overflow: hidden;
-
-  box-shadow:
-    0 0 90px 30px #00000060,
-    0 0 20px 10px #00000040,
-    0 0 10px 3px #00000080;
-
-  -webkit-backdrop-filter: blur(3px);
-          backdrop-filter: blur(3px);
-}
-
-#image-overlay-image-container {
-  display: block;
-  position: relative;
-  overflow: hidden;
-  width: 80vmin;
-  height: 80vmin;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-#image-overlay-image,
-#image-overlay-image-thumb {
-  display: inline-block;
-  object-fit: contain;
-  width: 100%;
-  height: 100%;
-  background: rgba(0, 0, 0, 0.65);
-}
-
-#image-overlay-image {
-  position: absolute;
-  top: 3px;
-  left: 3px;
-  width: calc(100% - 6px);
-  height: calc(100% - 4px);
-}
-
-#image-overlay-image-thumb {
-  filter: blur(16px);
-  transform: scale(1.5);
-}
-
-#image-overlay-container.loaded #image-overlay-image-thumb {
-  opacity: 0;
-  pointer-events: none;
-  transition: opacity 0.25s;
-}
-
-#image-overlay-image-container::after {
-  content: "";
-  display: block;
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  height: 4px;
-  width: var(--download-progress);
-  background: var(--primary-color);
-  box-shadow: 0 -3px 12px 4px var(--primary-color);
-  transition: 0.25s;
-}
-
-#image-overlay-container.loaded #image-overlay-image-container::after {
-  width: 100%;
-  background: white;
-  opacity: 0;
-}
-
-#image-overlay-container.errored #image-overlay-image-container::after {
-  width: 100%;
-  background: red;
-}
-
-#image-overlay-container:not(.visible) #image-overlay-image-container::after {
-  width: 0 !important;
-}
-
-#image-overlay-action-container {
-  padding: 7px 4px 7px 4px;
-  border-radius: 0 0 5px 5px;
-  background: var(--bg-black-color);
-  color: white;
-  font-style: oblique;
-  text-align: center;
-  box-shadow:
-    0 3px 8px -5px var(--primary-color) inset;
-}
-
-#image-overlay-container #image-overlay-action-content-without-size:not(.visible),
-#image-overlay-container #image-overlay-action-content-with-size:not(.visible),
-#image-overlay-container #image-overlay-file-size-warning:not(.visible),
-#image-overlay-container #image-overlay-file-size-kilobytes:not(.visible),
-#image-overlay-container #image-overlay-file-size-megabytes:not(.visible) {
-  display: none;
-}
-
-#image-overlay-file-size-warning {
-  opacity: 0.8;
-  font-size: 0.9em;
-}
-
-/* important easter egg mode */
-
-html[data-language-code="preview-en"][data-url-key="localized.home"] #content
-  h1::after {
-  font-family: cursive;
-  display: block;
-  content: "(Preview Build)";
-  font-size: 0.8em;
-}
-
-/* Layout - Wide (most computers) */
-
-@media (min-width: 900px) {
-  #page-container.showing-sidebar-left #secondary-nav:not(.always-visible),
-  #page-container.showing-sidebar-right #secondary-nav:not(.always-visible) {
-    display: none;
-  }
-}
-
-/* Layout - Medium (tablets, some landscape mobiles)
- *
- * Note: Rules defined here are exclusive to "medium" width, i.e. they don't
- * additionally apply to "thin". Use the later section which applies to both
- * if so desired.
- */
-
-@media (min-width: 600px) and (max-width: 899.98px) {
-  /* Medium layout is mainly defined (to the user) by hiding the sidebar, so
-   * don't apply the similar layout change of widening the long-content area
-   * if this page doesn't have a sidebar to hide in the first place.
-   */
-  #page-container.showing-sidebar-left main,
-  #page-container.showing-sidebar-right main {
-    --responsive-padding-ratio: 0.06;
-  }
-}
-
-/* Layout - Wide or Medium */
-
-@media (min-width: 600px) {
-  .content-sticky-heading-container {
-    /* Safari doesn't always play nicely with position: sticky,
-     * this seems to fix images sometimes displaying above the
-     * position: absolute subheading (h2) child
-     *
-     * See also: https://stackoverflow.com/questions/50224855/
-     */
-    transform: translate3d(0, 0, 0);
-    z-index: 1;
-  }
-
-  /* Cover art floats to the right. It's positioned in HTML beneath the
-   * heading, so pull it up a little to "float" on top.
-   */
-  #cover-art-container {
-    float: right;
-    width: 40%;
-    max-width: 400px;
-    margin: -60px 0 10px 20px;
-
-    position: relative;
-    z-index: 2;
-  }
-
-  /* ...Except on top-indexes, where cover art is displayed prominently
-   * between the heading and subheading.
-   */
-  #content.top-index #cover-art-container {
-    float: none;
-    margin: 2em auto 2.5em auto;
-    max-width: 375px;
-  }
-
-  html[data-url-key="localized.home"] #page-container.showing-sidebar-left .grid-listing > .grid-item:not(:nth-child(n+7)) {
-    flex-basis: 23%;
-    margin: 15px;
-  }
-
-  html[data-url-key="localized.home"] #page-container.showing-sidebar-left .grid-listing > .grid-item:nth-child(n+7) {
-    flex-basis: 18%;
-    margin: 10px;
-  }
-}
-
-/* Layout - Medium or Thin */
-
-@media (max-width: 899.98px) {
-  .sidebar.collapsible,
-  .sidebar-box-joiner.collapsible,
-  .sidebar-column.all-boxes-collapsible {
-    display: none;
-  }
-
-  .layout-columns {
-    flex-direction: column;
-  }
-
-  .layout-columns > *:not(:last-child) {
-    margin-bottom: 10px;
-  }
-
-  .sidebar-column {
-    position: static !important;
-    max-width: unset !important;
-    flex-basis: unset !important;
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-    width: 100%;
-  }
-
-  .sidebar .news-entry:not(.first-news-entry) {
-    display: none;
-  }
-
-  .grid-listing > .grid-item {
-    flex-basis: 40%;
-  }
-}
-
-/* Layout - Thin (phones) */
-
-@media (max-width: 600px) {
-  .content-columns {
-    columns: 1;
-  }
-
-  main {
-    --responsive-padding-ratio: 0.02;
-  }
-
-  #cover-art-container {
-    margin: 25px 0 5px 0;
-    width: 100%;
-    max-width: unset;
-  }
-
-  #additional-names-box {
-    width: unset;
-    max-width: unset;
-  }
-
-  .nav-has-content .nav-main-links .nav-link-accent {
-    display: block;
-  }
-
-  /* Show sticky heading above cover art */
-
-  .content-sticky-heading-container {
-    z-index: 2;
-  }
-
-  .content-sticky-heading-row h1 {
-    padding-right: 10px;
-  }
-
-  /* Let sticky heading text span past lower-index cover art */
-
-  .content-sticky-heading-container.has-cover .content-sticky-heading-row,
-  .content-sticky-heading-container.has-cover .content-sticky-subheading-row {
-    grid-template-columns: 1fr 90px;
-  }
-
-  /* Disable grid features, just line header children up vertically */
-
-  #header {
-    display: block;
-  }
-
-  #header > div:not(:first-child) {
-    margin-top: 0.5em;
-  }
-}
+@layer interactivity;
+@layer layout;
+@layer material, print;
+@layer construction;
+@layer interaction;
+
+@import url(miscellany.css);
+@import url(page.css);
+@import url(search.css);
+@import url(tooltips.css);
+@import url(features.css);
+@import url(responsive.css);
+
+@import url(specific-pages.css);
diff --git a/src/static/css/specific-pages.css b/src/static/css/specific-pages.css
new file mode 100644
index 00000000..ef3a0c54
--- /dev/null
+++ b/src/static/css/specific-pages.css
@@ -0,0 +1,288 @@
+/* Obviously this file should be split up.
+ * Just porting from old CSS for now.
+ */
+
+/* Album commentary page */
+
+@layer layout {
+  html[data-url-key="localized.albumCommentary"] .content-heading-main-title {
+    margin-right: 0.25em;
+  }
+
+  html[data-url-key="localized.albumCommentary"] .content-heading-accent {
+    display: inline-block;
+  }
+
+  html[data-url-key="localized.albumCommentary"] p.track-info {
+    margin-left: 20px;
+  }
+}
+
+@layer print {
+  html[data-url-key="localized.albumCommentary"] li.no-commentary {
+    opacity: 0.7;
+  }
+
+  html[data-url-key="localized.albumCommentary"] .content-heading-accent {
+    font-weight: normal;
+    font-style: oblique;
+    font-size: 0.9rem;
+  }
+}
+
+/* Art tag gallery page */
+
+@layer layout {
+  html[data-url-key="localized.artTagGallery"] #descends-from-line {
+    margin-bottom: 0.25em;
+  }
+
+  html[data-url-key="localized.artTagGallery"] #descendants-line {
+    margin-top: 0.25em;
+  }
+
+  html[data-url-key="localized.artTagGallery"] #descends-from-line a,
+  html[data-url-key="localized.artTagGallery"] #descendants-line a {
+    display: inline-block;
+  }
+}
+
+@layer interaction {
+  html[data-url-key="localized.artTagGallery"] #featured-direct-line,
+  html[data-url-key="localized.artTagGallery"] #featured-indirect-line,
+  html[data-url-key="localized.artTagGallery"] #showing-direct-line,
+  html[data-url-key="localized.artTagGallery"] #showing-indirect-line {
+    display: none;
+  }
+
+  html[data-url-key="localized.artTagGallery"] #showing-all-line a,
+  html[data-url-key="localized.artTagGallery"] #showing-direct-line a,
+  html[data-url-key="localized.artTagGallery"] #showing-indirect-line a {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+}
+
+/* "Art Tag Network" listing */
+
+@layer layout {
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:not(#network-top-dl > dd) {
+    margin-left: 20px;
+    margin-bottom: 0;
+    padding-left: 10px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:not(#network-top-dl > dd):not(:last-child) {
+    padding-bottom: 20px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-stat-line {
+    padding-left: 10px;
+    margin-left: 20px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] .network-tag-stat {
+    display: inline-block;
+    text-align: right;
+    min-width: 5ch;
+    margin-right: 2px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-top-dl > dt:has(.network-tag.with-stat:not([style*="display: none"])) {
+    padding-left: 20px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dt + dt:has(+ dd) {
+    padding-top: 20px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt {
+    padding-left: 10px;
+    margin-left: 20px;
+    margin-bottom: 0;
+    padding-bottom: 2px;
+    max-width: unset;
+    position: relative;
+  }
+}
+
+@layer material {
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:not(#network-top-dl > dd).even,
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt:not(#network-top-dl > dt).even {
+    border-left: 1px solid #eaeaea;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:not(#network-top-dl > dd).odd,
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt:not(#network-top-dl > dt).odd {
+    border-left: 1px solid #7b7b7b;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:last-child:not(#network-top-dl > dd).odd::after,
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt:last-child:not(#network-top-dl > dt).odd::after {
+    content: "";
+    display: block;
+    width: 7px;
+    height: 7px;
+    background: #7b7b7b;
+    position: absolute;
+    bottom: -4px;
+    left: -4px;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dd:last-child:not(#network-top-dl > dd).even::after,
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dl dt:last-child:not(#network-top-dl > dt).even::after {
+    content: "";
+    display: block;
+    width: 6px;
+    height: 6px;
+    background: #eaeaea;
+    position: absolute;
+    bottom: -3px;
+    left: -3px;
+    border-bottom-right-radius: 6px;
+    border-top-left-radius: 3px;
+  }
+}
+
+@layer print {
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] .network-tag-stat {
+    text-align: right;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] dt:has(+ dd) .network-tag-stat {
+    text-align: center;
+  }
+}
+
+@layer interaction {
+  html[data-url-key="localized.listing"][data-url-value0="tags/network"] #network-stat-line a {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+}
+
+/* Artist rolling window page */
+
+@layer layout {
+  html[data-url-key="localized.artistRollingWindow"] #content p {
+    text-align: center;
+  }
+
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-selection-control a {
+    display: inline-block;
+    padding: 5px;
+  }
+
+
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-source-area {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    min-height: calc(100vh - 260px);
+  }
+
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-source-area .grid-listing {
+    width: 100%;
+  }
+}
+
+@layer material {
+  html[data-url-key="localized.artistRollingWindow"] #content input[type=number] {
+    width: 3em;
+    margin: 0 0.25em;
+    background: black;
+    color: white;
+    border: 1px dotted var(--primary-color);
+    padding: 4px;
+    border-radius: 3px;
+  }
+
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-source-area {
+    border: 1px dashed #ffffff42;
+    border-top-style: solid;
+    border-bottom-style: solid;
+  }
+}
+
+@layer material {
+  html[data-url-key="localized.artistRollingWindow"] .grid-item.peeking {
+    opacity: 0.8;
+    background: #ffffff24;
+  }
+}
+
+@layer construction {
+  html[data-url-key="localized.artistRollingWindow"] .grid-item > span:not(:first-of-type) {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    flex-wrap: wrap;
+  }
+}
+
+@layer interaction {
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-selection-control a {
+    text-decoration: underline;
+    text-decoration-style: dotted;
+  }
+
+  html[data-url-key="localized.artistRollingWindow"] #timeframe-selection-control a:not([href]) {
+    text-decoration: none;
+    opacity: 0.7;
+  }
+}
+
+@layer construction {
+  html[data-url-key="localized.artistRollingWindow"] .grid-item > span:not(:first-of-type) > *:not([style*="display: none"]) ~ *::before {
+    content: '\00b7';
+    margin-left: 0.5ch;
+    margin-right: 0.5ch;
+  }
+}
+
+/* Group info page */
+
+@layer print {
+  html[data-url-key="localized.groupInfo"] .by a {
+    color: var(--page-primary-color);
+  }
+}
+
+/* Homepage */
+
+@layer print {
+  html[data-url-key="localized.home"] #content h1 {
+    text-align: center;
+    font-size: 2.5em;
+  }
+
+  html[data-language-code="preview-en"][data-url-key="localized.home"] #content h1::after {
+    font-family: cursive;
+    display: block;
+    content: "(Preview Build)";
+    font-size: 0.8em;
+  }
+}
+
+/* "Random Pages" listing */
+
+@layer interaction {
+  html[data-url-key="localized.listing"][data-url-value0="random"] #data-loading-line,
+  html[data-url-key="localized.listing"][data-url-value0="random"] #data-loaded-line,
+  html[data-url-key="localized.listing"][data-url-value0="random"] #data-error-line {
+    display: none;
+  }
+
+  html[data-url-key="localized.listing"][data-url-value0="random"] #content a:not([href]) {
+    opacity: 0.7;
+  }
+}
+
+/* News entries */
+
+@layer print {
+  html[data-url-key="localized.newsEntry"] .read-another-links {
+    font-style: oblique;
+    font-size: 0.9em;
+  }
+}
diff --git a/src/static/css/tooltips.css b/src/static/css/tooltips.css
new file mode 100644
index 00000000..116c9181
--- /dev/null
+++ b/src/static/css/tooltips.css
@@ -0,0 +1,445 @@
+@layer layout {
+  .text-with-tooltip {
+    position: relative;
+  }
+
+  .tooltip {
+    position: absolute;
+    z-index: 3;
+    left: -10px;
+    top: calc(1em + 1px);
+
+    display: none;
+  }
+
+  .tooltip-content {
+    display: block;
+  }
+
+  :where(.isolate-tooltip-z-indexing) {
+    position: relative;
+    z-index: 1;
+  }
+
+  :where(.isolate-tooltip-z-indexing > *) {
+    position: relative;
+    z-index: -1;
+  }
+}
+
+@layer material {
+  .tooltip {
+    font-size: 1rem;
+  }
+
+  .cover-artwork .tooltip,
+  #sidebar .tooltip {
+    font-size: 0.9rem;
+  }
+
+  .offset-tooltips .tooltip {
+    left: 14px;
+  }
+
+  .tooltip-content {
+    background: var(--bg-black-color);
+    border: 1px dotted var(--primary-color);
+    border-radius: 6px;
+
+    -webkit-backdrop-filter:
+      brightness(1.5) saturate(1.4) blur(4px);
+
+            backdrop-filter:
+      brightness(1.5) saturate(1.4) blur(4px);
+
+    box-shadow:
+      0 4px 6px 7px #0007,
+      0 4px 4px 2px #0004,
+      0 -2px 4px -2px var(--primary-color) inset;
+
+    text-indent: 0;
+  }
+}
+
+@layer print {
+  .tooltip-content {
+    font-size: 1rem;
+  }
+
+  .cover-artwork .content-tooltip .tooltip-content {
+    font-size: 0.85rem;
+  }
+}
+
+@layer interaction {
+  .tooltip {
+    display: none;
+  }
+
+  .text-with-tooltip .text-with-tooltip-interaction-cue {
+    text-decoration-line: underline;
+    text-decoration-style: dotted;
+  }
+
+  .text-with-tooltip > .hoverable:hover .text-with-tooltip-interaction-cue,
+  .text-with-tooltip > .hoverable.has-visible-tooltip .text-with-tooltip-interaction-cue {
+    text-decoration-style: wavy !important;
+  }
+}
+
+/* "General" tooltips */
+
+@layer layout {
+  .datetimestamp-tooltip,
+  .missing-duration-tooltip,
+  .rerelease-tooltip,
+  .first-release-tooltip,
+  .other-release-tooltip,
+  .content-tooltip {
+    padding: 3px 4px 2px 2px;
+    left: -10px;
+  }
+
+  .datetimestamp-tooltip .tooltip-content,
+  .missing-duration-tooltip .tooltip-content {
+    padding: 5px 6px;
+    white-space: nowrap;
+    font-size: 0.9em;
+  }
+}
+
+@layer interaction {
+  .text-with-tooltip.datetimestamp .text-with-tooltip-interaction-cue,
+  .text-with-tooltip.missing-duration .text-with-tooltip-interaction-cue,
+  .text-with-tooltip.commentary-date .text-with-tooltip-interaction-cue,
+  .text-with-tooltip.wiki-edits .text-with-tooltip-interaction-cue,
+  .text-with-tooltip.rerelease .text-with-tooltip-interaction-cue,
+  .text-with-tooltip.first-release .text-with-tooltip-interaction-cue {
+    cursor: default;
+  }
+}
+
+/* Commentary date tooltip */
+
+@layer layout {
+  .commentary-date-tooltip {
+    right: -10px;
+    padding: 4px 4px 2px 2px;
+    left: unset;
+  }
+
+  .commentary-date-tooltip .tooltip-content {
+    padding: 2px 6px;
+    white-space: nowrap;
+    font-size: 0.9em;
+  }
+
+  .commentary-date-tooltip span.cute-break {
+    margin-top: 3px;
+    margin-bottom: 3px;
+  }
+}
+
+@layer print {
+  .commentary-date-tooltip .relative-to {
+    opacity: 0.8;
+  }
+}
+
+/* Contribution tooltip */
+
+@layer interactivity {
+  .contribution-tooltip .tooltip-content {
+    -webkit-user-select: none;
+            user-select: none;
+  }
+}
+
+@layer layout {
+  .contribution-tooltip {
+    padding: 3px 6px 6px 6px;
+    left: -34px;
+  }
+
+  .contribution-tooltip .tooltip-content {
+    display: grid;
+    grid-template-columns:
+      [icon-start] 26px [icon-end handle-start] auto [handle-end platform-start] auto [platform-end];
+
+    padding: 6px 2px 2px 2px;
+  }
+
+  .contribution-tooltip .external-link {
+    display: grid;
+    grid-template-columns: subgrid;
+    grid-column-start: icon-start;
+    grid-column-end: handle-end;
+
+    height: 1.4em;
+  }
+
+  .contribution-tooltip .chronology-heading {
+    grid-column-start: handle-start;
+    grid-column-end: platform-end;
+
+    min-width: 30ch;
+    margin-bottom: 2px;
+  }
+
+  .contribution-tooltip .chronology-link {
+    display: grid;
+    grid-column-start: icon-start;
+    grid-column-end: handle-end;
+    grid-template-columns: subgrid;
+
+    height: 1.2em;
+  }
+
+  .contribution-tooltip .external-icon,
+  .contribution-tooltip .chronology-symbol {
+    grid-column-start: icon-start;
+    grid-column-end: icon-end;
+  }
+
+  .contribution-tooltip .external-icon svg {
+    width: 18px !important;
+    height: 18px !important;
+    top: -0.1em;
+  }
+
+  .contribution-tooltip .chronology-symbol {
+    text-align: center;
+  }
+
+  .contribution-tooltip .external-handle,
+  .contribution-tooltip .chronology-text {
+    grid-column-start: handle-start;
+    grid-column-end: handle-end;
+
+    width: max-content;
+    max-width: 200px;
+
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+
+  .contribution-tooltip .external-handle {
+    padding-right: 8px;
+  }
+
+  .contribution-tooltip .chronology-text {
+    padding-right: 6px;
+  }
+
+  .contribution-tooltip .external-platform,
+  .contribution-tooltip .chronology-info {
+    grid-column-start: platform-start;
+    grid-column-end: platform-end;
+    white-space: nowrap;
+    padding-right: 4px;
+  }
+}
+
+@layer print {
+  .contribution-tooltip .tooltip-divider,
+  .tooltip-content hr.cute,
+  .tooltip-content span.cute-break {
+    display: block;
+    grid-column-start: icon-start;
+    grid-column-end: platform-end;
+    border-top: 1px dotted var(--primary-color);
+  }
+
+  /* Don't mind me... */
+  .tooltip-content .tooltip-divider,
+  .tooltip-content hr.cute {
+    margin-top: 3px;
+    margin-bottom: 4px;
+  }
+
+  .contribution-tooltip .chronology-heading {
+    font-size: 0.85em;
+    font-style: oblique;
+  }
+
+  .contribution-tooltip .chronology-text,
+  .contribution-tooltip .chronology-info {
+    font-size: 0.85em;
+  }
+}
+
+@layer interaction {
+  .contribution-tooltip .tooltip-content {
+    cursor: default;
+  }
+
+  .contribution-tooltip.show-info .external-platform,
+  .contribution-tooltip.show-info .chronology-info {
+    display: inline;
+    animation: external-platform 0.2s forwards linear;
+  }
+
+  @keyframes external-platform {
+    from {
+      opacity: 0;
+    }
+
+    to {
+      opacity: var(--external-platform-opacity);
+    }
+  }
+
+  .contribution-tooltip .external-platform,
+  .contribution-tooltip .chronology-info {
+    display: none;
+
+    --external-platform-opacity: 0.8;
+    opacity: 0.8;
+  }
+
+  .contribution-tooltip .external-link:hover,
+  .contribution-tooltip .chronology-link:hover {
+    filter: brightness(1.4);
+    text-decoration: none;
+  }
+
+  .contribution-tooltip .external-link:hover .external-handle,
+  .contribution-tooltip .chronology-link:hover .chronology-text {
+    text-decoration: underline;
+  }
+
+  .contribution-tooltip .external-link:hover + .external-platform,
+  .contribution-tooltip .chronology-link:hover + .chronology-info {
+    --external-platform-opacity: 1;
+    text-decoration: underline;
+    text-decoration-color: #ffffffaa;
+  }
+}
+
+/* Missing duration tooltip */
+
+@layer interaction {
+  .text-with-tooltip.missing-duration > .hoverable {
+    opacity: 0.5;
+  }
+
+  .text-with-tooltip.missing-duration > .hoverable:hover,
+  .text-with-tooltip.missing-duration > .hoverable.has-visible-tooltip {
+    opacity: 1;
+  }
+
+  .text-with-tooltip.missing-duration .text-with-tooltip-interaction-cue {
+    text-decoration: none !important;
+  }
+}
+
+/* Rerelease, first release, other release tooltips */
+
+@layer layout {
+  .rerelease-tooltip .tooltip-content,
+  .first-release-tooltip .tooltip-content,
+  .other-release-tooltip .tooltip-content {
+    padding: 3px 4.5px;
+  }
+
+  .rerelease-tooltip .tooltip-content,
+  .first-release-tooltip .tooltip-content {
+    width: 260px;
+  }
+
+  .other-release-tooltip .tooltip-content {
+    width: max-content;
+    max-width: 250px;
+  }
+
+  .other-release-tooltip .tooltip-content .when {
+    /* technically just putting this in a <span> was enough
+     * to keep it from wrapping all tight-like, for some
+     * reason, but im not taking any chances...
+     */
+    white-space: nowrap;
+  }
+}
+
+@layer print {
+  .rerelease-tooltip .tooltip-content,
+  .first-release-tooltip .tooltip-content,
+  .other-release-tooltip .tooltip-content {
+    font-size: 0.9rem;
+  }
+
+  .rerelease-tooltip .not-credited-on-first-release {
+    opacity: 0.9;
+  }
+}
+
+/* Thing name & wiki edits tooltips */
+
+@layer layout {
+  .thing-name-tooltip,
+  .wiki-edits-tooltip {
+    padding: 3px 4px 2px 2px;
+    left: -6px;
+  }
+
+  .thing-name-tooltip .tooltip-content,
+  .wiki-edits-tooltip .tooltip-content {
+    width: max-content;
+    padding: 3px 4.5px;
+  }
+
+  .thing-name-tooltip .tooltip-content,
+    max-width: 120px;
+  }
+
+  .wiki-edits-tooltip .tooltip-content {
+    max-width: 200px;
+  }
+}
+
+@layer print {
+  .text-with-tooltip.wiki-edits > .hoverable {
+    white-space: nowrap;
+  }
+
+  .thing-name-tooltip .tooltip-content,
+  .wiki-edits-tooltip .tooltip-content {
+    font-size: 0.85rem;
+  }
+}
+
+/* Content tooltips */
+
+@layer layout {
+  .content-tooltip-guy {
+    display: inline-block;
+  }
+
+  .content-tooltip-guy.has-link .text-with-tooltip-interaction-cue {
+    text-decoration-color: var(--primary-color);
+  }
+
+  .content-tooltip .tooltip-content {
+    padding: 3px 4.5px;
+    width: max-content;
+    max-width: 240px;
+  }
+
+  .cover-artwork .content-tooltip {
+    padding: 2px 3px;
+    width: max-content;
+    max-width: 220px;
+  }
+}
+
+@layer interaction {
+  .content-tooltip-guy:not(.has-link) .hoverable {
+    cursor: default;
+  }
+
+  .content-tooltip-guy .hoverable a {
+    text-decoration-color: transparent;
+    text-decoration-style: dotted;
+  }
+}
diff --git a/src/static/js/client-util.js b/src/static/js/client-util.js
index f06b707a..0c113758 100644
--- a/src/static/js/client-util.js
+++ b/src/static/js/client-util.js
@@ -1,12 +1,17 @@
-/* eslint-env browser */
-
 export function rebase(href, rebaseKey = 'rebaseLocalized') {
-  const relative = (document.documentElement.dataset[rebaseKey] || '.') + '/';
-  if (relative) {
-    return relative + href;
-  } else {
-    return href;
+  let result = document.documentElement.dataset[rebaseKey] || './';
+
+  if (!result.endsWith('/')) {
+    result += '/';
   }
+
+  if (href.startsWith('/')) {
+    href = href.slice(1);
+  }
+
+  result += href;
+
+  return result;
 }
 
 export function cssProp(el, ...args) {
@@ -30,7 +35,7 @@ export function cssProp(el, ...args) {
   }
 }
 
-export function templateContent(el) {
+export function templateContent(el, slots = {}) {
   if (el === null) {
     return null;
   }
@@ -39,7 +44,33 @@ export function templateContent(el) {
     throw new Error(`Expected a <template> element`);
   }
 
-  return el.content.cloneNode(true);
+  const content = el.content.cloneNode(true);
+
+  for (const [key, value] of Object.entries(slots)) {
+    const slot = content.querySelector(`slot[name="${key}"]`);
+
+    if (!slot) {
+      console.warn(`Slot ${key} missing in template:`, el);
+      continue;
+    }
+
+    if (value === null || value === undefined) {
+      console.warn(`Valueless slot ${key} in template:`, el);
+      continue;
+    }
+
+    slot.replaceWith(value);
+  }
+
+  return content;
+}
+
+export function decodeEntities(string) {
+  if (!string) return string;
+
+  const textarea = document.createElement('textarea');
+  textarea.innerHTML = string;
+  return textarea.value;
 }
 
 // Curry-style, so multiple points can more conveniently be tested at once.
@@ -74,12 +105,12 @@ export function getVisuallyContainingElement(child) {
 const getLinkHref = (type, directory) => rebase(`${type}/${directory}`);
 */
 
-export const openAlbum = d => rebase(`album/${d}`);
-export const openArtTag = d => rebase(`tag/${d}`);
-export const openArtist = d => rebase(`artist/${d}`);
-export const openFlash = d => rebase(`flash/${d}`);
-export const openGroup = d => rebase(`group/${d}`);
-export const openTrack = d => rebase(`track/${d}`);
+export const openAlbum = d => rebase(`album/${d}/`);
+export const openArtTag = d => rebase(`tag/${d}/`);
+export const openArtist = d => rebase(`artist/${d}/`);
+export const openFlash = d => rebase(`flash/${d}/`);
+export const openGroup = d => rebase(`group/${d}/`);
+export const openTrack = d => rebase(`track/${d}/`);
 
 // TODO: This should also use urlSpec.
 
@@ -120,3 +151,10 @@ export function dispatchInternalEvent(event, eventName, ...args) {
 
   return results;
 }
+
+const languageCode = document.documentElement.getAttribute('lang');
+
+export function formatDate(inputDate) {
+  const date = new Date(inputDate);
+  return date.toLocaleDateString(languageCode);
+}
diff --git a/src/static/js/client/additional-names-box.js b/src/static/js/client/additional-names-box.js
index 558ef06f..a6d9b098 100644
--- a/src/static/js/client/additional-names-box.js
+++ b/src/static/js/client/additional-names-box.js
@@ -1,14 +1,17 @@
-/* eslint-env browser */
-
 import {cssProp} from '../client-util.js';
 
 import {info as hashLinkInfo} from './hash-link.js';
+import {info as stickyHeadingInfo} from './sticky-heading.js';
 
 export const info = {
   id: 'additionalNamesBoxInfo',
 
   box: null,
+
   links: null,
+  stickyHeadingLink: null,
+
+  contentContainer: null,
   mainContentContainer: null,
 
   state: {
@@ -23,6 +26,16 @@ export function getPageReferences() {
   info.links =
     document.querySelectorAll('a[href="#additional-names-box"]');
 
+  info.stickyHeadingLink =
+    document.querySelector(
+      '.content-sticky-heading-container' +
+      ' ' +
+      'a[href="#additional-names-box"]' +
+      ':not(:where([inert] *))');
+
+  info.contentContainer =
+    document.querySelector('#content');
+
   info.mainContentContainer =
     document.querySelector('#content .main-content-container');
 }
@@ -33,6 +46,34 @@ export function addInternalListeners() {
       return false;
     }
   });
+
+  stickyHeadingInfo.event.whenStuckStatusChanges.push((index, stuck) => {
+    const {state} = info;
+
+    if (!info.stickyHeadingLink) return;
+
+    const container = stickyHeadingInfo.contentContainers[index];
+    if (container !== info.contentContainer) return;
+
+    if (stuck) {
+      if (!state.visible) {
+        info.stickyHeadingLink.removeAttribute('href');
+
+        if (info.stickyHeadingLink.hasAttribute('title')) {
+          info.stickyHeadingLink.dataset.restoreTitle = info.stickyHeadingLink.getAttribute('title');
+          info.stickyHeadingLink.removeAttribute('title');
+        }
+      }
+    } else {
+      info.stickyHeadingLink.setAttribute('href', '#additional-names-box');
+
+      const {restoreTitle} = info.stickyHeadingLink.dataset;
+      if (restoreTitle) {
+        info.stickyHeadingLink.setAttribute('title', restoreTitle);
+        delete info.stickyHeadingLink.dataset.restoreTitle;
+      }
+    }
+  });
 }
 
 export function addPageListeners() {
@@ -48,6 +89,7 @@ function handleAdditionalNamesBoxLinkClicked(domEvent) {
 
   domEvent.preventDefault();
 
+  if (!domEvent.target.hasAttribute('href')) return;
   if (!info.box || !info.mainContentContainer) return;
 
   const margin =
@@ -58,7 +100,30 @@ function handleAdditionalNamesBoxLinkClicked(domEvent) {
       ? info.box.getBoundingClientRect()
       : info.mainContentContainer.getBoundingClientRect());
 
-  if (top + 20 < margin || top > 0.4 * window.innerHeight) {
+  const {bottom, height} =
+    (state.visible
+      ? info.box.getBoundingClientRect()
+      : {bottom: null});
+
+  const boxFitsInFrame =
+    (height
+      ? height < window.innerHeight - margin - 60
+      : null);
+
+  const worthScrolling =
+    top + 20 < margin ||
+
+    (height && boxFitsInFrame
+      ? top > 0.7 * window.innerHeight
+   : height && !boxFitsInFrame
+      ? top > 0.4 * window.innerHeight
+      : top > 0.5 * window.innerHeight) ||
+
+    (bottom && boxFitsInFrame
+      ? bottom > window.innerHeight - 20
+      : false);
+
+  if (worthScrolling) {
     if (!state.visible) {
       toggleAdditionalNamesBox();
     }
diff --git a/src/static/js/client/album-commentary-sidebar.js b/src/static/js/client/album-commentary-sidebar.js
index c5eaf81b..d7c4a591 100644
--- a/src/static/js/client/album-commentary-sidebar.js
+++ b/src/static/js/client/album-commentary-sidebar.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {empty} from '../../shared-util/sugar.js';
 
 import {info as hashLinkInfo} from './hash-link.js';
diff --git a/src/static/js/client/art-tag-gallery-filter.js b/src/static/js/client/art-tag-gallery-filter.js
new file mode 100644
index 00000000..b7fff70d
--- /dev/null
+++ b/src/static/js/client/art-tag-gallery-filter.js
@@ -0,0 +1,149 @@
+export const info = {
+  id: 'artTagGalleryFilterInfo',
+
+  featuredAllLine: null,
+  showingAllLine: null,
+  showingAllLink: null,
+
+  featuredDirectLine: null,
+  showingDirectLine: null,
+  showingDirectLink: null,
+
+  featuredIndirectLine: null,
+  showingIndirectLine: null,
+  showingIndirectLink: null,
+
+  gridItems: null,
+  gridItemsOnlyFeaturedIndirectly: null,
+  gridItemsFeaturedDirectly: null,
+};
+
+export function getPageReferences() {
+  if (document.documentElement.dataset.urlKey !== 'localized.artTagGallery') {
+    return;
+  }
+
+  info.featuredAllLine =
+    document.getElementById('featured-all-line');
+
+  info.featuredDirectLine =
+    document.getElementById('featured-direct-line');
+
+  info.featuredIndirectLine =
+    document.getElementById('featured-indirect-line');
+
+  info.showingAllLine =
+    document.getElementById('showing-all-line');
+
+  info.showingDirectLine =
+    document.getElementById('showing-direct-line');
+
+  info.showingIndirectLine =
+    document.getElementById('showing-indirect-line');
+
+  info.showingAllLink =
+    info.showingAllLine?.querySelector('a') ?? null;
+
+  info.showingDirectLink =
+    info.showingDirectLine?.querySelector('a') ?? null;
+
+  info.showingIndirectLink =
+    info.showingIndirectLine?.querySelector('a') ?? null;
+
+  info.gridItems =
+    Array.from(
+      document.querySelectorAll('#content .grid-listing .grid-item'));
+
+  info.gridItemsOnlyFeaturedIndirectly =
+    info.gridItems
+      .filter(gridItem => gridItem.classList.contains('featured-indirectly'));
+
+  info.gridItemsFeaturedDirectly =
+    info.gridItems
+      .filter(gridItem => !gridItem.classList.contains('featured-indirectly'));
+}
+
+function filterArtTagGallery(showing) {
+  let gridItemsToShow;
+
+  switch (showing) {
+    case 'all':
+      gridItemsToShow = info.gridItems;
+      break;
+
+    case 'direct':
+      gridItemsToShow = info.gridItemsFeaturedDirectly;
+      break;
+
+    case 'indirect':
+      gridItemsToShow = info.gridItemsOnlyFeaturedIndirectly;
+      break;
+  }
+
+  for (const gridItem of info.gridItems) {
+    if (gridItemsToShow.includes(gridItem)) {
+      gridItem.style.removeProperty('display');
+    } else {
+      gridItem.style.display = 'none';
+    }
+  }
+}
+
+export function addPageListeners() {
+  const orderShowing = [
+    'all',
+    'direct',
+    'indirect',
+  ];
+
+  const orderFeaturedLines = [
+    info.featuredAllLine,
+    info.featuredDirectLine,
+    info.featuredIndirectLine,
+  ];
+
+  const orderShowingLines = [
+    info.showingAllLine,
+    info.showingDirectLine,
+    info.showingIndirectLine,
+  ];
+
+  const orderShowingLinks = [
+    info.showingAllLink,
+    info.showingDirectLink,
+    info.showingIndirectLink,
+  ];
+
+  for (let index = 0; index < orderShowing.length; index++) {
+    if (!orderShowingLines[index]) continue;
+
+    let nextIndex = index;
+    do {
+      if (nextIndex === orderShowing.length) {
+        nextIndex = 0;
+      } else {
+        nextIndex++;
+      }
+    } while (!orderShowingLinks[nextIndex]);
+
+    const currentFeaturedLine = orderFeaturedLines[index];
+    const currentShowingLine = orderShowingLines[index];
+    const currentShowingLink = orderShowingLinks[index];
+
+    const nextFeaturedLine = orderFeaturedLines[nextIndex];
+    const nextShowingLine = orderShowingLines[nextIndex];
+    const nextShowing = orderShowing[nextIndex];
+
+    currentShowingLink.addEventListener('click', event => {
+      event.preventDefault();
+
+      currentFeaturedLine.style.display = 'none';
+      currentShowingLine.style.display = 'none';
+
+      nextFeaturedLine.style.display = 'inline';
+      nextShowingLine.style.display = 'inline';
+
+      filterArtTagGallery(nextShowing);
+    });
+  }
+}
diff --git a/src/static/js/client/art-tag-network.js b/src/static/js/client/art-tag-network.js
new file mode 100644
index 00000000..d0576152
--- /dev/null
+++ b/src/static/js/client/art-tag-network.js
@@ -0,0 +1,145 @@
+import {cssProp} from '../client-util.js';
+
+import {atOffset, stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'artTagNetworkInfo',
+
+  noneStatLink: null,
+  totalUsesStatLink: null,
+  directUsesStatLink: null,
+  descendantsStatLink: null,
+  leavesStatLink: null,
+
+  tagsWithoutStats: null,
+  tagsWithStats: null,
+
+  totalUsesStats: null,
+  directUsesStats: null,
+  descendantsStats: null,
+  leavesStats: null,
+};
+
+export function getPageReferences() {
+  if (
+    document.documentElement.dataset.urlKey !== 'localized.listing' ||
+    document.documentElement.dataset.urlValue0 !== 'tags/network'
+  ) {
+    return;
+  }
+
+  info.noneStatLink =
+    document.getElementById('network-stat-none');
+
+  info.totalUsesStatLink =
+    document.getElementById('network-stat-total-uses');
+
+  info.directUsesStatLink =
+    document.getElementById('network-stat-direct-uses');
+
+  info.descendantsStatLink =
+    document.getElementById('network-stat-descendants');
+
+  info.leavesStatLink =
+    document.getElementById('network-stat-leaves');
+
+  info.tagsWithoutStats =
+    document.querySelectorAll('.network-tag:not(.with-stat)');
+
+  info.tagsWithStats =
+    document.querySelectorAll('.network-tag.with-stat');
+
+  info.totalUsesStats =
+    Array.from(document.getElementsByClassName('network-tag-total-uses-stat'));
+
+  info.directUsesStats =
+    Array.from(document.getElementsByClassName('network-tag-direct-uses-stat'));
+
+  info.descendantsStats =
+    Array.from(document.getElementsByClassName('network-tag-descendants-stat'));
+
+  info.leavesStats =
+    Array.from(document.getElementsByClassName('network-tag-leaves-stat'));
+}
+
+export function addPageListeners() {
+  if (!info.noneStatLink) return;
+
+  const linkOrder = [
+    info.noneStatLink,
+    info.totalUsesStatLink,
+    info.directUsesStatLink,
+    info.descendantsStatLink,
+    info.leavesStatLink,
+  ];
+
+  const statsOrder = [
+    null,
+    info.totalUsesStats,
+    info.directUsesStats,
+    info.descendantsStats,
+    info.leavesStats,
+  ];
+
+  const stitched =
+    stitchArrays({
+      link: linkOrder,
+      stats: statsOrder,
+    });
+
+  for (const [index, {link}] of stitched.entries()) {
+    const next = atOffset(stitched, index, +1, {wrap: true});
+
+    link.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+
+      cssProp(link, 'display', 'none');
+      cssProp(next.link, 'display', null);
+
+      if (next.stats === null) {
+        hideArtTagNetworkStats();
+      } else {
+        showArtTagNetworkStats(next.stats);
+      }
+    });
+  }
+}
+
+function showArtTagNetworkStats(stats) {
+  for (const tagElement of info.tagsWithoutStats) {
+    cssProp(tagElement, 'display', 'none');
+  }
+
+  for (const tagElement of info.tagsWithStats) {
+    cssProp(tagElement, 'display', null);
+  }
+
+  const allStats = [
+    ...info.totalUsesStats,
+    ...info.directUsesStats,
+    ...info.descendantsStats,
+    ...info.leavesStats,
+  ];
+
+  const otherStats =
+    allStats
+      .filter(stat => !stats.includes(stat));
+
+  for (const statElement of otherStats) {
+    cssProp(statElement, 'display', 'none');
+  }
+
+  for (const statElement of stats) {
+    cssProp(statElement, 'display', null);
+  }
+}
+
+function hideArtTagNetworkStats() {
+  for (const tagElement of info.tagsWithoutStats) {
+    cssProp(tagElement, 'display', null);
+  }
+
+  for (const tagElement of info.tagsWithStats) {
+    cssProp(tagElement, 'display', 'none');
+  }
+}
diff --git a/src/static/js/client/artist-external-link-tooltip.js b/src/static/js/client/artist-external-link-tooltip.js
index 21ddfb91..2eadf916 100644
--- a/src/static/js/client/artist-external-link-tooltip.js
+++ b/src/static/js/client/artist-external-link-tooltip.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {accumulateSum, empty} from '../../shared-util/sugar.js';
 
 import {info as hoverableTooltipInfo, repositionCurrentTooltip}
diff --git a/src/static/js/client/artist-rolling-window.js b/src/static/js/client/artist-rolling-window.js
new file mode 100644
index 00000000..b8ff7354
--- /dev/null
+++ b/src/static/js/client/artist-rolling-window.js
@@ -0,0 +1,571 @@
+import {cssProp, formatDate} from '../client-util.js';
+
+import {sortByDate} from '../../shared-util/sort.js';
+import {chunkByConditions, chunkByProperties, empty, stitchArrays}
+  from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'artistRollingWindowInfo',
+
+  timeframeMonthsBefore: null,
+  timeframeMonthsAfter: null,
+  timeframeMonthsPeek: null,
+
+  contributionKind: null,
+  contributionGroup: null,
+
+  timeframeSelectionSomeLine: null,
+  timeframeSelectionNoneLine: null,
+
+  timeframeSelectionContributionCount: null,
+  timeframeSelectionTimeframeCount: null,
+  timeframeSelectionFirstDate: null,
+  timeframeSelectionLastDate: null,
+
+  timeframeSelectionControl: null,
+  timeframeSelectionMenu: null,
+  timeframeSelectionPrevious: null,
+  timeframeSelectionNext: null,
+
+  timeframeEmptyLine: null,
+
+  sourceArea: null,
+  sourceGrid: null,
+  sources: null,
+};
+
+export function getPageReferences() {
+  if (document.documentElement.dataset.urlKey !== 'localized.artistRollingWindow') {
+    return;
+  }
+
+  info.timeframeMonthsBefore =
+    document.getElementById('timeframe-months-before');
+
+  info.timeframeMonthsAfter =
+    document.getElementById('timeframe-months-after');
+
+  info.timeframeMonthsPeek =
+    document.getElementById('timeframe-months-peek');
+
+  info.contributionKind =
+    document.getElementById('contribution-kind');
+
+  info.contributionGroup =
+    document.getElementById('contribution-group');
+
+  info.timeframeSelectionSomeLine =
+    document.getElementById('timeframe-selection-some');
+
+  info.timeframeSelectionNoneLine =
+    document.getElementById('timeframe-selection-none');
+
+  info.timeframeSelectionContributionCount =
+    document.getElementById('timeframe-selection-contribution-count');
+
+  info.timeframeSelectionTimeframeCount =
+    document.getElementById('timeframe-selection-timeframe-count');
+
+  info.timeframeSelectionFirstDate =
+    document.getElementById('timeframe-selection-first-date');
+
+  info.timeframeSelectionLastDate =
+    document.getElementById('timeframe-selection-last-date');
+
+  info.timeframeSelectionControl =
+    document.getElementById('timeframe-selection-control');
+
+  info.timeframeSelectionMenu =
+    document.getElementById('timeframe-selection-menu');
+
+  info.timeframeSelectionPrevious =
+    document.getElementById('timeframe-selection-previous');
+
+  info.timeframeSelectionNext =
+    document.getElementById('timeframe-selection-next');
+
+  info.timeframeEmptyLine =
+    document.getElementById('timeframe-empty');
+
+  info.sourceArea =
+    document.getElementById('timeframe-source-area');
+
+  info.sourceGrid =
+    info.sourceArea.querySelector('.grid-listing');
+
+  info.sources =
+    info.sourceGrid.getElementsByClassName('grid-item');
+}
+
+export function addPageListeners() {
+  if (!info.sourceArea) {
+    return;
+  }
+
+  for (const input of [
+    info.timeframeMonthsBefore,
+    info.timeframeMonthsAfter,
+    info.timeframeMonthsPeek,
+    info.contributionKind,
+    info.contributionGroup,
+  ]) {
+    input.addEventListener('change', () => {
+      updateArtistRollingWindow()
+    });
+  }
+
+  info.timeframeSelectionMenu.addEventListener('change', () => {
+    updateRollingWindowTimeframeSelection();
+  });
+
+  const eatClicks = (element, callback) => {
+    element.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+      callback();
+    });
+
+    element.addEventListener('mousedown', domEvent => {
+      if (domEvent.detail > 1) {
+        domEvent.preventDefault();
+      }
+    });
+  };
+
+  eatClicks(info.timeframeSelectionNext, nextRollingTimeframeSelection);
+  eatClicks(info.timeframeSelectionPrevious, previousRollingTimeframeSelection);
+}
+
+export function mutatePageContent() {
+  if (!info.sourceArea) {
+    return;
+  }
+
+  updateArtistRollingWindow();
+}
+
+function previousRollingTimeframeSelection() {
+  const menu = info.timeframeSelectionMenu;
+
+  if (menu.selectedIndex > 0) {
+    menu.selectedIndex--;
+  }
+
+  updateRollingWindowTimeframeSelection();
+}
+
+function nextRollingTimeframeSelection() {
+  const menu = info.timeframeSelectionMenu;
+
+  if (menu.selectedIndex < menu.length - 1) {
+    menu.selectedIndex++;
+  }
+
+  updateRollingWindowTimeframeSelection();
+}
+
+function getArtistRollingWindowSourceInfo() {
+  const sourceElements =
+    Array.from(info.sources);
+
+  const sourceTimeElements =
+    sourceElements
+      .map(el => Array.from(el.getElementsByTagName('time')));
+
+  const sourceTimeClasses =
+    sourceTimeElements
+      .map(times => times
+        .map(time => Array.from(time.classList)));
+
+  const sourceKinds =
+    sourceTimeClasses
+      .map(times => times
+        .map(classes => classes
+          .find(cl => cl.endsWith('-contribution-date'))
+          .slice(0, -'-contribution-date'.length)));
+
+  const sourceGroups =
+    sourceElements
+      .map(el =>
+        Array.from(el.querySelectorAll('.contribution-group'))
+          .map(data => data.value));
+
+  const sourceDates =
+    sourceTimeElements
+      .map(times => times
+        .map(time => new Date(time.getAttribute('datetime'))));
+
+  return stitchArrays({
+    element: sourceElements,
+    kinds: sourceKinds,
+    groups: sourceGroups,
+    dates: sourceDates,
+  });
+}
+
+function getArtistRollingWindowTimeframeInfo() {
+  const contributionKind =
+    info.contributionKind.value;
+
+  const contributionGroup =
+    info.contributionGroup.value;
+
+  const sourceInfo =
+    getArtistRollingWindowSourceInfo();
+
+  const principalSources =
+    sourceInfo.filter(source => {
+      if (!source.kinds.includes(contributionKind)) {
+        return false;
+      }
+
+      if (contributionGroup !== '-') {
+        if (!source.groups.includes(contributionGroup)) {
+          return false;
+        }
+      }
+
+      return true;
+    });
+
+  const principalSourceDates =
+    principalSources.map(source =>
+      stitchArrays({
+        kind: source.kinds,
+        date: source.dates,
+      }).find(({kind}) => kind === contributionKind)
+        .date);
+
+  const getPeekDate = inputDate => {
+    const date = new Date(inputDate);
+
+    date.setMonth(
+      (date.getMonth()
+     - parseInt(info.timeframeMonthsBefore.value)
+     - parseInt(info.timeframeMonthsPeek.value)));
+
+    return date;
+  };
+
+  const getEntranceDate = inputDate => {
+    const date = new Date(inputDate);
+
+    date.setMonth(
+      (date.getMonth()
+     - parseInt(info.timeframeMonthsBefore.value)));
+
+    return date;
+  };
+
+  const getExitDate = inputDate => {
+    const date = new Date(inputDate);
+
+    date.setMonth(
+      (date.getMonth()
+     + parseInt(info.timeframeMonthsAfter.value)));
+
+    return date;
+  };
+
+  const principalSourceIndices =
+    Array.from({length: principalSources.length}, (_, i) => i);
+
+  const timeframeSourceChunks =
+    chunkByConditions(principalSourceIndices, [
+      (previous, next) =>
+        +principalSourceDates[previous] !==
+        +principalSourceDates[next],
+    ]);
+
+  const timeframeSourceChunkDates =
+    timeframeSourceChunks
+      .map(indices => indices[0])
+      .map(index => principalSourceDates[index]);
+
+  const timeframeSourceChunkPeekDates =
+    timeframeSourceChunkDates
+      .map(getPeekDate);
+
+  const timeframeSourceChunkEntranceDates =
+    timeframeSourceChunkDates
+      .map(getEntranceDate);
+
+  const timeframeSourceChunkExitDates =
+    timeframeSourceChunkDates
+      .map(getExitDate);
+
+  const peekDateInfo =
+    stitchArrays({
+      peek: timeframeSourceChunkPeekDates,
+      indices: timeframeSourceChunks,
+    }).map(({peek, indices}) => ({
+        date: peek,
+        peek: indices,
+      }));
+
+  const entranceDateInfo =
+    stitchArrays({
+      entrance: timeframeSourceChunkEntranceDates,
+      indices: timeframeSourceChunks,
+    }).map(({entrance, indices}) => ({
+        date: entrance,
+        entrance: indices,
+      }));
+
+  const exitDateInfo =
+    stitchArrays({
+      exit: timeframeSourceChunkExitDates,
+      indices: timeframeSourceChunks,
+    }).map(({exit, indices}) => ({
+        date: exit,
+        exit: indices,
+      }));
+
+  const dateInfoChunks =
+    chunkByProperties(
+      sortByDate([
+        ...peekDateInfo,
+        ...entranceDateInfo,
+        ...exitDateInfo,
+      ]),
+      ['date']);
+
+  const dateInfo =
+    dateInfoChunks
+      .map(({chunk}) =>
+        Object.assign({
+          peek: null,
+          entrance: null,
+          exit: null,
+        }, ...chunk));
+
+  const timeframeInfo =
+    dateInfo.reduce(
+      (accumulator, {date, peek, entrance, exit}) => {
+        const previous = accumulator.at(-1);
+
+        // These mustn't be mutated!
+        let peeking = (previous ? previous.peeking : []);
+        let tracking = (previous ? previous.tracking : []);
+
+        if (peek) {
+          peeking =
+            peeking.concat(peek);
+        }
+
+        if (entrance) {
+          peeking =
+            peeking.filter(index => !entrance.includes(index));
+
+          tracking =
+            tracking.concat(entrance);
+        }
+
+        if (exit) {
+          tracking =
+            tracking.filter(index => !exit.includes(index));
+        }
+
+        return [...accumulator, {
+          date,
+          peeking,
+          tracking,
+          peek,
+          entrance,
+          exit,
+        }];
+      },
+      []);
+
+  const indicesToSources = indices =>
+    (indices
+      ? indices.map(index => principalSources[index])
+      : null);
+
+  const finalizedTimeframeInfo =
+    timeframeInfo.map(({
+      date,
+      peeking,
+      tracking,
+      peek,
+      entrance,
+      exit,
+    }) => ({
+      date,
+      peeking: indicesToSources(peeking),
+      tracking: indicesToSources(tracking),
+      peek: indicesToSources(peek),
+      entrance: indicesToSources(entrance),
+      exit: indicesToSources(exit),
+    }));
+
+  return finalizedTimeframeInfo;
+}
+
+function updateArtistRollingWindow() {
+  const timeframeInfo =
+    getArtistRollingWindowTimeframeInfo();
+
+  if (empty(timeframeInfo)) {
+    cssProp(info.timeframeSelectionControl, 'display', 'none');
+    cssProp(info.timeframeSelectionSomeLine, 'display', 'none');
+    cssProp(info.timeframeSelectionNoneLine, 'display', null);
+
+    updateRollingWindowTimeframeSelection(timeframeInfo);
+
+    return;
+  }
+
+  cssProp(info.timeframeSelectionControl, 'display', null);
+  cssProp(info.timeframeSelectionSomeLine, 'display', null);
+  cssProp(info.timeframeSelectionNoneLine, 'display', 'none');
+
+  // The last timeframe is just the exit of the final tracked sources,
+  // so we aren't going to display a menu option for it, and will just use
+  // it as the end of the final option's date range.
+
+  const usedTimeframes = timeframeInfo.slice(0, -1);
+  const firstTimeframe = timeframeInfo.at(0);
+  const lastTimeframe = timeframeInfo.at(-1);
+
+  const sourceCount =
+    timeframeInfo
+      .flatMap(({entrance}) => entrance ?? [])
+      .length;
+
+  const timeframeCount =
+    usedTimeframes.length;
+
+  info.timeframeSelectionContributionCount.innerText = sourceCount;
+  info.timeframeSelectionTimeframeCount.innerText = timeframeCount;
+
+  const firstDate = firstTimeframe.date;
+  const lastDate = lastTimeframe.date;
+
+  info.timeframeSelectionFirstDate.innerText = formatDate(firstDate);
+  info.timeframeSelectionLastDate.innerText = formatDate(lastDate);
+
+  while (info.timeframeSelectionMenu.firstChild) {
+    info.timeframeSelectionMenu.firstChild.remove();
+  }
+
+  for (const [index, timeframe] of usedTimeframes.entries()) {
+    const nextTimeframe = timeframeInfo[index + 1];
+
+    const option = document.createElement('option');
+
+    option.appendChild(document.createTextNode(
+      `${formatDate(timeframe.date)} – ${formatDate(nextTimeframe.date)}`));
+
+    info.timeframeSelectionMenu.appendChild(option);
+  }
+
+  updateRollingWindowTimeframeSelection(timeframeInfo);
+}
+
+function updateRollingWindowTimeframeSelection(timeframeInfo) {
+  timeframeInfo ??= getArtistRollingWindowTimeframeInfo();
+
+  updateRollingWindowTimeframeSelectionControls(timeframeInfo);
+  updateRollingWindowTimeframeSelectionSources(timeframeInfo);
+}
+
+function updateRollingWindowTimeframeSelectionControls(timeframeInfo) {
+  const currentIndex =
+    info.timeframeSelectionMenu.selectedIndex;
+
+  const atFirstTimeframe =
+    currentIndex === 0;
+
+  // The last actual timeframe is empty and not displayed as a menu option.
+  const atLastTimeframe =
+    currentIndex === timeframeInfo.length - 2;
+
+  if (atFirstTimeframe) {
+    info.timeframeSelectionPrevious.removeAttribute('href');
+  } else {
+    info.timeframeSelectionPrevious.setAttribute('href', '#');
+  }
+
+  if (atLastTimeframe) {
+    info.timeframeSelectionNext.removeAttribute('href');
+  } else {
+    info.timeframeSelectionNext.setAttribute('href', '#');
+  }
+}
+
+function updateRollingWindowTimeframeSelectionSources(timeframeInfo) {
+  const currentIndex =
+    info.timeframeSelectionMenu.selectedIndex;
+
+  const contributionGroup =
+    info.contributionGroup.value;
+
+  cssProp(info.sourceGrid, 'display', null);
+
+  const {peeking: peekingSources, tracking: trackingSources} =
+    (empty(timeframeInfo)
+      ? {peeking: [], tracking: []}
+      : timeframeInfo[currentIndex]);
+
+  const peekingElements =
+    peekingSources.map(source => source.element);
+
+  const trackingElements =
+    trackingSources.map(source => source.element);
+
+  const showingElements =
+    [...trackingElements, ...peekingElements];
+
+  const hidingElements =
+    Array.from(info.sources)
+      .filter(element =>
+        !peekingElements.includes(element) &&
+        !trackingElements.includes(element));
+
+  for (const element of peekingElements) {
+    element.classList.add('peeking');
+    element.classList.remove('tracking');
+  }
+
+  for (const element of trackingElements) {
+    element.classList.remove('peeking');
+    element.classList.add('tracking');
+  }
+
+  for (const element of hidingElements) {
+    element.classList.remove('peeking');
+    element.classList.remove('tracking');
+    cssProp(element, 'display', 'none');
+  }
+
+  for (const element of showingElements) {
+    cssProp(element, 'display', null);
+
+    for (const time of element.getElementsByTagName('time')) {
+      for (const className of time.classList) {
+        if (!className.endsWith('-contribution-date')) continue;
+
+        const kind = className.slice(0, -'-contribution-date'.length);
+        if (kind === info.contributionKind.value) {
+          cssProp(time, 'display', null);
+        } else {
+          cssProp(time, 'display', 'none');
+        }
+      }
+    }
+
+    for (const data of element.getElementsByClassName('contribution-group')) {
+      if (contributionGroup === '-' || data.value !== contributionGroup) {
+        cssProp(data, 'display', null);
+      } else {
+        cssProp(data, 'display', 'none');
+      }
+    }
+  }
+
+  if (empty(peekingElements) && empty(trackingElements)) {
+    cssProp(info.timeframeEmptyLine, 'display', null);
+  } else {
+    cssProp(info.timeframeEmptyLine, 'display', 'none');
+  }
+}
diff --git a/src/static/js/client/css-compatibility-assistant.js b/src/static/js/client/css-compatibility-assistant.js
index 6e7b15b5..37b0645a 100644
--- a/src/static/js/client/css-compatibility-assistant.js
+++ b/src/static/js/client/css-compatibility-assistant.js
@@ -1,22 +1,28 @@
-/* eslint-env browser */
+import {stitchArrays} from '../../shared-util/sugar.js';
 
 export const info = {
   id: 'cssCompatibilityAssistantInfo',
 
-  coverArtContainer: null,
-  coverArtImageDetails: null,
+  coverArtworks: null,
+  coverArtworkImageDetails: null,
 };
 
 export function getPageReferences() {
-  info.coverArtContainer =
-    document.getElementById('cover-art-container');
+  info.coverArtworks =
+    Array.from(document.querySelectorAll('.cover-artwork'));
 
-  info.coverArtImageDetails =
-    info.coverArtContainer?.querySelector('.image-details');
+  info.coverArtworkImageDetails =
+    info.coverArtworks
+      .map(artwork => artwork.querySelector('.image-details'));
 }
 
 export function mutatePageContent() {
-  if (info.coverArtImageDetails) {
-    info.coverArtContainer.classList.add('has-image-details');
-  }
+  stitchArrays({
+    coverArtwork: info.coverArtworks,
+    imageDetails: info.coverArtworkImageDetails,
+  }).forEach(({coverArtwork, imageDetails}) => {
+      if (imageDetails) {
+        coverArtwork.classList.add('has-image-details');
+      }
+    });
 }
diff --git a/src/static/js/client/datetimestamp-tooltip.js b/src/static/js/client/datetimestamp-tooltip.js
index 46d1cd5b..00530484 100644
--- a/src/static/js/client/datetimestamp-tooltip.js
+++ b/src/static/js/client/datetimestamp-tooltip.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 // TODO: Maybe datetimestamps can just be incorporated into text-with-tooltip?
 
 import {stitchArrays} from '../../shared-util/sugar.js';
diff --git a/src/static/js/client/dragged-link.js b/src/static/js/client/dragged-link.js
index 56021e7f..3a4ee314 100644
--- a/src/static/js/client/dragged-link.js
+++ b/src/static/js/client/dragged-link.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 export const info = {
   id: `draggedLinkInfo`,
 
diff --git a/src/static/js/client/expandable-grid-section.js b/src/static/js/client/expandable-grid-section.js
new file mode 100644
index 00000000..4d6e0058
--- /dev/null
+++ b/src/static/js/client/expandable-grid-section.js
@@ -0,0 +1,83 @@
+import {cssProp} from '../client-util.js';
+
+import {stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'expandableGallerySectionInfo',
+
+  items: null,
+  toggles: null,
+  expandCues: null,
+  collapseCues: null,
+};
+
+export function getPageReferences() {
+  const expandos =
+    Array.from(document.querySelectorAll('.grid-expando'));
+
+  const grids =
+    expandos
+      .map(expando => expando.closest('.grid-listing'));
+
+  info.items =
+    grids
+      .map(grid => grid.querySelectorAll('.grid-item'))
+      .map(items => Array.from(items));
+
+  info.toggles =
+    expandos
+      .map(expando => expando.querySelector('.grid-expando-toggle'));
+
+  info.expandCues =
+    info.toggles
+      .map(toggle => toggle.querySelector('.grid-expand-cue'));
+
+  info.collapseCues =
+    info.toggles
+      .map(toggle => toggle.querySelector('.grid-collapse-cue'));
+}
+
+export function addPageListeners() {
+  stitchArrays({
+    items: info.items,
+    toggle: info.toggles,
+    expandCue: info.expandCues,
+    collapseCue: info.collapseCues,
+  }).forEach(({
+      items,
+      toggle,
+      expandCue,
+      collapseCue,
+    }) => {
+      toggle.addEventListener('click', domEvent => {
+        domEvent.preventDefault();
+
+        const collapsed =
+          items.some(item =>
+            item.classList.contains('hidden-by-expandable-cut'));
+
+        for (const item of items) {
+          if (
+            !item.classList.contains('hidden-by-expandable-cut') &&
+            !item.classList.contains('shown-by-expandable-cut')
+          ) continue;
+
+          if (collapsed) {
+            item.classList.remove('hidden-by-expandable-cut');
+            item.classList.add('shown-by-expandable-cut');
+          } else {
+            item.classList.add('hidden-by-expandable-cut');
+            item.classList.remove('shown-by-expandable-cut');
+          }
+        }
+
+        if (collapsed) {
+          cssProp(expandCue, 'display', 'none');
+          cssProp(collapseCue, 'display', null);
+        } else {
+          cssProp(expandCue, 'display', null);
+          cssProp(collapseCue, 'display', 'none');
+        }
+      });
+    });
+}
diff --git a/src/static/js/client/gallery-style-selector.js b/src/static/js/client/gallery-style-selector.js
new file mode 100644
index 00000000..44f98ac3
--- /dev/null
+++ b/src/static/js/client/gallery-style-selector.js
@@ -0,0 +1,121 @@
+import {cssProp} from '../client-util.js';
+
+import {stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'galleryStyleSelectorInfo',
+
+  selectors: null,
+  sections: null,
+
+  selectorStyleInputs: null,
+  selectorStyleInputStyles: null,
+
+  selectorReleaseItems: null,
+  selectorReleaseItemStyles: null,
+
+  selectorCountAll: null,
+  selectorCountFiltered: null,
+  selectorCountFilteredCount: null,
+  selectorCountNone: null,
+};
+
+export function getPageReferences() {
+  info.selectors =
+    Array.from(document.querySelectorAll('.gallery-style-selector'));
+
+  info.sections =
+    info.selectors
+      .map(selector => selector.closest('section'));
+
+  info.selectorStyleInputs =
+    info.selectors
+      .map(selector => selector.querySelectorAll('.styles input'))
+      .map(inputs => Array.from(inputs));
+
+  info.selectorStyleInputStyles =
+    info.selectorStyleInputs
+      .map(inputs => inputs
+        .map(input => input.closest('label').dataset.style));
+
+  info.selectorReleaseItems =
+    info.sections
+      .map(section => section.querySelectorAll('.grid-item'))
+      .map(items => Array.from(items));
+
+  info.selectorReleaseItemStyles =
+    info.selectorReleaseItems
+      .map(items => items
+        .map(item => item.dataset.style));
+
+  info.selectorCountAll =
+    info.selectors
+      .map(selector => selector.querySelector('.count.all'));
+
+  info.selectorCountFiltered =
+    info.selectors
+      .map(selector => selector.querySelector('.count.filtered'));
+
+  info.selectorCountFilteredCount =
+    info.selectorCountFiltered
+      .map(selector => selector.querySelector('span'));
+
+  info.selectorCountNone =
+    info.selectors
+      .map(selector => selector.querySelector('.count.none'));
+}
+
+export function addPageListeners() {
+  for (const index of info.selectors.keys()) {
+    for (const input of info.selectorStyleInputs[index]) {
+      input.addEventListener('input', () => updateVisibleReleases(index));
+    }
+  }
+}
+
+function updateVisibleReleases(index) {
+  const inputs = info.selectorStyleInputs[index];
+  const inputStyles = info.selectorStyleInputStyles[index];
+
+  const selectedStyles =
+    stitchArrays({input: inputs, style: inputStyles})
+      .filter(({input}) => input.checked)
+      .map(({style}) => style);
+
+  const releases = info.selectorReleaseItems[index];
+  const releaseStyles = info.selectorReleaseItemStyles[index];
+
+  let visible = 0;
+
+  stitchArrays({
+    release: releases,
+    style: releaseStyles,
+  }).forEach(({release, style}) => {
+      if (selectedStyles.includes(style)) {
+        release.classList.remove('hidden-by-style-mismatch');
+        visible++;
+      } else {
+        release.classList.add('hidden-by-style-mismatch');
+      }
+    });
+
+  const countAll = info.selectorCountAll[index];
+  const countFiltered = info.selectorCountFiltered[index];
+  const countFilteredCount = info.selectorCountFilteredCount[index];
+  const countNone = info.selectorCountNone[index];
+
+  if (visible === releases.length) {
+    cssProp(countAll, 'display', null);
+    cssProp(countFiltered, 'display', 'none');
+    cssProp(countNone, 'display', 'none');
+  } else if (visible === 0) {
+    cssProp(countAll, 'display', 'none');
+    cssProp(countFiltered, 'display', 'none');
+    cssProp(countNone, 'display', null);
+  } else {
+    cssProp(countAll, 'display', 'none');
+    cssProp(countFiltered, 'display', null);
+    cssProp(countNone, 'display', 'none');
+    countFilteredCount.innerHTML = visible;
+  }
+}
diff --git a/src/static/js/client/hash-link.js b/src/static/js/client/hash-link.js
index 27035e29..02ffdc23 100644
--- a/src/static/js/client/hash-link.js
+++ b/src/static/js/client/hash-link.js
@@ -1,6 +1,5 @@
-/* eslint-env browser */
-
-import {filterMultipleArrays, stitchArrays} from '../../shared-util/sugar.js';
+import {filterMultipleArrays, stitchArrays, unique}
+  from '../../shared-util/sugar.js';
 
 import {dispatchInternalEvent} from '../client-util.js';
 
@@ -11,6 +10,9 @@ export const info = {
   hrefs: null,
   targets: null,
 
+  details: null,
+  detailsIDs: null,
+
   state: {
     highlightedTarget: null,
     scrollingAfterClick: false,
@@ -40,6 +42,19 @@ export function getPageReferences() {
     info.hrefs,
     info.targets,
     (_link, _href, target) => target);
+
+  info.details =
+    unique([
+      ...document.querySelectorAll('details[id]'),
+      ...
+        Array.from(document.querySelectorAll('summary[id]'))
+          .map(summary => summary.closest('details')),
+    ]);
+
+  info.detailsIDs =
+    info.details.map(details =>
+      details.id ||
+      details.querySelector('summary').id);
 }
 
 function processScrollingAfterHashLinkClicked() {
@@ -60,6 +75,15 @@ function processScrollingAfterHashLinkClicked() {
   }, 200);
 }
 
+export function mutatePageContent() {
+  if (location.hash.length > 1) {
+    const target = document.getElementById(location.hash.slice(1));
+    if (target) {
+      expandDetails(target);
+    }
+  }
+}
+
 export function addPageListeners() {
   // Instead of defining a scroll offset (to account for the sticky heading)
   // in JavaScript, we interface with the CSS property 'scroll-margin-top'.
@@ -94,6 +118,8 @@ export function addPageListeners() {
         return;
       }
 
+      expandDetails(target);
+
       // Hide skipper box right away, so the layout is updated on time for the
       // math operations coming up next.
       const skipper = document.getElementById('skippers');
@@ -143,4 +169,32 @@ export function addPageListeners() {
       state.highlightedTarget = null;
     });
   }
+
+  stitchArrays({
+    details: info.details,
+    id: info.detailsIDs,
+  }).forEach(({details, id}) => {
+      details.addEventListener('toggle', () => {
+        if (!details.open) {
+          detractHash(id);
+        }
+      });
+    });
+}
+
+function expandDetails(target) {
+  if (target.nodeName === 'SUMMARY') {
+    const details = target.closest('details');
+    if (details) {
+      details.open = true;
+    }
+  } else if (target.nodeName === 'DETAILS') {
+    target.open = true;
+  }
+}
+
+function detractHash(id) {
+  if (location.hash === '#' + id) {
+    history.pushState({}, undefined, location.href.replace(/#.*$/, ''));
+  }
 }
diff --git a/src/static/js/client/hoverable-tooltip.js b/src/static/js/client/hoverable-tooltip.js
index 484f2ab0..cdf37ee1 100644
--- a/src/static/js/client/hoverable-tooltip.js
+++ b/src/static/js/client/hoverable-tooltip.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {empty, filterMultipleArrays} from '../../shared-util/sugar.js';
 
 import {WikiRect} from '../rectangles.js';
@@ -118,17 +116,17 @@ export function registerTooltipElement(tooltip) {
     handleTooltipMouseLeft(tooltip);
   });
 
-  tooltip.addEventListener('focusin', event => {
-    handleTooltipReceivedFocus(tooltip, event.relatedTarget);
+  tooltip.addEventListener('focusin', domEvent => {
+    handleTooltipReceivedFocus(tooltip, domEvent.relatedTarget);
   });
 
-  tooltip.addEventListener('focusout', event => {
+  tooltip.addEventListener('focusout', domEvent => {
     // This event gets activated for tabbing *between* links inside the
     // tooltip, which is no good and certainly doesn't represent the focus
     // leaving the tooltip.
-    if (currentlyShownTooltipHasFocus(event.relatedTarget)) return;
+    if (currentlyShownTooltipHasFocus(domEvent.relatedTarget)) return;
 
-    handleTooltipLostFocus(tooltip, event.relatedTarget);
+    handleTooltipLostFocus(tooltip, domEvent.relatedTarget);
   });
 }
 
@@ -158,20 +156,20 @@ export function registerTooltipHoverableElement(hoverable, tooltip) {
     handleTooltipHoverableMouseLeft(hoverable);
   });
 
-  hoverable.addEventListener('focusin', event => {
-    handleTooltipHoverableReceivedFocus(hoverable, event);
+  hoverable.addEventListener('focusin', domEvent => {
+    handleTooltipHoverableReceivedFocus(hoverable, domEvent);
   });
 
-  hoverable.addEventListener('focusout', event => {
-    handleTooltipHoverableLostFocus(hoverable, event);
+  hoverable.addEventListener('focusout', domEvent => {
+    handleTooltipHoverableLostFocus(hoverable, domEvent);
   });
 
-  hoverable.addEventListener('touchend', event => {
-    handleTooltipHoverableTouchEnded(hoverable, event);
+  hoverable.addEventListener('touchend', domEvent => {
+    handleTooltipHoverableTouchEnded(hoverable, domEvent);
   });
 
-  hoverable.addEventListener('click', event => {
-    handleTooltipHoverableClicked(hoverable, event);
+  hoverable.addEventListener('click', domEvent => {
+    handleTooltipHoverableClicked(hoverable, domEvent);
   });
 }
 
@@ -416,7 +414,7 @@ function handleTooltipHoverableTouchEnded(hoverable, domEvent) {
     }, 1200);
 }
 
-function handleTooltipHoverableClicked(hoverable) {
+function handleTooltipHoverableClicked(hoverable, domEvent) {
   const {state} = info;
 
   // Don't navigate away from the page if the this hoverable was recently
@@ -426,7 +424,7 @@ function handleTooltipHoverableClicked(hoverable) {
     state.currentlyActiveHoverable === hoverable &&
     state.hoverableWasRecentlyTouched
   ) {
-    event.preventDefault();
+    domEvent.preventDefault();
   }
 }
 
@@ -576,6 +574,17 @@ export function showTooltipFromHoverable(hoverable) {
 
   hoverable.classList.add('has-visible-tooltip');
 
+  const isolator =
+    hoverable.closest('.isolate-tooltip-z-indexing > *');
+
+  if (isolator) {
+    for (const child of isolator.parentElement.children) {
+      cssProp(child, 'z-index', null);
+    }
+
+    cssProp(isolator, 'z-index', '1');
+  }
+
   positionTooltipFromHoverableWithBrains(hoverable);
 
   cssProp(tooltip, 'display', 'block');
@@ -667,12 +676,12 @@ export function positionTooltipFromHoverableWithBrains(hoverable) {
 
     for (let i = 0; i < numBaselineRects; i++) {
       for (const [dir1, dir2] of [
+        ['down', 'right'],
+        ['down', 'left'],
         ['right', 'down'],
         ['left', 'down'],
         ['right', 'up'],
         ['left', 'up'],
-        ['down', 'right'],
-        ['down', 'left'],
         ['up', 'right'],
         ['up', 'left'],
       ]) {
@@ -705,6 +714,8 @@ export function positionTooltip(tooltip, x, y) {
   cssProp(tooltip, {
     left: `${x - tooltipRect.x}px`,
     top: `${y - tooltipRect.y}px`,
+    right: 'unset',
+    bottom: 'unset',
   });
 }
 
@@ -712,6 +723,8 @@ export function resetDynamicTooltipPositioning(tooltip) {
   cssProp(tooltip, {
     left: null,
     top: null,
+    right: null,
+    bottom: null,
   });
 }
 
@@ -995,6 +1008,14 @@ export function getTooltipBaselineOpportunityAreas(tooltip) {
   return results;
 }
 
+export function mutatePageContent() {
+  for (const isolatorRoot of document.querySelectorAll('.isolate-tooltip-z-indexing')) {
+    if (isolatorRoot.firstElementChild) {
+      cssProp(isolatorRoot.firstElementChild, 'z-index', '1');
+    }
+  }
+}
+
 export function addPageListeners() {
   const {state} = info;
 
diff --git a/src/static/js/client/image-overlay.js b/src/static/js/client/image-overlay.js
index b51d57a4..0595bff7 100644
--- a/src/static/js/client/image-overlay.js
+++ b/src/static/js/client/image-overlay.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {getColors} from '../../shared-util/colors.js';
 
 import {cssProp} from '../client-util.js';
@@ -66,8 +64,13 @@ export function getPageReferences() {
   info.fileSizeWarning =
     document.getElementById('image-overlay-file-size-warning');
 
+  const linkQuery = [
+    '.image-link',
+    '.image-media-link',
+  ].join(', ');
+
   info.links =
-    Array.from(document.querySelectorAll('.image-link'))
+    Array.from(document.querySelectorAll(linkQuery))
       .filter(link => !link.closest('.no-image-preview'));
 }
 
@@ -88,10 +91,13 @@ function handleContainerClicked(evt) {
     return;
   }
 
-  // If you clicked anything close to or beneath the action bar, don't hide
-  // the image overlay.
+  // If you clicked anything near the action bar, don't hide the
+  // image overlay.
   const rect = info.actionContainer.getBoundingClientRect();
-  if (evt.clientY >= rect.top - 40) {
+  if (
+    evt.clientY >= rect.top - 40 && evt.clientY <= rect.bottom + 40 &&
+    evt.clientX >= rect.left + 20 && evt.clientX <= rect.right - 20
+  ) {
     return;
   }
 
@@ -141,13 +147,24 @@ function getImageLinkDetails(imageLink) {
       a.href,
 
     embeddedSrc:
-      img.src,
+      img?.src ||
+      img?.currentSrc ||
+      a.dataset.embedSrc,
 
     originalFileSize:
-      img.dataset.originalSize,
+      img?.dataset.originalSize ??
+      a.dataset.originalSize ??
+      null,
 
     availableThumbList:
-      img.dataset.thumbs,
+      img?.dataset.thumbs ??
+      a.dataset.thumbs ??
+      null,
+
+    dimensions:
+      img?.dataset.dimensions?.split('x') ??
+      a.dataset.dimensions?.split('x') ??
+      null,
 
     color:
       cssProp(imageLink, '--primary-color'),
@@ -170,7 +187,7 @@ function getImageSources(details) {
     };
   } else {
     return {
-      mainSrc: originalSrc,
+      mainSrc: details.originalSrc,
       thumbSrc: null,
       mainThumb: '',
       thumbThumb: '',
@@ -211,15 +228,31 @@ async function loadOverlayImage(details) {
   if (details.thumbSrc) {
     info.thumbImage.src = details.thumbSrc;
     info.thumbImage.style.display = null;
+    info.container.classList.remove('no-thumb');
   } else {
     info.thumbImage.src = '';
     info.thumbImage.style.display = 'none';
+    info.container.classList.add('no-thumb');
   }
 
   // Show the thumbnail size on each <img> element's data attributes.
   // Y'know, just for debugging convenience.
   info.mainImage.dataset.displayingThumb = details.mainThumb;
-  info.thumbImage.dataset.displayingThumb = details.thumbThubm;
+  info.thumbImage.dataset.displayingThumb = details.thumbThumb;
+
+  if (details.dimensions) {
+    info.mainImage.width = details.dimensions[0];
+    info.mainImage.height = details.dimensions[1];
+    info.thumbImage.width = details.dimensions[0];
+    info.thumbImage.height = details.dimensions[1];
+    cssProp(info.thumbImage, 'aspect-ratio', details.dimensions.join('/'));
+  } else {
+    info.mainImage.removeAttribute('width');
+    info.mainImage.removeAttribute('height');
+    info.thumbImage.removeAttribute('width');
+    info.thumbImage.removeAttribute('height');
+    cssProp(info.thumbImage, 'aspect-ratio', null);
+  }
 
   info.mainImage.addEventListener('load', handleMainImageLoaded);
   info.mainImage.addEventListener('error', handleMainImageErrored);
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index 52d2afd6..9296dff1 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -1,19 +1,24 @@
-/* eslint-env browser */
-
 import '../group-contributions-table.js';
 
 import * as additionalNamesBoxModule from './additional-names-box.js';
 import * as albumCommentarySidebarModule from './album-commentary-sidebar.js';
+import * as artTagGalleryFilterModule from './art-tag-gallery-filter.js';
+import * as artTagNetworkModule from './art-tag-network.js';
 import * as artistExternalLinkTooltipModule from './artist-external-link-tooltip.js';
+import * as artistRollingWindowModule from './artist-rolling-window.js';
 import * as cssCompatibilityAssistantModule from './css-compatibility-assistant.js';
 import * as datetimestampTooltipModule from './datetimestamp-tooltip.js';
 import * as draggedLinkModule from './dragged-link.js';
+import * as expandableGridSectionModule from './expandable-grid-section.js';
+import * as galleryStyleSelectorModule from './gallery-style-selector.js';
 import * as hashLinkModule from './hash-link.js';
 import * as hoverableTooltipModule from './hoverable-tooltip.js';
 import * as imageOverlayModule from './image-overlay.js';
 import * as intrapageDotSwitcherModule from './intrapage-dot-switcher.js';
 import * as liveMousePositionModule from './live-mouse-position.js';
+import * as memorableDetailsModule from './memorable-details.js';
 import * as quickDescriptionModule from './quick-description.js';
+import * as revealAllGridControlModule from './reveal-all-grid-control.js';
 import * as scriptedLinkModule from './scripted-link.js';
 import * as sidebarSearchModule from './sidebar-search.js';
 import * as stickyHeadingModule from './sticky-heading.js';
@@ -24,16 +29,23 @@ import * as wikiSearchModule from './wiki-search.js';
 export const modules = [
   additionalNamesBoxModule,
   albumCommentarySidebarModule,
+  artTagGalleryFilterModule,
+  artTagNetworkModule,
   artistExternalLinkTooltipModule,
+  artistRollingWindowModule,
   cssCompatibilityAssistantModule,
   datetimestampTooltipModule,
   draggedLinkModule,
+  expandableGridSectionModule,
+  galleryStyleSelectorModule,
   hashLinkModule,
   hoverableTooltipModule,
   imageOverlayModule,
   intrapageDotSwitcherModule,
   liveMousePositionModule,
+  memorableDetailsModule,
   quickDescriptionModule,
+  revealAllGridControlModule,
   scriptedLinkModule,
   sidebarSearchModule,
   stickyHeadingModule,
@@ -44,7 +56,11 @@ export const modules = [
 
 const clientInfo = window.hsmusicClientInfo = Object.create(null);
 
-const clientSteps = {
+// These steps are always run in the listed order, on page load.
+// So for example, all modules' getPageReferences steps are evaluated, then
+// all modules' addInternalListeners steps are evaluated, and so on.
+const setupSteps = {
+  bindSessionStorage: [],
   getPageReferences: [],
   addInternalListeners: [],
   mutatePageContent: [],
@@ -52,6 +68,18 @@ const clientSteps = {
   addPageListeners: [],
 };
 
+// These steps are run only on certain triggers. Those are global events,
+// so all modules (which specify that step) respond in sequence.
+const situationalSteps = {
+  /* There's none yet... sorry... */
+};
+
+const stepInfoSymbol = Symbol();
+
+const boundSessionStorage =
+  window.hsmusicBoundSessionStorage =
+  Object.create(null);
+
 for (const module of modules) {
   const {info} = module;
 
@@ -106,7 +134,7 @@ for (const module of modules) {
             break;
 
           case 'boolean':
-            formatRead = Boolean;
+            formatRead = value => value === 'true' ? true : false;
             formatWrite = String;
             break;
 
@@ -136,12 +164,47 @@ for (const module of modules) {
 
       const storageKey = `hsmusic.${infoKey}.${key}`;
 
+      // There are two storage systems besides actual session storage in play.
+      //
+      // "Fallback" is for if session storage is not available, which may
+      // suddenly become the case, i.e. access is temporarily revoked or fails.
+      // The fallback value is controlled completely internally i.e. in this
+      // infrastructure, in this lexical scope.
+      //
+      // "Bound" is for if the value kept in session storage was saved to
+      // the page when the page was initially loaded, rather than a living
+      // window on session storage (which may be affected by pages later in
+      // the history stack). Whether or not bound storage is in effect is
+      // controlled at page load (of course), by each module's own logic.
+      //
+      // Asterisk: Bound storage can't work miracles and if the page is
+      // actually deloaded with its JavaScript state discarded, the bound
+      // values are lost, even if the browser recreates on-page form state.
+
       let fallbackValue = defaultValue;
+      let boundValue = undefined;
+
+      const updateBoundValue = (givenValue = undefined) => {
+        if (givenValue) {
+          if (
+            infoKey in boundSessionStorage &&
+            key in boundSessionStorage[infoKey]
+          ) {
+            boundSessionStorage[infoKey][key] = givenValue;
+          }
+        } else {
+          boundValue = boundSessionStorage[infoKey]?.[key];
+        }
+      };
 
       Object.defineProperty(info.session, key, {
         get: () => {
+          updateBoundValue();
+
           let value;
-          try {
+          if (boundValue !== undefined) {
+            value = boundValue ?? defaultValue;
+          } else try {
             value = sessionStorage.getItem(storageKey) ?? defaultValue;
           } catch (error) {
             if (error instanceof DOMException) {
@@ -177,21 +240,23 @@ for (const module of modules) {
             return;
           }
 
-          let operation;
+          let sessionOperation;
           if (value === '') {
             fallbackValue = null;
-            operation = () => {
+            updateBoundValue(null);
+            sessionOperation = () => {
               sessionStorage.removeItem(storageKey);
             };
           } else {
             fallbackValue = value;
-            operation = () => {
+            updateBoundValue(value);
+            sessionOperation = () => {
               sessionStorage.setItem(storageKey, value);
             };
           }
 
           try {
-            operation();
+            sessionOperation();
           } catch (error) {
             if (!(error instanceof DOMException)) {
               throw error;
@@ -204,28 +269,72 @@ for (const module of modules) {
     Object.preventExtensions(info.session);
   }
 
-  for (const key of Object.keys(clientSteps)) {
-    if (Object.hasOwn(module, key)) {
-      const fn = module[key];
+  for (const stepsObject of [setupSteps, situationalSteps]) {
+    for (const key of Object.keys(stepsObject)) {
+      if (Object.hasOwn(module, key)) {
+        const fn = module[key];
 
-      Object.defineProperty(fn, 'name', {
-        value: `${infoKey}/${fn.name}`,
-      });
+        fn[stepInfoSymbol] = info;
+
+        Object.defineProperty(fn, 'name', {
+          value: `${infoKey}/${fn.name}`,
+        });
 
-      clientSteps[key].push(fn);
+        stepsObject[key].push(fn);
+      }
     }
   }
 }
 
-for (const [key, steps] of Object.entries(clientSteps)) {
-  for (const step of steps) {
+function evaluateBindSessionStorageStep(bindSessionStorage) {
+  const {id: infoKey, session: moduleExposedSessionObject} =
+    bindSessionStorage[stepInfoSymbol];
+
+  const generator = bindSessionStorage();
+
+  let lastBoundValue;
+  while (true) {
+    const {value: key, done} = generator.next(lastBoundValue);
+    const storageKey = `hsmusic.${infoKey}.${key}`;
+
+    let value = undefined;
     try {
-      step();
+      value = sessionStorage.getItem(storageKey);
+    } catch (error) {
+      if (!(error instanceof DOMException)) {
+        throw error;
+      }
+    }
+
+    if (value === undefined) {
+      // This effectively gets the default value.
+      value = moduleExposedSessionObject[key];
+    }
+
+    boundSessionStorage[infoKey] ??= Object.create(null);
+    boundSessionStorage[infoKey][key] = value;
+
+    lastBoundValue = value;
+
+    if (done) break;
+  }
+}
+
+function evaluateStep(stepsObject, key) {
+  for (const step of stepsObject[key]) {
+    try {
+      if (key === 'bindSessionStorage') {
+        evaluateBindSessionStorageStep(step);
+      } else {
+        step();
+      }
     } catch (error) {
-      // TODO: Be smarter about not running later steps for the same module!
-      // Or maybe not, since an error is liable to cause explosions anyway.
       console.error(`During ${key}, failed to run ${step.name}`);
       console.error(error);
     }
   }
 }
+
+for (const key of Object.keys(setupSteps)) {
+  evaluateStep(setupSteps, key);
+}
diff --git a/src/static/js/client/intrapage-dot-switcher.js b/src/static/js/client/intrapage-dot-switcher.js
index d06bc5a6..b9a27a9b 100644
--- a/src/static/js/client/intrapage-dot-switcher.js
+++ b/src/static/js/client/intrapage-dot-switcher.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {stitchArrays} from '../../shared-util/sugar.js';
 
 import {cssProp} from '../client-util.js';
diff --git a/src/static/js/client/live-mouse-position.js b/src/static/js/client/live-mouse-position.js
index 36a28429..32fc5bf4 100644
--- a/src/static/js/client/live-mouse-position.js
+++ b/src/static/js/client/live-mouse-position.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 export const info = {
   id: 'liveMousePositionInfo',
 
diff --git a/src/static/js/client/memorable-details.js b/src/static/js/client/memorable-details.js
new file mode 100644
index 00000000..57d9fde8
--- /dev/null
+++ b/src/static/js/client/memorable-details.js
@@ -0,0 +1,62 @@
+import {stitchArrays} from '../../shared-util/sugar.js';
+
+export const info = {
+  id: 'memorableDetailsInfo',
+
+  details: null,
+  ids: null,
+
+  session: {
+    openDetails: {
+      type: 'json',
+      maxLength: settings => settings.maxOpenDetailsStorage,
+    },
+  },
+
+  settings: {
+    maxOpenDetailsStorage: 1000,
+  },
+};
+
+export function getPageReferences() {
+  info.details =
+    Array.from(document.querySelectorAll('details.memorable'));
+
+  info.ids =
+    info.details.map(details => details.getAttribute('data-memorable-id'));
+}
+
+export function mutatePageContent() {
+  stitchArrays({
+    details: info.details,
+    id: info.ids,
+  }).forEach(({details, id}) => {
+      if (info.session.openDetails?.includes(id)) {
+        details.open = true;
+      }
+    });
+}
+
+export function addPageListeners() {
+  for (const [index, details] of info.details.entries()) {
+    details.addEventListener('toggle', () => {
+      handleDetailsToggled(index);
+    });
+  }
+}
+
+function handleDetailsToggled(index) {
+  const details = info.details[index];
+  const id = info.ids[index];
+
+  if (details.open) {
+    if (info.session.openDetails) {
+      info.session.openDetails = [...info.session.openDetails, id];
+    } else {
+      info.session.openDetails = [id];
+    }
+  } else if (info.session.openDetails?.includes(id)) {
+    info.session.openDetails =
+      info.session.openDetails.filter(item => item !== id);
+  }
+}
diff --git a/src/static/js/client/quick-description.js b/src/static/js/client/quick-description.js
index cff82252..9117d48c 100644
--- a/src/static/js/client/quick-description.js
+++ b/src/static/js/client/quick-description.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {stitchArrays} from '../../shared-util/sugar.js';
 
 export const info = {
diff --git a/src/static/js/client/reveal-all-grid-control.js b/src/static/js/client/reveal-all-grid-control.js
new file mode 100644
index 00000000..0572a190
--- /dev/null
+++ b/src/static/js/client/reveal-all-grid-control.js
@@ -0,0 +1,70 @@
+import {cssProp} from '../client-util.js';
+
+export const info = {
+  id: 'revealAllGridControlInfo',
+
+  revealAllLinks: null,
+  revealables: null,
+
+  revealLabels: null,
+  concealLabels: null,
+};
+
+export function getPageReferences() {
+  info.revealAllLinks =
+    Array.from(document.querySelectorAll('.reveal-all a'));
+
+  info.revealables =
+    info.revealAllLinks
+      .map(link => link.closest('.grid-listing'))
+      .map(listing => listing.querySelectorAll('.reveal'));
+
+  info.revealLabels =
+    info.revealAllLinks
+      .map(link => link.querySelector('.reveal-label'));
+
+  info.concealLabels =
+    info.revealAllLinks
+      .map(link => link.querySelector('.conceal-label'));
+}
+
+export function addPageListeners() {
+  for (const [index, link] of info.revealAllLinks.entries()) {
+    link.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+      handleRevealAllLinkClicked(index);
+    });
+  }
+}
+
+export function addInternalListeners() {
+  // Don't even think about it. "Reveal all artworks" is a stable control,
+  // meaning it only changes because the user interacted with it directly.
+}
+
+function handleRevealAllLinkClicked(index) {
+  const revealables = info.revealables[index];
+  const revealLabel = info.revealLabels[index];
+  const concealLabel = info.concealLabels[index];
+
+  const shouldReveal =
+    (cssProp(revealLabel, 'display') === 'none'
+      ? false
+      : true);
+
+  for (const revealable of revealables) {
+    if (shouldReveal) {
+      revealable.classList.add('revealed');
+    } else {
+      revealable.classList.remove('revealed');
+    }
+  }
+
+  if (shouldReveal) {
+    cssProp(revealLabel, 'display', 'none');
+    cssProp(concealLabel, 'display', null);
+  } else {
+    cssProp(revealLabel, 'display', null);
+    cssProp(concealLabel, 'display', 'none');
+  }
+}
diff --git a/src/static/js/client/scripted-link.js b/src/static/js/client/scripted-link.js
index 8b8d8a13..badc6ccb 100644
--- a/src/static/js/client/scripted-link.js
+++ b/src/static/js/client/scripted-link.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {pick, stitchArrays} from '../../shared-util/sugar.js';
 
 import {
diff --git a/src/static/js/client/sidebar-search.js b/src/static/js/client/sidebar-search.js
index 9d2cae34..386bf477 100644
--- a/src/static/js/client/sidebar-search.js
+++ b/src/static/js/client/sidebar-search.js
@@ -1,10 +1,15 @@
-/* eslint-env browser */
-
 import {getColors} from '../../shared-util/colors.js';
-import {accumulateSum, empty} from '../../shared-util/sugar.js';
+
+import {
+  accumulateSum,
+  compareArrays,
+  empty,
+  unique,
+} from '../../shared-util/sugar.js';
 
 import {
   cssProp,
+  decodeEntities,
   openAlbum,
   openArtist,
   openArtTag,
@@ -33,6 +38,9 @@ export const info = {
   searchLabel: null,
   searchInput: null,
 
+  contextContainer: null,
+  contextBackLink: null,
+
   progressRule: null,
   progressContainer: null,
   progressLabel: null,
@@ -41,6 +49,14 @@ export const info = {
   failedRule: null,
   failedContainer: null,
 
+  filterContainer: null,
+  albumFilterLink: null,
+  artistFilterLink: null,
+  flashFilterLink: null,
+  groupFilterLink: null,
+  tagFilterLink: null,
+  trackFilterLink: null,
+
   resultsRule: null,
   resultsContainer: null,
   results: null,
@@ -49,6 +65,8 @@ export const info = {
   endSearchLine: null,
   endSearchLink: null,
 
+  standbyInputPlaceholder: null,
+
   preparingString: null,
   loadingDataString: null,
   searchingString: null,
@@ -58,22 +76,47 @@ export const info = {
   currentResultString: null,
   endSearchString: null,
 
+  backString: null,
+
   albumResultKindString: null,
   artistResultKindString: null,
+  flashResultKindString: null,
   groupResultKindString: null,
+  singleResultKindString: null,
   tagResultKindString: null,
 
+  groupResultDisambiguatorString: null,
+  flashResultDisambiguatorString: null,
+  trackResultDisambiguatorString1: null,
+  trackResultDisambiguatorString2: null,
+
+  albumResultFilterString: null,
+  artistResultFilterString: null,
+  flashResultFilterString: null,
+  groupResultFilterString: null,
+  tagResultFilterString: null,
+  trackResultFilterString: null,
+
   state: {
     sidebarColumnShownForSearch: null,
 
     tidiedSidebar: null,
     collapsedDetailsForTidiness: null,
 
+    recallingRecentSearch: null,
+    recallingRecentSearchFromMouse: null,
+
+    justPerformedActiveQuery: false,
+
+    currentValue: null,
+
     workerStatus: null,
     searchStage: null,
 
     stoppedTypingTimeout: null,
     stoppedScrollingTimeout: null,
+    focusFirstResultTimeout: null,
+    dismissChangeEventTimeout: null,
 
     indexDownloadStatuses: Object.create(null),
   },
@@ -83,11 +126,20 @@ export const info = {
       type: 'string',
     },
 
+    activeQueryContextPageName: {type: 'string'},
+    activeQueryContextPagePathname: {type: 'string'},
+    activeQueryContextPageColor: {type: 'string'},
+    zapActiveQueryContext: {type: 'boolean'},
+
     activeQueryResults: {
       type: 'json',
       maxLength: settings => settings.maxActiveResultsStorage,
     },
 
+    activeFilterType: {
+      type: 'string',
+    },
+
     repeatQueryOnReload: {
       type: 'boolean',
       default: false,
@@ -102,10 +154,26 @@ export const info = {
     stoppedTypingDelay: 800,
     stoppedScrollingDelay: 200,
 
+    pressDownToFocusFirstResultLatency: 500,
+    dismissChangeEventAfterFocusingFirstResultLatency: 50,
+
     maxActiveResultsStorage: 100000,
   },
 };
 
+export function* bindSessionStorage() {
+  if (yield 'activeQuery') {
+    yield 'activeQueryContextPageName';
+    yield 'activeQueryContextPagePathname';
+    yield 'activeQueryContextPageColor';
+    yield 'zapActiveQueryContext';
+
+    yield 'activeQueryResults';
+    yield 'activeFilterType';
+    yield 'resultsScrollOffset';
+  }
+}
+
 export function getPageReferences() {
   info.pageContainer =
     document.getElementById('page-container');
@@ -126,6 +194,9 @@ export function getPageReferences() {
   info.searchSidebarColumn =
     info.searchBox.closest('.sidebar-column');
 
+  info.standbyInputPlaceholder =
+    info.searchInput.placeholder;
+
   const findString = classPart =>
     info.searchBox.querySelector(`.wiki-search-${classPart}-string`);
 
@@ -144,6 +215,9 @@ export function getPageReferences() {
   info.noResultsString =
     findString('no-results');
 
+  info.backString =
+    findString('back');
+
   info.currentResultString =
     findString('current-result');
 
@@ -156,11 +230,47 @@ export function getPageReferences() {
   info.artistResultKindString =
     findString('artist-result-kind');
 
+  info.flashResultKindString =
+    findString('flash-result-kind');
+
   info.groupResultKindString =
     findString('group-result-kind');
 
+  info.singleResultKindString =
+    findString('single-result-kind');
+
   info.tagResultKindString =
     findString('tag-result-kind');
+
+  info.groupResultDisambiguatorString =
+    findString('group-result-disambiguator');
+
+  info.flashResultDisambiguatorString =
+    findString('flash-result-disambiguator');
+
+  info.trackResultDisambiguatorString1 =
+    findString('track-result-album-disambiguator');
+
+  info.trackResultDisambiguatorString2 =
+    findString('track-result-artist-disambiguator');
+
+  info.albumResultFilterString =
+    findString('album-result-filter');
+
+  info.artistResultFilterString =
+    findString('artist-result-filter');
+
+  info.flashResultFilterString =
+    findString('flash-result-filter');
+
+  info.groupResultFilterString =
+    findString('group-result-filter');
+
+  info.tagResultFilterString =
+    findString('tag-result-filter');
+
+  info.trackResultFilterString =
+    findString('track-result-filter');
 }
 
 export function addInternalListeners() {
@@ -197,6 +307,25 @@ export function addInternalListeners() {
 export function mutatePageContent() {
   if (!info.searchBox) return;
 
+  // Context section
+
+  info.contextContainer =
+    document.createElement('div');
+
+  info.contextContainer.classList.add('wiki-search-context-container');
+
+  info.contextBackLink =
+    document.createElement('a');
+
+  info.contextContainer.appendChild(
+    templateContent(info.backString, {
+      page: info.contextBackLink,
+    }));
+
+  cssProp(info.contextContainer, 'display', 'none');
+
+  info.searchBox.appendChild(info.contextContainer);
+
   // Progress section
 
   info.progressRule =
@@ -250,6 +379,38 @@ export function mutatePageContent() {
   info.searchBox.appendChild(info.failedRule);
   info.searchBox.appendChild(info.failedContainer);
 
+  // Filter section
+
+  info.filterContainer =
+    document.createElement('div');
+
+  info.filterContainer.classList.add('wiki-search-filter-container');
+
+  cssProp(info.filterContainer, 'display', 'none');
+
+  forEachFilter((type, _filterLink) => {
+    // TODO: It's probably a sin to access `session` during this step LOL
+    const {session} = info;
+
+    const filterLink = document.createElement('a');
+
+    filterLink.href = '#';
+    filterLink.classList.add('wiki-search-filter-link');
+
+    if (session.activeFilterType === type) {
+      filterLink.classList.add('active');
+    }
+
+    const string = info[type + 'ResultFilterString'];
+    filterLink.appendChild(templateContent(string));
+
+    info[type + 'FilterLink'] = filterLink;
+
+    info.filterContainer.appendChild(filterLink);
+  });
+
+  info.searchBox.appendChild(info.filterContainer);
+
   // Results section
 
   info.resultsRule =
@@ -298,22 +459,71 @@ export function mutatePageContent() {
 
   info.searchBox.appendChild(info.endSearchRule);
   info.searchBox.appendChild(info.endSearchLine);
+
+  // Accommodate the web browser reconstructing the search input with a value
+  // that was previously entered (or restored after recall), i.e. because
+  // the user is traversing very far back in history and yet the browser is
+  // trying to rebuild the page as-was anyway, by telling it "no don't".
+  info.searchInput.value = '';
 }
 
 export function addPageListeners() {
   if (!info.searchInput) return;
 
+  info.searchInput.addEventListener('mousedown', _domEvent => {
+    const {state} = info;
+
+    if (state.recallingRecentSearch) {
+      state.recallingRecentSearchFromMouse = true;
+    }
+  });
+
+  info.searchInput.addEventListener('focus', _domEvent => {
+    const {session, state} = info;
+
+    if (state.recallingRecentSearch) {
+      info.searchInput.value = session.activeQuery;
+      info.searchInput.placeholder = info.standbyInputPlaceholder;
+      showSidebarSearchResults(session.activeQueryResults);
+      state.recallingRecentSearch = false;
+    }
+  });
+
+  info.searchLabel.addEventListener('click', domEvent => {
+    const {state} = info;
+
+    if (state.recallingRecentSearchFromMouse) {
+      if (info.searchInput.selectionStart === info.searchInput.selectionEnd) {
+        info.searchInput.select();
+      }
+
+      state.recallingRecentSearchFromMouse = false;
+      return;
+    }
+
+    const inputRect = info.searchInput.getBoundingClientRect();
+    if (domEvent.clientX < inputRect.left - 3) {
+      info.searchInput.select();
+    }
+  });
+
   info.searchInput.addEventListener('change', _domEvent => {
-    if (info.searchInput.value) {
-      activateSidebarSearch(info.searchInput.value);
+    const {state} = info;
+
+    if (state.dismissChangeEventTimeout) {
+      state.dismissChangeEventTimeout = null;
+      clearTimeout(state.dismissChangeEventTimeout);
+      return;
     }
+
+    activateSidebarSearch(info.searchInput.value);
   });
 
   info.searchInput.addEventListener('input', _domEvent => {
     const {settings, state} = info;
 
     if (!info.searchInput.value) {
-      clearSidebarSearch();
+      clearSidebarSearch(); // ...but don't clear filter
       return;
     }
 
@@ -323,17 +533,67 @@ export function addPageListeners() {
 
     state.stoppedTypingTimeout =
       setTimeout(() => {
+        state.stoppedTypingTimeout = null;
         activateSidebarSearch(info.searchInput.value);
       }, settings.stoppedTypingDelay);
+
+    if (state.focusFirstResultTimeout) {
+      clearTimeout(state.focusFirstResultTimeout);
+      state.focusFirstResultTimeout = null;
+    }
   });
 
   info.searchInput.addEventListener('drop', handleDroppedIntoSearchInput);
 
+  info.searchInput.addEventListener('keydown', domEvent => {
+    const {settings, state} = info;
+
+    if (domEvent.key === 'ArrowUp' || domEvent.key === 'ArrowDown') {
+      domEvent.preventDefault();
+    }
+
+    if (domEvent.key === 'ArrowDown') {
+      if (state.stoppedTypingTimeout) {
+        clearTimeout(state.stoppedTypingTimeout);
+        state.stoppedTypingTimeout = null;
+
+        if (state.focusFirstResultTimeout) {
+          clearTimeout(state.focusFirstResultTimeout);
+        }
+
+        state.focusFirstResultTimeout =
+          setTimeout(() => {
+            state.focusFirstResultTimeout = null;
+          }, settings.pressDownToFocusFirstResultLatency);
+
+        activateSidebarSearch(info.searchInput.value);
+      } else {
+        focusFirstSidebarSearchResult();
+      }
+    }
+  });
+
+  document.addEventListener('selectionchange', _domEvent => {
+    const {state} = info;
+
+    if (state.focusFirstResultTimeout) {
+      clearTimeout(state.focusFirstResultTimeout);
+      state.focusFirstResultTimeout = null;
+    }
+  });
+
   info.endSearchLink.addEventListener('click', domEvent => {
     domEvent.preventDefault();
     clearSidebarSearch();
+    clearSidebarFilter();
     possiblyHideSearchSidebarColumn();
-    restoreSidebarSearchColumn();
+  });
+
+  forEachFilter((type, filterLink) => {
+    filterLink.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+      toggleSidebarSearchFilter(type);
+    });
   });
 
   info.resultsContainer.addEventListener('scroll', () => {
@@ -348,6 +608,18 @@ export function addPageListeners() {
         saveSidebarSearchResultsScrollOffset();
       }, settings.stoppedScrollingDelay);
   });
+
+  document.addEventListener('keypress', domEvent => {
+    const {tagName} = document.activeElement ?? {};
+    if (tagName === 'INPUT' || tagName === 'TEXTAREA') {
+      return;
+    }
+
+    if (domEvent.shiftKey && domEvent.code === 'Slash') {
+      domEvent.preventDefault();
+      info.searchLabel.click();
+    }
+  });
 }
 
 export function initializeState() {
@@ -356,11 +628,11 @@ export function initializeState() {
   if (!info.searchInput) return;
 
   if (session.activeQuery) {
-    info.searchInput.value = session.activeQuery;
     if (session.repeatQueryOnReload) {
+      info.searchInput.value = session.activeQuery;
       activateSidebarSearch(session.activeQuery);
     } else if (session.activeQueryResults) {
-      showSidebarSearchResults(session.activeQueryResults);
+      considerRecallingRecentSidebarSearch();
     }
   }
 }
@@ -417,9 +689,28 @@ function trackSidebarSearchDownloadEnds(event) {
   }
 }
 
+function forEachFilter(callback) {
+  const filterOrder = [
+    'track',
+    'album',
+    'artist',
+    'group',
+    'flash',
+    'tag',
+  ];
+
+  for (const type of filterOrder) {
+    callback(type, info[type + 'FilterLink']);
+  }
+}
+
 async function activateSidebarSearch(query) {
   const {session, state} = info;
 
+  if (!query) {
+    return;
+  }
+
   if (state.stoppedTypingTimeout) {
     clearTimeout(state.stoppedTypingTimeout);
     state.stoppedTypingTimeout = null;
@@ -441,18 +732,60 @@ async function activateSidebarSearch(query) {
     return;
   }
 
+  state.justPerformedActiveQuery = true;
   state.searchStage = 'complete';
   updateSidebarSearchStatus();
 
+  recordActiveQueryContext();
+
   session.activeQuery = query;
   session.activeQueryResults = results;
   session.resultsScrollOffset = 0;
 
   showSidebarSearchResults(results);
+
+  if (state.focusFirstResultTimeout) {
+    clearTimeout(state.focusFirstResultTimeout);
+    state.focusFirstResultTimeout = null;
+    focusFirstSidebarSearchResult();
+  }
+}
+
+function recordActiveQueryContext() {
+  const {session} = info;
+
+  if (document.documentElement.dataset.urlKey === 'localized.home') {
+    session.activeQueryContextPageName = null;
+    session.activeQueryContextPagePathname = null;
+    session.activeQueryContextPageColor = null;
+    session.zapActiveQueryContext = true;
+    return;
+  }
+
+  // Zapping means subsequent searches don't record context.
+  if (session.zapActiveQueryContext) {
+    return;
+  }
+
+  // We also don't overwrite existing context.
+  if (session.activeQueryContextPagePathname) {
+    return;
+  }
+
+  session.activeQueryContextPageName =
+    decodeEntities(document.querySelector('title').dataset.withoutWikiName) ||
+    document.title;
+
+  session.activeQueryContextPagePathname =
+    location.pathname;
+
+  session.activeQueryContextPageColor =
+    document.querySelector('.color-style')?.dataset.color ??
+    null;
 }
 
 function clearSidebarSearch() {
-  const {session, state} = info;
+  const {state} = info;
 
   if (state.stoppedTypingTimeout) {
     clearTimeout(state.stoppedTypingTimeout);
@@ -465,12 +798,34 @@ function clearSidebarSearch() {
   info.searchInput.value = '';
 
   state.searchStage = null;
+  state.justPerformedActiveQuery = false;
+
+  clearActiveQuery();
+
+  hideSidebarSearchResults();
+}
+
+function clearActiveQuery() {
+  const {session} = info;
 
   session.activeQuery = null;
   session.activeQueryResults = null;
   session.resultsScrollOffset = null;
 
-  hideSidebarSearchResults();
+  session.activeQueryContextPageName = null;
+  session.activeQueryContextPagePathname = null;
+  session.activeQueryContextPageColor = null;
+  session.zapActiveQueryContext = false;
+}
+
+function clearSidebarFilter() {
+  const {session} = info;
+
+  toggleSidebarSearchFilter(session.activeFilterType);
+
+  forEachFilter((_type, filterLink) => {
+    filterLink.classList.remove('shown', 'hidden');
+  });
 }
 
 function updateSidebarSearchStatus() {
@@ -559,63 +914,185 @@ function showSidebarSearchFailed() {
 }
 
 function showSidebarSearchResults(results) {
-  console.debug(`Showing search results:`, results);
+  const {session} = info;
 
-  showSearchSidebarColumn();
+  console.debug(`Showing search results:`, tidyResults(results));
 
-  const flatResults =
-    Object.entries(results)
-      .filter(([index]) => index === 'generic')
-      .flatMap(([index, results]) => results
-        .flatMap(({doc, id}) => ({
-          index,
-          reference: id ?? null,
-          referenceType: (id ? id.split(':')[0] : null),
-          directory: (id ? id.split(':')[1] : null),
-          data: doc,
-        })));
+  showSearchSidebarColumn();
 
   info.searchBox.classList.add('showing-results');
   info.searchSidebarColumn.classList.add('search-showing-results');
 
-  while (info.results.firstChild) {
-    info.results.firstChild.remove();
+  let filterType = session.activeFilterType;
+  let shownAnyResults =
+    fillResultElements(results, {filterType: session.activeFilterType});
+
+  showFilterElements(results);
+
+  if (!shownAnyResults) {
+    shownAnyResults = toggleSidebarSearchFilter(filterType);
+    filterType = null;
   }
 
-  cssProp(info.resultsRule, 'display', 'block');
-  cssProp(info.resultsContainer, 'display', 'block');
+  if (shownAnyResults) {
+    showContextControls();
+
+    cssProp(info.endSearchRule, 'display', 'block');
+    cssProp(info.endSearchLine, 'display', 'block');
 
-  if (empty(flatResults)) {
+    tidySidebarSearchColumn();
+  } else {
     const p = document.createElement('p');
     p.classList.add('wiki-search-no-results');
     p.appendChild(templateContent(info.noResultsString));
     info.results.appendChild(p);
   }
 
-  for (const result of flatResults) {
-    const el = generateSidebarSearchResult(result);
+  restoreSidebarSearchResultsScrollOffset();
+}
+
+function tidyResults(results) {
+  const tidiedResults =
+    results.results.map(({doc, id}) => ({
+      reference: id ?? null,
+      referenceType: (id ? id.split(':')[0] : null),
+      directory: (id ? id.split(':')[1] : null),
+      data: doc,
+    }));
+
+  return tidiedResults;
+}
+
+function fillResultElements(results, {
+  filterType = null,
+} = {}) {
+  const tidiedResults = tidyResults(results);
+
+  let filteredResults = tidiedResults;
+
+  if (filterType) {
+    filteredResults = filteredResults
+      .filter(result => result.referenceType === filterType);
+  }
+
+  if (!filterType) {
+    filteredResults = filteredResults
+      .filter(result => {
+        if (result.referenceType !== 'track') return true;
+        if (result.data.classification !== 'single') return true;
+        return !filteredResults.find(otherResult => {
+          if (otherResult.referenceType !== 'album') return false;
+          return otherResult.name === result.parentName;
+        });
+      });
+  }
+
+  filteredResults = filteredResults
+
+  while (info.results.firstChild) {
+    info.results.firstChild.remove();
+  }
+
+  cssProp(info.resultsRule, 'display', 'block');
+  cssProp(info.resultsContainer, 'display', 'block');
+
+  if (empty(filteredResults)) {
+    return false;
+  }
+
+  for (const result of filteredResults) {
+    let el;
+    try {
+      el = generateSidebarSearchResult(result, filteredResults);
+    } catch (error) {
+      console.error(`Error showing result:`, result);
+      console.error(error);
+    }
+
     if (!el) continue;
 
     info.results.appendChild(el);
   }
 
-  if (!empty(flatResults)) {
-    cssProp(info.endSearchRule, 'display', 'block');
-    cssProp(info.endSearchLine, 'display', 'block');
+  return true;
+}
 
-    tidySidebarSearchColumn();
+function showFilterElements(results) {
+  const {queriedKind} = results;
+
+  const tidiedResults = tidyResults(results);
+
+  const allReferenceTypes =
+    unique(tidiedResults.map(result => result.referenceType));
+
+  let shownAny = false;
+
+  forEachFilter((type, filterLink) => {
+    filterLink.classList.remove('shown', 'hidden');
+
+    if (allReferenceTypes.includes(type)) {
+      shownAny = true;
+      cssProp(filterLink, 'display', null);
+
+      if (queriedKind) {
+        filterLink.setAttribute('inert', 'inert');
+      } else {
+        filterLink.removeAttribute('inert');
+      }
+
+      if (type === queriedKind) {
+        filterLink.classList.add('active-from-query');
+      } else {
+        filterLink.classList.remove('active-from-query');
+      }
+    } else {
+      cssProp(filterLink, 'display', 'none');
+    }
+  });
+
+  if (shownAny) {
+    cssProp(info.filterContainer, 'display', null);
+  } else {
+    cssProp(info.filterContainer, 'display', 'none');
   }
+}
 
-  restoreSidebarSearchResultsScrollOffset();
+function showContextControls() {
+  const {session} = info;
+
+  const shouldShow =
+    session.activeQueryContextPagePathname &&
+    location.pathname !== session.activeQueryContextPagePathname;
+
+  if (shouldShow) {
+    info.contextBackLink.href =
+      session.activeQueryContextPagePathname;
+
+    cssProp(info.contextBackLink,
+      '--primary-color',
+      session.activeQueryContextPageColor);
+
+    while (info.contextBackLink.firstChild) {
+      info.contextBackLink.firstChild.remove();
+    }
+
+    info.contextBackLink.appendChild(
+      document.createTextNode(
+        session.activeQueryContextPageName));
+
+    cssProp(info.contextContainer, 'display', 'block');
+  } else {
+    cssProp(info.contextContainer, 'display', 'none');
+  }
 }
 
-function generateSidebarSearchResult(result) {
+function generateSidebarSearchResult(result, results) {
   const preparedSlots = {
     color:
       result.data.color ?? null,
 
     name:
-      result.data.name ?? result.data.primaryName ?? null,
+      getSearchResultName(result),
 
     imageSource:
       getSearchResultImageSource(result),
@@ -627,7 +1104,9 @@ function generateSidebarSearchResult(result) {
         openAlbum(result.directory);
 
       preparedSlots.kindString =
-        info.albumResultKindString;
+        (result.data.classification === 'single'
+          ? info.singleResultKindString
+          : info.albumResultKindString);
 
       break;
     }
@@ -656,6 +1135,9 @@ function generateSidebarSearchResult(result) {
       preparedSlots.href =
         openFlash(result.directory);
 
+      preparedSlots.kindString =
+        info.flashResultKindString;
+
       break;
     }
 
@@ -680,9 +1162,95 @@ function generateSidebarSearchResult(result) {
       return null;
   }
 
+  const compareReferenceType = otherResult =>
+    otherResult.referenceType === result.referenceType;
+
+  const compareName = otherResult =>
+    getSearchResultName(otherResult) === getSearchResultName(result);
+
+  const ambiguousWith =
+    results.filter(otherResult =>
+      otherResult !== result &&
+      compareReferenceType(otherResult) &&
+      compareName(otherResult));
+
+  if (!empty(ambiguousWith)) disambiguate: {
+    const allAmbiguous = [result, ...ambiguousWith];
+
+    // First search for an ideal disambiguation, which disambiguates
+    // all ambiguous results in the same way.
+    let disambiguation = null, i;
+    for (i = 0; i < result.data.disambiguators.length; i++) {
+      const disambiguations =
+        allAmbiguous.map(r => r.data.disambiguators[i]);
+
+      if (unique(disambiguations).length === allAmbiguous.length) {
+        disambiguation = result.data.disambiguators[i];
+        break;
+      }
+    }
+
+    // Otherwise, search for a disambiguation which disambiguates
+    // *this result* with at least one other result which it is
+    // *otherwise* ambiguous with.
+    if (!disambiguation) {
+      for (i = 1; i < result.data.disambiguators.length; i++) {
+        const otherwiseAmbiguousWith =
+          ambiguousWith.filter(otherResult =>
+            compareArrays(
+              otherResult.data.disambiguators.slice(0, i),
+              result.data.disambiguators.slice(0, i)));
+
+        if (
+          otherwiseAmbiguousWith.find(otherResult =>
+            otherResult.data.disambiguators[i] !==
+            result.data.disambiguators[i])
+        ) {
+          disambiguation = result.data.disambiguators[i];
+          break;
+        }
+      }
+    }
+
+    // Otherwise, search for a disambiguation which disambiguates
+    // this result at all.
+    if (!disambiguation) {
+      for (i = 0; i < result.data.disambiguators.length; i++) {
+        if (
+          ambiguousWith.find(otherResult =>
+            otherResult.data.disambiguators[i] !==
+            result.data.disambiguators[i])
+        ) {
+          disambiguation = result.data.disambiguators[i];
+          break;
+        }
+      }
+    }
+
+    if (!disambiguation) {
+      break disambiguate;
+    }
+
+    const string =
+      info[result.referenceType + 'ResultDisambiguatorString' + (i + 1)];
+
+    if (!string) break disambiguate;
+
+    preparedSlots.disambiguate = disambiguation;
+    preparedSlots.disambiguatorString = string;
+  }
+
   return generateSidebarSearchResultTemplate(preparedSlots);
 }
 
+function getSearchResultName(result) {
+  return (
+    result.data.name ??
+    result.data.primaryName ??
+    null
+  );
+}
+
 function getSearchResultImageSource(result) {
   const {artwork} = result.data;
   if (!artwork) return null;
@@ -758,6 +1326,15 @@ function generateSidebarSearchResultTemplate(slots) {
     }
   }
 
+  if (!accentSpan && slots.disambiguate) {
+    accentSpan = document.createElement('span');
+    accentSpan.classList.add('wiki-search-result-disambiguator');
+    accentSpan.appendChild(
+      templateContent(slots.disambiguatorString, {
+        disambiguator: slots.disambiguate,
+      }));
+  }
+
   if (!accentSpan && slots.kindString) {
     accentSpan = document.createElement('span');
     accentSpan.classList.add('wiki-search-result-kind');
@@ -775,10 +1352,31 @@ function generateSidebarSearchResultTemplate(slots) {
     saveSidebarSearchResultsScrollOffset();
   });
 
+  link.addEventListener('keydown', domEvent => {
+    if (domEvent.key === 'ArrowDown') {
+      const elem = link.nextElementSibling;
+      if (elem) {
+        domEvent.preventDefault();
+        elem.focus({focusVisible: true});
+      }
+    } else if (domEvent.key === 'ArrowUp') {
+      domEvent.preventDefault();
+      const elem = link.previousElementSibling;
+      if (elem) {
+        elem.focus({focusVisible: true});
+      } else {
+        info.searchInput.focus();
+      }
+    }
+  });
+
   return link;
 }
 
 function hideSidebarSearchResults() {
+  cssProp(info.contextContainer, 'display', 'none');
+  cssProp(info.filterContainer, 'display', 'none');
+
   cssProp(info.resultsRule, 'display', 'none');
   cssProp(info.resultsContainer, 'display', 'none');
 
@@ -788,6 +1386,28 @@ function hideSidebarSearchResults() {
 
   cssProp(info.endSearchRule, 'display', 'none');
   cssProp(info.endSearchLine, 'display', 'none');
+
+  restoreSidebarSearchColumn();
+}
+
+function focusFirstSidebarSearchResult() {
+  const {settings, state} = info;
+
+  const elem = info.results.firstChild;
+  if (!elem?.classList.contains('wiki-search-result')) {
+    return;
+  }
+
+  if (state.dismissChangeEventTimeout) {
+    clearTimeout(state.dismissChangeEventTimeout);
+  }
+
+  state.dismissChangeEventTimeout =
+    setTimeout(() => {
+      state.dismissChangeEventTimeout = null;
+    }, settings.dismissChangeEventAfterFocusingFirstResultLatency);
+
+  elem.focus({focusVisible: true});
 }
 
 function saveSidebarSearchResultsScrollOffset() {
@@ -851,7 +1471,7 @@ function possiblyHideSearchSidebarColumn() {
 // This should be called after results are shown, since it checks the
 // elements added to understand the current search state.
 function tidySidebarSearchColumn() {
-  const {state} = info;
+  const {session, state} = info;
 
   // Don't *re-tidy* the sidebar if we've already tidied it to display
   // some results. This flag will get cleared if the search is dismissed
@@ -860,17 +1480,24 @@ function tidySidebarSearchColumn() {
     return;
   }
 
-  const here = location.href.replace(/\/$/, '');
+  const hrefHere = location.href.replace(/\/$/, '');
   const currentPageIsResult =
     Array.from(info.results.querySelectorAll('a'))
       .some(link => {
-        const there = link.href.replace(/\/$/, '');
-        return here === there;
+        const hrefThere = link.href.replace(/\/$/, '');
+        return hrefHere === hrefThere;
       });
 
+  const currentPageIsContext =
+    location.pathname === session.activeQueryContextPagePathname;
+
   // Don't tidy the sidebar if you've navigated to some other page than
   // what's in the current result list.
-  if (!currentPageIsResult) {
+  if (
+    !state.justPerformedActiveQuery &&
+    !currentPageIsResult &&
+    !currentPageIsContext
+  ) {
     return;
   }
 
@@ -891,6 +1518,36 @@ function tidySidebarSearchColumn() {
   }
 }
 
+function toggleSidebarSearchFilter(toggleType) {
+  const {session} = info;
+
+  if (!toggleType) return null;
+
+  let shownAnyResults = null;
+
+  forEachFilter((type, filterLink) => {
+    if (type === toggleType) {
+      const filterActive = filterLink.classList.toggle('active');
+      const filterType = (filterActive ? type : null);
+
+      if (cssProp(filterLink, 'display') !== 'none') {
+        filterLink.classList.add(filterActive ? 'shown' : 'hidden');
+      }
+
+      if (session.activeQueryResults) {
+        shownAnyResults =
+          fillResultElements(session.activeQueryResults, {filterType});
+      }
+
+      session.activeFilterType = filterType;
+    } else {
+      filterLink.classList.remove('active');
+    }
+  });
+
+  return shownAnyResults;
+}
+
 function restoreSidebarSearchColumn() {
   const {state} = info;
 
@@ -904,6 +1561,24 @@ function restoreSidebarSearchColumn() {
 
   state.collapsedDetailsForTidiness = [];
   state.tidiedSidebar = null;
+
+  info.searchInput.placeholder = info.standbyInputPlaceholder;
+}
+
+function considerRecallingRecentSidebarSearch() {
+  const {session, state} = info;
+
+  if (document.documentElement.dataset.urlKey === 'localized.home') {
+    return forgetRecentSidebarSearch();
+  }
+
+  info.searchInput.placeholder = session.activeQuery;
+  state.recallingRecentSearch = true;
+}
+
+function forgetRecentSidebarSearch() {
+  clearActiveQuery();
+  clearSidebarFilter();
 }
 
 async function handleDroppedIntoSearchInput(domEvent) {
@@ -932,7 +1607,7 @@ async function handleDroppedIntoSearchInput(domEvent) {
   let droppedURL;
   try {
     droppedURL = new URL(droppedText);
-  } catch (error) {
+  } catch {
     droppedURL = null;
   }
 
diff --git a/src/static/js/client/sticky-heading.js b/src/static/js/client/sticky-heading.js
index ae63eab5..c69e137f 100644
--- a/src/static/js/client/sticky-heading.js
+++ b/src/static/js/client/sticky-heading.js
@@ -1,13 +1,17 @@
-/* eslint-env browser */
-
 import {filterMultipleArrays, stitchArrays} from '../../shared-util/sugar.js';
-import {dispatchInternalEvent, templateContent} from '../client-util.js';
+import {cssProp, dispatchInternalEvent, templateContent}
+  from '../client-util.js';
 
 export const info = {
   id: 'stickyHeadingInfo',
 
+  stickyRoots: null,
+
   stickyContainers: null,
+  staticContainers: null,
 
+  stickyHeadingRows: null,
+  stickyHeadings: null,
   stickySubheadingRows: null,
   stickySubheadings: null,
 
@@ -17,21 +21,33 @@ export const info = {
 
   contentContainers: null,
   contentHeadings: null,
+  contentCoverColumns: null,
   contentCovers: null,
   contentCoversReveal: null,
 
+  referenceCollapsedHeading: null,
+
   state: {
     displayedHeading: null,
   },
 
   event: {
     whenDisplayedHeadingChanges: [],
+    whenStuckStatusChanges: [],
   },
 };
 
 export function getPageReferences() {
+  info.stickyRoots =
+    Array.from(document.querySelectorAll('.content-sticky-heading-root:not([inert])'));
+
   info.stickyContainers =
-    Array.from(document.getElementsByClassName('content-sticky-heading-container'));
+    info.stickyRoots
+      .map(el => el.querySelector('.content-sticky-heading-container'));
+
+  info.staticContainers =
+    info.stickyRoots
+      .map(el => el.nextElementSibling);
 
   info.stickyCoverContainers =
     info.stickyContainers
@@ -45,6 +61,14 @@ export function getPageReferences() {
     info.stickyCovers
       .map(el => el?.querySelector('.image-text-area'));
 
+  info.stickyHeadingRows =
+    info.stickyContainers
+      .map(el => el.querySelector('.content-sticky-heading-row'));
+
+  info.stickyHeadings =
+    info.stickyHeadingRows
+      .map(el => el.querySelector('h1'));
+
   info.stickySubheadingRows =
     info.stickyContainers
       .map(el => el.querySelector('.content-sticky-subheading-row'));
@@ -55,11 +79,15 @@ export function getPageReferences() {
 
   info.contentContainers =
     info.stickyContainers
-      .map(el => el.parentElement);
+      .map(el => el.closest('.content-sticky-heading-root').parentElement);
 
-  info.contentCovers =
+  info.contentCoverColumns =
     info.contentContainers
-      .map(el => el.querySelector('#cover-art-container'));
+      .map(el => el.querySelector('#artwork-column'));
+
+  info.contentCovers =
+    info.contentCoverColumns
+      .map(el => el ? el.querySelector('.cover-artwork') : null);
 
   info.contentCoversReveal =
     info.contentCovers
@@ -68,6 +96,10 @@ export function getPageReferences() {
   info.contentHeadings =
     info.contentContainers
       .map(el => Array.from(el.querySelectorAll('.content-heading')));
+
+  info.referenceCollapsedHeading =
+    info.stickyHeadings
+      .map(el => el.querySelector('.reference-collapsed-heading'));
 }
 
 export function mutatePageContent() {
@@ -137,15 +169,61 @@ function topOfViewInside(el, scroll = window.scrollY) {
     scroll < el.offsetTop + el.offsetHeight);
 }
 
+function updateStuckStatus(index) {
+  const {event} = info;
+
+  const contentContainer = info.contentContainers[index];
+  const stickyContainer = info.stickyContainers[index];
+
+  const wasStuck = stickyContainer.classList.contains('stuck');
+  const stuck = topOfViewInside(contentContainer);
+
+  if (stuck === wasStuck) return;
+
+  if (stuck) {
+    stickyContainer.classList.add('stuck');
+  } else {
+    stickyContainer.classList.remove('stuck');
+  }
+
+  dispatchInternalEvent(event, 'whenStuckStatusChanges', index, stuck);
+}
+
+function updateCollapseStatus(index) {
+  const stickyContainer = info.stickyContainers[index];
+  const staticContainer = info.staticContainers[index];
+  const stickyHeading = info.stickyHeadings[index];
+  const referenceCollapsedHeading = info.referenceCollapsedHeading[index];
+
+  const {height: uncollapsedHeight} = stickyHeading.getBoundingClientRect();
+  const {height: collapsedHeight} = referenceCollapsedHeading.getBoundingClientRect();
+
+  if (
+    staticContainer.getBoundingClientRect().bottom < 4 ||
+    staticContainer.getBoundingClientRect().top < -80
+  ) {
+    if (!stickyContainer.classList.contains('collapse')) {
+      stickyContainer.classList.add('collapse');
+      cssProp(stickyContainer, '--uncollapsed-heading-height', uncollapsedHeight + 'px');
+      cssProp(stickyContainer, '--collapsed-heading-height', collapsedHeight + 'px');
+    }
+  } else {
+    stickyContainer.classList.remove('collapse');
+  }
+}
+
 function updateStickyCoverVisibility(index) {
   const stickyCoverContainer = info.stickyCoverContainers[index];
-  const contentCover = info.contentCovers[index];
+  const stickyContainer = info.stickyContainers[index];
+  const contentCoverColumn = info.contentCoverColumns[index];
 
-  if (contentCover && stickyCoverContainer) {
-    if (contentCover.getBoundingClientRect().bottom < 4) {
+  if (contentCoverColumn && stickyCoverContainer) {
+    if (contentCoverColumn.getBoundingClientRect().bottom < 4) {
       stickyCoverContainer.classList.add('visible');
+      stickyContainer.classList.add('cover-visible');
     } else {
       stickyCoverContainer.classList.remove('visible');
+      stickyContainer.classList.remove('cover-visible');
     }
   }
 }
@@ -157,26 +235,31 @@ function getContentHeadingClosestToStickySubheading(index) {
     return null;
   }
 
-  const stickySubheading = info.stickySubheadings[index];
-
-  if (stickySubheading.childNodes.length === 0) {
-    // Supply a non-breaking space to ensure correct basic line height.
-    stickySubheading.appendChild(document.createTextNode('\xA0'));
-  }
-
-  const stickyContainer = info.stickyContainers[index];
-  const stickyRect = stickyContainer.getBoundingClientRect();
+  const stickyHeadingRow = info.stickyHeadingRows[index];
+  const stickyRect = stickyHeadingRow.getBoundingClientRect();
 
-  // TODO: Should this compute with the subheading row instead of h2?
-  const subheadingRect = stickySubheading.getBoundingClientRect();
+  // Subheadings only appear when the sticky heading is collapsed,
+  // so the used bottom edge should always be *as though* it's only
+  // displaying one line of text. Subtract the current discrepancy.
+  const stickyHeading = info.stickyHeadings[index];
+  const referenceCollapsedHeading = info.referenceCollapsedHeading[index];
+  const correctBottomEdge =
+    stickyHeading.getBoundingClientRect().height -
+    referenceCollapsedHeading.getBoundingClientRect().height;
 
-  const stickyBottom = stickyRect.bottom + subheadingRect.height;
+  const stickyBottom =
+    (stickyRect.bottom
+   - correctBottomEdge);
 
   // Iterate from bottom to top of the content area.
   const contentHeadings = info.contentHeadings[index];
-  for (const heading of contentHeadings.slice().reverse()) {
+  for (const heading of contentHeadings.toReversed()) {
+    if (heading.nodeName === 'SUMMARY' && !heading.closest('details').open) {
+      continue;
+    }
+
     const headingRect = heading.getBoundingClientRect();
-    if (headingRect.y + headingRect.height / 1.5 < stickyBottom + 20) {
+    if (headingRect.y + headingRect.height / 1.5 < stickyBottom + 40) {
       return heading;
     }
   }
@@ -187,7 +270,12 @@ function getContentHeadingClosestToStickySubheading(index) {
 function updateStickySubheadingContent(index) {
   const {event, state} = info;
 
-  const closestHeading = getContentHeadingClosestToStickySubheading(index);
+  const stickyContainer = info.stickyContainers[index];
+
+  const closestHeading =
+    (stickyContainer.classList.contains('collapse')
+      ? getContentHeadingClosestToStickySubheading(index)
+      : null);
 
   if (state.displayedHeading === closestHeading) return;
 
@@ -233,6 +321,8 @@ function updateStickySubheadingContent(index) {
 }
 
 export function updateStickyHeadings(index) {
+  updateStuckStatus(index);
+  updateCollapseStatus(index);
   updateStickyCoverVisibility(index);
   updateStickySubheadingContent(index);
 }
diff --git a/src/static/js/client/summary-nested-link.js b/src/static/js/client/summary-nested-link.js
index 23857fa5..1c4e7e4b 100644
--- a/src/static/js/client/summary-nested-link.js
+++ b/src/static/js/client/summary-nested-link.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {
   empty,
   filterMultipleArrays,
diff --git a/src/static/js/client/text-with-tooltip.js b/src/static/js/client/text-with-tooltip.js
index dd207e04..2b855756 100644
--- a/src/static/js/client/text-with-tooltip.js
+++ b/src/static/js/client/text-with-tooltip.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {stitchArrays} from '../../shared-util/sugar.js';
 
 import {registerTooltipElement, registerTooltipHoverableElement}
diff --git a/src/static/js/client/wiki-search.js b/src/static/js/client/wiki-search.js
index 2446c172..9a6e29c1 100644
--- a/src/static/js/client/wiki-search.js
+++ b/src/static/js/client/wiki-search.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {promiseWithResolvers} from '../../shared-util/sugar.js';
 
 import {dispatchInternalEvent} from '../client-util.js';
diff --git a/src/static/js/group-contributions-table.js b/src/static/js/group-contributions-table.js
index 72ad2327..bef85fad 100644
--- a/src/static/js/group-contributions-table.js
+++ b/src/static/js/group-contributions-table.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 // TODO: Update to clientSteps style.
 
 const groupContributionsTableInfo =
diff --git a/src/static/js/info-card.js b/src/static/js/info-card.js
index 1d9f7c86..05d5d801 100644
--- a/src/static/js/info-card.js
+++ b/src/static/js/info-card.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 // Note: This is a super ancient chunk of code which isn't actually in use,
 // so it's just commented out here.
 
diff --git a/src/static/js/lazy-loading.js b/src/static/js/lazy-loading.js
index 1df56f08..0c8aef31 100644
--- a/src/static/js/lazy-loading.js
+++ b/src/static/js/lazy-loading.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 // Lazy loading! Roll your own. Woot.
 // This file includes a 8unch of fall8acks and stuff like that, and is written
 // with fairly Olden JavaScript(TM), so as to work on pretty much any 8rowser
diff --git a/src/static/js/rectangles.js b/src/static/js/rectangles.js
index cdab2cb8..24382ef8 100644
--- a/src/static/js/rectangles.js
+++ b/src/static/js/rectangles.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 import {info as liveMousePositionInfo} from './client/live-mouse-position.js';
 
 export class WikiRect extends DOMRect {
@@ -510,4 +508,46 @@ export class WikiRect extends DOMRect {
       height: this.height,
     });
   }
+
+  // Other utilities
+
+  #display = null;
+
+  display() {
+    if (!this.#display) {
+      this.#display = document.createElement('div');
+      document.body.appendChild(this.#display);
+    }
+
+    Object.assign(this.#display.style, {
+      position: 'fixed',
+      background: '#000c',
+      border: '3px solid var(--primary-color)',
+      borderRadius: '4px',
+      top: this.top + 'px',
+      left: this.left + 'px',
+      width: this.width + 'px',
+      height: this.height + 'px',
+      pointerEvents: 'none',
+    });
+
+    let i = 0;
+    const int = setInterval(() => {
+      i++;
+      if (i >= 3) clearInterval(int);
+      if (!this.#display) return;
+
+      this.#display.style.display = 'none';
+      setTimeout(() => {
+        this.#display.style.display = '';
+      }, 200);
+    }, 600);
+  }
+
+  hide() {
+    if (this.#display) {
+      this.#display.remove();
+      this.#display = null;
+    }
+  }
 }
diff --git a/src/static/js/search-worker.js b/src/static/js/search-worker.js
index 1b4684ad..9ccaa95d 100644
--- a/src/static/js/search-worker.js
+++ b/src/static/js/search-worker.js
@@ -1,12 +1,12 @@
-/* eslint-env worker */
-
 import FlexSearch from '../lib/flexsearch/flexsearch.bundle.module.min.js';
 
-import {makeSearchIndex, searchSpec} from '../shared-util/search-spec.js';
+import {default as searchSpec, makeSearchIndex}
+  from '../shared-util/search-shape.js';
 
 import {
   empty,
   groupArray,
+  permutations,
   promiseWithResolvers,
   stitchArrays,
   unique,
@@ -33,7 +33,7 @@ postStatus('alive');
 Promise.all([
   loadDependencies(),
   loadDatabase(),
-]).then(main)
+]).then(() => main())
   .then(
     () => {
       postStatus('ready');
@@ -130,7 +130,7 @@ async function loadDatabase() {
 
   try {
     idb = await promisifyIDBRequest(request);
-  } catch (error) {
+  } catch {
     console.warn(`Couldn't load search IndexedDB - won't use an internal cache.`);
     console.warn(request.error);
     idb = null;
@@ -183,7 +183,7 @@ function fetchIndexes(keysNeedingFetch) {
           })));
 }
 
-async function main() {
+async function main(fromRetry = false) {
   const prepareIndexDataPromise = prepareIndexData();
 
   indexes =
@@ -195,17 +195,50 @@ async function main() {
 
   const {indexData, idbIndexData} = await prepareIndexDataPromise;
 
+  const understoodKeys = Object.keys(searchSpec);
+  const unexpectedKeysFromCache =
+    Object.keys(idbIndexData)
+      .filter(key => !understoodKeys.includes(key));
+
+  // This step is largely "unnecessary" because the rest of the code pays
+  // attention to which keys are understood anyway, but we delete unexpected
+  // keys from the index anyway, to trim stored data that isn't being used.
+  if (idb && !empty(unexpectedKeysFromCache)) {
+    for (const key of unexpectedKeysFromCache) {
+      console.warn(`Unexpected search index in cache, deleting: ${key}`);
+    }
+
+    const transaction =
+      idb.transaction(['indexes'], 'readwrite');
+
+    const store =
+      transaction.objectStore('indexes');
+
+    for (const [key] of unexpectedKeysFromCache) {
+      try {
+        await promisifyIDBRequest(store.delete(key));
+      } catch (error) {
+        console.warn(`Error deleting ${key} from internal search cache`);
+        console.warn(error);
+        continue;
+      }
+    }
+  }
+
   const keysNeedingFetch =
     (idbIndexData
       ? Object.keys(indexData)
+          .filter(key => understoodKeys.includes(key))
           .filter(key =>
             indexData[key].md5 !==
             idbIndexData[key]?.md5)
-      : Object.keys(indexData));
+      : Object.keys(indexData)
+          .filter(key => understoodKeys.includes(key)));
 
   const keysFromCache =
     Object.keys(indexData)
-      .filter(key => !keysNeedingFetch.includes(key))
+      .filter(key => understoodKeys.includes(key))
+      .filter(key => !keysNeedingFetch.includes(key));
 
   const cacheArrayBufferPromises =
     keysFromCache
@@ -234,10 +267,20 @@ async function main() {
   }
 
   function importIndexes(keys, jsons) {
+    const succeeded = [];
+    const failed = [];
+
     stitchArrays({key: keys, json: jsons})
       .forEach(({key, json}) => {
-        importIndex(key, json);
+        try {
+          importIndex(key, json);
+          succeeded.push([key, null]);
+        } catch (caughtError) {
+          failed.push([key, caughtError]);
+        }
       });
+
+    return {succeeded, failed};
   }
 
   if (idb) {
@@ -245,6 +288,8 @@ async function main() {
     console.debug(`Fetching indexes anew:`, keysNeedingFetch);
   }
 
+  let signalRetryNeeded = false;
+
   await Promise.all([
     async () => {
       const cacheArrayBuffers =
@@ -254,7 +299,34 @@ async function main() {
         cacheArrayBuffers
           .map(arrayBufferToJSON);
 
-      importIndexes(keysFromCache, cacheJSONs);
+      const importResults =
+        importIndexes(keysFromCache, cacheJSONs);
+
+      if (empty(importResults.failed)) return;
+      if (!idb) return;
+
+      const transaction =
+        idb.transaction(['indexes'], 'readwrite');
+
+      const store =
+        transaction.objectStore('indexes');
+
+      for (const [key, error] of importResults.failed) {
+        console.warn(`Failed to import search index from cache: ${key}`);
+        console.warn(error);
+      }
+
+      for (const [key] of importResults.failed) {
+        try {
+          await promisifyIDBRequest(store.delete(key));
+        } catch (error) {
+          console.warn(`Error deleting ${key} from internal search cache`);
+          console.warn(error);
+          continue;
+        }
+      }
+
+      signalRetryNeeded = true;
     },
 
     async () => {
@@ -265,7 +337,21 @@ async function main() {
         fetchArrayBuffers
           .map(arrayBufferToJSON);
 
-      importIndexes(keysNeedingFetch, fetchJSONs);
+      const importResults =
+        importIndexes(keysNeedingFetch, fetchJSONs);
+
+      if (empty(importResults.failed)) return;
+
+      for (const [key, error] of importResults.failed) {
+        console.warn(`Failed to import search index from fetch: ${key}`);
+        console.warn(error);
+      }
+
+      console.warn(
+        `Trying again would just mean fetching this same data, ` +
+        `so this is needs outside intervention.`);
+
+      throw new Error(`Failed to load search data from fresh fetch`);
     },
 
     async () => {
@@ -300,11 +386,19 @@ async function main() {
       }
     },
   ].map(fn => fn()));
+
+  if (signalRetryNeeded) {
+    if (fromRetry) {
+      console.error(`Already retried, this is probably a logic / code flow error.`);
+      throw new Error(`Failed to load good search data even on a retry`);
+    } else {
+      console.warn(`Trying to load search data again, hopefully from fresh conditions`);
+      return main(true);
+    }
+  }
 }
 
 function importIndex(indexKey, indexData) {
-  // If this fails, it's because an outdated index was cached.
-  // TODO: If this fails, try again once with a cache busting url.
   for (const [key, value] of Object.entries(indexData)) {
     indexes[indexKey].import(key, JSON.stringify(value));
   }
@@ -371,65 +465,85 @@ function postActionResult(id, status, value) {
 }
 
 function performSearchAction({query, options}) {
-  const {generic, ...otherIndexes} = indexes;
+  const {queriedKind} = processTerms(query);
+  const genericResults = queryGenericIndex(query, options);
+  const verbatimResults = queryVerbatimIndex(query, options);
 
-  const genericResults =
-    queryGenericIndex(generic, query, options);
+  const verbatimIDs =
+    new Set(verbatimResults?.map(result => result.id));
 
-  const otherResults =
-    withEntries(otherIndexes, entries => entries
-      .map(([indexName, index]) => [
-        indexName,
-        index.search(query, options),
-      ]));
+  const commonResults =
+    (verbatimResults && genericResults
+      ? genericResults
+          .filter(({id}) => verbatimIDs.has(id))
+      : verbatimResults ?? genericResults);
 
   return {
-    generic: genericResults,
-    ...otherResults,
+    results: commonResults,
+    queriedKind,
   };
 }
 
-function queryGenericIndex(index, query, options) {
-  const interestingFieldCombinations = [
-    ['primaryName', 'parentName', 'groups'],
-    ['primaryName', 'parentName'],
-    ['primaryName', 'groups', 'contributors'],
-    ['primaryName', 'groups', 'artTags'],
-    ['primaryName', 'groups'],
-    ['primaryName', 'contributors'],
-    ['primaryName', 'artTags'],
-    ['parentName', 'groups', 'artTags'],
-    ['parentName', 'artTags'],
-    ['groups', 'contributors'],
-    ['groups', 'artTags'],
-
-    // This prevents just matching *everything* tagged "john" if you
-    // only search "john", but it actually supports matching more than
-    // *two* tags at once: "john rose lowas" works! This is thanks to
-    // flexsearch matching multiple field values in a single query.
-    ['artTags', 'artTags'],
-
-    ['contributors', 'parentName'],
-    ['contributors', 'groups'],
-    ['primaryName', 'contributors'],
-    ['primaryName'],
-  ];
+const interestingFieldCombinations = [
+  ['primaryName'],
+  ['additionalNames'],
+
+  ['primaryName', 'parentName', 'groups'],
+  ['primaryName', 'parentName'],
+  ['primaryName', 'groups', 'contributors'],
+  ['primaryName', 'groups', 'artTags'],
+  ['primaryName', 'groups'],
+  ['additionalNames', 'groups'],
+  ['primaryName', 'contributors'],
+  ['primaryName', 'artTags'],
+  ['parentName', 'groups', 'artTags'],
+  ['parentName', 'artTags'],
+  ['groups', 'contributors'],
+  ['groups', 'artTags'],
+
+  // This prevents just matching *everything* tagged "john" if you
+  // only search "john", but it actually supports matching more than
+  // *two* tags at once: "john rose lowas" works! This is thanks to
+  // flexsearch matching multiple field values in a single query.
+  ['artTags', 'artTags'],
+
+  ['contributors', 'parentName'],
+  ['contributors', 'groups'],
+  ['primaryName', 'contributors'],
+];
+
+function queryGenericIndex(query, options) {
+  return queryIndex({
+    indexKey: 'generic',
+    termsKey: 'genericTerms',
+  }, query, options);
+}
+
+function queryVerbatimIndex(query, options) {
+  return queryIndex({
+    indexKey: 'verbatim',
+    termsKey: 'verbatimTerms',
+  }, query, options);
+}
 
+function queryIndex({termsKey, indexKey}, query, options) {
   const interestingFields =
     unique(interestingFieldCombinations.flat());
 
-  const {genericTerms, queriedKind} =
+  const {[termsKey]: terms, queriedKind} =
     processTerms(query);
 
+  if (empty(terms)) return null;
+
   const particles =
-    particulate(genericTerms);
+    particulate(terms);
 
   const groupedParticles =
     groupArray(particles, ({length}) => length);
 
   const queriesBy = keys =>
     (groupedParticles.get(keys.length) ?? [])
-      .flatMap(permutations)
+      .flatMap(particles => Array.from(permutations(particles)))
       .map(values => values.map(({terms}) => terms.join(' ')))
       .map(values =>
         stitchArrays({
@@ -437,7 +551,7 @@ function queryGenericIndex(index, query, options) {
           query: values,
         }));
 
-  const boilerplate = queryBoilerplate(index);
+  const boilerplate = queryBoilerplate(indexes[indexKey]);
 
   const particleResults =
     Object.fromEntries(
@@ -459,62 +573,73 @@ function queryGenericIndex(index, query, options) {
             ])),
       ]));
 
-  const results = new Set();
+  let matchedResults = new Set();
 
   for (const interestingFieldCombination of interestingFieldCombinations) {
     for (const query of queriesBy(interestingFieldCombination)) {
-      const idToMatchingFieldsMap = new Map();
-      for (const {field, query: fieldQuery} of query) {
-        for (const id of particleResults[field][fieldQuery]) {
-          if (idToMatchingFieldsMap.has(id)) {
-            idToMatchingFieldsMap.get(id).push(field);
-          } else {
-            idToMatchingFieldsMap.set(id, [field]);
-          }
-        }
-      }
+      const [firstQueryFieldLine, ...restQueryFieldLines] = query;
 
       const commonAcrossFields =
-        Array.from(idToMatchingFieldsMap.entries())
-          .filter(([id, matchingFields]) =>
-            matchingFields.length === interestingFieldCombination.length)
-          .map(([id]) => id);
+        new Set(
+          particleResults
+            [firstQueryFieldLine.field]
+            [firstQueryFieldLine.query]);
+
+      for (const currQueryFieldLine of restQueryFieldLines) {
+        const tossResults = new Set(commonAcrossFields);
+
+        const keepResults =
+          particleResults
+            [currQueryFieldLine.field]
+            [currQueryFieldLine.query];
+
+        for (const result of keepResults) {
+          tossResults.delete(result);
+        }
+
+        for (const result of tossResults) {
+          commonAcrossFields.delete(result);
+        }
+      }
 
       for (const result of commonAcrossFields) {
-        results.add(result);
+        matchedResults.add(result);
       }
     }
   }
 
-  const constituted =
-    boilerplate.constitute(results);
+  matchedResults = Array.from(matchedResults);
+
+  const filteredResults =
+    (queriedKind
+      ? matchedResults.filter(id => id.split(':')[0] === queriedKind)
+      : matchedResults);
 
-  const constitutedAndFiltered =
-    constituted
-      .filter(({id}) =>
-        (queriedKind
-          ? id.split(':')[0] === queriedKind
-          : true));
+  const constitutedResults =
+    boilerplate.constitute(filteredResults);
 
-  return constitutedAndFiltered;
+  return constitutedResults;
 }
 
 function processTerms(query) {
   const kindTermSpec = [
-    {kind: 'album', terms: ['album']},
-    {kind: 'artist', terms: ['artist']},
-    {kind: 'flash', terms: ['flash']},
-    {kind: 'group', terms: ['group']},
-    {kind: 'tag', terms: ['art tag', 'tag']},
-    {kind: 'track', terms: ['track']},
+    {kind: 'album', terms: ['album', 'albums', 'release', 'releases']},
+    {kind: 'artist', terms: ['artist', 'artists']},
+    {kind: 'flash', terms: ['flash', 'flashes']},
+    {kind: 'group', terms: ['group', 'groups']},
+    {kind: 'tag', terms: ['art tag', 'art tags', 'tag', 'tags']},
+    {kind: 'track', terms: ['track', 'tracks']},
   ];
 
   const genericTerms = [];
+  const verbatimTerms = [];
   let queriedKind = null;
 
   const termRegexp =
     new RegExp(
-      String.raw`(?<kind>${kindTermSpec.flatMap(spec => spec.terms).join('|')})` +
+      String.raw`(?<kind>(?<=^|\s)(?:${kindTermSpec.flatMap(spec => spec.terms).join('|')})(?=$|\s))` +
+      String.raw`|(?<=^|\s)(?<quote>["'])(?<regularVerbatim>.+?)\k<quote>(?=$|\s)` +
+      String.raw`|(?<=^|\s)[“”‘’](?<curlyVerbatim>.+?)[“”‘’](?=$|\s)` +
       String.raw`|[^\s\-]+`,
       'gi');
 
@@ -530,10 +655,16 @@ function processTerms(query) {
       continue;
     }
 
+    const verbatim = groups.regularVerbatim || groups.curlyVerbatim;
+    if (verbatim) {
+      verbatimTerms.push(verbatim);
+      continue;
+    }
+
     genericTerms.push(match[0]);
   }
 
-  return {genericTerms, queriedKind};
+  return {genericTerms, verbatimTerms, queriedKind};
 }
 
 function particulate(terms) {
@@ -562,27 +693,6 @@ function particulate(terms) {
   return results;
 }
 
-// This function doesn't even come close to "performant",
-// but it only operates on small data here.
-function permutations(array) {
-  switch (array.length) {
-    case 0:
-      return [];
-
-    case 1:
-      return [array];
-
-    default:
-      return array.flatMap((item, index) => {
-        const behind = array.slice(0, index);
-        const ahead = array.slice(index + 1);
-        return (
-          permutations([...behind, ...ahead])
-            .map(rest => [item, ...rest]));
-      });
-  }
-}
-
 function queryBoilerplate(index) {
   const idToDoc = {};
 
diff --git a/src/static/js/xhr-util.js b/src/static/js/xhr-util.js
index 8a43072c..bc0698da 100644
--- a/src/static/js/xhr-util.js
+++ b/src/static/js/xhr-util.js
@@ -1,5 +1,3 @@
-/* eslint-env browser */
-
 /**
  * This fetch function is adapted from a `loadImage` function
  * credited to Parziphal, Feb 13, 2017.
diff --git a/src/static/misc/icons.svg b/src/static/misc/icons.svg
index 0c2cf3e9..87cb0169 100644
--- a/src/static/misc/icons.svg
+++ b/src/static/misc/icons.svg
@@ -34,6 +34,7 @@
     <path d="M 21.67012,2.5595806 C 21.281556,2.6425058 21.182046,2.7041073 20.355164,3.3769857 20.042418,3.6304997 19.575668,4.0072168 19.319784,4.2109758 18.793802,4.6327094 18.656383,4.7724975 18.535549,5.0165344 L 18.452624,5.1894925 17.308257,5.9926819 C 16.678026,6.4357391 16.092811,6.8479955 16.005148,6.9095971 L 15.848774,7.0209537 15.69714,6.9190742 15.545505,6.8148255 13.022211,6.4428469 C 11.633807,6.239088 10.475225,6.0661298 10.449162,6.059022 10.408885,6.0519141 10.378084,5.9997897 10.321221,5.8434166 10.226449,5.585164 10.001367,5.1468453 9.9326572,5.0852438 9.8639478,5.0212729 9.3237497,4.7014188 9.2858411,4.7014188 9.2479324,4.7014188 9.2479324,4.7037881 9.2811025,4.6066472 9.3024261,4.5474149 9.3355962,4.5189835 9.4327371,4.4763363 9.5725252,4.4123654 9.5843716,4.3886725 9.6341267,4.0190633 9.6578196,3.8484744 9.6554503,3.8129351 9.6246495,3.76318 9.5938488,3.7181635 9.5891102,3.6684084 9.6009566,3.5072966 L 9.6175417,3.305907 9.5322472,3.2656291 C 9.4777536,3.2395669 9.4351063,3.1945504 9.4066749,3.1329488 9.3403347,2.9836836 9.1365758,2.7917711 8.9541405,2.7041073 8.7977673,2.6306593 8.7835516,2.6282901 8.4850211,2.6282901 8.2007063,2.6282901 8.1675362,2.6330286 8.0395945,2.6922609 7.7837112,2.8130947 7.5491515,3.0926709 7.5112429,3.3201227 7.4993964,3.3983093 7.4828114,3.4243715 7.430687,3.4456951 7.2885296,3.5049274 7.2766831,3.5404667 7.2766831,3.8792752 7.2766831,4.2346687 7.2885296,4.2725773 7.430687,4.3389174 7.4828114,4.3626103 7.5254586,4.3934111 7.5254586,4.4099961 7.5254586,4.4242119 7.5301972,4.4526434 7.5373051,4.4692284 7.5467822,4.4952906 7.4614878,4.5284606 7.1724344,4.6042779 6.9639369,4.6611409 6.7649165,4.7251117 6.7270079,4.7488046 6.6748835,4.7796054 6.6346056,4.8506841 6.5493111,5.071028 6.3858301,5.4785459 6.3052742,5.7770765 6.246042,6.1917022 6.1702247,6.712946 6.2010255,6.9356593 6.3905687,7.2223434 6.4308466,7.2839449 6.4616474,7.3360693 6.4569088,7.3408079 6.4521702,7.3455464 6.2081334,7.5113967 5.9143414,7.7104171 5.2106623,8.1890137 4.0354944,9.0680203 3.9738929,9.1627919 3.8909677,9.2907335 3.7535489,11.00373 3.7203789,12.370811 L 3.7037938,12.993934 3.3104917,13.344589 C 2.7157999,13.870571 2.2229876,14.344429 1.5548478,15.022046 0.79193642,15.796804 0.78008997,15.81102 0.73744275,16.017148 0.69716482,16.21143 0.64504044,16.801383 0.65925618,16.924586 0.66873334,16.998034 0.64977902,17.038312 0.51946807,17.227855 0.05982581,17.888887 -0.09180875,18.675491 0.05271794,19.620838 0.19013676,20.51406 0.77772068,21.321988 1.3842589,21.44993 1.4742919,21.468884 3.966785,21.475992 10.354391,21.475992 H 19.196581 L 19.362431,21.378851 C 19.748626,21.146661 20.293562,20.663326 20.646587,20.241592 21.184415,19.597145 21.698551,18.587827 21.961543,17.661435 22.205579,16.803752 22.309828,16.038471 22.321675,14.995984 L 22.331152,14.379968 22.66996,14.135932 C 23.07274,13.844509 23.108279,13.813708 23.127233,13.728414 23.148557,13.626534 23.10354,13.493854 22.913997,13.095813 22.473309,12.174159 21.947327,11.607899 21.288664,11.342539 20.926163,11.195643 19.736779,11.032162 18.739308,10.989514 18.29862,10.97056 18.33179,10.994253 18.327052,10.695723 18.324682,10.463532 18.286774,10.321375 18.210956,10.266881 18.168309,10.23608 18.056953,10.212387 17.83187,10.183956 17.656543,10.162632 17.509647,10.143678 17.504908,10.141309 17.502539,10.13657 17.488323,10.044168 17.476477,9.9351804 17.443307,9.6698199 17.348535,9.2788871 17.237178,8.9448172 17.185054,8.7955519 17.144776,8.6699796 17.144776,8.6676103 17.144776,8.6628717 17.258502,8.5989009 17.400659,8.5254529 17.540447,8.4496356 18.128031,8.13452 18.706138,7.824143 L 19.760472,7.260252 H 19.959493 C 20.06848,7.260252 20.222484,7.243667 20.30304,7.2223434 20.419135,7.1915426 21.146507,6.8906428 22.890304,6.1514243 23.018246,6.0969306 23.243328,5.9476653 23.394963,5.8197237 23.641369,5.6088569 23.859344,5.2558327 23.949377,4.9170242 24.010978,4.6824645 24.018086,4.2180836 23.961223,3.9858932 23.781157,3.2466747 23.129603,2.6496137 22.395123,2.5477342 22.151086,2.5121948 21.859663,2.5169334 21.67012,2.5595806 Z M 22.357214,2.9268206 C 22.786055,2.99553 23.179358,3.2703676 23.418656,3.6636698 23.631892,4.0214326 23.688755,4.5331992 23.553705,4.9146549 23.40444,5.3387578 23.006399,5.7273214 22.594143,5.8505245 22.414077,5.9050181 22.018406,5.9239724 21.831232,5.8884331 21.317096,5.7865536 20.838499,5.3671893 20.672649,4.874377 20.644217,4.7843439 20.615786,4.6232322 20.606309,4.490552 L 20.592093,4.2631001 20.53523,4.4052576 C 20.504429,4.4834441 20.471259,4.6137551 20.464151,4.6966802 L 20.449936,4.8435762 19.620684,5.4145751 18.791433,5.9832047 18.772478,5.8813252 C 18.717985,5.5899026 18.810387,5.2202933 18.99993,4.9928415 19.158672,4.800929 20.33621,3.8105658 21.018565,3.2964298 21.475838,2.9481442 21.854925,2.8438954 22.357214,2.9268206 Z M 8.9778334,3.2822141 C 9.2147624,3.3177534 9.4303678,3.3746164 9.4493221,3.4054172 9.4801229,3.452803 9.4706457,3.6447155 9.4303678,3.7750264 9.3948284,3.8982295 9.3948284,3.8982295 9.3071647,3.8911216 9.2289781,3.8840138 9.2171317,3.8745366 9.1815923,3.7773957 9.1436837,3.6778855 9.1342065,3.6684084 9.032327,3.649454 8.8878004,3.6233919 8.8238295,3.6423462 8.7717051,3.7300099 8.7480122,3.7702878 8.7029957,3.8129351 8.6698257,3.8271508 8.6034856,3.8508437 8.3404944,3.8555823 8.2078141,3.8342587 8.1296276,3.8200429 8.1154118,3.8081965 8.0940882,3.7323792 8.0822418,3.6849934 8.0703953,3.5760061 8.0703953,3.4883423 8.0703953,3.2585212 8.0751339,3.2561519 8.4755439,3.2561519 8.6579792,3.2561519 8.8854311,3.2679984 8.9778334,3.2822141 Z M 9.0773436,3.943246 C 9.0962979,4.000109 9.1270986,4.0380176 9.179223,4.0593412 9.269256,4.0996191 9.269256,4.1043577 9.2052852,4.310486 9.1602687,4.4550126 9.1484223,4.4715977 9.0583892,4.5142449 8.925709,4.5734771 8.7574894,4.5711079 8.6129627,4.5095063 8.4755439,4.4502741 8.4281581,4.3768261 8.4092038,4.1920215 L 8.394988,4.0688184 8.5466226,4.0522333 C 8.7385351,4.0285404 8.8309374,3.9906318 8.8735846,3.9100759 8.9020161,3.8603209 8.9233397,3.8484744 8.9802027,3.853213 9.0370656,3.8579516 9.0560199,3.8769059 9.0773436,3.943246 Z M 7.8287277,5.0497044 C 8.3120629,5.5306703 9.0844514,5.6207033 9.326119,5.2274012 9.3971977,5.111306 9.4019363,5.1089367 9.4753843,5.1302603 9.6791432,5.1871233 9.6815125,5.1894925 9.6815125,5.3624507 9.6815125,5.5093467 9.7123133,5.7794458 9.743114,5.8979103 9.7549605,5.9500346 9.7502219,5.9500346 9.4753843,5.9642504 9.2976875,5.9737275 9.1318372,5.9974204 9.0181113,6.0305905 8.9209704,6.059022 8.5466226,6.2248723 8.1912291,6.3978304 7.6747239,6.6489752 7.5349358,6.7105767 7.5136121,6.6868838 7.4662263,6.6347594 7.210343,6.2153951 7.2198202,6.2059179 7.2269281,6.2011794 7.2885296,6.2343494 7.3596083,6.2793659 L 7.4899192,6.3622911 7.4970271,6.1419471 C 7.5088736,5.8647402 7.4685956,5.5140853 7.3927784,5.2250319 7.3619776,5.1018288 7.340654,4.9952108 7.3477618,4.9881029 7.357239,4.980995 7.4330563,4.9620407 7.52072,4.947825 7.6083837,4.9312399 7.6818317,4.9193935 7.6865703,4.9170242 7.6889396,4.9170242 7.7529104,4.9762564 7.8287277,5.0497044 Z M 18.542657,5.7249521 C 18.542657,5.895541 18.59952,6.1419471 18.668229,6.2864738 L 18.722723,6.4049383 16.448205,7.790973 C 15.19485,8.5515151 14.16184,9.1675305 14.152363,9.155684 14.140516,9.1462068 14.126301,9.0727589 14.119193,8.992203 14.102608,8.8216141 14.149993,8.6747181 14.251873,8.5775772 14.320582,8.5112371 18.495271,5.6064876 18.526072,5.6041183 18.535549,5.601749 18.542657,5.658612 18.542657,5.7249521 Z M 12.595739,6.7982405 C 14.100238,7.0138458 15.353593,7.1939119 15.379655,7.2010198 15.405717,7.2057583 15.445995,7.2247127 15.469688,7.2412977 15.507597,7.2697292 15.476796,7.2981606 15.166419,7.5161353 L 14.822872,7.7601722 13.25914,7.5445668 C 12.273516,7.4095173 11.662239,7.3360693 11.610115,7.3455464 11.517712,7.3621315 11.328169,7.480596 11.306845,7.5350896 11.299738,7.554044 11.3258,7.6914628 11.363708,7.8407281 11.456111,8.1984908 11.52482,8.6841953 11.543774,9.1130368 11.560359,9.4471067 11.55799,9.4636917 11.515343,9.4636917 11.437156,9.4636917 10.195648,9.2978414 10.183802,9.2883642 10.179063,9.2812564 10.160109,9.1296218 10.141155,8.9519251 10.079553,8.3193246 9.9326572,7.6914628 9.6886204,7.0019994 9.6175417,6.7982405 9.5535708,6.6015894 9.546463,6.56605 9.5275087,6.4618013 9.5914795,6.4073076 9.7383755,6.4073076 9.8047156,6.4073076 11.09124,6.5826351 12.595739,6.7982405 Z M 13.247294,7.8881139 C 13.870417,7.9734083 14.384553,8.0468563 14.389292,8.0515949 14.39403,8.0563334 14.292151,8.1321507 14.164209,8.2198145 14.02916,8.3122168 13.915434,8.4046191 13.898849,8.4425277 13.839616,8.5681001 13.711675,8.9400786 13.602687,9.2978414 13.541086,9.4992311 13.484223,9.6721892 13.474746,9.6816664 13.467638,9.6911435 13.164369,9.6650814 12.801867,9.6248034 12.230869,9.5584633 12.145574,9.5442476 12.15979,9.5134468 12.169267,9.4921232 12.211914,9.3736587 12.256931,9.2480863 12.42515,8.7576433 12.380134,8.3027396 12.121881,7.9023296 12.069757,7.8217737 12.02711,7.750695 12.02711,7.7459565 12.02711,7.7246328 12.117143,7.73411 13.247294,7.8881139 Z M 16.846245,9.0111573 C 16.933909,9.2338706 17.040527,9.6698199 17.073697,9.9328111 17.087913,10.053645 17.09739,10.157894 17.092652,10.160263 17.085544,10.16974 16.405558,10.086815 16.322632,10.067861 16.272877,10.056014 16.272877,10.053645 16.310786,9.9446576 16.339217,9.8617324 16.348695,9.7290522 16.351064,9.4423681 L 16.353433,9.0561738 16.542976,8.9519251 C 16.644856,8.8950621 16.741997,8.8476763 16.758582,8.8476763 16.772797,8.8476763 16.813075,8.9211243 16.846245,9.0111573 Z M 15.983824,9.6224341 C 15.971977,9.7290522 15.950654,9.8617324 15.934069,9.9138568 15.910376,9.9920434 15.89616,10.008628 15.844036,10.008628 15.737418,10.006259 14.938967,9.8949025 14.922382,9.8783175 14.884473,9.8427781 14.986353,9.776438 15.46258,9.5252932 L 15.971977,9.2575635 15.988562,9.3428579 C 15.99804,9.3902437 15.99567,9.5158161 15.983824,9.6224341 Z M 14.06233,10.162632 C 16.152043,10.416146 17.872148,10.624644 17.883994,10.624644 17.893472,10.624644 17.902949,10.693353 17.902949,10.778648 V 10.932652 H 17.635219 C 17.29878,10.932652 17.23007,10.965822 17.038158,11.209858 16.943386,11.333062 16.888893,11.382817 16.853353,11.382817 16.824922,11.382817 16.052533,11.30463 15.140357,11.207489 L 13.477115,11.034531 13.339696,10.911328 C 12.915593,10.532241 12.406196,10.297682 11.768857,10.186325 11.337646,10.112877 10.366237,10.008628 10.103246,10.008628 10.065337,10.008628 10.060599,9.9849355 10.060599,9.8404088 V 9.6745585 L 10.162478,9.6887743 C 10.216972,9.6958821 11.972616,9.9091182 14.06233,10.162632 Z M 10.084292,10.420885 C 11.261829,10.501441 11.59116,10.541719 12.055541,10.660183 12.380134,10.740739 12.522291,10.802341 12.716573,10.93739 13.081444,11.190904 13.446314,11.6387 13.66192,12.098342 13.749583,12.285516 13.853832,12.5722 13.841986,12.584047 13.82777,12.600632 10.003736,12.259454 9.9895202,12.242869 9.980043,12.233392 9.9445037,12.13862 9.9065951,12.034371 9.7146826,11.479957 9.3877205,11.046377 9.015742,10.849726 8.8190909,10.747847 8.8214602,10.747847 8.378403,10.890004 7.8026655,11.077178 6.956829,11.43968 6.1749633,11.835351 5.8172005,12.015417 5.6134416,12.122035 4.8718538,12.522445 4.7462814,12.591155 4.7439121,12.591155 4.8126215,12.531922 4.9002853,12.456105 5.5636865,12.008309 5.8669556,11.816397 6.776763,11.247767 7.7766033,10.762063 8.6508714,10.465901 L 8.9588791,10.361653 9.2550403,10.373499 C 9.4185213,10.380607 9.7904998,10.401931 10.084292,10.420885 Z M 18.779586,11.382817 C 19.575668,11.425464 20.70345,11.574729 21.042258,11.683716 21.478207,11.823505 21.883356,12.157574 22.212687,12.648017 22.33589,12.832822 22.577558,13.256925 22.615467,13.356435 22.63916,13.415667 22.63679,13.420406 22.575189,13.420406 22.489894,13.420406 19.556713,13.13846 19.551975,13.131353 19.549605,13.126614 19.523543,13.029473 19.495112,12.911009 19.334,12.266562 18.893312,11.745318 18.251234,11.43968 18.151724,11.392294 18.068799,11.352016 18.068799,11.347277 18.068799,11.344908 18.118554,11.344908 18.182525,11.349647 18.244127,11.354385 18.511856,11.368601 18.779586,11.382817 Z M 15.384394,11.643439 C 16.990772,11.797442 17.050004,11.80692 17.424352,11.991724 17.886364,12.216807 18.234649,12.541399 18.409977,12.90627 18.549765,13.197693 18.594781,13.403821 18.608997,13.832662 L 18.620844,14.185687 18.516595,14.171471 C 18.459732,14.164363 17.33195,14.060114 16.014625,13.939281 L 13.614534,13.718937 V 13.562563 13.40619 L 13.946235,13.091075 C 14.247134,12.80676 14.277935,12.768851 14.277935,12.695403 14.277935,12.517707 14.031529,11.875629 13.846724,11.577098 L 13.799339,11.501281 H 13.858571 C 13.891741,11.501281 14.578835,11.565252 15.384394,11.643439 Z M 9.0394349,12.8731 C 9.4114134,13.598103 9.5132929,14.531603 9.3237497,15.469842 9.1531608,16.31094 8.6840414,17.092805 8.124889,17.462415 7.8239891,17.663804 7.6486617,17.718298 7.3003761,17.720667 7.0658163,17.720667 6.9899991,17.71119 6.8596881,17.666174 6.5469418,17.559556 6.2531499,17.313149 6.0185902,16.960125 5.7011053,16.483898 5.5399936,15.960285 5.4997157,15.27556 L 5.4831306,15.019677 5.6205494,14.924905 C 5.6963667,14.872781 5.7674454,14.825395 5.7769226,14.823026 5.7863997,14.818287 5.7958769,14.927274 5.7958769,15.062324 5.7958769,15.199743 5.8029848,15.327684 5.8100926,15.349008 5.8219391,15.379809 5.8645863,15.386917 6.0209595,15.386917 6.1275775,15.386917 6.2649963,15.394025 6.3242286,15.401132 L 6.4308466,15.415348 6.4687553,15.574091 C 6.4877096,15.659385 6.5137718,15.756526 6.5208796,15.787327 L 6.5374647,15.84419 6.2270877,15.832343 C 5.878802,15.818127 5.8906485,15.81102 5.9427729,15.981608 L 5.9688351,16.074011 H 6.2957971 6.6227591 L 6.7080536,16.24223 C 6.8691653,16.564454 7.0895092,16.820337 7.3216997,16.950648 L 7.4377949,17.019358 7.3453926,17.038312 C 7.210343,17.066743 6.8644267,17.059635 6.7672858,17.026465 6.6914685,17.000403 6.6890992,17.002772 6.7293772,17.033573 6.8170409,17.099913 7.0397541,17.170992 7.2056045,17.180469 7.3998862,17.194685 7.684201,17.128345 7.8524206,17.031204 7.994578,16.948279 8.2386149,16.713719 8.3760337,16.528914 8.9138625,15.801542 9.1128829,14.64059 8.8617382,13.702352 8.8048752,13.491485 8.6485021,13.133722 8.5466226,12.979718 L 8.4968675,12.90627 8.6816721,12.77359 C 8.7811823,12.700142 8.8759539,12.64091 8.8925389,12.63854 8.9067547,12.63854 8.9730948,12.745158 9.0394349,12.8731 Z M 11.778334,13.396713 C 12.581523,13.448837 13.25914,13.491485 13.285203,13.491485 13.32785,13.491485 13.330219,13.512808 13.330219,13.977189 V 14.465263 L 13.218862,14.451047 C 13.154892,14.443939 12.941656,14.427354 12.740266,14.413139 12.446474,14.391815 12.373026,14.394184 12.356441,14.417877 12.346964,14.434462 12.330379,14.526864 12.320902,14.624005 12.309055,14.759055 12.313794,14.806441 12.337487,14.830134 12.365918,14.853826 13.022211,14.913059 13.268618,14.913059 13.311265,14.913059 13.313634,14.927274 13.299418,15.202112 13.282833,15.47695 13.1928,16.287247 13.173846,16.308571 13.169107,16.313309 12.955871,16.303832 12.699988,16.289616 12.318532,16.265923 12.22613,16.265923 12.202437,16.291985 12.185852,16.308571 12.157421,16.400973 12.140836,16.493375 12.117143,16.630794 12.117143,16.671072 12.140836,16.694765 12.162159,16.716088 12.323271,16.735043 12.619432,16.751628 12.868208,16.765843 13.074336,16.782429 13.079074,16.787167 13.098029,16.806121 12.92744,17.483738 12.818452,17.820178 L 12.707096,18.158986 12.562569,18.156617 C 12.484383,18.156617 12.264039,18.14714 12.074495,18.135293 11.820981,18.121077 11.719102,18.123447 11.688301,18.142401 11.633807,18.17794 11.503497,18.47884 11.520082,18.526226 11.539036,18.573612 11.603007,18.58072 12.081603,18.599674 12.311424,18.606782 12.500968,18.623367 12.500968,18.632844 12.500968,18.687338 12.140836,19.355477 11.984462,19.592406 L 11.799658,19.871983 11.25709,19.867244 10.712154,19.862506 10.593689,20.009401 C 10.363868,20.293716 10.370976,20.312671 10.686091,20.317409 10.804556,20.319778 11.008315,20.326886 11.136257,20.336364 L 11.373186,20.350579 11.200227,20.499845 C 10.998838,20.672803 10.759539,20.836284 10.508395,20.976072 L 10.328329,21.073213 8.6911493,21.068474 7.0516006,21.061366 7.2624674,20.900255 C 8.4708053,19.983339 9.4635378,18.17794 9.8686864,16.156936 9.9989974,15.50775 10.053491,14.995984 10.08903,14.053006 L 10.117462,13.301942 H 10.216972 C 10.273835,13.301942 10.975145,13.344589 11.778334,13.396713 Z M 20.608678,14.216487 21.880987,14.330213 21.892833,14.424985 C 21.899941,14.479479 21.909418,14.631113 21.916526,14.763793 L 21.926003,15.00783 H 21.817016 C 21.755414,15.00783 21.556394,14.995984 21.376328,14.981768 21.110967,14.962814 21.039889,14.962814 21.018565,14.988876 20.973549,15.036262 20.952225,15.351377 20.990134,15.384547 21.006719,15.396394 21.222324,15.422456 21.46873,15.439041 L 21.914157,15.467473 21.897572,15.735202 C 21.883356,16.005301 21.833601,16.419927 21.783846,16.671072 L 21.757784,16.808491 H 21.589564 C 21.497162,16.806121 21.279187,16.796644 21.10386,16.784798 20.928532,16.775321 20.772159,16.770582 20.755574,16.77769 20.715296,16.791906 20.630002,17.118868 20.653694,17.166253 20.672649,17.206531 20.722404,17.21127 21.381067,17.249179 21.584825,17.261025 21.646427,17.272872 21.646427,17.298934 21.646427,17.355797 21.373959,18.161355 21.298141,18.324836 L 21.229432,18.47884 20.933271,18.462255 C 20.772159,18.455147 20.53523,18.44567 20.407288,18.44567 L 20.177467,18.443301 20.089803,18.618628 C 20.042418,18.7134 20.011617,18.808171 20.018725,18.829495 20.032941,18.867404 20.115866,18.87925 20.70345,18.910051 L 21.025673,18.929005 20.859823,19.21332 C 20.76979,19.369693 20.613417,19.61373 20.513906,19.755887 L 20.331471,20.011771 19.786534,20.009401 19.241598,20.007032 19.104179,20.177621 C 19.028362,20.270023 18.969129,20.362426 18.973868,20.383749 18.980976,20.41455 19.040208,20.424027 19.348216,20.438243 19.549605,20.44772 19.758103,20.459567 19.807858,20.461936 L 19.90263,20.469044 19.70124,20.637263 C 19.592253,20.732035 19.419294,20.867084 19.315046,20.940532 L 19.130241,21.073213 H 17.829501 16.53113 L 16.941017,20.663326 C 17.376966,20.229745 17.639958,19.902783 17.917165,19.44551 18.53318,18.433824 18.969129,17.116498 19.144457,15.74231 19.196581,15.334792 19.236859,14.671391 19.227382,14.358645 L 19.217905,14.081438 19.277137,14.093284 C 19.310307,14.100392 19.909737,14.154886 20.608678,14.216487 Z M 4.9997955,16.074011 C 5.0211191,16.168782 5.0329655,16.254077 5.0282269,16.261185 5.0187498,16.2754 4.7249578,16.47679 3.6895781,17.1781 L 3.2654752,17.464784 3.1446414,17.303672 C 3.0783013,17.213639 2.9740525,17.102283 2.9100817,17.052528 2.8484801,17.002772 2.7963558,16.950648 2.7939865,16.938802 2.7916172,16.924586 3.2725831,16.5763 3.860167,16.164044 L 4.931086,15.412979 4.9476711,15.657016 C 4.9571482,15.792065 4.9808411,15.979239 4.9997955,16.074011 Z M 2.5309953,17.481369 C 2.6589369,17.642481 2.822418,17.983659 2.8840195,18.218218 2.985899,18.618628 2.9645753,19.237013 2.8318951,19.684809 2.6897377,20.161036 2.3296056,20.639633 1.9884279,20.803114 1.8770712,20.855238 1.8083618,20.871823 1.6377729,20.878931 1.5193084,20.883669 1.3795203,20.876562 1.3250267,20.862346 1.1710228,20.819699 0.95304814,20.660956 0.82984506,20.497475 0.59054677,20.17999 0.45075866,19.78195 0.42469647,19.343631 0.41285002,19.125656 0.44602008,18.637583 0.47919014,18.554657 0.4886673,18.526226 0.68294908,18.393546 0.69479553,18.405392 0.69953411,18.410131 0.68768766,18.481209 0.66636405,18.564135 0.62608612,18.737093 0.59291606,19.234644 0.61660896,19.329415 0.6308247,19.388648 0.63556328,19.391017 0.82747577,19.391017 H 1.0217575 L 1.0596662,19.533174 1.0975748,19.675332 H 0.88670802 C 0.6782105,19.675332 0.67584121,19.675332 0.69005695,19.727456 0.71611914,19.81275 0.73033488,19.817489 0.94594027,19.834074 L 1.1520685,19.84829 1.2255165,19.98097 C 1.3084416,20.125497 1.5358935,20.376641 1.6046029,20.397965 1.6306651,20.407442 1.6496194,20.421658 1.6496194,20.433504 1.6496194,20.466674 1.3937361,20.487998 1.2942259,20.461936 1.1615457,20.428766 1.1994543,20.469044 1.343981,20.51643 1.5027234,20.570923 1.7467603,20.554338 1.8960256,20.483259 2.0310751,20.416919 2.2419419,20.208422 2.3627757,20.025987 2.7252771,19.462095 2.7797707,18.58072 2.4859788,17.971812 2.4054229,17.803592 2.1874482,17.550078 2.0666144,17.483738 2.0287058,17.462415 2.038183,17.448199 2.161386,17.360535 L 2.3011741,17.263394 2.3675143,17.31078 C 2.4054229,17.336842 2.4788709,17.41266 2.5309953,17.481369 Z M 4.7154807,17.502693 4.8268373,17.640111 4.4785517,17.805962 C 4.2866392,17.895995 4.1255274,17.967074 4.1231582,17.962335 4.0994653,17.941011 4.0686645,17.696974 4.0852495,17.680389 4.1089424,17.656697 4.5970162,17.355797 4.6017547,17.360535 4.604124,17.362905 4.6538791,17.426875 4.7154807,17.502693 Z M 6.6867299,18.296405 C 6.8762731,18.393546 7.0563392,18.670753 7.136895,18.988237 7.1961273,19.21332 7.1890194,19.630315 7.1250486,19.900414 7.0113227,20.376641 6.7222693,20.798375 6.3976766,20.957117 6.262627,21.023458 6.2318263,21.030565 6.0612374,21.023458 5.8148312,21.011611 5.6868896,20.935794 5.5328857,20.710711 5.3575582,20.457197 5.3101725,20.277131 5.3101725,19.864875 5.3101725,19.44551 5.3528197,19.239382 5.4997157,18.94559 5.682151,18.585458 5.9143414,18.348529 6.172594,18.265604 6.3218593,18.218218 6.5611576,18.232434 6.6867299,18.296405 Z M 4.4003651,18.827126 C 4.6491405,18.966914 4.8007751,19.31283 4.8007751,19.739302 4.8007751,20.172882 4.6799413,20.533015 4.440643,20.798375 4.1658054,21.106383 3.7819804,21.139553 3.54979,20.871823 3.3744625,20.675172 3.2844295,20.395596 3.2844295,20.063895 3.2844295,19.616099 3.4171097,19.260706 3.6872088,18.983499 3.860167,18.808171 3.9241378,18.77974 4.1586975,18.777371 4.2724234,18.775001 4.3316557,18.786848 4.4003651,18.827126 Z"/>
   </symbol>
 
+  <symbol id="icon-nintendoMusic" viewBox="0 0 114 114"><path d="m 25.732087,113.50416 c -6.588515,-0.68012 -8.695543,-1.22214 -12.553652,-3.22936 -1.762772,-0.9171 -2.993406,-1.82849 -4.819732,-3.56942 -4.002744,-3.81559 -6.122812,-7.644635 -7.313171,-13.208305 -0.57951298,-2.7086 -0.57982798,-2.7284 -0.57982798,-36.44445 0,-33.62928 0.0018,-33.74274 0.57492598,-36.41917 1.686613,-7.87615 6.06013,-13.7685635 12.652915,-17.0471985 6.30684,-3.13643301 6.476747,-3.14880001 43.259379,-3.14880001 36.83125,0 36.91674,0.0063 43.334176,3.17739101 2.30211,1.13757 3.12687,1.734291 5.36135,3.879006 3.97494,3.8152415 5.99397,7.4652015 7.1956,13.0080215 0.53982,2.4901 0.5558,3.30337 0.66081,33.64033 0.0716,20.68964 0.0125,32.32756 -0.17666,34.79271 -0.60064,7.82649 -2.45786,12.343825 -6.99853,17.022595 -2.97211,3.0625 -5.98498,4.91141 -10.136116,6.22023 -4.18488,1.31945 -4.22496,1.32091 -37.91772,1.37306 -17.389737,0.0269 -32.034424,0.006 -32.543747,-0.0466 z M 50.073754,90.083395 c 5.28821,-1.00894 11.01156,-4.94764 13.30048,-9.15315 1.62746,-2.99019 1.61562,-2.84326 1.71693,-21.30579 l 0.0926,-16.87504 1.51367,0.80678 c 3.87465,2.06517 6.93228,5.07249 7.98053,7.84925 1.15152,3.0503 0.16775,6.87834 -2.63159,10.24005 -1.08169,1.29899 -1.22105,1.64952 -0.86804,2.18341 0.30452,0.46055 5.11665,4.07164 5.42586,4.07164 0.99334,0 4.56281,-5.3344 5.63772,-8.42531 2.66837,-7.67291 -1.02275,-15.80495 -10.79188,-23.77593 -4.79047,-3.90873 -5.90889,-5.43704 -6.2796,-8.58104 -0.10769,-0.91337 -0.34912,-1.69723 -0.56478,-1.83372 -0.20718,-0.13112 -1.37934,-0.24022 -2.60481,-0.24245 -3.55907,-0.006 -3.19583,-2.32457 -3.19583,20.39499 v 19.52328 l -1.19679,-0.52939 c -7.671865,-3.39363 -19.019411,0.11167 -23.831713,7.36171 -3.779121,5.69349 -2.626548,12.55586 2.702974,16.0934 3.63067,2.4099 8.421397,3.18425 13.594279,2.19731 z"/></symbol>
   <symbol id="icon-soundcloud" viewBox="0 0 40 40"><path d="M13.8,27.4l0.3-4.2L13.8,14c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.1-0.2-0.1-0.3-0.1c-0.1,0-0.2,0-0.3,0.1C13.1,13.8,13,13.9,13,14 l-0.2,9.2l0.2,4.2c0,0.1,0.1,0.2,0.1,0.3c0.1,0.1,0.2,0.1,0.3,0.1C13.7,27.8,13.8,27.7,13.8,27.4z M18.8,26.9l0.2-3.7l-0.2-10.3 c0-0.2-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.1-0.3-0.1s-0.2,0-0.3,0.1c-0.1,0.1-0.2,0.2-0.2,0.4l0,0.1l-0.2,10.1c0,0,0.1,1.4,0.2,4.1v0 c0,0.1,0,0.2,0.1,0.3c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.2-0.2,0.2-0.4L18.8,26.9z M1.2,20.9l0.3,2.2 l-0.3,2.2c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.1-0.1-0.2-0.2l-0.3-2.2l0.3-2.2c0-0.1,0.1-0.2,0.2-0.2S1.2,20.8,1.2,20.9z M2.7,19.5 l0.4,3.6l-0.4,3.6c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2L2,23.2l0.4-3.6c0-0.1,0.1-0.2,0.2-0.2C2.6,19.4,2.7,19.4,2.7,19.5z M4.2,18.9l0.4,4.3l-0.4,4.2c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2l-0.4-4.2l0.4-4.3c0-0.1,0.1-0.2,0.2-0.2 C4.2,18.7,4.2,18.7,4.2,18.9z M5.8,18.8l0.4,4.4l-0.4,4.3c0,0.2-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2L5,23.2l0.4-4.4 c0-0.2,0.1-0.2,0.2-0.2C5.7,18.5,5.8,18.6,5.8,18.8z M7.4,19.1l0.4,4.1l-0.4,4.3c0,0.2-0.1,0.3-0.3,0.3c-0.1,0-0.1,0-0.2-0.1 c-0.1-0.1-0.1-0.1-0.1-0.2l-0.3-4.3l0.3-4.1c0-0.1,0-0.1,0.1-0.2C7,18.8,7,18.8,7.1,18.8C7.3,18.8,7.4,18.9,7.4,19.1L7.4,19.1z M9,16.5l0.4,6.7L9,27.5c0,0.1,0,0.2-0.1,0.2c-0.1,0.1-0.1,0.1-0.2,0.1c-0.2,0-0.3-0.1-0.3-0.3l-0.3-4.3l0.3-6.7 c0-0.2,0.1-0.3,0.3-0.3c0.1,0,0.1,0,0.2,0.1S9,16.4,9,16.5z M10.5,15l0.3,8.2l-0.3,4.3c0,0.1,0,0.2-0.1,0.2 c-0.1,0.1-0.1,0.1-0.2,0.1c-0.2,0-0.3-0.1-0.3-0.3l-0.3-4.3L9.9,15c0-0.2,0.1-0.3,0.3-0.3c0.1,0,0.2,0,0.2,0.1 C10.5,14.8,10.5,14.9,10.5,15z M12.2,14.3l0.3,8.9l-0.3,4.2c0,0.2-0.1,0.4-0.4,0.4c-0.2,0-0.3-0.1-0.4-0.4l-0.3-4.2l0.3-8.9 c0-0.1,0-0.2,0.1-0.3c0.1-0.1,0.2-0.1,0.2-0.1c0.1,0,0.2,0,0.3,0.1C12.1,14.1,12.2,14.2,12.2,14.3z M18.8,27.3L18.8,27.3L18.8,27.3z M15.4,14.2l0.3,8.9l-0.3,4.2c0,0.1,0,0.2-0.1,0.3c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3-0.1s-0.1-0.2-0.1-0.3l-0.2-4.2 l0.2-8.9c0-0.1,0-0.2,0.1-0.3c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0,0.3,0.1C15.4,14,15.4,14.1,15.4,14.2L15.4,14.2z M17.1,14.6 l0.2,8.6l-0.2,4.1c0,0.1,0,0.2-0.1,0.3c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3-0.1c-0.1-0.1-0.1-0.2-0.2-0.3L16,23.2l0.2-8.6 c0-0.1,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0,0.3,0.1C17.1,14.3,17.1,14.4,17.1,14.6z M20.7,23.2l-0.2,4 c0,0.2-0.1,0.3-0.2,0.4c-0.1,0.1-0.2,0.2-0.4,0.2c-0.1,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.2-0.2-0.2-0.4l-0.1-2l-0.1-2L19.4,12V12 c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0,0.3,0.1c0.2,0.1,0.2,0.2,0.3,0.5L20.7,23.2z M39.4,22.9 c0,1.4-0.5,2.5-1.4,3.5c-0.9,1-2,1.4-3.4,1.4H21.4c-0.1,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.2-0.2-0.2-0.4V11.5c0-0.3,0.2-0.5,0.5-0.6 c1-0.4,2-0.6,3-0.6c2.2,0,4.1,0.8,5.7,2.3c1.6,1.5,2.5,3.4,2.7,5.7c0.6-0.3,1.2-0.4,1.8-0.4c1.3,0,2.4,0.5,3.4,1.5 C38.9,20.3,39.4,21.5,39.4,22.9L39.4,22.9z"/></symbol>
   <symbol id="icon-steam" viewBox="-4 -4 96.32 96.47"><path d="M 44.084,0 C 20.846,0 1.809,17.918 0,40.689 l 23.71,9.803 c 2.009,-1.374 4.436,-2.179 7.047,-2.179 0.234,0 0.467,0.01 0.698,0.021 L 41.999,33.051 c 0,-0.073 0,-0.144 0,-0.216 0,-9.199 7.483,-16.683 16.683,-16.683 9.199,0 16.682,7.484 16.682,16.683 0,9.199 -7.483,16.684 -16.682,16.684 -0.127,0 -0.253,0 -0.379,-0.01 l -15.038,10.73 c 0.01,0.195 0.015,0.394 0.015,0.592 0,6.906 -5.617,12.522 -12.522,12.522 -6.061,0 -11.129,-4.326 -12.277,-10.055 L 1.524,56.292 c 5.25,18.568 22.309,32.181 42.56,32.181 24.432,0 44.237,-19.806 44.237,-44.235 C 88.321,19.805 68.515,0 44.084,0" /><path d="m 27.721,67.122 -5.434,-2.245 c 0.963,2.005 2.629,3.684 4.841,4.606 4.782,1.992 10.295,-0.277 12.288,-5.063 0.965,-2.314 0.971,-4.869 0.014,-7.189 C 38.475,54.91 36.673,53.1 34.356,52.134 32.057,51.177 29.594,51.212 27.43,52.029 l 5.613,2.321 c 3.527,1.47 5.195,5.52 3.725,9.047 -1.467,3.528 -5.52,5.196 -9.047,3.725" /><path d="m 69.796,32.835 c 0,-6.129 -4.986,-11.116 -11.116,-11.116 -6.129,0 -11.116,4.987 -11.116,11.116 0,6.13 4.987,11.115 11.116,11.115 6.13,0 11.116,-4.986 11.116,-11.115 M 50.348,32.816 c 0,-4.612 3.739,-8.35 8.351,-8.35 4.612,0 8.351,3.738 8.351,8.35 0,4.612 -3.739,8.35 -8.351,8.35 -4.612,0 -8.351,-3.739 -8.351,-8.35" /></symbol>
   <symbol id="icon-tiktok" viewBox="0 0 5 5.292"><path fill-rule="evenodd" clip-rule="evenodd" d="M 3.0056593,3.7402047 C 2.9917683,4.1032048 2.686077,4.394531 2.3113304,4.394531 c -0.08567,0 -0.1676888,-0.015266 -0.2434637,-0.043152 0.075775,0.027886 0.1578202,0.043152 0.2434902,0.043152 0.3747466,0 0.6804387,-0.2913262 0.6943554,-0.6542999 l 0.00132,-3.24141643 h 0.6058809 c 0.058393,0.30815327 0.2455538,0.57257183 0.5048391,0.73777393 7.93e-5,1.058e-4 1.852e-4,2.117e-4 2.645e-4,3.175e-4 0.1804944,0.1149589 0.3957012,0.1820292 0.6267297,0.1820292 v 0.1800449 c 0,0 0,0 2.65e-5,2.65e-5 V 2.227616 c -0.4291437,0 -0.8268027,-0.1341672 -1.1513855,-0.3618624 v 1.6436603 c 0,0.8208777 -0.6833226,1.4886974 -1.5232747,1.4886974 -0.3245565,0 -0.6255391,-0.1000367 -0.8729448,-0.269816 C 1.1970357,4.728163 1.1969034,4.7280043 1.1967446,4.727872 0.80416835,4.4583206 0.54689375,4.0127459 0.54689375,3.5092552 c 0,-0.8208513 0.68332265,-1.4886974 1.52327485,-1.4886974 0.069689,0 0.1380032,0.00561 0.2052587,0.014526 V 2.22669 C 1.5091597,2.2441785 0.88092205,2.817015 0.79752745,3.5485978 0.88100145,2.8170944 1.5091863,2.2443373 2.2754008,2.2268487 v 0.6340861 c -0.06498,-0.01987 -0.1336642,-0.031432 -0.2052851,-0.031432 -0.3835836,0 -0.6956521,0.3050312 -0.6956521,0.6799109 0,0.2610585 0.1515497,0.4878806 0.3729741,0.6017548 0,2.64e-5 0,2.64e-5 0,2.64e-5 0.096544,0.049661 0.2062111,0.078103 0.3226514,0.078103 0.3747467,0 0.6804387,-0.2913261 0.6943554,-0.6542997 l 0.00132,-3.24144299 H 3.593361 c 0,0.0701124 0.00691,0.13863846 0.019525,0.20525906 H 3.0070087 Z" /></symbol>
diff --git a/src/static/misc/image.svg b/src/static/misc/image.svg
new file mode 100644
index 00000000..a251b373
--- /dev/null
+++ b/src/static/misc/image.svg
@@ -0,0 +1,11 @@
+<!-- Copyright © (c) 2019-2023 The Bootstrap authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software. -->
+
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-image-fill" viewBox="0 0 16 16">
+  <path d="M.002 3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-12a2 2 0 0 1-2-2V3zm1 9v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12zm5-6.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0z"/>
+</svg>