# SPEC — App Components (cross-surface inventory)

*Companion to `SPEC.design-v35.md` (visual tokens) and `mcp/response-cards.html` (the MCP
card reference). This spec is about interaction components and where each one is allowed to
live — not colors/type, which stay owned by `brand/relay-tokens-v35.css`.*

## Why this exists

Mobile, Wearable, Slack, Chat, and Relay Agent (`concepts/app-*.html`) each got built as
one-off concept files. That's fine for a first cut, but three of them (Slack, Chat, Relay
Agent) already needed the same three things — a way to reach Graph/Streams/Explore, a
profile + security surface, and a notification entry point — and without a shared reference
each concept was about to reinvent them slightly differently. This doc is the single place
that says what each component is, which surfaces get it, which surfaces deliberately don't,
and which file is the canonical example to copy from.

This is a **planning document for static concept prototypes**, not a shipped component
library — relay-app has no shared component package yet (see relay-app `CLAUDE.md`: the web
app is vanilla TypeScript, no component abstraction). Treat "canonical file" below as *the
concept to copy markup/CSS from*, not an import path.

## Component inventory

| Component | What it is | Canonical concept | Notes |
|---|---|---|---|
| **Card — designed** (`.acard` family) | Relay's own fully-designed card: avatar, title, meta, working Approve/Deny buttons, resolved state | `app-mobile-v2.html` (`.card`), `app-relay-agent.html` (`.acard`) | Used wherever Relay owns the pixels end to end. Buttons are real, not markdown. |
| **Card — raw tool-call** (`.cm` family) | The MCP chat-tool block: glyph + bold title, `<hr>` framing, italic signal line, no interactive buttons | `mcp/response-cards.html`, reused verbatim in `app-slack.html` (Slack attachment chrome) and `app-chat.html` (host chat chrome) | Used wherever a *third-party* host is rendering the reply — Relay doesn't control the pixels, so the card degrades to what that host's markdown/attachment format allows. |
| **Tools panel** | A sheet that reveals feature shortcuts (Graph, Streams, Explore) without leaving the current surface | `app-relay-agent.html` (`.agentwin-tools`, opens above the input bar), `app-mobile-v2.html` (`.tools-sheet`, opens from Home's "Jump to a feature" row) | Explore is tier-gated (`Horizon`) — the panel must show it as reachable-but-locked, never hide it outright (fail-closed-visible, not fail-silent). |
| **Slide-up sheet** | An overlay that slides up from the bottom over the current screen (and the tab bar, on Mobile), collapsing back down on a second trigger, the backdrop, an explicit close, or navigating elsewhere. Never a route change. | `app-mobile-v2.html` (`.tools-sheet`, `.menu-sheet`) | The general mechanic behind both the Tools panel and the Menu (see below). Transform-only animation (`translateY`), never `height`/`max-height`, to avoid layout thrash. |
| **Notification bell** | Persistent header entry point for pending/resurfacing items, separate from the main feed/transcript; opens a small dropdown, not a sheet | `app-relay-agent.html` (`#bellBtn` + `.head-panel`), `app-mobile-v2.html` (`#bellBtn` in the persistent `.topbar`, on every screen) | Badge count is the *same number* as the Activity tab badge on Mobile — the bell is a shortcut into Activity, not a second inbox. See "Activity is the unified space" below. |
| **Menu (was: You tab)** | Identity, this-week stats, the tools/Explore entry, Security, and Settings — now a slide-up sheet triggered from the tab bar, not a persistent 5th screen | `app-mobile-v2.html` (`.menu-sheet`, `toggleMenu()`) | Deliberately *not* a screen: opening it never loses your place. The old "You" tab and Relay Agent's profile dropdown both feed from the same content described below. |
| **Profile & security panel** | Identity (name/handle/org/tier) + security (passkey status, active sessions, sign-out-other-sessions) + a link out to full account settings | `app-relay-agent.html` (`#profileBtn` + `.head-panel` profile variant, a small dropdown), `app-mobile-v2.html` (folded into the Menu sheet, a full section since there's more room) | Same content, different container: a compact dropdown where the surface is small/transient (Relay Agent), a full sheet section where it's the primary account entry point (Mobile). "Manage account" hands off to a fuller settings surface either way. |
| **Capture input bar** | Text/voice entry with a `+` affordance that opens the tools panel | `app-relay-agent.html` (`.agentwin-input`) | Mobile's equivalent is the Capture tab's Send/Claim segmented control (`app-mobile-v2.html`) — different chrome, same job. |
| **Approve/Deny action pattern** | Primary = Approve, single tap. Deny = 5s undo toast before it finalizes. Never symmetric-weight buttons. | `app-mobile-v2.html` (`resolve()` + `.toast`) | Canonical behavior contract — every surface that can approve/deny (Mobile, Slack, Relay Agent) follows this timing, even where the visual chrome differs. |
| **Segmented control** | Two-way switch (Send/Claim, host picker, Graph/Catalog/Journey) | `app-mobile-v2.html` (`.segbar`) | Same `.segbar` CSS reused verbatim in `app-chat.html`'s host picker. |

## Surface capability matrix

| Surface | Card | Tools panel (Graph/Streams/Explore) | Notifications | Profile + security | Capture | Approve/Deny |
|---|:--:|:--:|:--:|:--:|:--:|:--:|
| **Mobile** (`app-mobile-v2.html`) | Designed | ✅ built — Home's "Jump to a feature" row opens a bottom sheet | ✅ built — persistent bell in a top bar on every screen, badge synced to Activity | ✅ built — folded into the Menu sheet (replaces the old You tab) | ✅ Send/Claim segmented | ✅ canonical behavior |
| **Wearable** (`app-wearable-v1.html`) | Designed (minimal) | ❌ **intentionally** — no Search/You/tools drawer, 3 faces only | ❌ Glance face covers this at a glance-level, no bell/list | ❌ **intentionally** — no account surface on a 40mm screen | Voice-only | ✅ (haptic-timed undo) |
| **Slack** (`app-slack.html`) | Raw tool-call (Slack attachment chrome) | Not applicable — Slack's own app-home / shortcuts would carry this, out of scope for the concept | Native Slack notifications carry this already | Not applicable — Slack owns identity | `/relay capture`, forward, react | ✅ canonical behavior |
| **Chat** (`app-chat.html`) | Raw tool-call (host chat chrome) | Not applicable — host's own UI, Relay has no chrome to add it to | Not applicable — host owns notifications | Not applicable — host owns identity | Natural language | ❌ deliberately fenced off (see concept notes) |
| **Relay Agent** (`app-relay-agent.html`) | Designed | ✅ built | ✅ built | ✅ built | ✅ text/voice + `+` tools | ✅ canonical behavior |

**Reading the matrix:** Slack and Chat get an "N/A" on tools/notifications/profile because
Relay is a guest inside someone else's chrome there — those jobs are the host's to do, and
Relay shouldn't try to rebuild a mini-app inside another app's window. Mobile and Relay Agent
are Relay's own chrome, so they both owe the user the full set. Wearable is the one surface
that opts out on purpose, not because it's unfinished — see its own concept notes.

**Rule: Activity is the one unified space for notifications and activity, not two competing
inboxes.** The Mobile bell and Relay Agent's bell both open a quick-glance dropdown of the same
"Needs you" + resurfacing items that lead the Activity feed, with a "View all in Activity" link
out — they are shortcuts *into* Activity, never a separate feed with its own state. Don't add
a second unread-count anywhere; Activity's badge is the single source of truth every bell reads
from.

## Open items

- No shared partial/include mechanism exists yet across `concepts/*.html` — every file still
  copy-pastes its own `<style>`. That's acceptable for concept prototypes but means this spec
  is the thing keeping them from drifting; re-check it whenever a surface gets new chrome.
- If/when relay-app grows a real component package (`@relay/ui` is CSS tokens only today —
  see relay-app `CLAUDE.md`), this table is the starting spec for what that package's
  interactive components should cover.

---
*Source of truth for tokens: `brand/relay-tokens-v35.css`. Source of truth for the MCP card
markup: `mcp/response-cards.html`. This file owns cross-surface component/behavior decisions
only.*
