How to display related posts by subcategory and not parent category

There is indeed a much simpler way to do this.

First, to get the child category, just check the value of each category’s parent. If it’s a top-level category, the parent will be 0. So the child category will pass the test if( 0 != $category->parent ):

$categories = get_the_category();
foreach( $categories as $category ){
    if( 0 != $category->parent )
        $child_cat = $category;
}

Then query for your posts using that category ID as the cat argument. To output a thumbnail for only the first post, simply check if the current_post of your query object is 0. That number gets automatically incremented for each post in your loop, starting at 0:

if( isset( $child_cat ) ){  
    echo 'More in ' . $child_cat->name;
    $args = array(
        'cat' => $child_cat->term_id,
        'post__not_in' => array( get_the_ID() )
    );
    $related = new WP_Query( $args );
    if( $related->have_posts() ){
        while( $related->have_posts() ){
            $related->the_post();
            if( 0 == $related->current_post ){
                the_post_thumbnail('medium');
            }
            // your template tags, etc..
            the_title();
        }
        wp_reset_postdata();
    }
}

Also note, you never need to use wp_reset_query() unless you overwrite the global $wp_query, which doesn’t happen here.