Show scheduled posts in loop (but don’t link to them)?

Is there a way to show scheduled posts in the regular loop but have them not link? I’d like to show that they’re coming soon.

Yes, if you don’t want to link to them, then don’t create links for them.

This code makes them visible, but you can click into the post, which I don’t want.

pre_get_posts changes what posts a query fetches, it has nothing to do with how those posts are displayed, how they are displayed is entirely the job of the theme templates.

Oddly though, your pre_get_posts filter does not modify the query object. The consequence of this is that all following queries will have future posts, even if they aren’t the main query, as long as they happen after it.

You would be much better off just modifying the query instead of changing WP globals e.g.

add_action( 'pre_get_posts', function ( $wp_query ) {
    if ( is_admin() ) {
        return;
    }
    if ( ! $wp_query->is_main_query()) {
        return;
    }
    $wp_query->set( 'post_status', [ 'publish', 'future' ] );
});

So, how do you only link to the published ones? Easy, just check the status and only link if it’s publish

if ( get_post_status() == 'publish' ) {
    // show the post in the loop normally
} else {
   // it's a scheduled post, show it differently
}