diff --git a/.claude/skills/css-keyframes-animations/SKILL.md b/.claude/skills/css-keyframes-animations/SKILL.md
new file mode 100644
index 000000000..58b12e548
--- /dev/null
+++ b/.claude/skills/css-keyframes-animations/SKILL.md
@@ -0,0 +1,392 @@
+---
+name: css-keyframes-animations
+description: This skill should be used when writing or reviewing CSS animations in Angular components. Use when creating entrance/exit animations, implementing @keyframes instead of @angular/animations, applying timing functions and fill modes, creating staggered animations, or ensuring GPU-accelerated performance. Essential for modern Angular 20+ components using animate.enter/animate.leave directives and converting legacy Angular animations to native CSS.
+---
+
+# CSS @keyframes Animations
+
+## Overview
+
+Implement native CSS @keyframes animations for Angular applications, replacing @angular/animations with GPU-accelerated, zero-bundle-size alternatives. This skill provides comprehensive guidance on creating performant entrance/exit animations, staggered effects, and proper timing configurations.
+
+## When to Use This Skill
+
+Apply this skill when:
+- **Writing Angular components** with entrance/exit animations
+- **Converting @angular/animations** to native CSS @keyframes
+- **Implementing animate.enter/animate.leave** in Angular 20+ templates
+- **Creating staggered animations** for lists or collections
+- **Debugging animation issues** (snap-back, wrong starting positions, choppy playback)
+- **Optimizing animation performance** for GPU acceleration
+- **Reviewing animation code** for accessibility and best practices
+
+## Quick Start
+
+### Basic Animation Setup
+
+1. **Define @keyframes** in component CSS:
+```css
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+```
+
+2. **Apply animation** to element:
+```css
+.element {
+ animation: fadeIn 0.3s ease-out;
+}
+```
+
+3. **Use with Angular 20+ directives**:
+```html
+@if (visible()) {
+
+```
+
+## Migration from @angular/animations
+
+### Before (Angular Animations)
+
+```typescript
+import { trigger, state, style, transition, animate } from '@angular/animations';
+
+@Component({
+ animations: [
+ trigger('fadeIn', [
+ transition(':enter', [
+ style({ opacity: 0 }),
+ animate('300ms ease-out', style({ opacity: 1 }))
+ ])
+ ])
+ ]
+})
+```
+
+### After (CSS @keyframes)
+
+```typescript
+@Component({
+ template: `
+ @if (show()) {
+
Content
+ }
+ `,
+ styles: [`
+ .fade-in { animation: fadeIn 0.3s ease-out; }
+
+ @keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+ `]
+})
+```
+
+**Benefits:**
+- Zero JavaScript bundle size (~60KB savings)
+- GPU hardware acceleration
+- Standard CSS (transferable skills)
+- Better performance
diff --git a/.claude/skills/css-keyframes-animations/assets/animations.css b/.claude/skills/css-keyframes-animations/assets/animations.css
new file mode 100644
index 000000000..b285100e3
--- /dev/null
+++ b/.claude/skills/css-keyframes-animations/assets/animations.css
@@ -0,0 +1,278 @@
+/**
+ * Reusable CSS @keyframes Animations
+ *
+ * Common animation patterns for Angular applications.
+ * Import this file in your component styles or global styles.
+ */
+
+/* ============================================
+ FADE ANIMATIONS
+ ============================================ */
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+.fade-in {
+ animation: fadeIn 0.3s ease-out;
+}
+
+.fade-out {
+ animation: fadeOut 0.3s ease-in;
+}
+
+/* ============================================
+ SLIDE ANIMATIONS
+ ============================================ */
+
+@keyframes slideInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideOutDown {
+ from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+}
+
+@keyframes slideInDown {
+ from {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideOutUp {
+ from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+}
+
+@keyframes slideInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideOutLeft {
+ from {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(-20px);
+ }
+}
+
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideOutRight {
+ from {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(20px);
+ }
+}
+
+.slide-in-up { animation: slideInUp 0.3s ease-out; }
+.slide-out-down { animation: slideOutDown 0.3s ease-in; }
+.slide-in-down { animation: slideInDown 0.3s ease-out; }
+.slide-out-up { animation: slideOutUp 0.3s ease-in; }
+.slide-in-left { animation: slideInLeft 0.3s ease-out; }
+.slide-out-left { animation: slideOutLeft 0.3s ease-in; }
+.slide-in-right { animation: slideInRight 0.3s ease-out; }
+.slide-out-right { animation: slideOutRight 0.3s ease-in; }
+
+/* ============================================
+ SCALE ANIMATIONS
+ ============================================ */
+
+@keyframes scaleIn {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes scaleOut {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+
+.scale-in { animation: scaleIn 0.2s ease-out; }
+.scale-out { animation: scaleOut 0.2s ease-in; }
+
+/* ============================================
+ UTILITY ANIMATIONS
+ ============================================ */
+
+/* Loading Spinner */
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+.spin {
+ animation: spin 1s linear infinite;
+}
+
+/* Skeleton Loading */
+@keyframes shimmer {
+ 0% { background-position: -200% 0; }
+ 100% { background-position: 200% 0; }
+}
+
+.shimmer {
+ background: linear-gradient(
+ 90deg,
+ #f0f0f0 25%,
+ #e0e0e0 50%,
+ #f0f0f0 75%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+}
+
+/* Attention Pulse */
+@keyframes attention-pulse {
+ 0%, 100% {
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.5);
+ }
+ 50% {
+ box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
+ }
+}
+
+.attention-pulse {
+ animation: attention-pulse 2s ease-in-out infinite;
+}
+
+/* Shake (Error Feedback) */
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
+ 20%, 40%, 60%, 80% { transform: translateX(5px); }
+}
+
+.shake {
+ animation: shake 0.5s ease-in-out;
+}
+
+/* Breathing/Pulsing */
+@keyframes breathe {
+ 0%, 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+ 50% {
+ opacity: 0.7;
+ transform: scale(1.05);
+ }
+}
+
+.breathe {
+ animation: breathe 2s ease-in-out infinite;
+}
+
+/* ============================================
+ TOAST/NOTIFICATION ANIMATIONS
+ ============================================ */
+
+@keyframes toastIn {
+ from {
+ opacity: 0;
+ transform: translateY(100%) scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@keyframes toastOut {
+ from {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(100%) scale(0.9);
+ }
+}
+
+.toast-in {
+ animation: toastIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.toast-out {
+ animation: toastOut 0.2s ease-in forwards;
+}
+
+/* ============================================
+ ACCESSIBILITY
+ ============================================ */
+
+/* Respect user's motion preferences */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/.claude/skills/css-keyframes-animations/references/keyframes-guide.md b/.claude/skills/css-keyframes-animations/references/keyframes-guide.md
new file mode 100644
index 000000000..378175a16
--- /dev/null
+++ b/.claude/skills/css-keyframes-animations/references/keyframes-guide.md
@@ -0,0 +1,833 @@
+# CSS @keyframes Deep Dive
+
+A comprehensive guide for Angular developers transitioning from `@angular/animations` to native CSS animations.
+
+---
+
+## Table of Contents
+
+1. [Understanding @keyframes](#understanding-keyframes)
+2. [Basic Syntax](#basic-syntax)
+3. [Animation Properties](#animation-properties)
+4. [Timing Functions (Easing)](#timing-functions-easing)
+5. [Fill Modes](#fill-modes)
+6. [Advanced Techniques](#advanced-techniques)
+7. [Angular 20+ Integration](#angular-20-integration)
+8. [Common Patterns & Recipes](#common-patterns--recipes)
+9. [Performance Tips](#performance-tips)
+10. [Debugging Animations](#debugging-animations)
+
+---
+
+## Understanding @keyframes
+
+The `@keyframes` at-rule controls the intermediate steps in a CSS animation sequence by defining styles for keyframes (waypoints) along the animation. Unlike transitions (which only animate between two states), keyframes let you define multiple intermediate steps.
+
+### How It Differs from @angular/animations
+
+| @angular/animations | Native CSS @keyframes |
+|---------------------|----------------------|
+| ~60KB JavaScript bundle | Zero JS overhead |
+| CPU-based rendering | GPU hardware acceleration |
+| Angular-specific syntax | Standard CSS (transferable skills) |
+| `trigger()`, `state()`, `animate()` | `@keyframes` + CSS classes |
+
+---
+
+## Basic Syntax
+
+### The @keyframes Rule
+
+```css
+@keyframes animation-name {
+ from {
+ /* Starting styles (same as 0%) */
+ }
+ to {
+ /* Ending styles (same as 100%) */
+ }
+}
+```
+
+### Percentage-Based Keyframes
+
+For more control, use percentages to define multiple waypoints:
+
+```css
+@keyframes bounce {
+ 0% {
+ transform: translateY(0);
+ }
+ 25% {
+ transform: translateY(-30px);
+ }
+ 50% {
+ transform: translateY(0);
+ }
+ 75% {
+ transform: translateY(-15px);
+ }
+ 100% {
+ transform: translateY(0);
+ }
+}
+```
+
+### Combining Multiple Percentages
+
+You can apply the same styles to multiple keyframes:
+
+```css
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+ 50% {
+ opacity: 0.7;
+ transform: scale(1.05);
+ }
+}
+```
+
+### Applying the Animation
+
+```css
+.element {
+ animation: bounce 1s ease-in-out infinite;
+}
+```
+
+---
+
+## Animation Properties
+
+### Individual Properties
+
+| Property | Description | Example |
+|----------|-------------|---------|
+| `animation-name` | Name of the @keyframes | `animation-name: bounce;` |
+| `animation-duration` | How long one cycle takes | `animation-duration: 2s;` |
+| `animation-timing-function` | Speed curve (easing) | `animation-timing-function: ease-in;` |
+| `animation-delay` | Wait before starting | `animation-delay: 500ms;` |
+| `animation-iteration-count` | How many times to run | `animation-iteration-count: 3;` or `infinite` |
+| `animation-direction` | Forward, reverse, or alternate | `animation-direction: alternate;` |
+| `animation-fill-mode` | Styles before/after animation | `animation-fill-mode: forwards;` |
+| `animation-play-state` | Pause or play | `animation-play-state: paused;` |
+
+### Shorthand Syntax
+
+```css
+/* animation: name duration timing-function delay iteration-count direction fill-mode play-state */
+.element {
+ animation: slideIn 0.5s ease-out 0.2s 1 normal forwards running;
+}
+```
+
+**Minimum required:** name and duration
+
+```css
+.element {
+ animation: fadeIn 1s;
+}
+```
+
+### Multiple Animations
+
+Apply multiple animations to a single element:
+
+```css
+.element {
+ animation:
+ fadeIn 0.5s ease-out,
+ slideUp 0.5s ease-out,
+ pulse 2s ease-in-out 0.5s infinite;
+}
+```
+
+---
+
+## Timing Functions (Easing)
+
+The timing function controls how the animation progresses over time—where it speeds up and slows down.
+
+### Keyword Values
+
+| Keyword | Cubic-Bezier Equivalent | Description |
+|---------|------------------------|-------------|
+| `linear` | `cubic-bezier(0, 0, 1, 1)` | Constant speed |
+| `ease` | `cubic-bezier(0.25, 0.1, 0.25, 1)` | Default: slow start, fast middle, slow end |
+| `ease-in` | `cubic-bezier(0.42, 0, 1, 1)` | Slow start, fast end |
+| `ease-out` | `cubic-bezier(0, 0, 0.58, 1)` | Fast start, slow end |
+| `ease-in-out` | `cubic-bezier(0.42, 0, 0.58, 1)` | Slow start and end |
+
+### Custom Cubic-Bezier
+
+Create custom easing curves with `cubic-bezier(x1, y1, x2, y2)`:
+
+```css
+/* Bouncy overshoot effect */
+.element {
+ animation-timing-function: cubic-bezier(0.68, -0.6, 0.32, 1.6);
+}
+
+/* Smooth deceleration */
+.element {
+ animation-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
+}
+```
+
+**Tool:** Use [cubic-bezier.com](https://cubic-bezier.com) to visualize and create custom curves.
+
+### Popular Easing Functions
+
+```css
+/* Ease Out Quart - Great for enter animations */
+cubic-bezier(0.25, 1, 0.5, 1)
+
+/* Ease In Out Cubic - Smooth state changes */
+cubic-bezier(0.65, 0, 0.35, 1)
+
+/* Ease Out Back - Slight overshoot */
+cubic-bezier(0.34, 1.56, 0.64, 1)
+
+/* Ease In Out Back - Overshoot both ends */
+cubic-bezier(0.68, -0.6, 0.32, 1.6)
+```
+
+### Steps Function
+
+For frame-by-frame animations (like sprite sheets):
+
+```css
+/* 6 discrete steps */
+.sprite {
+ animation: walk 1s steps(6) infinite;
+}
+
+/* Step positions */
+steps(4, jump-start) /* Jump at start of each interval */
+steps(4, jump-end) /* Jump at end of each interval (default) */
+steps(4, jump-both) /* Jump at both ends */
+steps(4, jump-none) /* No jump at ends */
+```
+
+### Timing Function Per Keyframe
+
+Apply different easing to different segments:
+
+```css
+@keyframes complexMove {
+ 0% {
+ transform: translateX(0);
+ animation-timing-function: ease-out;
+ }
+ 50% {
+ transform: translateX(100px);
+ animation-timing-function: ease-in;
+ }
+ 100% {
+ transform: translateX(200px);
+ }
+}
+```
+
+**Important:** The timing function applies to each step individually, not the entire animation.
+
+---
+
+## Fill Modes
+
+Fill modes control what styles apply before and after the animation runs.
+
+### Values
+
+| Value | Before Animation | After Animation |
+|-------|-----------------|-----------------|
+| `none` | Original styles | Original styles |
+| `forwards` | Original styles | **Last keyframe styles** |
+| `backwards` | **First keyframe styles** | Original styles |
+| `both` | **First keyframe styles** | **Last keyframe styles** |
+
+### Common Problem: Element Snaps Back
+
+```css
+/* BAD: Element disappears then reappears after animation */
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+.element {
+ animation: fadeOut 1s; /* Element snaps back to opacity: 1 */
+}
+
+/* GOOD: Element stays invisible */
+.element {
+ animation: fadeOut 1s forwards;
+}
+```
+
+### Backwards Fill Mode (for delays)
+
+```css
+@keyframes slideIn {
+ from {
+ transform: translateX(-100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+/* Without backwards: element visible at original position during delay */
+/* With backwards: element starts at first keyframe position during delay */
+.element {
+ animation: slideIn 0.5s ease-out 1s backwards;
+}
+```
+
+---
+
+## Advanced Techniques
+
+### Animation Direction
+
+Control playback direction:
+
+```css
+animation-direction: normal; /* 0% → 100% */
+animation-direction: reverse; /* 100% → 0% */
+animation-direction: alternate; /* 0% → 100% → 0% */
+animation-direction: alternate-reverse; /* 100% → 0% → 100% */
+```
+
+**Use Case:** Breathing/pulsing effects
+
+```css
+@keyframes breathe {
+ from { transform: scale(1); }
+ to { transform: scale(1.1); }
+}
+
+.element {
+ animation: breathe 2s ease-in-out infinite alternate;
+}
+```
+
+### Staggered Animations
+
+Create cascading effects with animation-delay:
+
+```css
+.item { animation: fadeSlideIn 0.5s ease-out backwards; }
+.item:nth-child(1) { animation-delay: 0ms; }
+.item:nth-child(2) { animation-delay: 100ms; }
+.item:nth-child(3) { animation-delay: 200ms; }
+.item:nth-child(4) { animation-delay: 300ms; }
+
+/* Or use CSS custom properties */
+.item {
+ animation: fadeSlideIn 0.5s ease-out backwards;
+ animation-delay: calc(var(--i, 0) * 100ms);
+}
+```
+
+In your template:
+
+```html
+
First
+
Second
+
Third
+```
+
+### @starting-style (Modern CSS)
+
+Define styles for when an element first enters the DOM:
+
+```css
+.modal {
+ opacity: 1;
+ transform: scale(1);
+ transition: opacity 0.3s, transform 0.3s;
+
+ @starting-style {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+```
+
+### Animating Auto Height
+
+Use CSS Grid for height: auto animations:
+
+```css
+.accordion-content {
+ display: grid;
+ grid-template-rows: 0fr;
+ transition: grid-template-rows 0.3s ease-out;
+}
+
+.accordion-content.open {
+ grid-template-rows: 1fr;
+}
+
+.accordion-content > div {
+ overflow: hidden;
+}
+```
+
+### Pause/Play with CSS
+
+```css
+.element {
+ animation: spin 2s linear infinite;
+ animation-play-state: running;
+}
+
+.element:hover {
+ animation-play-state: paused;
+}
+
+/* Or with a class */
+.element.paused {
+ animation-play-state: paused;
+}
+```
+
+---
+
+## Angular 20+ Integration
+
+### Using animate.enter and animate.leave
+
+Angular 20.2+ provides `animate.enter` and `animate.leave` to apply CSS classes when elements enter/leave the DOM.
+
+```typescript
+@Component({
+ selector: 'app-example',
+ template: `
+ @if (isVisible()) {
+
+ Content here
+
+ }
+
+ `,
+ styles: [`
+ .fade-in {
+ animation: fadeIn 0.3s ease-out;
+ }
+
+ .fade-out {
+ animation: fadeOut 0.3s ease-in;
+ }
+
+ @keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+
+ @keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+ }
+ `]
+})
+export class ExampleComponent {
+ isVisible = signal(false);
+ toggle() { this.isVisible.update(v => !v); }
+}
+```
+
+### Dynamic Animation Classes
+
+```typescript
+@Component({
+ template: `
+ @if (show()) {
+
+ Dynamic animations!
+
+ }
+ `
+})
+export class DynamicAnimComponent {
+ show = signal(false);
+ enterAnimation = signal('slide-in-right');
+ leaveAnimation = signal('slide-out-left');
+}
+```
+
+### Reusable Animation CSS File
+
+Create a shared `animations.css`:
+
+```css
+/* animations.css */
+
+/* Fade animations */
+.fade-in { animation: fadeIn 0.3s ease-out; }
+.fade-out { animation: fadeOut 0.3s ease-in; }
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+/* Slide animations */
+.slide-in-up { animation: slideInUp 0.3s ease-out; }
+.slide-out-down { animation: slideOutDown 0.3s ease-in; }
+
+@keyframes slideInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideOutDown {
+ from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+}
+
+/* Scale animations */
+.scale-in { animation: scaleIn 0.2s ease-out; }
+.scale-out { animation: scaleOut 0.2s ease-in; }
+
+@keyframes scaleIn {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes scaleOut {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+```
+
+Import in `styles.css` or `angular.json`:
+
+```css
+@import 'animations.css';
+```
+
+---
+
+## Common Patterns & Recipes
+
+### Loading Spinner
+
+```css
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+```
+
+### Skeleton Loading
+
+```css
+@keyframes shimmer {
+ 0% { background-position: -200% 0; }
+ 100% { background-position: 200% 0; }
+}
+
+.skeleton {
+ background: linear-gradient(
+ 90deg,
+ #f0f0f0 25%,
+ #e0e0e0 50%,
+ #f0f0f0 75%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+}
+```
+
+### Attention Pulse
+
+```css
+@keyframes attention-pulse {
+ 0%, 100% {
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.5);
+ }
+ 50% {
+ box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
+ }
+}
+
+.notification-badge {
+ animation: attention-pulse 2s ease-in-out infinite;
+}
+```
+
+### Shake (Error Feedback)
+
+```css
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
+ 20%, 40%, 60%, 80% { transform: translateX(5px); }
+}
+
+.error-input {
+ animation: shake 0.5s ease-in-out;
+}
+```
+
+### Slide Down Menu
+
+```css
+@keyframes slideDown {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.dropdown-menu {
+ animation: slideDown 0.2s ease-out forwards;
+}
+```
+
+### Toast Notification
+
+```css
+@keyframes toastIn {
+ from {
+ opacity: 0;
+ transform: translateY(100%) scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@keyframes toastOut {
+ from {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(100%) scale(0.9);
+ }
+}
+
+.toast {
+ animation: toastIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.toast.leaving {
+ animation: toastOut 0.2s ease-in forwards;
+}
+```
+
+---
+
+## Performance Tips
+
+### Use Transform and Opacity
+
+These properties are GPU-accelerated and don't trigger layout:
+
+```css
+/* GOOD - GPU accelerated */
+@keyframes good {
+ from { transform: translateX(0); opacity: 0; }
+ to { transform: translateX(100px); opacity: 1; }
+}
+
+/* AVOID - Triggers layout recalculation */
+@keyframes avoid {
+ from { left: 0; width: 100px; }
+ to { left: 100px; width: 200px; }
+}
+```
+
+### Use will-change Sparingly
+
+```css
+.element {
+ will-change: transform, opacity;
+}
+
+/* Remove after animation */
+.element.animation-complete {
+ will-change: auto;
+}
+```
+
+### Respect Reduced Motion
+
+```css
+@keyframes fadeSlide {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.element {
+ animation: fadeSlide 0.3s ease-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .element {
+ animation: none;
+ /* Or use a simpler fade */
+ animation: fadeIn 0.1s ease-out;
+ }
+}
+```
+
+### Avoid Animating Layout Properties
+
+Properties that trigger layout (reflow):
+
+- `width`, `height`
+- `top`, `left`, `right`, `bottom`
+- `margin`, `padding`
+- `font-size`
+- `border-width`
+
+Use `transform: scale()` instead of `width/height` when possible.
+
+---
+
+## Debugging Animations
+
+### Browser DevTools
+
+1. **Chrome DevTools** → More Tools → Animations
+ - Pause, slow down, or step through animations
+ - Inspect timing curves
+
+2. **Firefox** → Inspector → Animations tab
+ - Visual timeline of all animations
+
+### Force Slow Motion
+
+```css
+/* Temporarily add to debug */
+* {
+ animation-duration: 3s !important;
+}
+```
+
+### Animation Events in JavaScript
+
+```typescript
+element.addEventListener('animationstart', (e) => {
+ console.log('Started:', e.animationName);
+});
+
+element.addEventListener('animationend', (e) => {
+ console.log('Ended:', e.animationName);
+ // Clean up class, remove element, etc.
+});
+
+element.addEventListener('animationiteration', (e) => {
+ console.log('Iteration:', e.animationName);
+});
+```
+
+### Common Issues
+
+| Problem | Solution |
+|---------|----------|
+| Animation not running | Check `animation-duration` is > 0 |
+| Element snaps back | Add `animation-fill-mode: forwards` |
+| Animation starts wrong | Use `animation-fill-mode: backwards` with delay |
+| Choppy animation | Use `transform` instead of layout properties |
+| Animation restarts on state change | Ensure Angular doesn't recreate the element |
+
+---
+
+## Quick Reference Card
+
+```css
+/* Basic setup */
+@keyframes name {
+ from { /* start */ }
+ to { /* end */ }
+}
+
+.element {
+ animation: name 0.3s ease-out forwards;
+}
+
+/* Angular 20+ */
+
+
+/* Shorthand order */
+animation: name duration timing delay count direction fill-mode state;
+
+/* Common timing functions */
+ease-out: cubic-bezier(0, 0, 0.58, 1) /* Enter animations */
+ease-in: cubic-bezier(0.42, 0, 1, 1) /* Exit animations */
+ease-in-out: cubic-bezier(0.42, 0, 0.58, 1) /* State changes */
+
+/* Fill modes */
+forwards → Keep end state
+backwards → Apply start state during delay
+both → Both of the above
+```
+
+---
+
+## Resources
+
+- [MDN CSS Animations Guide](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations)
+- [Angular Animation Migration Guide](https://angular.dev/guide/animations/migration)
+- [Cubic Bezier Tool](https://cubic-bezier.com)
+- [Easing Functions Cheat Sheet](https://easings.net)
+- [Josh W. Comeau's Keyframe Guide](https://www.joshwcomeau.com/animation/keyframe-animations/)