## Summary
Use `std::string_view` and case-insensitive comparisons to avoid string
allocations during CSS parsing.
**Hot path:** `resolveStyle` (called per HTML start tag during chapter
rendering) now does zero heap allocations. Previously it allocated a
normalized tag string, a vector of class strings, and a composite key
per class. For a chapter with ~2000 tags × 2 classes each, that's ~12
000 small short-lived allocations eliminated per page render — primarily
a heap-fragmentation win on the ESP32-C3's ~380KB RAM.
**Cold path:** CSS load no longer allocates per-rule selector vectors or
per-token strings; `splitOnChar`/`splitWhitespace` are gone, replaced
with callback-based tokenization (`forEachDelimitedToken`).
**Behavioral notes:**
- The selector `unordered_map` now uses an ASCII-case-insensitive
hash/equal. Selectors are stored with their original case rather than
pre-lowercased; the observable lookup result is unchanged.
- `stripTrailingImportant` is now case-insensitive (per CSS spec;
previously matched only lowercase `!important`).
**Cache compatibility:** `CSS_CACHE_VERSION` unchanged. Old caches
(lowercase selectors) load correctly under the new lookup; new caches
will contain verbatim-case selectors — both forms work.
---
### AI Usage
Did you use AI tools to help write this code? _**PARTIALLY**_