Almost every jq surprise comes from one design decision: a filter emits zero, one or many results, and everything downstream runs once per result. Once that lands, the rest of the language stops being strange.
What this cannot do, and it will tell you
jq is a complete language with variables, reduce, foreach, if, try and user-defined functions. This implements a documented subset, and anything outside it is reported as an error naming what was not understood rather than being quietly ignored. That distinction is the whole point: a playground that skips a construct gives you an answer that looks like jq's and is not. Supported: ., .foo, .foo.bar, ."odd key", .foo?, .[], .[]?, .[0], .[1:3], |, ,, [ ... ], { a: .b }, select(f), map(f), has("k"), keys, keys_unsorted, values, length, type, not, to_entries, from_entries, add, unique, sort, sort_by(f), reverse, min, max, flatten, first, last, empty, tostring, tonumber, ascii_downcase, ascii_upcase, startswith(s), endswith(s), contains(s), join(s), split(s), ==, !=, <, <=, >, >=, and, or, +, -, *, /, %, numbers, strings, booleans, arrays, objects, nulls. Everything here was checked against real jq, 82 filters over one document with complete agreement.