Like wrap_lines, but returns byte ranges into the original text
for each visual line. This is required for multi-line editing so
caret/selection mapping stays correct.
Greedy wrap into lines that fit max_width. Prefers breaking at whitespace,
falls back to grapheme boundaries. If max_lines is Some and we truncate,
caller can choose to ellipsize the last visible line.