diff --git a/.planning/phases/31-mobile-polish/31-UI-SPEC.md b/.planning/phases/31-mobile-polish/31-UI-SPEC.md
new file mode 100644
index 0000000..1ff6482
--- /dev/null
+++ b/.planning/phases/31-mobile-polish/31-UI-SPEC.md
@@ -0,0 +1,161 @@
+---
+phase: 31
+slug: mobile-polish
+status: draft
+shadcn_initialized: false
+preset: none
+created: 2026-04-12
+---
+
+# Phase 31 — UI Design Contract
+
+> Visual and interaction contract for mobile icon-based action buttons. Generated by gsd-ui-researcher, verified by gsd-ui-checker.
+
+---
+
+## Design System
+
+| Property | Value |
+|----------|-------|
+| Tool | none |
+| Preset | not applicable |
+| Component library | none (plain Tailwind) |
+| Icon library | lucide-react via LucideIcon component |
+| Font | System default (Tailwind default stack) |
+
+---
+
+## Spacing Scale
+
+Declared values (must be multiples of 4):
+
+| Token | Value | Usage |
+|-------|-------|-------|
+| xs | 4px | Icon gaps, inline padding |
+| sm | 8px | Compact element spacing, icon button padding |
+| md | 16px | Default element spacing |
+| lg | 24px | Section padding |
+| xl | 32px | Layout gaps |
+| 2xl | 48px | Major section breaks |
+| 3xl | 64px | Page-level spacing |
+
+Exceptions: Touch targets minimum 44x44px (11 Tailwind units) for icon-only buttons on mobile
+
+---
+
+## Typography
+
+| Role | Size | Weight | Line Height |
+|------|------|--------|-------------|
+| Body | 14px (text-sm) | 400 | 1.5 |
+| Label | 12px (text-xs) | 500 | 1.5 |
+| Heading | 24px (text-2xl) | 700 (bold) | 1.2 |
+| Display | 20px (text-xl) | 600 (semibold) | 1.2 |
+
+Note: Icon-only buttons have no text labels on mobile. Tooltips (if added) use text-xs (12px).
+
+---
+
+## Color
+
+| Role | Value | Usage |
+|------|-------|-------|
+| Dominant (60%) | white (#ffffff) | Background, surfaces |
+| Secondary (30%) | gray-50 (#f9fafb) / gray-100 (#f3f4f6) | Cards, hover states, icon button hover bg |
+| Accent (10%) | gray-700 (#374151) | Primary action icon buttons (Edit) |
+| Destructive | red-500 (#ef4444) | Delete/Remove icon buttons only |
+
+Accent reserved for: Edit button (primary action), icon button active/pressed states
+
+### Icon Button Color Mapping
+
+| Action | Icon Color | Hover BG | Notes |
+|--------|-----------|----------|-------|
+| Edit | gray-700 (white bg variant) | gray-100 | Primary action, most prominent |
+| Duplicate | gray-500 | gray-50 | Secondary action |
+| Delete/Remove | red-400 | red-50 | Destructive — matches existing pattern |
+| Pick as Winner | amber-700 | amber-100 | Matches existing candidate resolve pattern |
+| Add to Collection | white (on gray-700 bg) | gray-800 | Primary CTA on catalog detail |
+| Add to Thread | gray-700 | gray-50 | Secondary CTA on catalog detail |
+
+---
+
+## Copywriting Contract
+
+| Element | Copy |
+|---------|------|
+| Primary CTA | n/a (icon-only on mobile, text preserved on desktop) |
+| Empty state heading | n/a (no new empty states in this phase) |
+| Empty state body | n/a |
+| Error state | n/a (no new error states in this phase) |
+| Destructive confirmation | Existing ConfirmDialog patterns unchanged |
+
+### Icon-to-Action Mapping (Mobile)
+
+| Action | Lucide Icon Name | Size | aria-label |
+|--------|-----------------|------|------------|
+| Edit | `pencil` | 16px | "Edit" |
+| Delete | `trash-2` | 16px | "Delete" |
+| Remove from Collection | `trash-2` | 16px | "Remove from Collection" |
+| Duplicate | `copy` | 16px | "Duplicate" |
+| Pick as Winner | `trophy` | 14px | "Pick as winner" |
+| Add to Collection | `plus` | 16px | "Add to Collection" |
+| Add to Thread | `message-square-plus` | 16px | "Add to Thread" |
+| Add Items (setup) | `plus` | 16px | "Add Items" |
+| Toggle Public | `globe` | 16px | "Toggle public" |
+| Delete Setup | `trash-2` | 16px | "Delete Setup" |
+
+### Accessibility
+
+- Every icon-only button MUST have `aria-label` matching the action text shown on desktop
+- Icon buttons use `title` attribute matching `aria-label` for hover tooltip on touch-and-hold
+- Minimum touch target: 44x44px (achieved via `min-w-[44px] min-h-[44px]` or equivalent padding)
+
+---
+
+## Responsive Breakpoint Contract
+
+| Breakpoint | Behavior |
+|------------|----------|
+| Below `md:` (< 768px) | Icon-only buttons, no text labels |
+| `md:` and above (>= 768px) | Full text buttons (current behavior, unchanged) |
+
+Implementation pattern:
+```tsx
+{/* Desktop: text button */}
+
+{/* Mobile: icon-only button */}
+
+```
+
+---
+
+## Registry Safety
+
+| Registry | Blocks Used | Safety Gate |
+|----------|-------------|-------------|
+| n/a | none | not required |
+
+No shadcn or third-party registries. All components are hand-rolled with Tailwind CSS.
+
+---
+
+## Checker Sign-Off
+
+- [ ] Dimension 1 Copywriting: PASS
+- [ ] Dimension 2 Visuals: PASS
+- [ ] Dimension 3 Color: PASS
+- [ ] Dimension 4 Typography: PASS
+- [ ] Dimension 5 Spacing: PASS
+- [ ] Dimension 6 Registry Safety: PASS
+
+**Approval:** pending