Okay, I've finally cracked it! For reference:
$emailBody .= "<html><body>Blah</body></html>";
$emailSubject = "Subject";
$emailCSV = "\"Col1\", \"Col2\"\r\n"; // etc.
$attachment = $emailCSV;
$boundary = md5(time());
$header = "From: Name <address@address.com>\r\n";
$header .= "MIME-Version: 1.0\r\n";
$header .= "Content-Type: multipart/mixed;boundary=\"" . $boundary . "\"\r\n";
$output = "--".$boundary."\r\n";
$output .= "Content-Type: text/csv; name=\"result.csv\";\r\n";
$output .= "Content-Disposition: attachment;\r\n\r\n";
$output .= $attachment."\r\n\r\n";
$output .= "--".$boundary."\r\n";
$output .= "Content-type: text/html; charset=\"utf-8\"\r\n";
$output .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
$output .= $emailBody."\r\n\r\n";
$output .= "--".$boundary."--\r\n\r\n";
mail($emailTo, $emailSubject, $output, $header);