diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 8b336a0..b4c6dbd 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -111,7 +111,7 @@ Plans:
| 1-5 | v1.0 | 36/36 | Shipped | 2026-04-07 |
| 6-9 | v1.1 | 25/25 | Shipped | 2026-04-08 |
| 10. Branding Data Foundation | v2.2 | 3/3 | Complete | 2026-04-08 |
-| 11. HTML Export Branding + ViewModel Integration | 4/4 | Complete | 2026-04-08 | — |
+| 11. HTML Export Branding + ViewModel Integration | 4/4 | Complete | 2026-04-08 | — |
| 12. Branding UI Views | v2.2 | 0/? | Not started | — |
| 13. User Directory ViewModel | v2.2 | 0/? | Not started | — |
| 14. User Directory View | v2.2 | 0/? | Not started | — |
diff --git a/.planning/STATE.md b/.planning/STATE.md
index 51a9571..f77b2d8 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -4,7 +4,7 @@ milestone: v2.2
milestone_name: Report Branding & User Directory
status: completed
stopped_at: Completed 11-03-PLAN.md — ViewModel branding wiring
-last_updated: "2026-04-08T12:51:43.354Z"
+last_updated: "2026-04-08T12:56:08.172Z"
last_activity: 2026-04-08 — Phase 11 planning completed
progress:
total_phases: 5
diff --git a/.planning/phases/11-html-export-branding/11-VERIFICATION.md b/.planning/phases/11-html-export-branding/11-VERIFICATION.md
new file mode 100644
index 0000000..83c4cbd
--- /dev/null
+++ b/.planning/phases/11-html-export-branding/11-VERIFICATION.md
@@ -0,0 +1,149 @@
+---
+phase: 11-html-export-branding
+verified: 2026-04-08T00:00:00Z
+status: passed
+score: 5/5 must-haves verified
+re_verification: false
+---
+
+# Phase 11: HTML Export Branding + ViewModel Integration — Verification Report
+
+**Phase Goal:** All five HTML reports display MSP and client logos in a consistent header, and administrators can manage logos from Settings and the profile dialog without touching the View layer.
+**Verified:** 2026-04-08
+**Status:** PASSED
+**Re-verification:** No — initial verification
+
+---
+
+## Goal Achievement
+
+### Observable Truths (from ROADMAP.md Success Criteria)
+
+| # | Truth | Status | Evidence |
+|---|-------|--------|----------|
+| 1 | Running any of the five HTML exports produces an HTML file whose header contains the MSP logo `` tag when an MSP logo is configured | VERIFIED | All 5 export services call `BrandingHtmlHelper.BuildBrandingHeader(branding)` between `
` and `
` (7 injection points across 5 files) |
+| 2 | When a client logo is configured, the HTML export header contains both logos side by side | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` emits both `` tags with a flex spacer when both logos are non-null; ViewModels assemble `ReportBranding(mspLogo, clientLogo)` from `IBrandingService.GetMspLogoAsync()` and `_currentProfile?.ClientLogo` |
+| 3 | When no logo is configured, the HTML export header contains no broken image placeholder | VERIFIED | `BuildBrandingHeader` returns `string.Empty` when branding is null or both logos are null; all 5 services use optional `ReportBranding? branding = null` preserving identical pre-branding output |
+| 4 | SettingsViewModel exposes browse/clear commands for MSP logo; ProfileManagementViewModel exposes browse/clear commands for client logo — both exercisable without a View | VERIFIED | `SettingsViewModel.BrowseMspLogoCommand` and `ClearMspLogoCommand` exist as `IAsyncRelayCommand`; `ProfileManagementViewModel` exposes `BrowseClientLogoCommand`, `ClearClientLogoCommand`, `AutoPullClientLogoCommand`; both backed by unit tests |
+| 5 | Auto-pulling the client logo from Entra branding API stores it in the tenant profile and falls back silently when no Entra branding is configured | VERIFIED | `AutoPullClientLogoAsync` calls `squareLogo` endpoint, pipes bytes to `ImportLogoFromBytesAsync`, calls `_profileService.UpdateProfileAsync`; catches `ODataError` with `ResponseStatusCode == 404` and sets informational `ValidationMessage` with no rethrow |
+
+**Score:** 5/5 truths verified
+
+---
+
+## Required Artifacts
+
+### Plan 01 — ReportBranding Model + BrandingHtmlHelper
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `SharepointToolbox/Core/Models/ReportBranding.cs` | Immutable DTO with MspLogo and ClientLogo | VERIFIED | Positional record `ReportBranding(LogoData? MspLogo, LogoData? ClientLogo)` — 8 lines, substantive |
+| `SharepointToolbox/Services/Export/BrandingHtmlHelper.cs` | Static helper generating branding header HTML | VERIFIED | Internal static class with `BuildBrandingHeader`, flex layout, data-URI format, empty-string fallback |
+| `SharepointToolbox.Tests/Services/Export/BrandingHtmlHelperTests.cs` | Unit tests covering all 4 branding states | VERIFIED | 105 lines, 8 `[Fact]` tests covering null branding, both-null, single logo, both logos |
+
+### Plan 02 — Branding Parameter in All 5 Export Services
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `SharepointToolbox/Services/Export/HtmlExportService.cs` | Optional branding param on BuildHtml + WriteAsync | VERIFIED | 4 signatures carry `ReportBranding? branding = null`; 2 injection points |
+| `SharepointToolbox/Services/Export/SearchHtmlExportService.cs` | Optional branding param | VERIFIED | BuildHtml + WriteAsync both carry param; injection confirmed |
+| `SharepointToolbox/Services/Export/StorageHtmlExportService.cs` | Optional branding param (both overloads) | VERIFIED | 3 signatures with param; 2 injection points |
+| `SharepointToolbox/Services/Export/DuplicatesHtmlExportService.cs` | Optional branding param | VERIFIED | BuildHtml + WriteAsync carry param; injection confirmed |
+| `SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs` | Optional branding param | VERIFIED | BuildHtml + WriteAsync carry param; injection confirmed |
+
+### Plan 03 — IBrandingService Wired into Export ViewModels
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs` | IBrandingService injection + branding in ExportHtmlAsync | VERIFIED | `IBrandingService? _brandingService` field; DI and test constructors present; 2 `WriteAsync` calls pass `branding` |
+| `SharepointToolbox/ViewModels/Tabs/SearchViewModel.cs` | IBrandingService injection | VERIFIED | Non-nullable `IBrandingService _brandingService`; single constructor; `WriteAsync` passes `branding` |
+| `SharepointToolbox/ViewModels/Tabs/StorageViewModel.cs` | IBrandingService injection | VERIFIED | Nullable field; DI + test constructors; `WriteAsync` passes `branding` |
+| `SharepointToolbox/ViewModels/Tabs/DuplicatesViewModel.cs` | IBrandingService injection | VERIFIED | Non-nullable field; single constructor; `WriteAsync` passes `branding` |
+| `SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs` | IBrandingService injection | VERIFIED | Nullable field; DI + test constructors; `WriteAsync` passes `branding` |
+
+### Plan 04 — Logo Management Commands + Service Extensions
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `SharepointToolbox/Services/ProfileService.cs` | UpdateProfileAsync | VERIFIED | `UpdateProfileAsync` at line 55, find-by-name-replace-save pattern |
+| `SharepointToolbox/Services/IBrandingService.cs` | ImportLogoFromBytesAsync declaration | VERIFIED | `Task ImportLogoFromBytesAsync(byte[] bytes);` at line 8 |
+| `SharepointToolbox/Services/BrandingService.cs` | ImportLogoFromBytesAsync implementation | VERIFIED | Implemented at line 40; `ImportLogoAsync` delegates to it at line 33 |
+| `SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs` | BrowseMspLogoCommand + ClearMspLogoCommand | VERIFIED | Both `IAsyncRelayCommand` fields at lines 50-51; IBrandingService injected via constructor |
+| `SharepointToolbox/ViewModels/ProfileManagementViewModel.cs` | BrowseClientLogoCommand + ClearClientLogoCommand + AutoPullClientLogoCommand | VERIFIED | All three at lines 40-42; 404 catch at line 235 |
+| `SharepointToolbox.Tests/ViewModels/SettingsViewModelLogoTests.cs` | Tests for MSP logo commands | VERIFIED | 72 lines (min 40 required); tests confirm command existence and ClearMspLogo path |
+| `SharepointToolbox.Tests/ViewModels/ProfileManagementViewModelLogoTests.cs` | Tests for client logo commands and auto-pull | VERIFIED | 118 lines (min 60 required); 7 tests including 404 handling |
+
+---
+
+## Key Link Verification
+
+| From | To | Via | Status | Details |
+|------|----|-----|--------|---------|
+| `BrandingHtmlHelper.cs` | `ReportBranding.cs` | parameter type | VERIFIED | `BuildBrandingHeader(ReportBranding? branding)` — type referenced directly |
+| `BrandingHtmlHelper.cs` | `LogoData.cs` | property access | VERIFIED | `msp.MimeType`, `msp.Base64`, `client.MimeType`, `client.Base64` |
+| `HtmlExportService.cs` | `BrandingHtmlHelper.cs` | static method call | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` at lines 76 and 232 |
+| `SearchHtmlExportService.cs` | `BrandingHtmlHelper.cs` | static method call | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` at line 47 |
+| `StorageHtmlExportService.cs` | `BrandingHtmlHelper.cs` | static method call | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` at lines 52 and 152 |
+| `DuplicatesHtmlExportService.cs` | `BrandingHtmlHelper.cs` | static method call | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` at line 56 |
+| `UserAccessHtmlExportService.cs` | `BrandingHtmlHelper.cs` | static method call | VERIFIED | `BrandingHtmlHelper.BuildBrandingHeader` at line 91 |
+| `PermissionsViewModel.cs` | `IBrandingService.cs` | constructor injection | VERIFIED | `IBrandingService? _brandingService` field; DI constructor at line 132 |
+| `PermissionsViewModel.cs` | `HtmlExportService.cs` | WriteAsync with branding | VERIFIED | `WriteAsync(..., branding)` at lines 330 and 332 |
+| `SettingsViewModel.cs` | `IBrandingService.cs` | constructor injection | VERIFIED | `IBrandingService _brandingService` field at line 14; constructor at line 53 |
+| `ProfileManagementViewModel.cs` | `ProfileService.cs` | UpdateProfileAsync call | VERIFIED | `_profileService.UpdateProfileAsync` at lines 175, 191, 232 |
+| `ProfileManagementViewModel.cs` | `Microsoft.Graph` | Organization.Branding.SquareLogo | VERIFIED | `graphClient.Organization[orgId].Branding.Localizations["default"].SquareLogo.GetAsync()` at lines 217-218 |
+
+---
+
+## Requirements Coverage
+
+| Requirement | Source Plan | Description | Status | Evidence |
+|-------------|------------|-------------|--------|----------|
+| BRAND-05 | 11-01, 11-02, 11-03 | All five HTML report types display MSP and client logos in a consistent header | SATISFIED | `BrandingHtmlHelper` generates flex-layout data-URI header; all 5 exporters inject it; all 5 ViewModels assemble and pass `ReportBranding` to `WriteAsync` |
+| BRAND-04 | 11-04 | User can auto-pull client logo from tenant's Entra branding API | SATISFIED | `AutoPullClientLogoCommand` implemented in `ProfileManagementViewModel`; calls squareLogo endpoint; persists via `UpdateProfileAsync`; handles 404 gracefully |
+
+**Note on REQUIREMENTS.md checkbox:** `BRAND-04` shows `[ ]` (unchecked) in REQUIREMENTS.md and "Pending" in the traceability table. The implementation in the codebase is complete (see `AutoPullClientLogoAsync` and related commands). This is a documentation tracking artifact that needs updating — the requirement itself is satisfied by the implementation.
+
+---
+
+## Anti-Patterns Found
+
+| File | Line | Pattern | Severity | Impact |
+|------|------|---------|----------|--------|
+| `HtmlExportService.cs` | 88, 257 | `placeholder="Filter permissions..."` | Info | HTML `` placeholder attribute in a filter UI element — this is valid HTML, not a code stub |
+
+No blockers or warnings found. The only `placeholder` matches are HTML form attribute strings in the legitimate permissions filter input, not code stubs.
+
+---
+
+## Human Verification Required
+
+### 1. Visual Logo Layout in Browser
+
+**Test:** Configure an MSP logo and a client logo in the application. Run any HTML export. Open the resulting HTML file in a browser.
+**Expected:** The header shows the MSP logo left-aligned and the client logo right-aligned in a flex row with 16px gap; both logos are max 60px tall and max 200px wide; no broken image icons appear.
+**Why human:** CSS rendering and visual layout cannot be verified by grep.
+
+### 2. No-Logo Regression
+
+**Test:** Clear both logos. Run any HTML export. Open the HTML file.
+**Expected:** The report body appears identical to a pre-branding export — no blank space where the header would be, no empty `
`.
+**Why human:** Visual comparison of rendered output requires a browser.
+
+### 3. Auto-Pull from Entra Branding (Live Tenant)
+
+**Test:** In the profile dialog, select a tenant with Entra branding configured. Click "Pull from Entra". Verify the logo appears after Phase 12 adds the preview control.
+**Expected:** The tenant's squareLogo is imported, stored in the profile, and `ValidationMessage` reads "Client logo pulled from Entra branding."
+**Why human:** Requires a live Graph API call to a real tenant. The 404 fallback path is tested by unit tests, but the success path requires a real tenant credential.
+
+---
+
+## Gaps Summary
+
+No gaps. All five success criteria are satisfied, all must-have artifacts exist with substantive implementations, all key links are wired end-to-end.
+
+The single documentation artifact to note: `REQUIREMENTS.md` still shows BRAND-04 as `[ ]` and "Pending" in the traceability table. The code fully implements the requirement; the tracking document was not updated during plan 04 execution. This does not affect goal achievement.
+
+---
+
+_Verified: 2026-04-08_
+_Verifier: Claude (gsd-verifier)_