The predicate shape stays at parent grain — one verdict per row, duplicates in the array irrelevant, intent readable. The unnest shape moves to element grain — which you want when the question is 'which element matched' — but every duplicate element is now a row, and forgetting the DISTINCT re-aggregation hands you the double-count. Predicate for yes/no, explode for which/how-many, and a grain sentence before any count.
SELECT COUNT(*) AS jars FROM ( SELECT jar, UNNEST(ingredients) AS ing FROM jars ) WHERE ing = 'nut';
SELECT COUNT(*) AS jars FROM jars WHERE list_contains(ingredients, 'nut');
The unnest-count read 2 jars for 'nut', not 1 — stew's array lists the element 'nut' twice, so exploding the duplicate double-counted the jar; a membership predicate stays at jar grain and answers once.
list_has_any / list_contains for membership (dialect spellings vary); unnest + join + DISTINCT re-agg when element detail is the question.