Offset pagination (LIMIT 20 OFFSET 40) skips or repeats items when rows are inserted while a user pages. Cursor pagination instead says "give me items after this one".
Items are dicts with created (a sortable value) and a unique id, and the list is ordered by (created, id). Implement:
encode_cursor(d) / decode_cursor(s): JSON with sorted keys, then URL-safe base64 with the = padding stripped (restore it when decoding). decode_cursor raises ValueError for a malformed cursor.paginate(items, limit, cursor=None): return (page, next_cursor). The page holds the next limit items strictly after the cursor's (created, id) position. next_cursor encodes the last item's created and id, or is None when there are no more items after the page.