Display message if no posts in Custom Post Type loop

It’s because you’re only checking if ( $loop ) {.

$loop isn’t empty, it’s a full WP_Query object with all the information about the query in it, even if no posts were returned. To check if the query actually returned any posts you need to use $loop->have_posts();:

$loop = new WP_Query( $args );

if ( $loop->have_posts() ):
    while ( $loop->have_posts() ) : $loop->the_post();

    endwhile;
else:

endif;