static_cast can perform only those casts where memory layout between the classes is known at compile-time. dynamic_cast can check information at run-time, which allows to more accurately check for cast correctness, as well as read run-time information regarding the memory layout.
Virtual inheritance puts a run-time information into each object which specifies what is the memory layout between the Base and Derived. Is one right after another or is there an additional gap? Because static_cast cannot access such information, the compiler will act conservatively and just give a compiler error.
In more detail:
Consider a complex inheritance structure, where - due to multiple inheritance - there are multiple copies of Base. The most typical scenario is a diamond inheritance:
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
In this scenario Bottom consists of Left and Right, where each has its own copy of Base. The memory structure of all the above classes is known at compile time and static_cast can be used without a problem.
Let us now consider the similar structure but with virtual inheritance of Base:
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
Using the virtual inheritance ensures that when Bottom is created, it contains only one copy of Base that is shared between object parts Left and Right. The layout of Bottom object can be for example:
Base part
Left part
Right part
Bottom part
Now, consider that you cast Bottom to Right (that is a valid cast). You obtain a Right pointer to an object that is in two pieces: Base and Right have a memory gap in between, containing the (now-irrelevant) Left part. The information about this gap is stored at run-time in a hidden field of Right (typically referred to as vbase_offset). You can read the details for example here.
However, the gap would not exist if you would just create a standalone Right object.
So, if I give you just a pointer to Right you do not know at compile time if it is a standalone object, or a part of something bigger (e.g. Bottom). You need to check the run-time information to properly cast from Right to Base. That is why static_cast will fail and dynamic_cast will not.
Note on dynamic_cast:
While static_cast does not use run-time information about the object, dynamic_cast uses and requires it to exist! Thus, the latter cast can be used only on those classes which contain at least one virtual function (e.g. a virtual destructor)