One styled character range inside a text node.
Offsets are [start, end) UTF-8 BYTE offsets into IRNode::text_content (the Figma exporter converts its per-character indices to byte offsets; codegen slices by byte and snaps to codepoint boundaries defensively). Only the fields a run actually overrides are set; everything else inherits the node's dominant style. Mixed-style text (a bold word, a colored span, a different size mid-string) becomes an ordered list of these so codegen can emit per-range s instead of collapsing to the first-char dominant style. One line box Chrome laid a text run out on, kept as CACHED LAYOUT beside the paragraph rather than in place of it.
The node stays one semantic paragraph — full text, one style, one container box — and these describe how the browser broke it. A renderer that finds the cache valid draws these verbatim, so no disagreement between our advances and Blink's can move a line; a renderer that finds it stale reflows and the paragraph behaves like any other text.
rect is relative to the node's own box. start / length are UTF-16 code units into text_content, because that is how the capture protocol indexes strings — see utf16_slice in the importer.