Inventory.cs
using Sandbox;

public sealed class Inventory : Component
{
	[Property] public InventoryPanel InventoryPanel;

	private float _appliedSlotHeightScale = 1f;

	protected override void OnStart()
	{
		InventoryPanel.GameObject.Enabled = !IsProxy && !Game.IsRunningInVR;
	}

	/// <summary>
	/// Runs in OnPreRender so it is guaranteed to follow this frame's locomotion (all OnUpdates run
	/// before any OnPreRender), and reads Input.VR.Head directly rather than the camera GameObject so
	/// it does not depend on where the camera's VRTrackedObject lands in the component update order.
	/// </summary>
	protected override void OnPreRender()
	{
		if ( IsProxy || !Game.IsRunningInVR )
			return;

		var player = GetComponentInParent<PlayerCharacterController>();
		if ( !player.IsValid() )
			return;

		var head = Input.VR.Head;

		// Yaw only so hip/chest slots stay at body height while still tracking look heading.
		WorldRotation = Rotation.FromYaw( head.Rotation.Angles().yaw );

		ApplyCalibratedSlotHeights( player.HeightScale );

		// Keep authored slot offsets relative to the head: standing matches the scaled prefab,
		// crouch/sit drops the holsters by the same amount the headset dropped.
		var standingEyeHeight = player.HeightScale * PlayerCharacterController.ReferenceStandingEyeHeight;
		var headHeight = head.Position.z - player.WorldPosition.z;
		LocalPosition = Vector3.Up * (headHeight - standingEyeHeight);
	}

	/// <summary>
	/// Prefab slots are authored for the 64" reference citizen. Scale their local height
	/// so holsters sit on the avatar after VR standing-eye-height calibration.
	/// </summary>
	private void ApplyCalibratedSlotHeights( float heightScale )
	{
		if ( heightScale <= 0f || heightScale.AlmostEqual( _appliedSlotHeightScale ) )
			return;

		var factor = heightScale / _appliedSlotHeightScale;
		foreach ( var slot in GetComponentsInChildren<InventorySlot>() )
		{
			var pos = slot.LocalPosition;
			slot.LocalPosition = pos.WithZ( pos.z * factor );
		}

		_appliedSlotHeightScale = heightScale;
	}
}