diff --git a/DESIGN.md b/DESIGN.md
index c1cd4e1..adff4ad 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -163,6 +163,11 @@ components:
textColor: "{colors.on-accent}"
rounded: "{rounded.pill}"
size: "46px"
+ keycap:
+ backgroundColor: "{colors.sunk}"
+ textColor: "{colors.ink}"
+ rounded: "{rounded.sm}"
+ padding: "4px 6px"
---
# Design System: Radial
@@ -376,6 +381,15 @@ goal on a project's list, a request everywhere else — and on a smart list it i
Its menu is generated from the space's own registry records, so what it offers is data rather than markup.
`⌘N` is the same button.
+### The keyboard map
+`/` puts the caret in quick find, `⌘N` is the ⊕ of the view being shown, Escape closes one layer per
+press — and `?` draws the map itself, over whatever is already open. It is the picker's paper card on
+the same scrim: a group heading per surface, a sans keycap per chord (`Esc` is a label printed on a key,
+not a machine identifier, so keycaps are never mono), and one plain sentence beside each. The pane bar
+carries a keyboard button next to the appearance toggle, titled with the key it stands for — a shortcut
+nobody can find is a shortcut nobody has. While the map is up it holds the keyboard: nothing behind it
+answers a key, and Escape takes the map away before it takes anything else.
+
### The URI chip
Mono, 11.5px, sunk, 4px radius, `cursor: copy`, flashing sage on copy. It is how a record's identity stays
available without a record ever being the headline.
diff --git a/packages/ui/src/app.css b/packages/ui/src/app.css
index 5803894..76d971c 100644
--- a/packages/ui/src/app.css
+++ b/packages/ui/src/app.css
@@ -829,6 +829,39 @@ select.sl { resize: none; cursor: pointer; padding-right: 8px; }
box-shadow: 0 2px 6px oklch(0.25 0.03 262 / 0.10), 0 24px 60px -14px oklch(0.25 0.03 262 / 0.4);
}
+/* ─── keyboard help ──────────────────────────────────────────────────────── */
+/* The map, drawn (`keys.ts` holds the table it draws). It is the same paper card on the same scrim
+ as the picker, one layer above it, because it is opened over whatever is already up — including
+ the picker. The keycaps are sans, not mono: `Esc` and `Home` are labels printed on a key, and mono
+ in this system means a machine identifier. */
+.keys-scrim { z-index: 60; }
+.keys-card {
+ width: min(520px, 100%); background: var(--paper);
+ border: 1px solid var(--line); border-radius: 13px; padding: 20px 24px 24px;
+ box-shadow: 0 2px 6px oklch(0.25 0.03 262 / 0.10), 0 24px 60px -14px oklch(0.25 0.03 262 / 0.4);
+ text-align: left;
+}
+.keys-head { display: flex; align-items: center; gap: 12px; }
+.keys-head h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.012em; }
+.keys-head .btn { margin-left: auto; flex: none; padding: 5px 11px; font-size: 12.5px; }
+.keys-group {
+ margin: 20px 0 0; padding-bottom: 5px; border-bottom: 1px solid var(--line-soft);
+ font-size: 12px; font-weight: 700; color: var(--ink-2);
+}
+.keys-when { margin: 7px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-2); }
+.keys-list {
+ margin: 11px 0 0; display: grid; grid-template-columns: max-content minmax(0, 1fr);
+ gap: 9px 14px; align-items: baseline;
+}
+.keys-list dt { display: flex; align-items: center; gap: 5px; }
+.keys-list dd { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
+.keys-list kbd {
+ font: 500 12px/1 var(--sans); color: var(--ink); white-space: nowrap;
+ background: var(--sunk); border: 1px solid var(--line); border-bottom-color: var(--stroke);
+ border-radius: 5px; padding: 4px 6px;
+}
+.keys-or { font-size: 11px; color: var(--ink-2); }
+
/* ─── skeleton ───────────────────────────────────────────────────────────── */
/* The shape of a space, while it is being read. Every bar is the same neutral the app already uses
for a sunk surface — a skeleton that shimmers in the accent would be the loading state advertising
@@ -950,6 +983,9 @@ select.sl { resize: none; cursor: pointer; padding-right: 8px; }
there is no disc, so the word stays — it is the only thing that would say what the button does. */
.acct-btn.with-disc .who-name { display: none; }
.acct-pop { width: min(292px, calc(100vw - 32px)); }
+ /* A viewport this narrow is a phone, and a phone has no keys to map. `?` still opens it for a
+ keyboard that happens to be attached; what goes is the button advertising it. */
+ .keys-btn { display: none; }
}
@media (prefers-reduced-motion: reduce) {
diff --git a/packages/ui/src/lib/components/PaneBar.svelte b/packages/ui/src/lib/components/PaneBar.svelte
index 8db8fbd..4f37d82 100644
--- a/packages/ui/src/lib/components/PaneBar.svelte
+++ b/packages/ui/src/lib/components/PaneBar.svelte
@@ -89,6 +89,23 @@
+
+
+