diff --git a/.agents/skills/angular-best-practices/AGENTS.md b/.agents/skills/angular-best-practices/AGENTS.md
new file mode 100644
index 0000000..73ba7f6
--- /dev/null
+++ b/.agents/skills/angular-best-practices/AGENTS.md
@@ -0,0 +1,1813 @@
+# Angular Best Practices
+
+**Version 1.0.0**
+Angular Best Practices
+March 7, 2026
+
+> **Note:**
+> This document is optimized for AI agents and LLMs. It provides
+> actionable guidelines for Angular applications with code examples
+> and impact assessments.
+
+---
+
+## Abstract
+
+Comprehensive guidelines for building performant, maintainable Angular applications. These rules cover performance optimization, bundle size reduction, reactive patterns with Signals and RxJS, state management, component architecture, server-side rendering, change detection strategies, testing patterns, and TypeScript best practices. Each rule includes impact assessment and actionable code examples.
+
+---
+
+## Table of Contents
+
+1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**
+ - 1.1 [Defer Await Until Needed](#11-defer-await-until-needed)
+ - 1.2 [Promise.all() for Independent Operations](#12-promise-all-for-independent-operations)
+2. [Bundle Optimization](#2-bundle-optimization) — **CRITICAL**
+ - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)
+ - 2.2 [Conditional Imports for Features](#22-conditional-imports-for-features)
+ - 2.3 [Defer Heavy Third-Party Libraries](#23-defer-heavy-third-party-libraries)
+ - 2.4 [Preload Critical Resources](#24-preload-critical-resources)
+ - 2.5 [Use @defer for Lazy Loading](#25-use-defer-for-lazy-loading)
+3. [JavaScript Performance](#3-javascript-performance) — **HIGH**
+ - 3.1 [Batch DOM Reads and Writes](#31-batch-dom-reads-and-writes)
+ - 3.2 [Cache API Results with Interceptors](#32-cache-api-results-with-interceptors)
+ - 3.3 [Memoize Expensive Function Results](#33-memoize-expensive-function-results)
+ - 3.4 [Pre-build Lookup Maps from Arrays](#34-pre-build-lookup-maps-from-arrays)
+ - 3.5 [Use content-visibility for Off-Screen Content](#35-use-content-visibility-for-off-screen-content)
+ - 3.6 [Use Immutable Array Methods](#36-use-immutable-array-methods)
+ - 3.7 [Use Passive Event Listeners](#37-use-passive-event-listeners)
+ - 3.8 [Use Set/Map for O(1) Lookups](#38-use-set-map-for-o-1-lookups)
+4. [TypeScript Best Practices](#4-typescript-best-practices) — **MEDIUM**
+ - 4.1 [Avoid Enums, Use const Objects](#41-avoid-enums-use-const-objects)
+ - 4.2 [Declare Return Types for Exported Functions](#42-declare-return-types-for-exported-functions)
+ - 4.3 [Handle noUncheckedIndexedAccess](#43-handle-nouncheckedindexedaccess)
+ - 4.4 [Prefer Explicit Undefined Over Optional](#44-prefer-explicit-undefined-over-optional)
+ - 4.5 [Prefer Interface Extends Over Intersections](#45-prefer-interface-extends-over-intersections)
+ - 4.6 [Use Discriminated Unions](#46-use-discriminated-unions)
+ - 4.7 [Use import type for Type-Only Imports](#47-use-import-type-for-type-only-imports)
+ - 4.8 [Use Readonly Properties by Default](#48-use-readonly-properties-by-default)
+ - 4.9 [Use Result Types Instead of Throwing](#49-use-result-types-instead-of-throwing)
+ - 4.10 [When any is Acceptable in Generics](#410-when-any-is-acceptable-in-generics)
+ - 4.11 [When to Use JSDoc Comments](#411-when-to-use-jsdoc-comments)
+5. [Signals & Reactivity](#5-signals-reactivity) — **HIGH**
+ - 5.1 [Avoid Effects for State Propagation](#51-avoid-effects-for-state-propagation)
+ - 5.2 [Use Computed for Derived State](#52-use-computed-for-derived-state)
+ - 5.3 [Use httpResource() for Signal-Based Data Fetching](#53-use-httpresource-for-signal-based-data-fetching)
+ - 5.4 [Use linkedSignal() for Dependent Resettable State](#54-use-linkedsignal-for-dependent-resettable-state)
+ - 5.5 [Use model() for Two-Way Binding](#55-use-model-for-two-way-binding)
+ - 5.6 [Use Optimistic Updates with Signals](#56-use-optimistic-updates-with-signals)
+ - 5.7 [Use resource() for Async Data Loading](#57-use-resource-for-async-data-loading)
+ - 5.8 [Use Signal Content Queries](#58-use-signal-content-queries)
+ - 5.9 [Use Signal View Queries](#59-use-signal-view-queries)
+ - 5.10 [Use Signals for Local State](#510-use-signals-for-local-state)
+ - 5.11 [Use toSignal for Observables](#511-use-tosignal-for-observables)
+6. [Component Patterns](#6-component-patterns) — **HIGH**
+ - 6.1 [Use Host Object for Element Bindings](#61-use-host-object-for-element-bindings)
+ - 6.2 [Use inject() Function for Dependencies](#62-use-inject-function-for-dependencies)
+7. [RxJS Patterns](#7-rxjs-patterns) — **HIGH**
+ - 7.1 [Choose the Right Flattening Operator](#71-choose-the-right-flattening-operator)
+ - 7.2 [Handle Errors in Streams](#72-handle-errors-in-streams)
+ - 7.3 [Use combineLatest for Multiple Streams](#73-use-combinelatest-for-multiple-streams)
+ - 7.4 [Use DestroyRef and takeUntilDestroyed](#74-use-destroyref-and-takeuntildestroyed)
+ - 7.5 [Use shareReplay for Multicasting](#75-use-sharereplay-for-multicasting)
+8. [Template Optimization](#8-template-optimization) — **HIGH**
+ - 8.1 [Use NgOptimizedImage for Images](#81-use-ngoptimizedimage-for-images)
+ - 8.2 [Use Pure Pipes for Transforms](#82-use-pure-pipes-for-transforms)
+ - 8.3 [Use Virtual Scrolling for Large Lists](#83-use-virtual-scrolling-for-large-lists)
+9. [SSR & Hydration](#9-ssr-hydration) — **HIGH**
+ - 9.1 [Configure Server Route Render Modes](#91-configure-server-route-render-modes)
+ - 9.2 [Enable Client Hydration](#92-enable-client-hydration)
+ - 9.3 [Fetch Data in Parallel on Server](#93-fetch-data-in-parallel-on-server)
+ - 9.4 [Run Non-Critical Work After Response](#94-run-non-critical-work-after-response)
+ - 9.5 [Set SEO Meta Tags for SSR Pages](#95-set-seo-meta-tags-for-ssr-pages)
+ - 9.6 [Use Incremental Hydration with @defer](#96-use-incremental-hydration-with-defer)
+ - 9.7 [Use LRU Cache for SSR Computations](#97-use-lru-cache-for-ssr-computations)
+ - 9.8 [Use TransferState to Avoid Refetch](#98-use-transferstate-to-avoid-refetch)
+10. [Forms](#10-forms) — **MEDIUM**
+ - 10.1 [Create Reusable Validators](#101-create-reusable-validators)
+ - 10.2 [Handle Form Submission Properly](#102-handle-form-submission-properly)
+ - 10.3 [Use ControlValueAccessor for Custom Controls](#103-use-controlvalueaccessor-for-custom-controls)
+ - 10.4 [Use ngx-formly for Dynamic Forms](#104-use-ngx-formly-for-dynamic-forms)
+11. [Architecture](#11-architecture) — **HIGH**
+ - 11.1 [Enforce Module Boundaries](#111-enforce-module-boundaries)
+ - 11.2 [Use Barrel Files for Public APIs](#112-use-barrel-files-for-public-apis)
+ - 11.3 [Use Domain-Driven Folder Structure](#113-use-domain-driven-folder-structure)
+12. [Testing](#12-testing) — **HIGH**
+ - 12.1 [Automate Accessibility Testing with axe-core](#121-automate-accessibility-testing-with-axe-core)
+ - 12.2 [Component Testing with Angular Testing Library](#122-component-testing-with-angular-testing-library)
+ - 12.3 [Mocking with ng-mocks and MSW](#123-mocking-with-ng-mocks-and-msw)
+ - 12.4 [Test Signals and Computed Values](#124-test-signals-and-computed-values)
+ - 12.5 [Unit Testing with Vitest](#125-unit-testing-with-vitest)
+ - 12.6 [Use CDK Component Test Harnesses](#126-use-cdk-component-test-harnesses)
+ - 12.7 [Use Object Mothers for Test Data](#127-use-object-mothers-for-test-data)
+13. [Infrastructure](#13-infrastructure) — **MEDIUM**
+ - 13.1 [Enable Scroll Position Restoration](#131-enable-scroll-position-restoration)
+ - 13.2 [Error Handling Patterns](#132-error-handling-patterns)
+ - 13.3 [Schema-Validate LocalStorage Data](#133-schema-validate-localstorage-data)
+ - 13.4 [Security Patterns](#134-security-patterns)
+ - 13.5 [Use CanDeactivate Guard for Unsaved Changes](#135-use-candeactivate-guard-for-unsaved-changes)
+ - 13.6 [Use Custom Preloading Strategy](#136-use-custom-preloading-strategy)
+ - 13.7 [Use Event Delegation for Lists](#137-use-event-delegation-for-lists)
+ - 13.8 [Use Factory Providers for Complex Setup](#138-use-factory-providers-for-complex-setup)
+ - 13.9 [Use Functional Route Guards](#139-use-functional-route-guards)
+ - 13.10 [Use Functional Route Resolvers](#1310-use-functional-route-resolvers)
+ - 13.11 [Use InjectionToken with Factory for Config](#1311-use-injectiontoken-with-factory-for-config)
+ - 13.12 [Use provideAppInitializer for Startup Logic](#1312-use-provideappinitializer-for-startup-logic)
+ - 13.13 [Use Route Input Binding](#1313-use-route-input-binding)
+14. [UI & Accessibility](#14-ui-accessibility) — **MEDIUM**
+ - 14.1 [Ensure Keyboard Navigation](#141-ensure-keyboard-navigation)
+ - 14.2 [Loading State Patterns](#142-loading-state-patterns)
+ - 14.3 [Manage Focus with CDK FocusTrap](#143-manage-focus-with-cdk-focustrap)
+ - 14.4 [Respect prefers-reduced-motion](#144-respect-prefers-reduced-motion)
+ - 14.5 [Theming Patterns](#145-theming-patterns)
+ - 14.6 [Use ARIA Roles and Live Regions](#146-use-aria-roles-and-live-regions)
+15. [Data Handling](#15-data-handling) — **MEDIUM**
+ - 15.1 [Compose Mappers for Nested Data](#151-compose-mappers-for-nested-data)
+ - 15.2 [Use Functional HTTP Interceptors](#152-use-functional-http-interceptors)
+ - 15.3 [Use Pure Mapper Functions for DTOs](#153-use-pure-mapper-functions-for-dtos)
+
+---
+
+## 1. Eliminating Waterfalls
+
+**Impact: CRITICAL** (2-10× improvement)
+
+### 1.1 Defer Await Until Needed
+
+**Impact: HIGH** (avoids blocking unused code paths)
+
+Move `await` into branches where needed. Check cheapest conditions first (local state) before making expensive async calls.
+
+**Example:**
+
+```typescript
+async function handleRequest(userId: string, skipProcessing: boolean) {
+ if (skipProcessing) return { skipped: true }; // Fast path - no await
+ const userData = await fetchUserData(userId);
+ return processUserData(userData);
+}
+```
+
+### 1.2 Promise.all() for Independent Operations
+
+**Impact: CRITICAL** (2-10x improvement, eliminates waterfalls)
+
+Execute independent async operations concurrently instead of sequentially. Use `forkJoin()` for RxJS, `Promise.allSettled()` for partial failures.
+
+**Example:**
+
+```typescript
+const [user, posts] = await Promise.all([fetchUser(id), fetchPosts(id)]);
+```
+
+---
+
+## 2. Bundle Optimization
+
+**Impact: CRITICAL** (Reduces initial load)
+
+### 2.1 Avoid Barrel File Imports
+
+**Impact: CRITICAL** (200-800ms import cost, slow builds)
+
+Import directly from source files instead of barrel files (index.ts) to avoid loading thousands of unused modules. For **creating** barrel files for your domain's public API, see `arch-barrel-files.md`.
+
+**Incorrect:**
+
+```typescript
+import { Check, X, Menu } from 'lucide-angular';
+import { debounce } from 'lodash';
+```
+
+**Correct:**
+
+```typescript
+import { LucideCheck } from 'lucide-angular/check';
+import debounce from 'lodash/debounce';
+```
+
+### 2.2 Conditional Imports for Features
+
+**Impact: MEDIUM** (Excludes unused code from bundle)
+
+Use conditional imports and feature flags to exclude unused code from production bundles.
+
+**Example:**
+
+```typescript
+providers: [
+ provideRouter(routes),
+ ...(isDevMode() ? [provideDevTools()] : []),
+]
+```
+
+### 2.3 Defer Heavy Third-Party Libraries
+
+**Impact: MEDIUM** (Reduces initial bundle size)
+
+Load heavy libraries (Chart.js, PDF.js, Leaflet, Quill) only when needed using `@defer` or dynamic imports.
+
+**Example:**
+
+```typescript
+async generatePdf(content: string) {
+ const { jsPDF } = await import('jspdf');
+ return new jsPDF().text(content, 10, 10);
+}
+```
+
+### 2.4 Preload Critical Resources
+
+**Impact: LOW** (Improves perceived performance)
+
+Use `withPreloading(PreloadAllModules)` for routes. Use `@defer (prefetch on idle)` for components. Use `` for critical fonts.
+
+**Example:**
+
+```html
+@defer (on viewport; prefetch on idle) { }
+@defer (on interaction; prefetch on hover) { }
+```
+
+### 2.5 Use @defer for Lazy Loading
+
+**Impact: HIGH** (reduces initial bundle size)
+
+Use `@defer` for component-level lazy loading. Triggers: `on viewport`, `on interaction`, `on idle`, `on timer(ms)`, `when condition()`.
+
+**Example:**
+
+```html
+@defer (on viewport) {
+
+} @placeholder {
+
+} @loading (minimum 200ms) {
+
+}
+```
+
+---
+
+## 3. JavaScript Performance
+
+**Impact: HIGH** (Runtime performance)
+
+### 3.1 Batch DOM Reads and Writes
+
+**Impact: MEDIUM** (avoids layout thrashing)
+
+When manipulating the DOM directly, batch all reads together and all writes together to avoid layout thrashing.
+
+**Incorrect:**
+
+```typescript
+for (const el of elements) {
+ const height = el.offsetHeight; // READ - triggers layout
+ el.style.height = `${height * 2}px`; // WRITE - invalidates layout
+}
+```
+
+**Correct:**
+
+```typescript
+const heights = elements.map(el => el.offsetHeight);
+elements.forEach((el, i) => el.style.height = `${heights[i] * 2}px`);
+```
+
+**In Angular, prefer template bindings** which let Angular handle DOM updates efficiently. When direct DOM access is needed, use `requestAnimationFrame()`.
+**Properties that trigger layout:** `offsetTop/Left/Width/Height`, `scrollTop/Left/Width/Height`, `clientTop/Left/Width/Height`, `getComputedStyle()`, `getBoundingClientRect()`
+
+### 3.2 Cache API Results with Interceptors
+
+**Impact: MEDIUM** (Eliminates redundant network requests)
+
+Use an HTTP interceptor or service-level caching with signals to avoid redundant API calls — prefer Angular-idiomatic patterns over raw `sessionStorage`.
+
+**Example:**
+
+```typescript
+// Service-level caching with signals
+private configCache = signal(null);
+loadConfig = () => this.configCache() ?? this.http.get('/api/config').pipe(
+ tap(d => this.configCache.set(d))
+);
+```
+
+### 3.3 Memoize Expensive Function Results
+
+**Impact: MEDIUM** (avoid redundant computation)
+
+Use `computed()` for automatic memoization of expensive calculations. Computed signals only recalculate when dependencies change.
+
+**Example:**
+
+```typescript
+statistics = computed(() =>
+ this.data().reduce((stats, point) => computeStats(stats, point), initial)
+);
+
+// Or use TanStack Query with staleTime for API caching
+```
+
+### 3.4 Pre-build Lookup Maps from Arrays
+
+**Impact: MEDIUM** (O(n²) to O(n) for repeated lookups)
+
+When looking up items multiple times, build a Map first. Converts O(n) lookups to O(1).
+
+**Example:**
+
+```typescript
+const userMap = new Map(users.map(u => [u.id, u]));
+return orders.map(order => ({
+ ...order,
+ user: userMap.get(order.userId),
+}));
+```
+
+### 3.5 Use content-visibility for Off-Screen Content
+
+**Impact: HIGH** (10x faster initial render for long pages)
+
+Apply `content-visibility: auto` to skip rendering of off-screen content until scrolled into view. Add `contain-intrinsic-size` to reserve space for scroll calculations: `.card { content-visibility: auto; contain-intrinsic-size: 0 200px; }`.
+
+### 3.6 Use Immutable Array Methods
+
+**Impact: MEDIUM** (Cleaner code, no accidental mutations)
+
+Use ES2023's `toSorted()`, `toReversed()`, and `toSpliced()` instead of mutating methods that require copying. Instead of `[...items].sort()`, use `items.toSorted()`. Instead of `[...items].reverse()`, use `items.toReversed()`. Instead of splice with a copy, use `items.toSpliced(index, 1)`.
+
+### 3.7 Use Passive Event Listeners
+
+**Impact: LOW** (improves scroll performance)
+
+Add `{ passive: true }` to `addEventListener()` for `scroll`, `wheel`, `touchstart`, `touchmove`, and `touchend` events. Passive listeners improve scroll performance by telling the browser that `preventDefault()` won't be called. Avoid when you need to call `preventDefault()`.
+
+### 3.8 Use Set/Map for O(1) Lookups
+
+**Impact: MEDIUM** (O(n) to O(1) lookup performance)
+
+Convert arrays to Set/Map for repeated membership checks. Array `includes()` is O(n); Set `has()` is O(1). Convert when checking membership more than once or arrays have >10 items.
+
+**Incorrect (O(n) per check):**
+
+```typescript
+const allowedIds = ['a', 'b', 'c'];
+const filtered = items.filter(item => allowedIds.includes(item.id));
+```
+
+**Correct (O(1) per check):**
+
+```typescript
+const allowedIds = new Set(['a', 'b', 'c']);
+const filtered = items.filter(item => allowedIds.has(item.id));
+```
+
+---
+
+## 4. TypeScript Best Practices
+
+**Impact: MEDIUM** (Type safety & maintainability)
+
+### 4.1 Avoid Enums, Use const Objects
+
+**Impact: MEDIUM** (Better tree-shaking, clearer behavior)
+
+Use `as const` objects instead of enums for predictable behavior and tree-shaking.
+
+**Incorrect:**
+
+```typescript
+enum Direction { Up, Down } // Numeric enums create reverse mappings
+Object.keys(Direction).length; // 4, not 2!
+```
+
+**Correct:**
+
+```typescript
+const Direction = { Up: 'UP', Down: 'DOWN' } as const;
+type Direction = (typeof Direction)[keyof typeof Direction]; // 'UP' | 'DOWN'
+```
+
+### 4.2 Declare Return Types for Exported Functions
+
+**Impact: LOW** (Improves AI comprehension, serves as documentation)
+
+Add explicit return types to exported/public functions: `function getUser(id: string): User`. This helps AI tools understand your API contracts and catches accidental return type changes. Internal/private functions can rely on inference.
+
+### 4.3 Handle noUncheckedIndexedAccess
+
+**Impact: LOW** (Safer array/object access)
+
+With `noUncheckedIndexedAccess`, array/object indexing returns `T | undefined`. Always check before using.
+
+**Example:**
+
+```typescript
+const first = users[0];
+if (first) { console.log(first.name); }
+// Or: console.log(first?.name ?? 'Unknown');
+```
+
+### 4.4 Prefer Explicit Undefined Over Optional
+
+**Impact: MEDIUM** (Catches missing property bugs at compile time)
+
+Use `prop: T | undefined` instead of `prop?: T` when omission is a bug. Optional props (`?`) allow complete omission, while `T | undefined` requires explicit `undefined` to signal intentional absence.
+
+### 4.5 Prefer Interface Extends Over Intersections
+
+**Impact: LOW** (Better performance, clearer error messages)
+
+Use `interface Dog extends Animal` instead of `type Dog = Animal & { breed: string }`. Interface extends produces clearer error messages (shows "Dog" vs full intersection) and has better TypeScript compiler performance.
+
+### 4.6 Use Discriminated Unions
+
+**Impact: HIGH** (Prevents impossible states)
+
+Model mutually exclusive states with a discriminant property to prevent impossible states at compile time.
+
+**Example:**
+
+```typescript
+type State =
+ | { status: 'idle' }
+ | { status: 'loading' }
+ | { status: 'success'; data: T }
+ | { status: 'error'; error: Error };
+```
+
+### 4.7 Use import type for Type-Only Imports
+
+**Impact: LOW** (Smaller bundles, cleaner transpilation)
+
+Use `import type { User } from './user'` instead of `import { type User } from './user'` — the statement-level form is clearer, always fully erased at transpilation, and aligns with `verbatimModuleSyntax` in TypeScript 5.0+.
+
+### 4.8 Use Readonly Properties by Default
+
+**Impact: MEDIUM** (Prevents accidental mutation)
+
+Mark properties as `readonly` by default; omit only when mutation is intentional. Use `Readonly` for objects and `readonly T[]` or `ReadonlyArray` for arrays to catch accidental mutations at compile time.
+
+### 4.9 Use Result Types Instead of Throwing
+
+**Impact: MEDIUM** (Explicit error handling, type-safe)
+
+For operations that can fail, return a Result type instead of throwing. Makes error handling explicit and type-safe.
+
+**Example:**
+
+```typescript
+type Result = { ok: true; value: T } | { ok: false; error: Error };
+function parseJson(input: string): Result {
+ try { return { ok: true, value: JSON.parse(input) }; }
+ catch (e) { return { ok: false, error: e as Error }; }
+}
+```
+
+### 4.10 When any is Acceptable in Generics
+
+**Impact: LOW** (Practical type safety in complex generics)
+
+Use `as any` inside generic function bodies when TypeScript can't verify conditional types. The public API remains typed; internal casts don't leak.
+
+**Example:**
+
+```typescript
+const flip = (x: T): T extends 'a' ? 'b' : 'a' => {
+ return (x === 'a' ? 'b' : 'a') as any; // OK - public API is still typed
+};
+```
+
+### 4.11 When to Use JSDoc Comments
+
+**Impact: LOW** (Improves discoverability for non-obvious behavior)
+
+Use JSDoc for `@deprecated`, `@throws`, and non-obvious behavior. Don't repeat type information already in the signature.
+
+**Example:**
+
+```typescript
+/** @deprecated Use getUserById instead. Removed in v3.0. */
+function fetchUser(id: string): Observable { }
+```
+
+---
+
+## 5. Signals & Reactivity
+
+**Impact: HIGH** (Reactive state management)
+
+### 5.1 Avoid Effects for State Propagation
+
+**Impact: MEDIUM** (Prevents circular dependencies, cleaner data flow)
+
+Use `computed()` for derived state, not `effect()` with `.set()`. Effects are for external side effects (logging, DOM manipulation, analytics), not for propagating state between signals.
+
+**Example:**
+
+```typescript
+// Computed handles derivation without circular dependency risks
+fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
+```
+
+### 5.2 Use Computed for Derived State
+
+**Impact: HIGH** (Memoized derivation, prevents redundant calculations)
+
+Use `computed()` instead of getters for derived state. Getters re-run on every change detection; computed signals are memoized and only recalculate when dependencies change.
+
+**Example:**
+
+```typescript
+// Memoized; only runs when firstName or lastName changes
+fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
+```
+
+### 5.3 Use httpResource() for Signal-Based Data Fetching
+
+**Impact: HIGH** (Reactive GET with auto-refetch)
+
+Use `httpResource()` for reactive GET requests that auto-refetch when signal dependencies change, replacing manual HttpClient subscribe patterns.
+
+**Incorrect:**
+
+```typescript
+loading = signal(false);
+data = signal(null);
+ngOnInit() {
+ this.loading.set(true);
+ this.http.get(`/api/users/${this.userId()}`).subscribe(u => { this.data.set(u); this.loading.set(false); });
+}
+```
+
+**Correct:**
+
+```typescript
+userId = input.required();
+user = httpResource(() => `/api/users/${this.userId()}`);
+// Access: user.value(), user.isLoading(), user.error()
+```
+
+### 5.4 Use linkedSignal() for Dependent Resettable State
+
+**Impact: HIGH** (Auto-resets derived state)
+
+Use `linkedSignal()` when a writable signal's value should reset automatically when a source signal changes.
+
+**Incorrect:**
+
+```typescript
+options = signal(['Ground', 'Air', 'Sea']);
+selected = signal(this.options()[0]);
+constructor() { effect(() => this.selected.set(this.options()[0])); }
+```
+
+**Correct:**
+
+```typescript
+options = signal(['Ground', 'Air', 'Sea']);
+selected = linkedSignal(() => this.options()[0]);
+// Writable: selected.set('Air') — auto-resets when options change
+```
+
+### 5.5 Use model() for Two-Way Binding
+
+**Impact: HIGH** (Simplified two-way binding)
+
+Use `model()` instead of input+output pairs when parent and child need synchronized state via `[(value)]` syntax.
+
+**Incorrect:**
+
+```typescript
+@Input() value = 0;
+@Output() valueChange = new EventEmitter();
+update(v: number) { this.value = v; this.valueChange.emit(v); }
+```
+
+**Correct:**
+
+```typescript
+value = model(0);
+update(v: number) { this.value.set(v); }
+// Parent:
+```
+
+### 5.6 Use Optimistic Updates with Signals
+
+**Impact: MEDIUM** (Instant UI feedback)
+
+Update signal state immediately before API calls and rollback on error to provide instant UI feedback.
+
+**Incorrect:**
+
+```typescript
+async delete(id: string) {
+ await firstValueFrom(this.http.delete(`/api/items/${id}`));
+ this.items.update(items => items.filter(i => i.id !== id));
+}
+```
+
+**Correct:**
+
+```typescript
+delete(id: string) {
+ const prev = this.items();
+ this.items.update(items => items.filter(i => i.id !== id));
+ this.http.delete(`/api/items/${id}`).subscribe({ error: () => this.items.set(prev) });
+}
+```
+
+### 5.7 Use resource() for Async Data Loading
+
+**Impact: HIGH** (Built-in loading/error/cancellation)
+
+Use the `resource()` API for generic async operations with built-in loading, error, and cancellation state via `ResourceRef`.
+
+**Incorrect:**
+
+```typescript
+data = signal(undefined);
+loading = signal(false);
+error = signal(null);
+loadUser() { this.loading.set(true); fetch(`/api/users/${this.id()}`).then(r => r.json()).then(d => this.data.set(d)).finally(() => this.loading.set(false)); }
+```
+
+**Correct:**
+
+```typescript
+userResource = resource({
+ params: () => ({ id: this.userId() }),
+ loader: ({ params }) => fetch(`/api/users/${params.id}`).then(r => r.json()),
+});
+// Access: userResource.value(), userResource.isLoading(), userResource.error()
+```
+
+### 5.8 Use Signal Content Queries
+
+**Impact: HIGH** (Reactive, no lifecycle hooks)
+
+Use `contentChild()` and `contentChildren()` signal-based queries instead of `@ContentChild` and `@ContentChildren` decorators.
+
+**Incorrect:**
+
+```typescript
+@ContentChild(TemplateRef) template!: TemplateRef;
+ngAfterContentInit() { this.renderTemplate(this.template); }
+```
+
+**Correct:**
+
+```typescript
+template = contentChild.required(TemplateRef);
+// Reactive: use template() in computed() or effect(), no lifecycle hooks needed
+```
+
+### 5.9 Use Signal View Queries
+
+**Impact: HIGH** (Reactive, no lifecycle hooks)
+
+Use `viewChild()` and `viewChildren()` signal-based queries instead of `@ViewChild` and `@ViewChildren` decorators.
+
+**Incorrect:**
+
+```typescript
+@ViewChild('chart') chart!: ElementRef;
+ngAfterViewInit() { this.initChart(this.chart.nativeElement); }
+```
+
+**Correct:**
+
+```typescript
+chart = viewChild.required('chart');
+// Reactive: use chart() in computed() or effect(), no lifecycle hooks needed
+```
+
+### 5.10 Use Signals for Local State
+
+**Impact: HIGH** (Fine-grained reactivity, zoneless-ready)
+
+Use `signal(initialValue)` instead of plain class properties for component state — signals provide fine-grained reactivity and enable zoneless change detection, updated with `.set()` or `.update()`.
+
+### 5.11 Use toSignal for Observables
+
+**Impact: MEDIUM** (Cleaner templates, zoneless-ready)
+
+Use `toSignal()` to bridge RxJS observables to signals for simpler template usage. In templates, use `params()?.id` instead of `(params$ | async)?.id`.
+
+**Example:**
+
+```typescript
+// Converts observable to signal with automatic subscription management
+params = toSignal(this.route.params);
+```
+
+---
+
+## 6. Component Patterns
+
+**Impact: HIGH** (Component architecture)
+
+### 6.1 Use Host Object for Element Bindings
+
+**Impact: HIGH** (Centralizes host interactions)
+
+Use the `host` property in `@Component`/`@Directive` instead of `@HostBinding` and `@HostListener` decorators for centralized, declarative host element interactions.
+
+**Incorrect:**
+
+```typescript
+@HostBinding('class.active') isActive = false;
+@HostListener('click') onClick() { this.isActive = true; }
+```
+
+**Correct:**
+
+```typescript
+@Component({
+ host: { '[class.active]': 'isActive()', '(click)': 'activate()' }
+})
+```
+
+### 6.2 Use inject() Function for Dependencies
+
+**Impact: HIGH** (Cleaner DI, works in functions)
+
+Prefer the `inject()` function over constructor injection for cleaner syntax, better type inference, and compatibility with functional guards/interceptors.
+
+**Incorrect:**
+
+```typescript
+@Component({ selector: 'app-user' })
+export class UserComponent {
+ constructor(private userService: UserService, private router: Router) {}
+}
+```
+
+**Correct:**
+
+```typescript
+@Component({ selector: 'app-user' })
+export class UserComponent {
+ private userService = inject(UserService);
+ private router = inject(Router);
+}
+```
+
+---
+
+## 7. RxJS Patterns
+
+**Impact: HIGH** (Memory leaks & cancellation)
+
+### 7.1 Choose the Right Flattening Operator
+
+**Impact: HIGH** (Prevents race conditions)
+
+Use `switchMap` to cancel previous (search/autocomplete), `exhaustMap` to ignore new (form submit), `concatMap` for ordered sequential, `mergeMap` for parallel — choosing wrong causes race conditions or lost requests.
+
+**Correct:**
+
+```typescript
+search$ = this.query$.pipe(switchMap(q => this.api.search(q))); // Cancel previous
+submit$ = this.click$.pipe(exhaustMap(() => this.api.save(this.form))); // Ignore while busy
+ordered$ = this.ids$.pipe(concatMap(id => this.api.process(id))); // Preserve order
+```
+
+### 7.2 Handle Errors in Streams
+
+**Impact: HIGH** (Prevents stream termination)
+
+Use `catchError` inside `switchMap` to handle errors without terminating the outer stream.
+
+**Incorrect:**
+
+```typescript
+// Error terminates entire stream - no more searches work
+search$ = this.term$.pipe(
+ switchMap(term => this.api.search(term)),
+ catchError(() => of([])) // Too late - outer stream dead
+);
+```
+
+**Correct:**
+
+```typescript
+search$ = this.term$.pipe(
+ switchMap(term => this.api.search(term).pipe(
+ catchError(() => of([])) // Recovers per-request
+ ))
+);
+```
+
+### 7.3 Use combineLatest for Multiple Streams
+
+**Impact: MEDIUM** (Combines dependent data)
+
+Use `combineLatest({ user: user$, permissions: perms$ })` to combine multiple streams instead of nested subscribes. Emits when any source emits, providing the latest value from each.
+
+**Example:**
+
+```typescript
+vm$ = combineLatest({
+ user: this.user$,
+ permissions: this.permissions$,
+ settings: this.settings$
+});
+```
+
+### 7.4 Use DestroyRef and takeUntilDestroyed
+
+**Impact: HIGH** (No manual unsubscribe logic)
+
+Use `takeUntilDestroyed()` in injection context and `DestroyRef.onDestroy()` for cleanup instead of implementing `OnDestroy` with a Subject.
+
+**Incorrect:**
+
+```typescript
+private destroy$ = new Subject();
+ngOnInit() { this.data$.pipe(takeUntil(this.destroy$)).subscribe(); }
+ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
+```
+
+**Correct:**
+
+```typescript
+private destroyRef = inject(DestroyRef);
+ngOnInit() { this.data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(); }
+// Outside injection context: pass DestroyRef explicitly
+```
+
+### 7.5 Use shareReplay for Multicasting
+
+**Impact: MEDIUM** (Avoids duplicate HTTP calls)
+
+Use `shareReplay({ bufferSize: 1, refCount: true })` to share results among multiple subscribers and avoid duplicate HTTP requests.
+
+**Example:**
+
+```typescript
+users$ = this.http.get('/api/users').pipe(
+ shareReplay({ bufferSize: 1, refCount: true })
+);
+```
+
+---
+
+## 8. Template Optimization
+
+**Impact: HIGH** (Lazy loading & pipes)
+
+### 8.1 Use NgOptimizedImage for Images
+
+**Impact: HIGH** (Optimizes LCP & Core Web Vitals)
+
+Use the `NgOptimizedImage` directive (`ngSrc`) with `priority` for above-fold images to enable automatic lazy loading, srcset generation, and preconnect hints.
+
+**Incorrect:**
+
+```html
+
+```
+
+**Correct:**
+
+```html
+
+```
+
+### 8.2 Use Pure Pipes for Transforms
+
+**Impact: MEDIUM** (Memoized, only recalculates on input change)
+
+Use pure pipes instead of method calls in templates for memoized transformations.
+
+**Incorrect:**
+
+```html
+
+{{ formatCurrency(price) }}
+```
+
+**Correct:**
+
+```html
+
+{{ price | currency:'USD' }}
+
+{{ user.name | initials }}
+```
+
+### 8.3 Use Virtual Scrolling for Large Lists
+
+**Impact: HIGH** (Renders only visible items)
+
+Use `CdkVirtualScrollViewport` with `*cdkVirtualFor` to render only visible items in long lists, reducing DOM nodes from thousands to ~20.
+
+**Incorrect:**
+
+```html
+@for (item of items; track item.id) {
+ {{ item.name }}
+}
+```
+
+**Correct:**
+
+```html
+
+ {{ item.name }}
+
+```
+
+---
+
+## 9. SSR & Hydration
+
+**Impact: HIGH** (Initial render & SEO)
+
+### 9.1 Configure Server Route Render Modes
+
+**Impact: HIGH** (Per-route rendering strategy)
+
+Use `ServerRoute` with `RenderMode.Prerender`, `RenderMode.Server`, or `RenderMode.Client` to control rendering strategy per route.
+
+**Incorrect:**
+
+```typescript
+const serverRoutes: ServerRoute[] = [
+ { path: '**', renderMode: RenderMode.Server }
+];
+```
+
+**Correct:**
+
+```typescript
+const serverRoutes: ServerRoute[] = [
+ { path: '', renderMode: RenderMode.Prerender },
+ { path: 'dashboard', renderMode: RenderMode.Client },
+ { path: '**', renderMode: RenderMode.Server }
+];
+```
+
+### 9.2 Enable Client Hydration
+
+**Impact: HIGH** (Preserves server-rendered DOM)
+
+Enable `provideClientHydration(withEventReplay())` to reuse server-rendered DOM instead of destroying and rebuilding it on the client.
+
+**Example:**
+
+```typescript
+bootstrapApplication(AppComponent, {
+ providers: [
+ provideRouter(routes),
+ provideClientHydration(withEventReplay())
+ ]
+});
+```
+
+### 9.3 Fetch Data in Parallel on Server
+
+**Impact: HIGH** (Reduces SSR response time by parallelization)
+
+Use `Promise.all()` or `forkJoin()` to fetch independent data sources concurrently during SSR to reduce total response time.
+
+**Example:**
+
+```typescript
+async resolve() {
+ const [user, posts] = await Promise.all([
+ this.userService.get(), // 200ms
+ this.postService.get(), // 300ms
+ ]);
+ return { user, posts }; // Total: 300ms (parallel, not 500ms)
+}
+```
+
+### 9.4 Run Non-Critical Work After Response
+
+**Impact: MEDIUM** (Faster Time to First Byte)
+
+Send the response first, then perform analytics, logging, or cache warming fire-and-forget style without blocking TTFB.
+
+**Example:**
+
+```typescript
+async handleRequest(req: Request) {
+ const html = await this.render(req);
+ // Fire-and-forget: don't await non-critical work
+ this.analytics.track(req).catch(console.error);
+ this.cache.warm(req).catch(console.error);
+ return html;
+}
+```
+
+### 9.5 Set SEO Meta Tags for SSR Pages
+
+**Impact: MEDIUM** (Improves search indexing)
+
+Use Angular's `Meta` and `Title` services to set Open Graph, Twitter Card, and canonical URL meta tags for server-rendered pages.
+
+**Incorrect:**
+
+```typescript
+export class ProductPage {
+ product = input.required();
+}
+```
+
+**Correct:**
+
+```typescript
+#meta = inject(Meta);
+#title = inject(Title);
+product = input.required();
+constructor() {
+ effect(() => this.#title.setTitle(this.product().name));
+}
+```
+
+### 9.6 Use Incremental Hydration with @defer
+
+**Impact: HIGH** (Reduces hydration cost)
+
+Enable `provideClientHydration(withIncrementalHydration())` and use `@defer (hydrate on ...)` triggers to hydrate components progressively instead of all at once.
+
+**Incorrect:**
+
+```html
+
+
+
+```
+
+**Correct:**
+
+```html
+
+@defer (hydrate on viewport) {
+
+}
+```
+
+### 9.7 Use LRU Cache for SSR Computations
+
+**Impact: HIGH** (Reduces server response time 50-90%)
+
+Cache expensive server-side computations with an LRU cache and TTL to avoid recomputation on repeated requests.
+
+**Example:**
+
+```typescript
+private cache = new Map();
+async renderPage(id: string) {
+ const cached = this.cache.get(id);
+ if (cached && Date.now() - cached.timestamp < 60000) return this.render(cached.data);
+ const data = await this.expensiveComputation(id);
+ this.cache.set(id, { data, timestamp: Date.now() });
+ return this.render(data);
+}
+```
+
+### 9.8 Use TransferState to Avoid Refetch
+
+**Impact: HIGH** (Eliminates duplicate API calls)
+
+Use `TransferState` to pass server-fetched data to the client, avoiding duplicate API requests during hydration.
+
+**Example:**
+
+```typescript
+data = this.transferState.hasKey(DATA_KEY)
+ ? this.transferState.get(DATA_KEY, null)
+ : this.http.get('/api/data').pipe(
+ tap(d => this.transferState.set(DATA_KEY, d))
+ );
+```
+
+---
+
+## 10. Forms
+
+**Impact: MEDIUM** (Form handling)
+
+### 10.1 Create Reusable Validators
+
+**Impact: MEDIUM** (DRY validation, consistent error handling)
+
+Extract common validation logic into reusable `ValidatorFn` functions instead of inline validators or repeated patterns.
+
+**Example:**
+
+```typescript
+export const emailValidator: ValidatorFn = (control) =>
+ control.value?.includes('@') ? null : { invalidEmail: true };
+
+// Usage: email: ['', [Validators.required, emailValidator]]
+```
+
+### 10.2 Handle Form Submission Properly
+
+**Impact: MEDIUM** (Better UX, proper error handling)
+
+Check validity and mark touched before submit, track loading state with a signal, and use `getRawValue()` to include disabled fields.
+
+**Example:**
+
+```typescript
+async onSubmit() {
+ if (this.form.invalid) { this.form.markAllAsTouched(); return; }
+ this.loading.set(true);
+ try { await this.api.submit(this.form.getRawValue()); }
+ finally { this.loading.set(false); }
+}
+```
+
+### 10.3 Use ControlValueAccessor for Custom Controls
+
+**Impact: MEDIUM** (Form integration, reusable custom inputs)
+
+Implement `ControlValueAccessor` to make custom components work with reactive forms via `formControlName` and `[(ngModel)]`.
+
+**Example:**
+
+```typescript
+@Component({ providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: RatingComponent, multi: true }] })
+export class RatingComponent implements ControlValueAccessor {
+ writeValue(v: number) { this.value.set(v); }
+}
+```
+
+### 10.4 Use ngx-formly for Dynamic Forms
+
+**Impact: MEDIUM** (Reduces form boilerplate by 60-80%)
+
+Use ngx-formly for forms driven by configuration (JSON/API). Define field configs instead of writing template markup. Create reusable custom field types for domain-specific inputs.
+
+**Incorrect:**
+
+```html
+
+@for (f of fields; track f.key) { }
+```
+
+**Correct:**
+
+```typescript
+fields: FormlyFieldConfig[] = [
+ { key: 'name', type: 'input', props: { label: 'Name', required: true } },
+ { key: 'role', type: 'select', props: { label: 'Role', options: this.roles$ } },
+];
+```
+
+---
+
+## 11. Architecture
+
+**Impact: HIGH** (Scalability)
+
+### 11.1 Enforce Module Boundaries
+
+**Impact: HIGH** (Prevents spaghetti dependencies)
+
+Use Sheriff or Nx to enforce dependency rules between domains and layers.
+
+**Incorrect:**
+
+```typescript
+// ui component imports from another domain's data layer
+import { OrderService } from '../../orders/data/order.service'; // Cross-domain!
+```
+
+**Correct:**
+
+```typescript
+// ui component only imports from own domain or shared
+import { OrderService } from '../data/order.service';
+import { formatCurrency } from '@app/shared/util';
+```
+
+Use Sheriff for standalone projects, Nx for monorepos.
+
+### 11.2 Use Barrel Files for Public APIs
+
+**Impact: MEDIUM** (Clear public contracts, encapsulation)
+
+Export only what other domains should access via `index.ts` barrel files. This rule is about **creating** focused barrel files for your domain boundaries. For avoiding **consuming** large barrel files from libraries, see `bundle-barrel-imports.md`.
+
+**Example:**
+
+```typescript
+// customers/index.ts - only export public API
+export { Customer } from './model/customer.model';
+export { CustomerService } from './data/customer.service';
+```
+
+### 11.3 Use Domain-Driven Folder Structure
+
+**Impact: MEDIUM** (Better organization, clear boundaries)
+
+Organize by domain (feature area) with layers inside each domain instead of grouping by technical type.
+
+**Example:**
+
+```typescript
+src/app/domains/
+ customers/feature/ # Smart components
+ customers/data/ # Services, state
+ customers/ui/ # Presentation components
+ customers/model/ # Types, interfaces
+```
+
+---
+
+## 12. Testing
+
+**Impact: HIGH** (Reliability)
+
+### 12.1 Automate Accessibility Testing with axe-core
+
+**Impact: HIGH** (Catches WCAG violations automatically)
+
+Use `vitest-axe` or `jest-axe` to check rendered components for WCAG violations. Run `axe()` on the container element after rendering and assert no violations.
+
+**Incorrect:**
+
+```typescript
+it('should be accessible', () => {
+ // Manual visual check only — misses hidden violations
+});
+```
+
+**Correct:**
+
+```typescript
+it('should have no a11y violations', async () => {
+ const { container } = await render(MyComponent);
+ expect(await axe(container)).toHaveNoViolations();
+});
+```
+
+### 12.2 Component Testing with Angular Testing Library
+
+**Impact: HIGH** (user-centric, reliable component tests)
+
+Use Angular Testing Library for user-centric tests. Prefer accessible queries: `getByRole`, `getByLabelText`, `getByText`. Use `getByTestId` as last resort.
+
+**Example:**
+
+```typescript
+it('should increment count', async () => {
+ await render(CounterComponent);
+ await userEvent.setup().click(screen.getByRole('button', { name: /increment/i }));
+ expect(screen.getByText('Count: 1')).toBeInTheDocument();
+});
+```
+
+### 12.3 Mocking with ng-mocks and MSW
+
+**Impact: MEDIUM** (Isolated, reliable tests)
+
+Use ng-mocks for component/service mocking. Use `HttpTestingController` for HTTP mocking in unit tests. Use MSW for integration tests.
+
+**Example:**
+
+```typescript
+TestBed.configureTestingModule({
+ imports: [ParentComponent, MockComponent(ChildComponent)],
+ providers: [MockProvider(UserService, { getUsers: () => of([mockUser]) })],
+});
+```
+
+### 12.4 Test Signals and Computed Values
+
+**Impact: HIGH** (Verifies reactive state logic)
+
+Test signals by setting values and asserting computed results. Use `TestBed.flushEffects()` to trigger pending effects. Wrap signal reads in `TestBed.runInInjectionContext` when needed.
+
+**Incorrect:**
+
+```typescript
+// Testing implementation details instead of behavior
+expect(component['_count']).toBe(1);
+```
+
+**Correct:**
+
+```typescript
+component.count.set(5);
+expect(component.doubled()).toBe(10);
+TestBed.flushEffects(); // Flush pending effects if needed
+```
+
+### 12.5 Unit Testing with Vitest
+
+**Impact: HIGH** (Fast, reliable tests for services and logic)
+
+Use Vitest for unit testing services, pipes, guards, and signals. For signals, set values directly and assert computed results. For pipes, instantiate and call `transform()`.
+
+**Example:**
+
+```typescript
+it('should fetch users', async () => {
+ TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
+ const service = TestBed.inject(UserService);
+ expect(await firstValueFrom(service.getUsers())).toBeDefined();
+});
+```
+
+### 12.6 Use CDK Component Test Harnesses
+
+**Impact: HIGH** (Stable tests decoupled from DOM structure)
+
+Use CDK test harnesses (`HarnessLoader`) to interact with Angular Material and custom components in tests. Harnesses abstract DOM details, making tests resilient to template changes.
+
+**Incorrect:**
+
+```typescript
+const button = fixture.debugElement.query(By.css('.mat-mdc-button'));
+button.nativeElement.click(); // Breaks if class name changes
+```
+
+**Correct:**
+
+```typescript
+const loader = TestbedHarnessEnvironment.loader(fixture);
+const button = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
+await button.click();
+```
+
+### 12.7 Use Object Mothers for Test Data
+
+**Impact: MEDIUM** (Consistent, maintainable test fixtures)
+
+Create factory functions (Object Mothers) that return valid default objects with optional overrides. Centralizes test data and ensures consistency across test suites.
+
+**Incorrect:**
+
+```typescript
+const user = { id: '1', name: 'John', email: 'j@e.com', role: 'admin', createdAt: new Date() };
+// Duplicated in every test file
+```
+
+**Correct:**
+
+```typescript
+export const createUser = (overrides: Partial = {}): User => ({
+ id: crypto.randomUUID(), name: 'John', email: 'john@example.com', ...overrides,
+});
+```
+
+---
+
+## 13. Infrastructure
+
+**Impact: MEDIUM** (Cross-cutting concerns)
+
+### 13.1 Enable Scroll Position Restoration
+
+**Impact: MEDIUM** (Better navigation UX)
+
+Use `withInMemoryScrolling({ scrollPositionRestoration: 'enabled' })` to automatically restore scroll positions when navigating back.
+
+**Incorrect:**
+
+```typescript
+// Manually scrolling to top on every navigation — loses back-button position
+router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => window.scrollTo(0, 0));
+```
+
+**Correct:**
+
+```typescript
+provideRouter(routes,
+ withInMemoryScrolling({ scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled' }),
+)
+```
+
+### 13.2 Error Handling Patterns
+
+**Impact: MEDIUM** (Resilience and user feedback)
+
+Use global `ErrorHandler` for unhandled exceptions. Centralize HTTP errors (401, 403, 500) in an interceptor. Use `retry({ count: 3, delay: 1000 })` for idempotent requests.
+
+**Example:**
+
+```typescript
+export const errorInterceptor: HttpInterceptorFn = (req, next) =>
+ next(req).pipe(catchError(err => {
+ if (err.status === 401) inject(Router).navigate(['/login']);
+ return throwError(() => err);
+ }));
+```
+
+### 13.3 Schema-Validate LocalStorage Data
+
+**Impact: MEDIUM** (Prevents runtime errors from corrupted storage)
+
+Use typed schemas (Zod) with versioning for localStorage to catch corrupted data and enable migrations.
+
+**Example:**
+
+```typescript
+const result = StorageSchema.safeParse(JSON.parse(localStorage.getItem('user') ?? 'null'));
+const user = result.success ? result.data.user : migrateOrDefault(result); // fallback on failure
+```
+
+### 13.4 Security Patterns
+
+**Impact: HIGH** (Prevents vulnerabilities)
+
+Never store tokens in `localStorage` (XSS-vulnerable). Use in-memory signals for access tokens and `HttpOnly` cookies for refresh tokens. Trust Angular's sanitization; avoid bypassing `DomSanitizer`. Enable CSRF with `withXsrfConfiguration()`.
+
+**Incorrect:**
+
+```typescript
+localStorage.setItem('accessToken', token); // Vulnerable to XSS
+```
+
+**Correct:**
+
+```typescript
+private accessToken = signal(null); // In-memory only
+provideHttpClient(withXsrfConfiguration({ cookieName: 'XSRF-TOKEN', headerName: 'X-XSRF-TOKEN' }));
+```
+
+### 13.5 Use CanDeactivate Guard for Unsaved Changes
+
+**Impact: MEDIUM** (Prevents accidental data loss)
+
+Implement `CanDeactivateFn` to warn users before navigating away from forms with unsaved data.
+
+**Incorrect:**
+
+```typescript
+// No guard — user navigates away and loses unsaved changes
+{ path: 'edit/:id', component: EditComponent }
+```
+
+**Correct:**
+
+```typescript
+export const unsavedChangesGuard: CanDeactivateFn<{ hasUnsavedChanges(): boolean }> =
+ (component) => component.hasUnsavedChanges() ? confirm('Discard changes?') : true;
+// Usage: { path: 'edit/:id', canDeactivate: [unsavedChangesGuard] }
+```
+
+### 13.6 Use Custom Preloading Strategy
+
+**Impact: MEDIUM** (Selective route preloading)
+
+Implement a selective preloading strategy instead of `PreloadAllModules` to control which lazy routes are preloaded based on route data flags.
+
+**Incorrect:**
+
+```typescript
+// Preloads every lazy route — wastes bandwidth on rarely visited routes
+provideRouter(routes, withPreloading(PreloadAllModules))
+```
+
+**Correct:**
+
+```typescript
+export class SelectivePreload extends PreloadingStrategy {
+ preload = (route: Route, load: () => Observable) =>
+ route.data?.['preload'] ? load() : of(null);
+}
+// Route: { path: 'dashboard', loadComponent: ..., data: { preload: true } }
+```
+
+### 13.7 Use Event Delegation for Lists
+
+**Impact: MEDIUM** (Reduces memory from O(n) to O(1) listeners)
+
+Attach one event listener to a parent instead of listeners on each child. Use `data-*` attributes and `event.target` to identify clicked item.
+
+**Example:**
+
+```html
+
+ @for (item of items(); track item.id) {
+
+ }
+
+
+```
+
+### 13.8 Use Factory Providers for Complex Setup
+
+**Impact: MEDIUM** (Conditional service creation)
+
+Use `useFactory` with `inject()` for conditional service creation based on environment or runtime dependencies.
+
+**Incorrect:**
+
+```typescript
+// Conditional logic in service constructor couples to environment
+@Injectable({ providedIn: 'root' })
+export class LogService {
+ constructor() { if (environment.production) { /* ... */ } }
+}
+```
+
+**Correct:**
+
+```typescript
+{ provide: LogService, useFactory: () => {
+ const env = inject(ENVIRONMENT);
+ return env.production ? new ProdLogService() : new DevLogService();
+ },
+}
+```
+
+### 13.9 Use Functional Route Guards
+
+**Impact: HIGH** (Protects routes, cleaner than class guards)
+
+Use functional `CanActivateFn` guards with `inject()` instead of class-based guards. Return `true`, `false`, or `UrlTree` for redirects.
+
+**Example:**
+
+```typescript
+export const authGuard: CanActivateFn = () => {
+ const auth = inject(AuthService);
+ const router = inject(Router);
+ return auth.isAuthenticated() || router.createUrlTree(['/login']);
+};
+// Usage: { path: 'dashboard', canActivate: [authGuard] }
+```
+
+### 13.10 Use Functional Route Resolvers
+
+**Impact: MEDIUM** (Preloads data before navigation)
+
+Use functional `ResolveFn` resolvers to preload data before component renders, avoiding empty state flicker.
+
+**Example:**
+
+```typescript
+export const userResolver: ResolveFn = (route) =>
+ inject(UserService).getById(route.paramMap.get('id')!).pipe(catchError(() => of(null)));
+```
+
+### 13.11 Use InjectionToken with Factory for Config
+
+**Impact: MEDIUM** (Type-safe, tree-shakeable config)
+
+Create self-providing `InjectionToken` instances with `factory` for browser globals and app configuration instead of string tokens or direct global access.
+
+**Incorrect:**
+
+```typescript
+// String tokens lose type safety and aren't tree-shakeable
+constructor(@Inject('API_URL') private apiUrl: string) {}
+const el = window.document.getElementById('app');
+```
+
+**Correct:**
+
+```typescript
+export const API_URL = new InjectionToken('API_URL', {
+ factory: () => environment.apiUrl,
+});
+// Usage: private apiUrl = inject(API_URL);
+```
+
+### 13.12 Use provideAppInitializer for Startup Logic
+
+**Impact: MEDIUM** (Blocks bootstrap until ready)
+
+Use `provideAppInitializer()` (Angular 19+) or `{ provide: APP_INITIALIZER, useFactory: ..., multi: true }` (Angular 17-18) to run async setup code before the app renders.
+
+**Incorrect:**
+
+```typescript
+// Startup logic in root component — app renders before init completes
+export class AppComponent {
+ constructor(private config: ConfigService) { config.load(); }
+}
+```
+
+**Correct:**
+
+```typescript
+// App providers — blocks rendering until init resolves
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideAppInitializer(() => inject(ConfigService).load()),
+ ],
+};
+```
+
+### 13.13 Use Route Input Binding
+
+**Impact: MEDIUM** (Cleaner component code, no ActivatedRoute)
+
+Enable `withComponentInputBinding()` in router config to bind route params, query params, and resolver data directly to component inputs. Use `id = input.required()` for `:id` params instead of injecting `ActivatedRoute`.
+
+---
+
+## 14. UI & Accessibility
+
+**Impact: MEDIUM** (User experience)
+
+### 14.1 Ensure Keyboard Navigation
+
+**Impact: MEDIUM** (Keyboard-only user access)
+
+All interactive elements must be keyboard-accessible. Use native `