# TeamsISO v2 — Studio Terminal (approved shape brief)

**Date approved:** 2026-05-13
**Approver:** Zac (operator + product owner)
**Host:** WPF .NET 8 (`src/TeamsISO.App/`). WinUI 3 rebuild is abandoned.
**Predecessor:** the WPF rollback at `1d1ce6a` (recording axed, settings pane tab fix, settings button wired).

## Why this redesign

The v1 GUI failed the "AI made that" test. Quote from the operator: "its cluttered, screams that AI made it - and relatively inefficient to navigate." The PRODUCT.md anti-references — card-grid-of-icons, always-visible side panel, footer-as-theatre — all describe the current build. v2 commits to a different aesthetic register entirely.

## Aesthetic register

**Broadcast-engineering instrument.** Not a SaaS dashboard. Not Material. Not Fluent default.

Reference proximity: Linear's keyboard-first density × Avid S6 console legibility × Blackmagic ATEM's information hierarchy. The operator mental model is "I'm sitting at an audio mixer; every region has a job, no region is theatre."

## What goes away

- The 72px left rail (no actual navigation — there's only one screen)
- The 380px always-visible settings pane (settings change rarely, shouldn't claim permanent real estate)
- The 6-column footer status row (theatre, not information)
- The custom chromeless title-bar caption buttons (look worse than system chrome, break on DPI scaling)
- The "by Wild Dragon" pill and the always-visible "TeamsISO" wordmark as decorative chrome
- The in-call control bar as a permanent strip (only relevant in-call; should appear conditionally)
- The seven identical ghost buttons in the in-call bar (textbook card-grid anti-pattern)

## What replaces it

```
┌─ system Windows title bar [_ □ ✕] ─────────────────────┐
│ 🐉 TeamsISO                          [⌘K]  [☾]  [⚙]  │   32px header — mark + wordmark left, 3 icons right
├────────────────────────────────────────────────────────┤
│ ● 02:14:32   PART 4 · LIVE 2   DISK 482g  CTRL :9755  │   transport strip — single mono line, replaces footer
├────────────────────────────────────────────────────────┤
│                                                        │
│   ▮ alice          ▮▮▮▮  t:5ms   alice         [LIVE] │
│   ▯ bob            ▮▮    t:8ms   bob           [— OFF]│   participants table = the canvas
│   ▮ carlos         ▮▮▮▮▮ t:9ms   carlos        [LIVE] │   (cyan-tinted row bg = active speaker)
│   ▮ guest 4        --    NO SIG  guest_4      [ERROR]│
│                                                        │
├────────────────────────────────────────────────────────┤
│ IN CALL · Daily standup         [mute] [cam] [leave]  │   conditional — only renders when in call
└────────────────────────────────────────────────────────┘
```

### Header (32px)

Left: Wild Dragon mark (~20px) + "TeamsISO" wordmark in Inter 13 Medium. Click on mark opens About.
Right: three icon buttons.
- `⌘K` (Tabler `ti-command`) — opens command palette (also Ctrl+K, Ctrl+P shortcut)
- `☾` / `☀` (Tabler `ti-moon` / `ti-sun`) — cycles theme dark ↔ light. Tooltip "Theme (System / Dark / Light)" — long-press could open the tri-state, but for v2 just a one-click cycle.
- `⚙` (Tabler `ti-settings`) — opens settings drawer

That's all the chrome. No nav rail because there's nothing to navigate to.

### Transport strip

Single horizontal line. Mono type (JetBrains Mono 12). Replaces the entire footer.

Fields:
- `● 02:14:32` — green dot + session timer when at least one ISO is live; both hidden otherwise
- `PART 4 · LIVE 2` — participant count and live-ISO count; "PART" / "LIVE" in Inter 11 SemiBold UPPER tracking 0.06em, numbers in mono
- `DISK 482g` — free disk space on the working volume; coral text if <10GB, hidden if no relevant volume is configured
- `CTRL :9755` — control surface bind; cyan text when active, hidden when off

No icons. No badges. No backgrounds. Just typed status — a console heads-up display.

### Participants table — the canvas

Five columns:

| # | Width | Content | Type |
|---|---|---|---|
| 1 | 24px | State LED — 8×8 filled cyan/coral or hollow neutral | hard-edged square, no rounding |
| 2 | * | Name (Inter 13/Medium) + codec/latency caption (Mono 11/Regular, tertiary fg) | "Alice Wong" / "NDIV5 · t:5ms" |
| 3 | 110px | Audio meter — 5 vertical bars, instantaneous level | hard-edged, cyan when LIVE, neutral when OFF |
| 4 | 130px | Output name | Mono 12 |
| 5 | 100px | ISO toggle pill | LIVE = cyan fill / OFF = hollow neutral / ERROR = coral outline |

Row height: 52px (was 56).

Active speaker: full-row background tint `bg.active-speaker` (cyan-tinted muted neutral). NOT a left-edge stripe — that trips the impeccable "side-stripe border" ban.

Each row reacts to:
- Click anywhere → focuses the row, keyboard-actions apply
- Click the pill → toggle ISO
- Right-click → context menu (preview, custom name, copy NDI source name, save snapshot)
- Hover → reveals a kebab affordance in column 5 right edge for less-frequent actions

### Conditional meeting bar

Renders below the table only when `TeamsControlBridge.DetectCallState().IsInCall == true`. Slides up from below on transition (~120ms ease-out-quart on `RenderTransform.Y` + `Opacity`).

Content: `IN CALL` label (Inter 11 SemiBold UPPER, cyan accent) + meeting title (Mono 12, truncated with ellipsis) + three buttons right-aligned (Mute / Cam / Leave). Share and Notes do NOT live here — they move to ⌘K, where they're invocable any time without the bar fighting for attention.

Width matches the table — not full-bleed; respects the page padding.

### Ctrl+K command palette

The redesign's navigation move. Replaces ~80% of what's in the v1 rail + tabbed settings.

Behavior:
- `Ctrl+K` (also `Ctrl+P`) opens a centered floating window over the main shell, 560×360px
- Search input at the top, results list below
- Empty input → frequent + recent commands
- Typing → fuzzy-matches across command label + category + keywords
- ↑/↓ navigates, Enter invokes, Esc closes

Command categories (each command has icon, label, optional value preview, optional shortcut hint):
- **Quick** — Enable all online, Stop all ISOs, Refresh discovery, Drop snapshot of all
- **Teams** — Launch Teams, Hide / show Teams windows, Mute, Toggle camera, Open share, Leave call
- **Presets** — Apply <preset name>… (one row per saved preset), Save current as preset, Manage presets
- **Output** — Framerate 24 / 30 / 60, Resolution 1080p / 720p, Aspect Pillarbox / Letterbox / Stretch
- **Network** — Apply transcoder topology, Restore default NDI groups, Edit output name template
- **App** — Theme dark / light / system, Open settings, About TeamsISO, Help (F1)

This is the keyboard-first surface broadcasters with Stream Decks already mentally use.

### Settings — slide-over drawer

Triggered from the header gear icon, or from `Open settings` in the palette, or hotkey `,` (comma).

- 420px wide, slides in from the right
- 40% canvas scrim behind
- Three tabs: **OUTPUT** (framerate / resolution / aspect / audio + Reset to defaults), **NETWORK** (discovery / output groups + Apply transcoder topology + Restore defaults + output name template), **APP** (theme tri-state, minimize to tray, sort order, Launch Teams on startup, Auto-hide Teams windows)
- Apply Changes button pinned to drawer footer; Esc dismisses; click outside the drawer dismisses

DISPLAY tab from v1 gets renamed APP and absorbs the theme tri-state.

### Empty states

- No participants yet: a single centered mono sentence, "no ndi sources yet — open teams and start a meeting", and one tertiary button "Refresh discovery (Ctrl+R)". No illustration, no mascot.
- Not in a call: meeting bar simply doesn't render. No placeholder.
- Discovery degraded: amber dot in transport strip's session timer position, mono text "NDI discovery — restarting". No banner.

## Color, theme, motion

**Color strategy:** Restrained (impeccable product default). Cyan accent earns its place — reserved for LIVE state, focus ring, active speaker tint. Coral reserved for destructive + error. Status amber for warnings. Green NOT used (would compete with cyan for "ok / live" semantics).

**Theme default:** Follow Windows. Theme persists per-operator via `UIPreferences.Theme`. Implementation: split `WildDragonTheme.xaml` into a single style + token-shape file plus two color-only ResourceDictionary files (`Theme.Dark.xaml`, `Theme.Light.xaml`). At runtime `ThemeManager` swaps the merged dictionary entry. WPF analog of WinUI's `ThemeDictionary`.

**Motion:**
- 120ms `cubic-bezier(0.16, 1, 0.3, 1)` (ease-out-quart) on the meeting bar slide-in/out
- 200ms ease-out on the drawer slide
- 180ms cross-fade on theme swap
- 90ms on focus + hover transitions
- No bounce, no elastic, no spring overshoots. Animate `RenderTransform` and `Opacity` only — never layout properties.

## Typography commitments

| Token | Family | Size | Weight | Used for |
|---|---|---|---|---|
| `text.timer` | JetBrains Mono | 14 | Medium | Session timer in transport strip — instrument-grade |
| `text.caption` | Inter | 11 | SemiBold (600) | UPPER + tracking 0.06em — transport-strip labels, "IN CALL", "SPEAKING" |
| `text.display` | Inter | 22 | SemiBold | Settings drawer headings only |
| `text.title` | Inter | 13 | Medium | Wordmark, table column headers |
| `text.body` | Inter | 13 | Regular | Participant display names |
| `text.mono.code` | JetBrains Mono | 12 | Regular | Output names, NDI IDs, meeting title |
| `text.mono.tech` | JetBrains Mono | 11 | Regular | Latency readouts, codec captions, transport-strip values |

## What this is NOT

- Not Fluent-styled. Default Fluent accent integration is generic Windows; TeamsISO is a broadcaster's tool.
- Not minimalism for its own sake. The participants table is *dense*. Density is the broadcaster's virtue.
- Not chromeless. Default system title bar stays. Chromeless windows break embarrassingly at 4K + DPI scaling.
- Not vanity-branded. The Wild Dragon mark sits small in the header as a quality cue, never as decoration.

## Migration path

The view-model surface in `src/TeamsISO.App/ViewModels/` is the contract. The redesign rewrites `MainWindow.xaml` and `Themes/*` but leaves view-models, the engine, the control surface server, and the OSC bridge untouched.

Order of operations (each step builds clean before the next):

1. **Theme split** — Refactor `WildDragonTheme.xaml` → `Themes/Theme.Tokens.xaml` (styles + key shape) + `Themes/Theme.Dark.xaml` + `Themes/Theme.Light.xaml` (color resources only). Port `ThemeManager` from the deleted WinUI project; wire system app-mode detection via registry (`HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme`).
2. **Main window shell** — Replace MainWindow.xaml's outer Grid. Add 32px header, transport strip, full-width content area, conditional meeting bar. Delete the 72px rail, the 380px right pane, the footer.
3. **Participants table redesign** — 5 columns, LED state, instantaneous audio meter, ISO pill.
4. **Settings drawer** — Slide-over from right, dismissable; reuses existing settings view-model.
5. **Command palette** — `Ctrl+K` floating window with fuzzy command list.

Each step is a self-contained commit so the v1 build remains shippable at any rollback point.

## Anti-references — explicit on the "AI made that" failure

These are the failure modes the redesign defends against:
- Card-grid-of-icons (the v1 in-call bar's seven identical ghost buttons)
- Always-visible side panel (the v1 380px settings sidebar)
- Decorative chrome (the v1 "by Wild Dragon" pill, the 72px nav rail, the six-column footer)
- Generic Inter at 13 for everything
- Default WPF DataGrid (Excel)
- Custom chromeless title bars that look generic
- Gradient text, glassmorphism, side-stripe borders (impeccable absolute bans)
- "Hero metric + supporting stats + gradient" SaaS dashboards
- Mascots, "Welcome!" copy, illustrated onboarding cards
