Code/UI/InventoryHud.razor
@using Sandbox;
@using System;
@using System.Linq;
@using Sandbox.UI;
@inherits PanelComponent

<root class="@(IsInventoryOpen ? "inventory-open" : "")" @ref="RootPanel"
      onmouseup=@(e => OnRootMouseUp(e))>

    @* ── Inventory Panel (shown/hidden on toggle) ── *@
    @if ( IsInventoryOpen )
    {
        <div class="inventory-overlay" onmousedown=@CloseInventory></div>

        <div class="inventory-panel">

            @* ── Equipment Panel (left column) ── *@
            @if ( Inventory?.UseEquipment == true && Inventory.Equipment != null )
            {
                <div class="equipment-panel">
                    <label class="panel-title">Equipment</label>

                    <div class="equip-layout">

                        @* Top — Head *@
                        <div class="equip-row">
                            @{ var headSlot = Inventory.Equipment.GetSlot( EquipSlot.Head ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Head ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Head && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Head, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Head; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = headSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( headSlot != null && !headSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && headSlot.Durability >= 0 && headSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(headSlot.Durability / headSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Head</label> }
                            </div>
                        </div>

                        @* Middle — WeaponMain, Chest, WeaponOffhand *@
                        <div class="equip-row">
                            @{ var mainSlot = Inventory.Equipment.GetSlot( EquipSlot.WeaponMain ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.WeaponMain ? "dragging" : "") @(HoverEquipSlot == EquipSlot.WeaponMain && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.WeaponMain, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.WeaponMain; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = mainSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( mainSlot != null && !mainSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && mainSlot.Durability >= 0 && mainSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(mainSlot.Durability / mainSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Main</label> }
                            </div>

                            @{ var chestSlot = Inventory.Equipment.GetSlot( EquipSlot.Chest ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Chest ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Chest && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Chest, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Chest; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = chestSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( chestSlot != null && !chestSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && chestSlot.Durability >= 0 && chestSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(chestSlot.Durability / chestSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Chest</label> }
                            </div>

                            @{ var offSlot = Inventory.Equipment.GetSlot( EquipSlot.WeaponOffhand ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.WeaponOffhand ? "dragging" : "") @(HoverEquipSlot == EquipSlot.WeaponOffhand && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.WeaponOffhand, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.WeaponOffhand; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = offSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( offSlot != null && !offSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && offSlot.Durability >= 0 && offSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(offSlot.Durability / offSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Off</label> }
                            </div>
                        </div>

                        @* Lower — Hands, Legs, Accessory1 *@
                        <div class="equip-row">
                            @{ var handsSlot = Inventory.Equipment.GetSlot( EquipSlot.Hands ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Hands ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Hands && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Hands, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Hands; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = handsSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( handsSlot != null && !handsSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && handsSlot.Durability >= 0 && handsSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(handsSlot.Durability / handsSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Hands</label> }
                            </div>

                            @{ var legsSlot = Inventory.Equipment.GetSlot( EquipSlot.Legs ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Legs ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Legs && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Legs, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Legs; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = legsSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( legsSlot != null && !legsSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && legsSlot.Durability >= 0 && legsSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(legsSlot.Durability / legsSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Legs</label> }
                            </div>

                            @{ var acc1Slot = Inventory.Equipment.GetSlot( EquipSlot.Accessory1 ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Accessory1 ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Accessory1 && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Accessory1, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Accessory1; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = acc1Slot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( acc1Slot != null && !acc1Slot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && acc1Slot.Durability >= 0 && acc1Slot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(acc1Slot.Durability / acc1Slot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Ring</label> }
                            </div>
                        </div>

                        @* Bottom — Feet, Accessory2 *@
                        <div class="equip-row">
                            @{ var feetSlot = Inventory.Equipment.GetSlot( EquipSlot.Feet ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Feet ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Feet && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Feet, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Feet; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = feetSlot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( feetSlot != null && !feetSlot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && feetSlot.Durability >= 0 && feetSlot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(feetSlot.Durability / feetSlot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Feet</label> }
                            </div>

                            @{ var acc2Slot = Inventory.Equipment.GetSlot( EquipSlot.Accessory2 ); }
                            <div class="equip-slot @(DragFromEquipSlot == EquipSlot.Accessory2 ? "dragging" : "") @(HoverEquipSlot == EquipSlot.Accessory2 && IsDragging ? "drag-over" : "")"
                                 onmousedown=@(e => OnEquipSlotMouseDown( EquipSlot.Accessory2, e ))
                                 onmouseover=@(() => { HoverEquipSlot = EquipSlot.Accessory2; HoverBagIndex = -1; HotbarHoverCol = -1; TooltipSlot = acc2Slot; })
                                 onmouseout=@(() => { HoverEquipSlot = EquipSlot.None; TooltipSlot = null; })>
                                @if ( acc2Slot != null && !acc2Slot.IsEmpty )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( Inventory?.EnableDurability == true && acc2Slot.Durability >= 0 && acc2Slot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(acc2Slot.Durability / acc2Slot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                                else { <label class="equip-label">Ring 2</label> }
                            </div>
                        </div>

                    </div>
                </div>
            }

            @* ── Loot Panel (middle column, only when a container is open) ── *@
            @if ( _openContainer != null )
            {
                <div class="loot-panel @(_openContainer.IsLootContainer ? "loot-panel-world" : "")">
                    <div class="loot-panel-header">
                        <label class="panel-title">@_openContainer.ContainerName</label>
                        @if ( _openContainer.IsLootContainer )
                        {
                            <label class="loot-badge">LOOT</label>
                        }
                    </div>

                    <div class="loot-grid-scroll">
                    <div class="bag-grid">
                        @for ( int row = 0; row < _openContainer.Height; row++ )
                        {
                            <div class="bag-row">
                            @for ( int col = 0; col < _openContainer.Width; col++ )
                            {
                                var localI = row * _openContainer.Width + col;
                                var slot   = _openContainer.Container.GetSlot( localI );

                                <div class="bag-slot @(DragFromLootIndex == localI ? "dragging" : "") @(HoverLootIndex == localI && IsDragging ? "drag-over" : "")"
                                     onmousedown=@(e => OnLootSlotMouseDown( localI, e ))
                                     onmouseover=@(() => { TooltipSlot = slot; HoverLootIndex = localI; HoverBagIndex = -1; HotbarHoverCol = -1; HoverEquipSlot = EquipSlot.None; })
                                     onmouseout=@(() => { TooltipSlot = null; HoverLootIndex = -1; })>
                                    @if ( !slot.IsEmpty )
                                    {
                                        @if ( Inventory?.EnableRarity == true )
                                        {
                                            <div class="rarity-border [email protected]().ToLower()"></div>
                                        }
                                        <img [email protected]?.ResourcePath class="item-icon" />
                                        @if ( slot.Quantity > 1 )
                                        {
                                            <label class="item-qty">@slot.Quantity</label>
                                        }
                                    }
                                </div>
                            }
                            </div>
                        }
                    </div>
                    </div>

                    <div class="loot-footer">
                        <div class="take-all-btn" onmousedown=@OnTakeAll>Take All</div>
                    </div>
                </div>
            }

            @* ── Bag Grid (right column) ── *@
            <div class="bag-panel">
                <label class="panel-title">Inventory</label>

                @if ( Inventory?.EnableSearch == true || Inventory?.EnableCategories == true || Inventory?.EnableSorting == true || Inventory?.EnableCrafting == true || Inventory?.EnableQuickStack == true )
                {
                    <div class="bag-toolbar" style="width: @(Inventory.BagWidth * (74 + 5) - 5)px;">
                        @if ( Inventory?.EnableSearch == true )
                        {
                            <input type="text" class="bag-search" placeholder="Search…"
                                   value=@SearchText onchange=@((string v) => { SearchText = v; }) />
                        }
                        <div class="toolbar-row">
                            @if ( Inventory?.EnableCategories == true )
                            {
                                var cats = (Inventory.VisibleCategories?.Count > 0)
                                    ? Inventory.VisibleCategories
                                    : Enum.GetValues<ItemCategory>().ToList();
                                <div class="category-tabs">
                                    <div class="category-tab @(CategoryFilter == null ? "active" : "")"
                                         onmousedown=@(() => CategoryFilter = null)>All</div>
                                    @foreach ( var cat in cats )
                                    {
                                        <div class="category-tab @(CategoryFilter == cat ? "active" : "")"
                                             onmousedown=@(() => CategoryFilter = cat)>@cat</div>
                                    }
                                </div>
                            }
                            <div class="toolbar-actions">
                                @if ( Inventory?.EnableSorting == true )
                                {
                                    <div class="sort-btn" onmousedown=@(() => Inventory?.SortBag())>Sort</div>
                                }
                                @if ( Inventory?.EnableQuickStack == true )
                                {
                                    <div class="sort-btn" onmousedown=@(() => Inventory?.QuickStackToNearby())>Quick Stack</div>
                                }
                            </div>
                        </div>
                    </div>
                }

                <div class="bag-grid">
                    @for ( int row = 0; row < Inventory.BagHeight; row++ )
                    {
                        <div class="bag-row">
                        @for ( int col = 0; col < Inventory.BagWidth; col++ )
                        {
                            var localI = row * Inventory.BagWidth + col;
                            var slot   = Inventory.Bag.GetSlot( localI );
                            var matches = ItemPassesFilter( slot );

                            <div class="bag-slot @(DragFromIndex == localI ? "dragging" : "") @(HoverBagIndex == localI && IsDragging ? "drag-over" : "") @(!matches ? "filtered-out" : "")"
                                 onmousedown=@(e => OnBagSlotMouseDown( localI, e ))
                                 onmouseover=@(() => { if ( matches ) TooltipSlot = slot; HoverBagIndex = localI; HotbarHoverCol = -1; HoverEquipSlot = EquipSlot.None; })
                                 onmouseout=@(() => TooltipSlot = null)>
                                @if ( !slot.IsEmpty && matches )
                                {
                                    @if ( Inventory?.EnableRarity == true )
                                    {
                                        <div class="rarity-border [email protected]().ToLower()"></div>
                                    }
                                    <img [email protected]?.ResourcePath class="item-icon" />
                                    @if ( slot.Quantity > 1 )
                                    {
                                        <label class="item-qty">@slot.Quantity</label>
                                    }
                                    @if ( Inventory?.EnableDurability == true && slot.Durability >= 0 && slot.Item.MaxDurability > 0 )
                                    {
                                        <div class="durability-bar">
                                            <div class="durability-fill" style="width: @(Math.Clamp(slot.Durability / slot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                        </div>
                                    }
                                }
                            </div>
                        }
                        </div>
                    }
                </div>

                @if ( Inventory?.EnableWeight == true && Inventory.MaxWeight > 0f )
                {
                    var weightPct = Math.Clamp( Inventory.CurrentWeight / Inventory.MaxWeight * 100f, 0f, 100f );
                    <div class="weight-bar @(Inventory.IsOverEncumbered ? "overencumbered" : "")" style="width: @(Inventory.BagWidth * (74 + 5) - 5)px;">
                        <div class="weight-fill" style="width: @(weightPct)%"></div>
                        <label class="weight-label">@($"{Inventory.CurrentWeight:F1} / {Inventory.MaxWeight:F1} kg")</label>
                    </div>
                }
            </div>

            @* ── Crafting Panel (right column, always shown when EnableCrafting is true) ── *@
            @if ( Inventory?.EnableCrafting == true && _openContainer == null )
            {
                var recipes = GetVisibleRecipes().ToList();
                <div class="crafting-panel">
                    <label class="panel-title">@( _openStation != null ? _openStation.StationName : "Crafting" )</label>

                    <input type="text" class="bag-search" placeholder="Search recipes…"
                           value=@_recipeSearch onchange=@((string v) => { _recipeSearch = v; }) />

                    <div class="recipe-list">
                        @foreach ( var recipe in recipes )
                        {
                            if ( recipe?.ResultItem == null ) continue;
                            if ( !string.IsNullOrWhiteSpace( _recipeSearch ) &&
                                 !recipe.RecipeName.ToLower().Contains( _recipeSearch.ToLower() ) ) continue;

                            bool canCraft   = Inventory.CanCraft( recipe );
                            bool selected   = _selectedRecipe == recipe;
                            var localRecipe = recipe;

                            <div class="recipe-entry @(selected ? "selected" : "") @(!canCraft ? "recipe-unavailable" : "")"
                                 onmousedown=@(() => { _selectedRecipe = localRecipe; _craftQuantity = 1; CancelCraft(); StateHasChanged(); })>
                                @if ( recipe.ResultItem.Icon != null )
                                {
                                    <img [email protected] class="recipe-icon" />
                                }
                                <div class="recipe-entry-text">
                                    <label class="recipe-name">@recipe.RecipeName</label>
                                    <label class="recipe-result-qty">[email protected]</label>
                                </div>
                            </div>
                        }
                    </div>

                    @if ( _selectedRecipe != null )
                    {
                        <div class="recipe-detail">

                            <div class="recipe-ingredients">
                                @foreach ( var ing in _selectedRecipe.Ingredients )
                                {
                                    if ( ing.Item == null ) continue;
                                    int have    = Inventory.Bag.CountItem( ing.Item );
                                    int need    = ing.Amount * _craftQuantity;
                                    bool enough = have >= need;
                                    <div class="ingredient-row">
                                        @if ( ing.Item.Icon != null )
                                        {
                                            <img [email protected] class="ingredient-icon" />
                                        }
                                        <label class="ingredient-name">@ing.Item.ItemName</label>
                                        <label class="ingredient-count @(enough ? "ingredient-ok" : "ingredient-missing")">@have / @need</label>
                                    </div>
                                }
                            </div>

                            <div class="craft-qty-row">
                                <div class="craft-qty-btn" onmousedown=@(() => { _craftQuantity = Math.Max( 1, _craftQuantity - 1 ); StateHasChanged(); })>-</div>
                                <label class="craft-qty-label">@_craftQuantity</label>
                                <div class="craft-qty-btn" onmousedown=@(() => { _craftQuantity = Math.Min( Inventory.GetMaxCraftable( _selectedRecipe ), _craftQuantity + 1 ); StateHasChanged(); })>+</div>
                                <div class="craft-qty-btn craft-qty-max" onmousedown=@(() => { _craftQuantity = Math.Max( 1, Inventory.GetMaxCraftable( _selectedRecipe ) ); StateHasChanged(); })>Max</div>
                            </div>

                            @if ( _isCrafting )
                            {
                                <div class="craft-progress-wrap">
                                    <div class="craft-progress-bar">
                                        <div class="craft-progress-fill" style="width: @($"{(int)Math.Min(_craftProgress * 100f, 100f)}")%"></div>
                                    </div>
                                    <label class="craft-progress-label">@_selectedRecipe.RecipeName (@_craftQueueRemaining left)</label>
                                </div>
                                <div class="craft-btn craft-cancel-btn" onmousedown=@(() => CancelCraft())>Cancel</div>
                            }
                            else
                            {
                                var canStart = Inventory.CanCraft( _selectedRecipe, _craftQuantity );
                                <div class="craft-btn @(!canStart ? "craft-btn-disabled" : "")"
                                     onmousedown=@(() => {
                                         if ( !Inventory.CanCraft( _selectedRecipe, 1 ) ) return;
                                         if ( Inventory.ConsumeCraftIngredients( _selectedRecipe ) )
                                         {
                                             _craftQueueRemaining = _craftQuantity;
                                             _craftStartTime      = RealTime.Now;
                                             _craftProgress       = 0f;
                                             _isCrafting          = true;
                                         }
                                     })>
                                    Craft x@_craftQuantity
                                </div>
                            }

                        </div>
                    }
                </div>
            }

        </div>

        @* ── Tooltip ── *@
        @if ( TooltipSlot != null && !TooltipSlot.IsEmpty )
        {
            var item = TooltipSlot.Item;
            <div class="item-tooltip">
                <label class="tooltip-name [email protected]().ToLower()">@item.ItemName</label>
                @if ( Inventory?.EnableCategories == true )
                {
                    <label class="tooltip-category">@item.Category</label>
                }
                @if ( Inventory?.EnableRarity == true && item.Rarity != ItemRarity.Common )
                {
                    <label class="tooltip-rarity">@item.Rarity</label>
                }
                @if ( item.EquipSlot != EquipSlot.None )
                {
                    <label class="tooltip-slot">@item.EquipSlot</label>
                }
                @if ( !string.IsNullOrEmpty( item.Description ) )
                {
                    <label class="tooltip-desc">@item.Description</label>
                }
                @if ( item.Weight > 0f )
                {
                    <label class="tooltip-weight">@item.Weight kg</label>
                }
                @if ( Inventory?.EnableDurability == true && TooltipSlot.Durability >= 0 && item.MaxDurability > 0 )
                {
                    var durPct = Math.Clamp( TooltipSlot.Durability / item.MaxDurability * 100f, 0f, 100f );
                    <label class="tooltip-durability">Durability: @((int)TooltipSlot.Durability) / @item.MaxDurability (@($"{durPct:F0}%"))</label>
                }
                @if ( Inventory?.EnableEquipmentStats == true && item.StatModifiers != null && item.StatModifiers.Length > 0 )
                {
                    var equippedItem = ( Inventory?.UseEquipment == true && item.EquipSlot != EquipSlot.None )
                        ? Inventory.Equipment?.GetSlot( item.EquipSlot )?.Item
                        : null;

                    <div class="tooltip-stats">
                        @if ( equippedItem != null )
                        {
                            <label class="tooltip-compare-label">vs @equippedItem.ItemName</label>
                        }
                        @foreach ( var mod in item.StatModifiers )
                        {
                            var equippedVal = GetEquippedStat( equippedItem, mod.Type );
                            var delta       = mod.Value - equippedVal;
                            var sign        = mod.Value >= 0 ? "+" : "";
                            var deltaSign   = delta >= 0 ? "+" : "";
                            <div class="tooltip-stat-row">
                                <label class="tooltip-stat @(mod.Value >= 0 ? "stat-positive" : "stat-negative")">@mod.Type: @($"{sign}{mod.Value:F0}")</label>
                                @if ( equippedItem != null )
                                {
                                    <label class="tooltip-stat-delta @(delta > 0 ? "stat-positive" : delta < 0 ? "stat-negative" : "stat-neutral")">(@($"{deltaSign}{delta:F0}"))</label>
                                }
                            </div>
                        }
                    </div>
                }
            </div>
        }
    }

    @* ── Context Menu ── *@
    @if ( ContextMenuOpen && ContextMenuSlot != null && !ContextMenuSlot.IsEmpty )
    {
        <div class="context-menu" style="left: @(ContextMenuPos.x)px; top: @(ContextMenuPos.y)px;">
            @if ( ContextMenuLootIndex >= 0 )
            {
                <div class="context-menu-item" onmousedown=@OnContextTake>Take</div>
            }
            else
            {
                @if ( ContextMenuBagIndex >= 0 && Inventory?.UseEquipment == true && ContextMenuSlot.Item.EquipSlot != EquipSlot.None )
                {
                    <div class="context-menu-item" onmousedown=@OnContextEquip>Equip</div>
                }
                @if ( ContextMenuBagIndex >= 0 && ContextMenuSlot.Quantity > 1 )
                {
                    <div class="context-menu-item" onmousedown=@OnContextSplit>Split Stack</div>
                }
                @if ( ContextMenuEquipSlot != EquipSlot.None && Inventory?.UseEquipment == true )
                {
                    <div class="context-menu-item" onmousedown=@OnContextUnequip>Unequip</div>
                }
                @if ( ContextMenuSlot.Item.IsUsable )
                {
                    <div class="context-menu-item" onmousedown=@OnContextUse>Use</div>
                }
                <div class="context-menu-item" onmousedown=@OnContextDrop>Drop</div>
                <div class="context-menu-item context-menu-destroy" onmousedown=@OnContextDestroy>Destroy</div>
            }
        </div>
    }

    @* ── Destroy Confirmation ── *@
    @if ( ShowDestroyConfirm )
    {
        <div class="confirm-overlay" onmousedown=@CancelDestroy>
            <div class="confirm-dialog" onmousedown=@((e) => { })>
                <label class="confirm-title">Destroy Item?</label>
                <label class="confirm-item">@DestroyConfirmSlot?.Item?.ItemName x@DestroyConfirmSlot?.Quantity</label>
                <div class="confirm-buttons">
                    <div class="confirm-btn confirm-yes" onmousedown=@ConfirmDestroy>Yes</div>
                    <div class="confirm-btn confirm-no" onmousedown=@CancelDestroy>No</div>
                </div>
            </div>
        </div>
    }

    @* ── Hotbar (always visible, rendered AFTER overlay so it's on top) ── *@
    @if ( Inventory?.UseHotbar == true )
    {
        <div class="hotbar-wrapper" @ref="HotbarWrapperPanel">
            <div class="hotbar" @ref="HotbarPanel">
                @for ( int i = 0; i < Inventory.BagWidth; i++ )
                {
                    var slotIndex = Inventory.HotbarStartIndex + i;
                    var slot = Inventory.Bag.GetSlot( slotIndex );
                    var isActive = i == Inventory.HotbarActiveIndex;
                    var localI = i;
                    var hotbarBagIndex = Inventory.HotbarStartIndex + localI;

                    <div class="hotbar-slot @(isActive ? "active" : "") @(DragFromIndex == hotbarBagIndex ? "dragging" : "") @(HotbarHoverCol == i && IsDragging ? "drag-over" : "")"
                          @ref="@(e => HotbarSlotPanels[localI] = (Panel)e)"
                          onmousedown=@(e => OnBagSlotMouseDown( hotbarBagIndex, e ))
                          onmouseover=@(() => { TooltipSlot = slot; HoverEquipSlot = EquipSlot.None; HoverBagIndex = -1; })
                         onmouseout=@(() => { TooltipSlot = null; HotbarHoverCol = -1; })>
                        @if ( !slot.IsEmpty )
                        {
                            @if ( Inventory?.EnableRarity == true )
                            {
                                <div class="rarity-border [email protected]().ToLower()"></div>
                            }
                            <img [email protected]?.ResourcePath class="item-icon" />
                            @if ( slot.Quantity > 1 )
                            {
                                <label class="item-qty">@slot.Quantity</label>
                            }
                            @if ( Inventory?.EnableDurability == true && slot.Durability >= 0 && slot.Item.MaxDurability > 0 )
                            {
                                <div class="durability-bar">
                                    <div class="durability-fill" style="width: @(Math.Clamp(slot.Durability / slot.Item.MaxDurability * 100f, 0f, 100f))%"></div>
                                </div>
                            }
                        }
                        <label class="hotbar-number">@(localI + 1)</label>
                    </div>
                }
            </div>
        </div>
    }

    @* ── Crosshair ── *@
    <div class="crosshair">
        <div class="crosshair-line crosshair-top"></div>
        <div class="crosshair-line crosshair-right"></div>
        <div class="crosshair-line crosshair-bottom"></div>
        <div class="crosshair-line crosshair-left"></div>
        <div class="crosshair-dot"></div>
    </div>

</root>

@code {

    [Property] public InventoryComponent Inventory { get; set; }
    [Property] public float ContainerCloseDistance { get; set; } = 200f;

    public bool IsInventoryOpen { get; private set; } = false;

    private string SearchText { get; set; } = "";
    private ItemCategory? CategoryFilter { get; set; } = null;

    private Panel HotbarWrapperPanel { get; set; }
    private Panel RootPanel { get; set; }
    private Panel HotbarPanel { get; set; }
    private Panel[] HotbarSlotPanels { get; set; } = new Panel[9];
    private bool ShiftHeld { get; set; }
    private Vector2 CursorPos { get; set; }
    private int DragFromIndex { get; set; } = -1;
    private EquipSlot DragFromEquipSlot { get; set; } = EquipSlot.None;
    private bool IsDragging { get; set; } = false;
    private int HoverBagIndex { get; set; } = -1;
    private int HotbarHoverCol { get; set; } = -1;
    private EquipSlot HoverEquipSlot { get; set; } = EquipSlot.None;
    private InventorySlot TooltipSlot { get; set; }

    // ─── Crafting State ───────────────────────────────────────────

    private CraftingStation  _openStation;
    private CraftingRecipe   _selectedRecipe;
    private int              _craftQuantity   = 1;
    private bool             _isCrafting      = false;
    private float            _craftProgress   = 0f;
    private float            _craftStartTime  = 0f;
    private int              _craftQueueRemaining = 0;
    private string           _recipeSearch    = "";

    public void OpenStation( CraftingStation station )
    {
        _openStation    = station;
        _selectedRecipe = null;
        _craftQuantity      = 1;
        IsInventoryOpen     = true;
        StateHasChanged();
    }

    public void CloseStation()
    {
        CancelCraft();
        _openStation    = null;
        _selectedRecipe = null;
        StateHasChanged();
    }

    private void CancelCraft()
    {
        _isCrafting          = false;
        _craftProgress       = 0f;
        _craftQueueRemaining = 0;
    }

    private System.Collections.Generic.IEnumerable<CraftingRecipe> GetVisibleRecipes()
    {
        if ( _openStation != null )
            return _openStation.Recipes ?? new System.Collections.Generic.List<CraftingRecipe>();

        return ResourceLibrary.GetAll<CraftingRecipe>()
            .Where( r => !r.RequiresCraftingStation );
    }

    // ─── Loot State ───────────────────────────────────────────────

    private StorageContainer _openContainer;
    private int DragFromLootIndex { get; set; } = -1;
    private int HoverLootIndex    { get; set; } = -1;

    /// <summary>Open a container's loot panel. Call from your interaction system.</summary>
    public void OpenContainer( StorageContainer container )
    {
        if ( _openContainer != null )
            _openContainer.OnContainerChanged -= StateHasChanged;

        _openContainer = container;

        if ( _openContainer != null )
            _openContainer.OnContainerChanged += StateHasChanged;

        IsInventoryOpen = true;
        StateHasChanged();
    }

    /// <summary>Close the loot panel without closing the inventory.</summary>
    public void CloseContainer()
    {
        if ( _openContainer != null )
            _openContainer.OnContainerChanged -= StateHasChanged;
        _openContainer = null;
        StateHasChanged();
    }

    // ─── Context Menu State ────────────────────────────────────────

    private bool ContextMenuOpen { get; set; } = false;
    private Vector2 ContextMenuPos { get; set; }
    private int ContextMenuBagIndex  { get; set; } = -1;
    private int ContextMenuLootIndex { get; set; } = -1;
    private EquipSlot ContextMenuEquipSlot { get; set; } = EquipSlot.None;
    private InventorySlot ContextMenuSlot { get; set; }

    // ─── Destroy Confirmation State ────────────────────────────────

    private bool ShowDestroyConfirm { get; set; } = false;
    private int DestroyConfirmBagIndex { get; set; } = -1;
    private EquipSlot DestroyConfirmEquipSlot { get; set; } = EquipSlot.None;
    private InventorySlot DestroyConfirmSlot { get; set; }

    // Must match SCSS values exactly
    private const float SlotSize   = 74f;
    private const float HotbarBottom  = 28f;  // bottom offset on .hotbar-wrapper

    // ─── Lifecycle ───────────────────────────────────────────────

    protected override void OnStart()
    {
        if ( Inventory != null )
        {
            Inventory.OnInventoryChanged += StateHasChanged;
            if ( Inventory.BagWidth > HotbarSlotPanels.Length )
                HotbarSlotPanels = new Panel[Inventory.BagWidth];
        }
        StorageContainer.OnOpenRequested += OpenContainer;
        CraftingStation.OnStationOpened  += OpenStation;
    }

    protected override void OnDestroy()
    {
        if ( Inventory != null )
            Inventory.OnInventoryChanged -= StateHasChanged;
        if ( _openContainer != null )
            _openContainer.OnContainerChanged -= StateHasChanged;
        StorageContainer.OnOpenRequested -= OpenContainer;
        CraftingStation.OnStationOpened  -= OpenStation;
    }

    protected override void OnUpdate()
    {
        ShiftHeld = Input.Down( "run" );

        if ( Input.Pressed( "inventory" ) )
        {
            if ( ContextMenuOpen )
            {
                CloseContextMenu();
                StateHasChanged();
                return;
            }
            IsInventoryOpen = !IsInventoryOpen;
            if ( !IsInventoryOpen )
            {
                CancelDrag();
                CloseContainer();
                CloseStation();
            }
            StateHasChanged();
        }

        if ( _openContainer != null )
        {
            float dist = Vector3.DistanceBetween( Inventory.Transform.Position, _openContainer.Transform.Position );
            if ( dist > ContainerCloseDistance )
            {
                CloseContainer();
                StateHasChanged();
            }
        }

        if ( _openStation != null )
        {
            float dist = Vector3.DistanceBetween( Inventory.Transform.Position, _openStation.Transform.Position );
            if ( dist > _openStation.CloseDistance )
            {
                CloseStation();
                StateHasChanged();
            }
        }

        if ( _isCrafting && _selectedRecipe != null && Inventory != null )
        {
            float craftTime = Math.Max( _selectedRecipe.CraftTime, 0.05f );
            _craftProgress = Math.Clamp( ( RealTime.Now - _craftStartTime ) / craftTime, 0f, 1f );

            if ( _craftProgress >= 1f )
            {
                Inventory.GiveCraftResult( _selectedRecipe );
                _craftQueueRemaining--;

                if ( _craftQueueRemaining > 0 && Inventory.CanCraft( _selectedRecipe ) )
                {
                    Inventory.ConsumeCraftIngredients( _selectedRecipe );
                    _craftStartTime = RealTime.Now;
                    _craftProgress  = 0f;
                }
                else
                {
                    CancelCraft();
                }
            }
            StateHasChanged();
        }

        if ( IsInventoryOpen )
        {
            Input.AnalogLook = Angles.Zero;

            if ( IsDragging )
            {
                var scale = RootPanel?.ScaleFromScreen ?? 1f;
                var screenPos = Mouse.Position;
                CursorPos = new Vector2( screenPos.x * scale, screenPos.y * scale );
                UpdateHotbarHoverFromMouse();
                StateHasChanged();
            }
        }

        if ( Inventory?.UseHotbar == true && !IsInventoryOpen )
        {
            for ( int i = 0; i < Inventory.BagWidth && i < 9; i++ )
            {
                if ( Input.Pressed( $"slot{i + 1}" ) )
                    Inventory.SetHotbarActive( i );
            }
        }
    }

    private void UpdateHotbarHoverFromMouse()
    {
        if ( Inventory == null || HotbarPanel == null ) return;

        var children = HotbarPanel?.Children?.ToList();
        bool found = false;

        if ( children != null )
        {
            for ( int i = 0; i < children.Count && i < Inventory.BagWidth; i++ )
            {
                var slot = children[i];
                var sp = slot.MousePosition;
                // Inside the slot panel: x in [0, 74), y in [0, 74)
                if ( sp.x >= 0 && sp.x < SlotSize && sp.y >= 0 && sp.y < SlotSize )
                {
                    found = true;
                    if ( HotbarHoverCol != i )
                    {
                        HotbarHoverCol = i;
                        HoverEquipSlot = EquipSlot.None;
                        HoverBagIndex  = -1;
                    }
                    break;
                }
            }
        }

        if ( !found && HotbarHoverCol != -1 )
            HotbarHoverCol = -1;
    }

    protected override int BuildHash()
    {
        return System.HashCode.Combine(
            IsInventoryOpen,
            Inventory?.BagDataHash,
            Inventory?.EquipDataHash,
            Inventory?.HotbarActiveIndex,
            System.HashCode.Combine(
                DragFromIndex, DragFromEquipSlot, HoverBagIndex,
                DragFromLootIndex, HoverLootIndex
            ),
            System.HashCode.Combine(
                HotbarHoverCol, IsDragging,
                ContextMenuOpen, ContextMenuBagIndex,
                ContextMenuEquipSlot
            ),
            System.HashCode.Combine(
                SearchText, CategoryFilter,
                _openContainer?.DataHash
            ),
            System.HashCode.Combine(
                _selectedRecipe?.ResourceName,
                _craftQuantity, _isCrafting, _craftQueueRemaining
            )
        );
    }

    // ─── Mouse Down ──────────────────────────────────────────────

    private void OnBagSlotMouseDown( int slotIndex, PanelEvent e )
    {
        if ( e is MousePanelEvent me && me.MouseButton == MouseButtons.Right )
        {
            CloseContextMenu();
            ShowBagContextMenu( slotIndex );
            return;
        }

        if ( Inventory == null ) return;
        var slot = Inventory.Bag.GetSlot( slotIndex );
        if ( slot.IsEmpty ) return;

        if ( !IsInventoryOpen )
        {
            if ( Inventory.UseHotbar && slotIndex >= Inventory.HotbarStartIndex )
                Inventory.SetHotbarActive( slotIndex - Inventory.HotbarStartIndex );
            return;
        }

        // Shift-click: auto-move without dragging
        if ( ShiftHeld )
        {
            if ( _openContainer != null )
                _openContainer.TransferFromPlayer( slotIndex, Inventory );
            else if ( Inventory.UseEquipment && slot.Item.EquipSlot != EquipSlot.None )
                Inventory.EquipFromBag( slotIndex );
            return;
        }

        DragFromIndex     = slotIndex;
        DragFromEquipSlot = EquipSlot.None;
        IsDragging        = true;
    }

    private void OnEquipSlotMouseDown( EquipSlot slotType, PanelEvent e )
    {
        if ( e is MousePanelEvent me && me.MouseButton == MouseButtons.Right )
        {
            CloseContextMenu();
            ShowEquipContextMenu( slotType );
            return;
        }

        if ( Inventory?.Equipment == null ) return;
        var slot = Inventory.Equipment.GetSlot( slotType );
        if ( slot == null || slot.IsEmpty ) return;

        // Shift-click: unequip straight to bag
        if ( ShiftHeld )
        {
            Inventory.UnequipToBag( slotType );
            return;
        }

        DragFromEquipSlot = slotType;
        DragFromIndex     = -1;
        IsDragging        = true;
    }

    // ─── Context Menu ─────────────────────────────────────────────

    private void ShowBagContextMenu( int slotIndex )
    {
        if ( Inventory == null ) return;
        var slot = Inventory.Bag.GetSlot( slotIndex );
        if ( slot.IsEmpty ) return;

        var scale = RootPanel?.ScaleFromScreen ?? 1f;
        ContextMenuPos   = new Vector2( Mouse.Position.x * scale, Mouse.Position.y * scale );
        ContextMenuBagIndex  = slotIndex;
        ContextMenuEquipSlot = EquipSlot.None;
        ContextMenuSlot      = slot;
        ContextMenuOpen      = true;
    }

    private void ShowEquipContextMenu( EquipSlot slotType )
    {
        if ( Inventory?.Equipment == null ) return;
        var slot = Inventory.Equipment.GetSlot( slotType );
        if ( slot == null || slot.IsEmpty ) return;

        var scale = RootPanel?.ScaleFromScreen ?? 1f;
        ContextMenuPos   = new Vector2( Mouse.Position.x * scale, Mouse.Position.y * scale );
        ContextMenuBagIndex  = -1;
        ContextMenuEquipSlot = slotType;
        ContextMenuSlot      = slot;
        ContextMenuOpen      = true;
    }

    private void CloseContextMenu()
    {
        ContextMenuOpen      = false;
        ContextMenuSlot      = null;
        ContextMenuBagIndex  = -1;
        ContextMenuLootIndex = -1;
        ContextMenuEquipSlot = EquipSlot.None;
    }

    private void OnContextEquip()
    {
        if ( Inventory != null && ContextMenuBagIndex >= 0 )
            Inventory.EquipFromBag( ContextMenuBagIndex );
        CloseContextMenu();
    }

    private void OnContextUnequip()
    {
        if ( Inventory != null && ContextMenuEquipSlot != EquipSlot.None )
            Inventory.UnequipToBag( ContextMenuEquipSlot );
        CloseContextMenu();
    }

    private void OnContextSplit()
    {
        if ( Inventory == null || ContextMenuBagIndex < 0 ) { CloseContextMenu(); return; }
        var fromSlot = Inventory.Bag.GetSlot( ContextMenuBagIndex );
        if ( fromSlot.IsEmpty || fromSlot.Quantity < 2 ) { CloseContextMenu(); return; }

        for ( int i = 0; i < Inventory.Bag.SlotCount; i++ )
        {
            if ( i == ContextMenuBagIndex ) continue;
            var target = Inventory.Bag.GetSlot( i );
            if ( target.IsEmpty )
            {
                Inventory.SplitStack( ContextMenuBagIndex, i );
                break;
            }
        }
        CloseContextMenu();
    }

    private void OnContextUse()
    {
        if ( Inventory == null ) { CloseContextMenu(); return; }
        if ( ContextMenuBagIndex >= 0 )
            Inventory.UseItem( ContextMenuBagIndex );
        else if ( ContextMenuEquipSlot != EquipSlot.None )
            Inventory.UseEquipItem( ContextMenuEquipSlot );
        CloseContextMenu();
    }

    private void OnContextDrop()
    {
        if ( Inventory == null ) { CloseContextMenu(); return; }
        if ( ContextMenuBagIndex >= 0 )
            Inventory.DropItem( ContextMenuBagIndex, ContextMenuSlot.Quantity );
        else if ( ContextMenuEquipSlot != EquipSlot.None )
            Inventory.DropEquipItem( ContextMenuEquipSlot, 1 );
        CloseContextMenu();
    }

    private void OnContextDestroy()
    {
        if ( Inventory == null ) { CloseContextMenu(); return; }

        if ( Inventory.EnableDestroyConfirm )
        {
            DestroyConfirmBagIndex  = ContextMenuBagIndex;
            DestroyConfirmEquipSlot = ContextMenuEquipSlot;
            DestroyConfirmSlot      = ContextMenuSlot;
            ShowDestroyConfirm      = true;
            CloseContextMenu();
            return;
        }

        DoDestroy();
    }

    private void ConfirmDestroy()
    {
        ShowDestroyConfirm = false;
        if ( Inventory == null ) return;
        int savedBagIndex  = DestroyConfirmBagIndex;
        var savedEquipSlot = DestroyConfirmEquipSlot;
        var savedSlot      = DestroyConfirmSlot;
        DestroyConfirmSlot = null;
        if ( savedBagIndex >= 0 )
            Inventory.DestroyItem( savedBagIndex, savedSlot?.Quantity ?? 1 );
        else if ( savedEquipSlot != EquipSlot.None )
            Inventory.DestroyEquipItem( savedEquipSlot, 1 );
    }

    // ─── Loot Handlers ───────────────────────────────────────────

    private void OnLootSlotMouseDown( int slotIndex, PanelEvent e )
    {
        if ( _openContainer == null ) return;

        if ( e is MousePanelEvent me && me.MouseButton == MouseButtons.Right )
        {
            CloseContextMenu();
            var slot = _openContainer.Container.GetSlot( slotIndex );
            if ( slot.IsEmpty ) return;
            var scale = RootPanel?.ScaleFromScreen ?? 1f;
            ContextMenuPos       = new Vector2( Mouse.Position.x * scale, Mouse.Position.y * scale );
            ContextMenuLootIndex = slotIndex;
            ContextMenuBagIndex  = -1;
            ContextMenuEquipSlot = EquipSlot.None;
            ContextMenuSlot      = slot;
            ContextMenuOpen      = true;
            return;
        }

        var lootSlot = _openContainer.Container.GetSlot( slotIndex );
        if ( lootSlot.IsEmpty ) return;

        // Shift-click: take straight to bag
        if ( ShiftHeld )
        {
            _openContainer.TransferSlotToPlayer( slotIndex, Inventory );
            return;
        }

        DragFromLootIndex = slotIndex;
        DragFromIndex     = -1;
        DragFromEquipSlot = EquipSlot.None;
        IsDragging        = true;
    }

    private void OnContextTake()
    {
        if ( _openContainer != null && ContextMenuLootIndex >= 0 && Inventory != null )
            _openContainer.TransferSlotToPlayer( ContextMenuLootIndex, Inventory );
        CloseContextMenu();
    }

    private void OnTakeAll()
    {
        if ( _openContainer != null && Inventory != null )
            _openContainer.TransferAllToPlayer( Inventory );
    }

    private void DoDestroy()
    {
        if ( Inventory == null ) return;
        if ( ContextMenuBagIndex >= 0 )
            Inventory.DestroyItem( ContextMenuBagIndex, ContextMenuSlot.Quantity );
        else if ( ContextMenuEquipSlot != EquipSlot.None )
            Inventory.DestroyEquipItem( ContextMenuEquipSlot, 1 );
    }

    private void CancelDestroy()
    {
        ShowDestroyConfirm = false;
        DestroyConfirmSlot = null;
    }

    // ─── Root Mouse Up ───────────────────────────────────────────

    private void OnRootMouseUp( PanelEvent e )
    {
        if ( ContextMenuOpen )
        {
            if ( e is MousePanelEvent me && me.MouseButton == MouseButtons.Right )
                return;
            CloseContextMenu();
            return;
        }

        if ( !IsDragging )
        {
            CancelDrag();
            return;
        }

        if ( DragFromLootIndex != -1 && _openContainer != null )
        {
            if ( HoverLootIndex != -1 && HoverLootIndex != DragFromLootIndex )
            {
                // Loot → loot: swap or merge within the container
                var from = _openContainer.Container.GetSlot( DragFromLootIndex );
                var to   = _openContainer.Container.GetSlot( HoverLootIndex );
                if ( !to.IsEmpty && to.Item == from.Item && to.Quantity < to.Item.MaxStack )
                {
                    int space = to.Item.MaxStack - to.Quantity;
                    int move  = Math.Min( space, from.Quantity );
                    to.Add( from.Item, move );
                    from.Remove( move );
                    _openContainer.ForcePush();
                }
                else
                {
                    _openContainer.SwapSlots( DragFromLootIndex, HoverLootIndex );
                }
            }
            else if ( HoverBagIndex != -1 || HotbarHoverCol != -1 )
            {
                // Loot → bag or hotbar
                _openContainer.TransferSlotToPlayer( DragFromLootIndex, Inventory );
            }
            CancelDrag();
            return;
        }

        if ( DragFromIndex != -1 )
        {
            // Bag → loot panel
            if ( HoverLootIndex != -1 && _openContainer != null )
            {
                _openContainer.TransferFromPlayer( DragFromIndex, Inventory, HoverLootIndex );
                CancelDrag();
                return;
            }

            if ( HoverEquipSlot != EquipSlot.None )
            {
                Inventory?.EquipFromBag( DragFromIndex );
            }
            else if ( HotbarHoverCol != -1 )
            {
                int targetIndex = Inventory.HotbarStartIndex + HotbarHoverCol;
                if ( targetIndex != DragFromIndex )
                {
                    if ( IsValidSlot( targetIndex ) )
                        TryMergeOrSwap( DragFromIndex, targetIndex );
                }
            }
            else if ( HoverBagIndex != -1 && HoverBagIndex != DragFromIndex )
            {
                if ( IsValidSlot( HoverBagIndex ) )
                    TryMergeOrSwap( DragFromIndex, HoverBagIndex );
            }
        }
        else if ( DragFromEquipSlot != EquipSlot.None )
        {
            int targetIndex = -1;
            if ( HotbarHoverCol != -1 )
                targetIndex = Inventory.HotbarStartIndex + HotbarHoverCol;
            else if ( HoverBagIndex != -1 )
                targetIndex = HoverBagIndex;

            if ( targetIndex >= 0 && IsValidSlot( targetIndex ) )
            {
                var targetSlot = Inventory.Bag.GetSlot( targetIndex );
                var draggedItem = Inventory?.Equipment?.GetSlot( DragFromEquipSlot )?.Item;

                if ( draggedItem != null )
                {
                    if ( !targetSlot.IsEmpty && targetSlot.Item.EquipSlot == DragFromEquipSlot )
                    {
                        // Swap: unequip current, place dragged into that bag slot, equip what was in bag
                        Inventory.UnequipToBag( DragFromEquipSlot );
                        Inventory.EquipFromBag( targetIndex );
                    }
                    else
                    {
                        // Move to empty bag slot (or bag slot with same item to merge)
                        Inventory.UnequipToBag( DragFromEquipSlot );
                        // UnequipToBag puts item into first open slot; find it and swap to target
                        int landedAt = FindLastAdded( draggedItem );
                        if ( landedAt >= 0 && landedAt != targetIndex )
                            Inventory.SwapSlots( landedAt, targetIndex );
                    }
                }
            }
            else
            {
                Inventory?.UnequipToBag( DragFromEquipSlot );
            }
        }

        CancelDrag();
    }

    // ─── Helpers ─────────────────────────────────────────────────

    private void TryMergeOrSwap( int fromIndex, int toIndex )
    {
        if ( Inventory == null ) return;

        var from = Inventory.Bag.GetSlot( fromIndex );
        var to   = Inventory.Bag.GetSlot( toIndex );

        if ( !to.IsEmpty && to.Item == from.Item && to.Quantity < to.Item.MaxStack )
            Inventory.MergeStacks( fromIndex, toIndex );
        else
            Inventory.SwapSlots( fromIndex, toIndex );
    }

    private bool IsValidSlot( int index )
    {
        if ( Inventory?.Bag == null ) return false;
        return index >= 0 && index < Inventory.Bag.SlotCount;
    }

    private bool ItemPassesFilter( InventorySlot slot )
    {
        if ( slot.IsEmpty ) return true;

        if ( CategoryFilter.HasValue && slot.Item.Category != CategoryFilter.Value )
            return false;

        if ( !string.IsNullOrWhiteSpace( SearchText ) )
        {
            var q = SearchText.Trim().ToLower();
            if ( !slot.Item.ItemName.ToLower().Contains( q )
              && !slot.Item.Description.ToLower().Contains( q ) )
                return false;
        }

        return true;
    }

    private float GetEquippedStat( InventoryItem equipped, StatType type )
    {
        if ( equipped?.StatModifiers == null ) return 0f;
        float total = 0f;
        foreach ( var mod in equipped.StatModifiers )
            if ( mod.Type == type ) total += mod.Value;
        return total;
    }

    private int FindLastAdded( InventoryItem item )
    {
        if ( Inventory?.Bag == null || item == null ) return -1;
        for ( int i = 0; i < Inventory.Bag.SlotCount; i++ )
        {
            var s = Inventory.Bag.GetSlot( i );
            if ( !s.IsEmpty && s.Item == item ) return i;
        }
        return -1;
    }

    private void CancelDrag()
    {
        DragFromIndex     = -1;
        DragFromEquipSlot = EquipSlot.None;
        DragFromLootIndex = -1;
        IsDragging        = false;
        HoverBagIndex     = -1;
        HoverLootIndex    = -1;
        HotbarHoverCol    = -1;
        HoverEquipSlot    = EquipSlot.None;
    }

    private void CloseInventory()
    {
        IsInventoryOpen = false;
        CloseContainer();
        CloseStation();
        CancelDrag();
        CloseContextMenu();
        SearchText = "";
        CategoryFilter = null;
        StateHasChanged();
    }
}