diff --git a/src/components/general/chatroom-stone-cluster.tsx b/src/components/general/chatroom-stone-cluster.tsx
new file mode 100644
index 0000000..6e95bee
--- /dev/null
+++ b/src/components/general/chatroom-stone-cluster.tsx
@@ -0,0 +1,48 @@
+import Row from "@moduletrace/twui/layout/Row";
+import Stack from "@moduletrace/twui/layout/Stack";
+import type { ChatroomBot } from "@/src/types";
+import ChatroomStone from "./chatroom-stone";
+
+type Props = {
+ bots: ChatroomBot[];
+};
+
+/**
+ * The square the cluster fills (`w-7 h-7`), the size of a rail avatar.
+ */
+const ClusterSize = 28;
+
+/**
+ * A chatroom's bots packed into one avatar-sized square: the asking bot
+ * on top, the two below it in a triangle (four make a square, the rest
+ * are left out). Stones stay small and apart: never overlapping, in a
+ * badge or outlined. One bot fills the square alone.
+ */
+export default function ChatroomStoneCluster({ bots }: Props) {
+ const shown = bots.slice(0, 4);
+ const stone = Math.floor((ClusterSize - 2) / 2);
+
+ if (shown.length <= 1) {
+ return shown[0] ? : null;
+ }
+
+ const top = shown.length == 3 ? shown.slice(0, 1) : shown.slice(0, 2);
+ const bottom = shown.length == 2 ? [] : shown.slice(top.length);
+
+ return (
+
+
+ {top.map((bot) => (
+
+ ))}
+
+ {bottom.length ? (
+
+ {bottom.map((bot) => (
+
+ ))}
+
+ ) : null}
+
+ );
+}
diff --git a/src/layouts/bots/(partials)/rail-chatroom-row.tsx b/src/layouts/bots/(partials)/rail-chatroom-row.tsx
index 65e6e08..26e07aa 100644
--- a/src/layouts/bots/(partials)/rail-chatroom-row.tsx
+++ b/src/layouts/bots/(partials)/rail-chatroom-row.tsx
@@ -2,7 +2,7 @@ import Link from "@moduletrace/twui/layout/Link";
import Span from "@moduletrace/twui/layout/Span";
import Stack from "@moduletrace/twui/layout/Stack";
import { twMerge } from "tailwind-merge";
-import ChatroomStone from "@/src/components/general/chatroom-stone";
+import ChatroomStoneCluster from "@/src/components/general/chatroom-stone-cluster";
import type { ChatroomSummary } from "@/src/types";
import chatroomStatusLine from "@/src/utils/chatroom-status-line";
@@ -13,12 +13,11 @@ type Props = {
};
/**
- * A chatroom in the rail, the same 48px row as a bot: the asking bot's
- * stone, the request and where it stands. Unread is said in words.
+ * A chatroom in the rail, the same 48px row as a bot: its bots' stones
+ * in one avatar-sized cluster, the request and where it stands. Unread
+ * is said in words.
*/
export default function RailChatroomRow({ chatroom, href, selected }: Props) {
- const requester = chatroom.bots[0];
-
return (
- {requester ? : null}
+