You are: when calling the ->skip() it will add the OFFSET and when calling the ->take() it will add the LIMIT.
The confusion might be because the query builder mutates itself instead of returning new instances.
I commented what happens
// creates a new query builder
$query_records = User::select($fields)
->Where('first_name', 'like', '%' .$s. '%');
// not a copy but a reference to same instance
$query_records_trashed=$query_records;
// this will run the query and would not be affected by
// the subsequent calls
$total_records=$query_records->count();
// as you call skip and take before the get method
// it mutates the query object
$records=$query_records->orderBy($sort_column, $sort_order)
->skip($page*$rows_per_page)
->take($rows_per_page)
->get();
// $query_records_trashed is already muted from
// the above statement skip, take, ...
$total_trashed_records=$query_records_trashed->onlyTrashed()->count();
One solution is to clone the query before adding new scopes:
$query_records = User::select($fields)
->Where('first_name', 'like', '%' .$s. '%');
$query_records_trashed=$query_records;
$total_records=$query_records->count();
// there is no clone method to chain
// so we use the cloneWithout
$records=$query_records
->cloneWithout([]) // clones the query builder so we don't mutate the original
->orderBy($sort_column, $sort_order)
->skip($page*$rows_per_page)
->take($rows_per_page)
->get();
$total_trashed_records=$query_records_trashed
->cloneWithout([]) // optional if it won't be used later in the code
->onlyTrashed()
->count();