diff --git a/themes/2026/assets/css/content.css b/themes/2026/assets/css/content.css
index fe5e877b..e6d82c0b 100644
--- a/themes/2026/assets/css/content.css
+++ b/themes/2026/assets/css/content.css
@@ -858,6 +858,49 @@ aside section dl > div {
padding-top: var(--space-2);
}
+body:has(> header.article-header) > main > aside.article-complementary-images {
+ width: min(100%, var(--max-width-reading));
+ margin-inline: auto;
+ margin-top: var(--space-6);
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section {
+ margin-top: 0;
+ border-top: 1px solid var(--color-border);
+ padding-top: var(--space-3);
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section > h2 {
+ margin-top: 0;
+ font-size: 1.02rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section > ul {
+ list-style: none;
+ margin-top: var(--space-3);
+ padding-left: 0;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
+ gap: var(--space-3);
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section > ul > li {
+ margin-top: 0;
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section > ul > li > a {
+ display: block;
+ border: 1px solid var(--color-border);
+}
+
+body:has(> header.article-header) > main > aside.article-complementary-images > section > ul > li > a > img {
+ width: 100%;
+ height: auto;
+ display: block;
+}
+
body:has(> header.article-header) > main > aside.article-toc {
position: fixed;
inset-inline-end: var(--space-2);
diff --git a/themes/2026/layouts/_partials/asides/complementary-images.html b/themes/2026/layouts/_partials/asides/complementary-images.html
new file mode 100644
index 00000000..3a9ab37e
--- /dev/null
+++ b/themes/2026/layouts/_partials/asides/complementary-images.html
@@ -0,0 +1,115 @@
+{{- $page := . -}}
+{{- $bundleImages := slice -}}
+{{- range $page.Resources.ByType "image" -}}
+ {{- if strings.HasPrefix .Name "images/" -}}
+ {{- $bundleImages = $bundleImages | append . -}}
+ {{- end -}}
+{{- end -}}
+
+{{- if gt (len $bundleImages) 0 -}}
+ {{- $coverPath := "" -}}
+ {{- with $page.Params.cover -}}
+ {{- $coverPath = strings.TrimSpace (printf "%v" .) -}}
+ {{- end -}}
+ {{- $coverImagePath := "" -}}
+ {{- if ne $coverPath "" -}}
+ {{- $coverPathLower := lower $coverPath -}}
+ {{- if or (strings.HasSuffix $coverPathLower ".yaml") (strings.HasSuffix $coverPathLower ".yml") -}}
+ {{- $coverDataFile := $page.Resources.GetMatch $coverPath -}}
+ {{- if $coverDataFile -}}
+ {{- $coverData := $coverDataFile.Content | transform.Unmarshal -}}
+ {{- with index $coverData "file" -}}
+ {{- $coverImagePath = strings.TrimSpace (printf "%v" .) -}}
+ {{- end -}}
+ {{- end -}}
+ {{- else -}}
+ {{- $coverImagePath = $coverPath -}}
+ {{- end -}}
+ {{- end -}}
+ {{- $coverImageName := "" -}}
+ {{- if ne $coverImagePath "" -}}
+ {{- $coverImageName = lower (path.Base $coverImagePath) -}}
+ {{- end -}}
+
+ {{- $contentImageNames := slice -}}
+ {{- $contentImageStems := slice -}}
+ {{- $imageTags := findRE `(?is)
]+src=["'][^"']+["'][^>]*>` .Content -}}
+ {{- range $imageTags -}}
+ {{- $srcMatch := findRESubmatch `(?is)src=["']([^"']+)["']` . -}}
+ {{- if gt (len $srcMatch) 0 -}}
+ {{- $src := lower (index (index $srcMatch 0) 1) -}}
+ {{- if or (in $src "/images/") (strings.HasPrefix $src "images/") (strings.HasPrefix $src "./images/") -}}
+ {{- $srcName := path.Base $src -}}
+ {{- $srcName = replaceRE "\\?.*$" "" $srcName -}}
+ {{- $srcName = replaceRE "#.*$" "" $srcName -}}
+ {{- $srcName = replaceRE "_hu_[^.]*" "" $srcName -}}
+ {{- $srcStem := $srcName | replaceRE "\\.[^.]+$" "" -}}
+ {{- $contentImageNames = $contentImageNames | append $srcName -}}
+ {{- $contentImageStems = $contentImageStems | append $srcStem -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+
+ {{- $unusedImages := slice -}}
+ {{- range $bundleImages -}}
+ {{- $imageName := path.Base .Name -}}
+ {{- $imageNameLower := lower $imageName -}}
+ {{- $isCoverImage := false -}}
+ {{- if and (ne $coverImageName "") (eq $imageNameLower $coverImageName) -}}
+ {{- $isCoverImage = true -}}
+ {{- end -}}
+
+ {{- if not $isCoverImage -}}
+ {{- $imageStem := $imageName | replaceRE "\\.[^.]+$" "" -}}
+ {{- $imageStemLower := lower $imageStem -}}
+ {{- $isUsedInContent := false -}}
+ {{- range $contentImageNames -}}
+ {{- if eq . $imageNameLower -}}
+ {{- $isUsedInContent = true -}}
+ {{- end -}}
+ {{- end -}}
+ {{- if not $isUsedInContent -}}
+ {{- range $contentImageStems -}}
+ {{- if eq . $imageStemLower -}}
+ {{- $isUsedInContent = true -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- if not $isUsedInContent -}}
+ {{- $unusedImages = $unusedImages | append . -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+
+ {{- if gt (len $unusedImages) 0 -}}
+
+ {{- end -}}
+{{- end -}}
diff --git a/themes/2026/layouts/liens-morts/single.html b/themes/2026/layouts/liens-morts/single.html
index 1687c95c..66cf7d1a 100644
--- a/themes/2026/layouts/liens-morts/single.html
+++ b/themes/2026/layouts/liens-morts/single.html
@@ -6,6 +6,7 @@
{{ partial "liens-morts/report.html" (dict "Page" .) }}
{{ partial "article-footer.html" . }}
+ {{ partial "asides/complementary-images.html" . }}
{{ partial "asides/toc.html" . }}
{{ end }}
diff --git a/themes/2026/layouts/page.html b/themes/2026/layouts/page.html
index 113cb180..0f898be0 100644
--- a/themes/2026/layouts/page.html
+++ b/themes/2026/layouts/page.html
@@ -5,6 +5,7 @@
{{ .Content }}
{{ partial "article-footer.html" . }}
+ {{ partial "asides/complementary-images.html" . }}
{{ partial "asides/toc.html" . }}
{{ end }}