diff --git a/apps/web/src/app/features/inventory/inventory-detail-panel.component.html b/apps/web/src/app/features/inventory/inventory-detail-panel.component.html
new file mode 100644
index 0000000..e981664
--- /dev/null
+++ b/apps/web/src/app/features/inventory/inventory-detail-panel.component.html
@@ -0,0 +1,60 @@
+@if (item(); as item) {
+
+
+
+ @if (statRows().length) {
+
+ @for (row of statRows(); track row.label) {
+
+
- {{ row.label }}
+ -
+ {{ row.value }}
+ @if (row.diff !== null && row.diff !== 0) {
+ 0"
+ [class.inventory-detail__diff--negative]="row.diff < 0"
+ >
+ ({{ row.diff > 0 ? '+' : '' }}{{ row.diff }})
+
+ }
+
+
+ }
+
+ }
+
+
+ @if (item.equipped) {
+ Ausgerüstet
+ } @else if (!isEquippable()) {
+ Nicht ausrüstbar
+ } @else if (!meetsLevelRequirement()) {
+
+ } @else {
+
+ }
+
+
+} @else {
+ Wähle einen Gegenstand aus deinem Inventar.
+}
diff --git a/apps/web/src/app/features/inventory/inventory-detail-panel.component.scss b/apps/web/src/app/features/inventory/inventory-detail-panel.component.scss
new file mode 100644
index 0000000..49fb865
--- /dev/null
+++ b/apps/web/src/app/features/inventory/inventory-detail-panel.component.scss
@@ -0,0 +1,124 @@
+:host {
+ display: block;
+}
+
+.inventory-detail {
+ padding: var(--ar-space-4);
+ border: 1px solid var(--ar-border-highlight);
+ border-radius: var(--ar-radius-sm);
+ background:
+ linear-gradient(125deg, rgb(255 255 255 / 0.045), transparent 42%), rgb(12 15 17 / 0.96);
+ box-shadow: var(--ar-shadow-raised);
+}
+
+.inventory-detail__header {
+ display: flex;
+ gap: var(--ar-space-3);
+ align-items: center;
+ margin-block-end: var(--ar-space-3);
+}
+
+.inventory-detail__icon {
+ inline-size: 4rem;
+ block-size: 4rem;
+ padding: var(--ar-space-1);
+ border: 1px solid var(--ar-border);
+ background: linear-gradient(180deg, #1b1f22, #0d1012);
+ object-fit: contain;
+}
+
+.inventory-detail__name {
+ margin: 0;
+ color: var(--ar-text);
+ font-family: Georgia, 'Times New Roman', serif;
+ font-size: 1.15rem;
+ font-weight: 400;
+}
+
+.inventory-detail__rarity {
+ margin: 0.15rem 0 0;
+ color: var(--ar-text-muted);
+ font-size: var(--ar-font-sm);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.inventory-detail__meta {
+ margin: 0.25rem 0 0;
+ color: var(--ar-text-muted);
+ font-size: var(--ar-font-sm);
+}
+
+.inventory-detail__stats {
+ display: grid;
+ gap: var(--ar-space-2);
+ margin: 0 0 var(--ar-space-3);
+ padding-block: var(--ar-space-2);
+ border-block: 1px solid rgb(155 122 66 / 0.45);
+}
+
+.inventory-detail__stat {
+ display: flex;
+ justify-content: space-between;
+}
+
+.inventory-detail__stat dt {
+ color: var(--ar-text-muted);
+}
+
+.inventory-detail__stat dd {
+ margin: 0;
+ font-family: Georgia, 'Times New Roman', serif;
+}
+
+.inventory-detail__diff--positive {
+ color: var(--ar-success);
+}
+
+.inventory-detail__diff--negative {
+ color: var(--ar-danger);
+}
+
+.inventory-detail__actions {
+ display: flex;
+ justify-content: center;
+}
+
+.inventory-detail__equipped {
+ color: var(--ar-gold);
+ font-family: Georgia, 'Times New Roman', serif;
+}
+
+.inventory-detail__note {
+ color: var(--ar-text-muted);
+ font-style: italic;
+}
+
+.inventory-detail__equip {
+ inline-size: 100%;
+ padding: var(--ar-space-2) var(--ar-space-4);
+ border: 1px solid var(--ar-border-highlight);
+ border-radius: var(--ar-radius-sm);
+ color: var(--ar-text);
+ background: linear-gradient(180deg, #263b4b, #17232d);
+ font-family: Georgia, 'Times New Roman', serif;
+ font-size: 1rem;
+}
+
+.inventory-detail__equip:hover:not(:disabled) {
+ border-color: #d6b26b;
+ background: linear-gradient(180deg, #315067, #1a2c3a);
+}
+
+.inventory-detail__equip:disabled {
+ border-color: var(--ar-border);
+ color: var(--ar-text-muted);
+ background: #1a1c1d;
+}
+
+.inventory-detail__empty {
+ padding: var(--ar-space-4);
+ color: var(--ar-text-muted);
+ font-style: italic;
+ text-align: center;
+}
diff --git a/apps/web/src/app/features/inventory/inventory-detail-panel.component.spec.ts b/apps/web/src/app/features/inventory/inventory-detail-panel.component.spec.ts
new file mode 100644
index 0000000..109e9e6
--- /dev/null
+++ b/apps/web/src/app/features/inventory/inventory-detail-panel.component.spec.ts
@@ -0,0 +1,106 @@
+import { TestBed } from '@angular/core/testing';
+import { vi } from 'vitest';
+import type { InventoryItem } from '../../core/api/game-api.models';
+import { InventoryDetailPanelComponent } from './inventory-detail-panel.component';
+
+const wornSword: InventoryItem = {
+ id: 'item-sword',
+ quantity: 1,
+ equipped: true,
+ item: {
+ key: 'worn-short-sword',
+ name: 'Abgenutztes Kurzschwert',
+ rarity: 'COMMON',
+ equipmentSlot: 'WEAPON',
+ requiredLevel: 1,
+ weaponDamage: 8,
+ bonusAttack: 0,
+ bonusHp: 0,
+ bonusArmor: 0,
+ iconPath: '/images/items/worn-short-sword.png',
+ },
+};
+
+const banditBlade: InventoryItem = {
+ id: 'item-blade',
+ quantity: 1,
+ equipped: false,
+ item: {
+ key: 'bandit-blade',
+ name: 'Räuberklinge',
+ rarity: 'COMMON',
+ equipmentSlot: 'WEAPON',
+ requiredLevel: 1,
+ weaponDamage: 11,
+ bonusAttack: 1,
+ bonusHp: 0,
+ bonusArmor: 0,
+ iconPath: '/images/items/bandit-blade.png',
+ },
+};
+
+async function setup(overrides: {
+ item?: InventoryItem | null;
+ equippedItemInSlot?: InventoryItem | null;
+ characterLevel?: number;
+ busy?: boolean;
+}) {
+ TestBed.resetTestingModule();
+ await TestBed.configureTestingModule({ imports: [InventoryDetailPanelComponent] }).compileComponents();
+ const fixture = TestBed.createComponent(InventoryDetailPanelComponent);
+ fixture.componentRef.setInput('item', overrides.item ?? null);
+ fixture.componentRef.setInput('equippedItemInSlot', overrides.equippedItemInSlot ?? null);
+ fixture.componentRef.setInput('characterLevel', overrides.characterLevel ?? 1);
+ fixture.componentRef.setInput('busy', overrides.busy ?? false);
+ fixture.detectChanges();
+ return fixture;
+}
+
+describe('InventoryDetailPanelComponent', () => {
+ it('shows a placeholder when nothing is selected', async () => {
+ const fixture = await setup({ item: null });
+ expect((fixture.nativeElement as HTMLElement).querySelector('[data-detail-empty]')).not.toBeNull();
+ });
+
+ it('shows Ausgerüstet for the currently equipped item, with no equip button', async () => {
+ const fixture = await setup({ item: wornSword });
+ const element = fixture.nativeElement as HTMLElement;
+
+ expect(element.querySelector('[data-detail-equipped]')?.textContent).toContain('Ausgerüstet');
+ expect(element.querySelector('[data-detail-equip]')).toBeNull();
+ });
+
+ it('shows the stat comparison against the equipped item in the same slot', async () => {
+ const fixture = await setup({ item: banditBlade, equippedItemInSlot: wornSword });
+ const text = (fixture.nativeElement as HTMLElement).querySelector('[data-detail-stats]')?.textContent ?? '';
+
+ expect(text).toContain('11');
+ expect(text).toContain('+3');
+ expect(text).toContain('+1');
+ });
+
+ it('shows a disabled Benötigt Stufe X button when the level requirement is not met', async () => {
+ const fixture = await setup({ item: { ...banditBlade, item: { ...banditBlade.item, requiredLevel: 5 } }, characterLevel: 1 });
+ const button = (fixture.nativeElement as HTMLElement).querySelector('[data-detail-equip]');
+
+ expect(button?.textContent).toContain('Benötigt Stufe 5');
+ expect(button?.disabled).toBe(true);
+ });
+
+ it('emits equip with the characterItemId when Ausrüsten is clicked', async () => {
+ const fixture = await setup({ item: banditBlade });
+ const emitted: string[] = [];
+ fixture.componentInstance.equip.subscribe((id: string) => emitted.push(id));
+
+ (fixture.nativeElement as HTMLElement).querySelector('[data-detail-equip]')?.click();
+
+ expect(emitted).toEqual(['item-blade']);
+ });
+
+ it('disables the equip button while busy', async () => {
+ const fixture = await setup({ item: banditBlade, busy: true });
+ const button = (fixture.nativeElement as HTMLElement).querySelector('[data-detail-equip]');
+
+ expect(button?.disabled).toBe(true);
+ });
+});
diff --git a/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts b/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts
new file mode 100644
index 0000000..291f1e0
--- /dev/null
+++ b/apps/web/src/app/features/inventory/inventory-detail-panel.component.ts
@@ -0,0 +1,80 @@
+import { Component, computed, input, output } from '@angular/core';
+import type { EquipmentSlot, InventoryItem } from '../../core/api/game-api.models';
+import { RARITY_LABELS } from '../../shared/item-card/item-card.component';
+
+const SLOT_LABELS: Readonly> = {
+ WEAPON: 'Waffe',
+ HEAD: 'Kopf',
+ CHEST: 'Brust',
+ HANDS: 'Handschuhe',
+ LEGS: 'Beine',
+ FEET: 'Stiefel',
+ AMULET: 'Amulett',
+};
+
+interface StatRow {
+ label: string;
+ value: number;
+ diff: number | null;
+}
+
+type StatKey = 'weaponDamage' | 'bonusAttack' | 'bonusHp' | 'bonusArmor';
+const STAT_LABELS: ReadonlyArray<{ label: string; key: StatKey }> = [
+ { label: 'Waffenschaden', key: 'weaponDamage' },
+ { label: 'Angriff', key: 'bonusAttack' },
+ { label: 'Leben', key: 'bonusHp' },
+ { label: 'Rüstung', key: 'bonusArmor' },
+];
+
+/** Selected-item details and equip comparison (spec §32–37). */
+@Component({
+ selector: 'app-inventory-detail-panel',
+ templateUrl: './inventory-detail-panel.component.html',
+ styleUrl: './inventory-detail-panel.component.scss',
+})
+export class InventoryDetailPanelComponent {
+ readonly item = input(null);
+ readonly equippedItemInSlot = input(null);
+ readonly characterLevel = input(1);
+ readonly busy = input(false);
+ readonly equip = output();
+
+ protected readonly rarityLabel = computed(() => {
+ const item = this.item();
+ return item ? RARITY_LABELS[item.item.rarity] : '';
+ });
+
+ protected readonly slotLabel = computed(() => {
+ const slot = this.item()?.item.equipmentSlot;
+ return slot ? SLOT_LABELS[slot] : null;
+ });
+
+ protected readonly statRows = computed(() => {
+ const item = this.item();
+ if (!item) {
+ return [];
+ }
+ const compareTo = this.equippedItemInSlot();
+ const comparable = compareTo && compareTo.id !== item.id ? compareTo.item : null;
+
+ return STAT_LABELS.map(({ label, key }) => ({
+ label,
+ value: item.item[key],
+ diff: comparable ? item.item[key] - comparable[key] : null,
+ })).filter((row) => row.value > 0 || (row.diff ?? 0) !== 0);
+ });
+
+ protected readonly isEquippable = computed(() => !!this.item()?.item.equipmentSlot);
+
+ protected readonly meetsLevelRequirement = computed(() => {
+ const item = this.item();
+ return item ? item.item.requiredLevel <= this.characterLevel() : true;
+ });
+
+ protected onEquip(): void {
+ const item = this.item();
+ if (item) {
+ this.equip.emit(item.id);
+ }
+ }
+}