If I were you, I would try to add some sort of index to your MySQL database for this fields:
cb.customers_basket_date_added
cb.customers_id
when this is done I would make changes your SQL, something similar to this:
SELECT DISTINCT
cb.customers_id,
cb.customers_basket_date_added,
c.customers_firstname,
c.customers_lastname,
c.customers_email_address,
c.customers_telephone,
sc.datemodified AS last_contacted
FROM
customers_basket cb
INNER JOIN customers c ON c.customers_id = cb.customers_id
LEFT JOIN scart sc ON cb.customers_id = sc.customers_id
WHERE
( cb.customers_basket_date_added BETWEEN '{start-date}' AND '{end-date}' ) AND
NOT EXISTS (
SELECT
sc.customers_id
FROM
scart sc
WHERE
sc.datemodified > '{date-modified}' AND
sc.{customerId-field} = cb.customers_id )
ORDER BY
cb.customers_basket_date_added DESC
I changed your NOT IN function to NOT EXISTS, and changed a lot more.
Why are you using distinct and not group by function?
when this SQL performs fast we can go to next step, the SQL inside your loop.
Is it posible to make a SQL dump so that I can download it and help you out more?
I think you have a big mistake in your 3rd block of SQL, maybe it can be done with only 1 SQL select and not 1 select * 2 select each loop run, this mean if you got 90 rows out you make 181 selects, I am sure that you can do it with less select statements being run against the SQL Server.