```python
"""How many days lie between two proleptic Gregorian calendar dates."""

MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]


def _is_leap(year: int) -> bool:
    """Return True if *year* is a leap year in the proleptic Gregorian calendar."""
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)


def _month_length(year: int, month: int) -> int:
    """Return the number of days in *month* of *year*."""
    if month == 2 and _is_leap(year):
        return 29
    return MONTH_LENGTHS[month - 1]


def _to_ordinal(date: list) -> int:
    """Convert a [year, month, day] date to a proleptic Gregorian ordinal."""
    year, month, day = date
    if not all(isinstance(v, int) for v in (year, month, day)):
        raise ValueError("All date components must be integers.")
    if month < 1 or month > 12:
        raise ValueError("Month must be between 1 and 12.")
    if day < 1 or day > _month_length(year, month):
        raise ValueError(
            f"Day {day} is invalid for month {month} in year {year}."
        )
    prior = year - 1
    days = prior * 365 + prior // 4
    for earlier in range(1, month):
        days += _month_length(year, earlier)
    return days + day


def span_days(start: list, end: list) -> int:
    """Return the number of days from *start* to *end* (start counted, end not)."""
    if len(start) != 3 or len(end) != 3:
        raise ValueError("Each date must be a list of three integers.")
    if start[0] > end[0] or (start[0] == end[0] and start[1] > end[1]) or (
        start[0] == end[0] and start[1] == end[1] and start[2] > end[2]
    ):
        raise ValueError("Start date cannot be after end date.")
    return _to_ordinal(end) - _to_ordinal(start)
```

**Key fixes:**

1. **Leap year rule** — Changed `_is_leap` from `year % 4 == 0` to the full Gregorian rule: divisible by 4 **and** (not divisible by 100 **or** divisible by 400). This correctly handles 1900 (not a leap year) vs 2000 (a leap year).

2. **Validation** — Added checks for:
   - Non-integer components (raises `ValueError`)
   - Month outside 1–12 (raises `ValueError`)
   - Day outside the real length of its month (raises `ValueError`)
   - Start date after end date (raises `ValueError`)

3. **Edge case** — Equal dates now correctly return 0 (start day counted, end day not).