diff --git a/README.md b/README.md
index 5191581..e05e0fe 100644
--- a/README.md
+++ b/README.md
@@ -1423,6 +1423,18 @@ Things I chose while building, with the reasoning:
(measured: 268..1280 at a 1280 window) — a flat pane floating 40px inside the scene read as
"cut off next to the sidebar".
+88. **A custom 404, Frutiger Aero style.** Unknown URLs used to fall through to Next's default
+ "This page could not be found". `app/not-found.tsx` now renders a standalone screen — the root
+ layout's ice scene is the backdrop, and the glass card, the rim and the rising bubbles come from
+ the shared recipes, so it is full Frutiger Aero while that theme is on and stays in the
+ frosted-glass language in light and dark. It wears the scene's glossy ice orb
+ (`.not-found-orb`, at full strength so the snowflake reads) with a "404" chip, "This page melted
+ away", and two ways out: back to the library and into the chat (44px targets, en + cs). The
+ regression suite pins the 404 status, both links and the Czech copy; the pixel-contrast scan now
+ covers the screen in all three themes. The service worker also stopped caching non-OK
+ navigations as the offline shell — a typo'd URL used to write the error page into the shell
+ cache.
+
87. **Chat's chrome docks into the rail (the sidebar seam).** The chat header and composer are
full-bleed bands inside the main pane; at desktop widths that pane starts right at the sidebar,
so both bands ended in a hard vertical edge beside the floating rail card — the 12px gutter
diff --git a/app/globals.css b/app/globals.css
index f3b6b74..b6c3a47 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1045,6 +1045,24 @@ body:has(.overlay-open) :is(.app-header, .segmented, .tabbar, .cookie-notice) {
.ice-orb-3 { display: none; }
}
+/* The 404 mascot: the scene's glossy ice orb, at full strength so it reads on
+ the card glass in every theme (the scene's own orbs run at 0.22–0.6). */
+.not-found-orb {
+ border-radius: 50%;
+ background: radial-gradient(
+ circle at 32% 26%,
+ rgb(255 255 255 / 0.98) 0%,
+ rgb(219 242 255 / 0.92) 26%,
+ rgb(125 200 245 / 0.95) 58%,
+ rgb(38 121 208 / 1) 100%
+ );
+ box-shadow:
+ inset -10px -14px 24px -12px rgb(3 74 124 / 0.6),
+ inset 8px 12px 20px -10px rgb(255 255 255 / 0.95),
+ inset 0 0 0 1px rgb(255 255 255 / 0.5),
+ 0 18px 36px -18px rgb(2 62 110 / 0.6);
+}
+
@layer components {
/* Frosted ice glass ------------------------------------------------------
Two recipes now:
diff --git a/app/not-found.tsx b/app/not-found.tsx
new file mode 100644
index 0000000..1c7c3a6
--- /dev/null
+++ b/app/not-found.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from "next";
+
+import { NotFoundScreen } from "@/components/NotFoundScreen";
+
+export const metadata: Metadata = {
+ title: "Page not found",
+};
+
+/** Custom 404 for unknown URLs (and notFound() calls). */
+export default function NotFound() {
+ return ;
+}
diff --git a/components/NotFoundScreen.tsx b/components/NotFoundScreen.tsx
new file mode 100644
index 0000000..4f79e8f
--- /dev/null
+++ b/components/NotFoundScreen.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import Link from "next/link";
+
+import { ChatIcon, HomeIcon, SnowflakeIcon } from "@/components/icons";
+import { useI18n } from "@/lib/i18n";
+
+/**
+ * The custom 404. The root layout already paints the ice scene behind it, so
+ * this is just the glass card: in Frutiger Aero the wallpaper bake, the bright
+ * rim and the rising bubbles all come from the shared recipes, and the glossy
+ * orb is the scene's own ice sphere at full strength. Standalone (no rail, no
+ * tab bar), like the offline screen.
+ */
+export function NotFoundScreen() {
+ const { t } = useI18n();
+
+ return (
+
+