When can SARGable predicates be pushed into a CTE or derived table?
There are a few issues in play here.
Pushing predicates past TOP
The optimizer cannot currently push a predicate past a TOP, even in the limited cases where it would be safe to do so*. This limitation accounts for the behaviour of all the queries in the question where the predicate is in a higher scope than the TOP.
The work around is to perform the rewrite manually. The fundamental issue is similar to the case of pushing predicates past a window function, except there is no corresponding specialized rule like SelOnSeqPrj.
My personal opinion is that an exploration rule like SelOnTop remains unimplemented because people have deliberately written queries with TOP in an effort to provide a kind of 'optimization fence'.
* Generally this means the predicate should appear in the ORDER BY clause associated with the TOP, and the direction of any inequality should agree with the direction of the sorting. The transformation would also need to account for the sorting behaviour of NULLs in SQL Server. Overall, the limitations probably mean this transformation would not be generally useful enough in practice to justify the additional exploration efforts.
Costing issues
The remaining execution plans in the question can be explained as cost-based choices due to the distribution of values in the Score column (many more rows &lt;= 500 than &gt;= 500), and the effect of the row goal introduced by the TOP.
For example, the query:
--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score &lt;= 500
ORDER BY
    c.Score ASC;

...produces a plan with an apparently unpushed predicate in a Filter:

Note that the Sort is estimated to produce 101 rows. This is the effect of the row goal added by the Top. This affects the estimated cost of the Sort and the Filter enough to make it seem like this is the cheaper option. The estimated cost of this plan is 2401.39 units.
If we disable row goals with a query hint:
--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score &lt;= 500
ORDER BY
    c.Score ASC
OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'));

...the execution plan produced is:

The predicate has been pushed into the scan as a residual non-sargable predicate, and the cost of the whole plan is 2402.32 units.
Notice that the &lt;= 500 predicate is not expected to filter out any rows. If you had chosen a smaller number, like &lt;= 50, the optimizer would have preferred the pushed-predicate plan regardless of the row goal effect.
For the query with Score DESC and a Score &gt;= 500 predicate:
--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score &gt;= 500
ORDER BY
    c.Score DESC;

Now the predicate is expected to be very selective, so the optimizer chooses to push the predicate and use the nonclustered index with lookups:

Again, the optimizer considered multiple alternatives and chose this as the apparently cheapest option, as usual.