An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
This is because IEnumerables are evaluated only when you perform an operation on them that forces them to become concreted. You can store a collection of IEnumerable, all of which have .Select() operations that use a value from the current scope (in this case i) and it may be different depending on what value it holds at the point that it's evaluated.
Every iteration of your loop ends with a call to string.Join which forces what's in itemRows to become concrete and evaluate the value of i, and that includes all previous entries in itemRows, so the value of i will just be what it is at that point in time for all IEnumerables in the list. This is why every row refers to the current value of i and not the value that i had when the .Select() operation was declared.
If you .ToList() on items before adding to itemRows then you'll have a List<List<Item>> that you'll be covariant-ly referring to by List<IEnumerable<Item>>. This means that the .Select() on items is evaluated/concreted once before adding to itemRows, rather than on every call to string.Join.