diff --git a/site/static/.well-known/security.txt b/site/static/.well-known/security.txt
new file mode 100644
index 0000000..53eaac3
--- /dev/null
+++ b/site/static/.well-known/security.txt
@@ -0,0 +1,6 @@
+# Security contact for roro9stack.net and the roro9stack firmware (RFC 9116).
+# Please report vulnerabilities privately by email, not in the public issue tracker.
+Contact: mailto:contact@roro9stack.net
+Expires: 2027-09-30T00:00:00.000Z
+Preferred-Languages: en, fr
+Canonical: https://roro9stack.net/.well-known/security.txt
diff --git a/site/static/apple-touch-icon.png b/site/static/apple-touch-icon.png
new file mode 100644
index 0000000..88e53b6
Binary files /dev/null and b/site/static/apple-touch-icon.png differ
diff --git a/site/static/favicon.ico b/site/static/favicon.ico
new file mode 100644
index 0000000..907abd9
Binary files /dev/null and b/site/static/favicon.ico differ
diff --git a/site/static/robots.txt b/site/static/robots.txt
new file mode 100644
index 0000000..601c542
--- /dev/null
+++ b/site/static/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Allow: /
+
+Sitemap: https://roro9stack.net/sitemap.xml
diff --git a/site/templates/base.html b/site/templates/base.html
index 2114593..abda627 100644
--- a/site/templates/base.html
+++ b/site/templates/base.html
@@ -10,6 +10,8 @@
{% if current_url %}{% endif %}
+
+
diff --git a/site/tools/make_favicons.py b/site/tools/make_favicons.py
new file mode 100644
index 0000000..3a2c99d
--- /dev/null
+++ b/site/tools/make_favicons.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+"""Draw the pixel "9" of static/img/favicon.svg as PNGs and an ICO, with the standard library.
+
+ python3 site/tools/make_favicons.py
+
+Writes static/favicon.ico (32 and 48 px, PNG inside) and static/apple-touch-icon.png (180 px).
+The 9 is a 7x7 grid, scaled by a whole number and centred on the background colour."""
+import struct
+import zlib
+from pathlib import Path
+
+BG = (0x00, 0x00, 0x55)
+FG = (0x00, 0xDB, 0xFF)
+# x, y, w, h: the same rectangles as favicon.svg
+RECTS = [(1, 0, 3, 1), (0, 1, 1, 2), (4, 1, 1, 5), (1, 3, 3, 1), (0, 6, 3, 1)]
+GRID = 7
+
+STATIC = Path(__file__).resolve().parent.parent / "static"
+
+
+def cells():
+ on = set()
+ for x, y, w, h in RECTS:
+ on.update((x + i, y + j) for i in range(w) for j in range(h))
+ return on
+
+
+def png(size):
+ scale = size // GRID
+ pad = (size - scale * GRID) // 2
+ on = cells()
+ rows = bytearray()
+ for py in range(size):
+ rows.append(0)
+ for px in range(size):
+ cx, cy = (px - pad) // scale, (py - pad) // scale
+ inside = 0 <= px - pad < scale * GRID and 0 <= py - pad < scale * GRID
+ rows.extend(FG if inside and (cx, cy) in on else BG)
+
+ def chunk(kind, data):
+ body = kind + data
+ return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body))
+
+ return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0)) +
+ chunk(b"IDAT", zlib.compress(bytes(rows), 9)) + chunk(b"IEND", b""))
+
+
+def ico(sizes):
+ images = [png(s) for s in sizes]
+ out = struct.pack("