Separate elements with IDs into their own matching pool
Elements with IDs can only match by their ID - they are semantically distinct entities. Previously, if an element with an ID failed to match in the ID matching phase, it could still incorrectly match in the heuristics or tagName phases. This change separates elements with IDs into a dedicated `idElements` pool at build time, so they: 1. Skip the expensive `isEqualNode` check entirely 2. Only participate in ID matching 3. Get removed if no ID match is found The idSet matching phase (for elements matched by descendant IDs) now correctly iterates over `candidateElements` since elements with idSets may not have IDs themselves. This is both a correctness fix and a performance optimization.