The text-input widget now renders multi-line word-wrapped text using
cl-tty.box:word-wrap instead of single-line truncation. The cursor
position is computed from the wrapped lines using the same algorithm
as position-cursor but now lives in the library where it belongs.
This is the critical step that enables passepartout to replace its
ad-hoc view-input + position-cursor with a simple (render input be) call.
Placeholder text is shown when value is empty, drawn with :dim style.
Block cursor (█) at the correct word-wrapped position. All tests pass
at 100% including integration tests.