### Context
This PR makes nested headers work correctly together with **Collaps…ibleColumns**, **HiddenColumns**, **ManualColumnMove**, and column insertion/removal. The entry point was nested headers misaligning with the grid body when columns are hidden inside a collapsible header group (reported in dev-handsontable #500; a client showstopper since v8.0, worked in v7), but fixing it surfaced a cluster of related issues in the same area, all addressed here.
**Root cause of the original misalignment:** the header colspan was incrementally mutated by per-operation node modifiers (`hideColumn`/`showColumn`), and to avoid double-counting when collapse fired the hide observer, those modifiers early-returned whenever any ancestor was `collapsible`. That blanket guard silently dropped genuine external hides inside a collapsible group, so the group's colspan was never reduced - a group claiming colspan 3 over 2 visible columns made the header row one column wider than the body, shifting every header to its right off its data column.
**Core fix:** derive each header node's `colspan` / `crossHiddenColumns` / `isHidden` from a single visibility source (a `ColumnVisibility` port backed by the column index mapper) via `syncVisibilityOnTree`, instead of mutating colspan per operation. The derivation is source-agnostic - it does not matter whether a column is hidden by `CollapsibleColumns`, `HiddenColumns`, or any future source - so the plugins stay decoupled. This deletes the `hideColumn`/`showColumn` node modifiers and the collapsible guard, and runs on every column index mapper `cacheUpdated` (moves and hides) so move-while-hidden stays aligned too.
### What this PR changes
**Header alignment & visibility**
- Derive header colspan/visibility from one source (`syncVisibilityOnTree` + `ColumnVisibility` adapter) instead of per-operation mutation; removes the `hideColumn`/`showColumn` modifiers and the collapsible guard.
- Show the hidden-column indicator only on the header closest to the cells (the bottom header row), not on wider parent/group headers that would point at an internal or duplicated boundary.
**Column insert / remove**
- NestedHeaders now follows structural changes: `afterCreateCol` extends the header structure (a column inserted inside a span widens it and every spanning ancestor; a column inserted at a boundary becomes a standalone header) and `afterRemoveCol` shrinks, re-anchors, or drops headers that overlap the removed range.
- The collapsed state is preserved across these rebuilds (and across `mapState`/`mergeStateWith`) by re-running the collapse on the rebuilt tree - restoring both the `isCollapsed` indicator and the `clonedTree` needed to expand again - rather than restoring the flag alone.
**Collapse semantics**
- A collapse is treated as effective when it owns columns (`affectedColumns.length > 0`), not only when the colspan changes. So collapsing a group whose extra child is already hidden by HiddenColumns now records the collapse (the indicator shows collapsed), and the group stays collapsed even if HiddenColumns later shows that column - only expanding the group reveals it.
**Selection**
- Removing a column/row no longer leaves a single-cell selection stranded on a now-hidden index; the highlight snaps to the nearest visible one. Applied to both `shiftColumns` and `shiftRows` in core selection (benefits every hiding plugin). Wider ranges are left untouched - they legitimately keep hidden indices within their copy/fill bounds.
**Internal refactors (no behavior change)**
- `TreeNode` made generic (`TreeNode<T>`) so the nested-headers state can be typed end-to-end, removing ~12 `as`-casts at call sites with no runtime change.
- Gated the ghost-table width rebuild (`buildWidthsMap`) behind an actual hiding change so a pure column move no longer rebuilds and measures a detached DOM table; visibility derivation stays unconditional.
- Removed a discarded intermediate matrix generation in the collapse-preserving rebuild, and deduplicated the selection snap into a shared `snapToNearestVisible` helper.
### How has this been tested?
New and existing unit + E2E tests, run under `main`, `horizon`, and `classic` themes.
- **E2E (new):** `nestedHeaders/__tests__/plugins/collapsibleColumns.spec.js` and `.../alteringColumns.spec.js` cover: hide under a collapsible header, initial `hiddenColumns` config, collapse with a hidden child (incl. round-trip ownership and staying recoverable when a collapsed sub-group loses its only visible column), collapse/expand keeping an external hide, the full client combo (CollapsibleColumns + ManualColumnMove + HiddenColumns, with an actual move), `fixedColumnsStart` frozen-overlay colspan clamp, nested groups, adding/removing columns left/right of and inside collapsed groups, and selection adjustment after each alter (`toEqualCellRange`), plus a width-alignment regression for an early-collapsed group.
- **E2E (selection snap):** row/column equivalents in `hiddenColumns/__tests__/altering.spec.js` and `hiddenRows/__tests__/altering.spec.js` (single-cell snap + multi-column/row range left intact).
- **Unit:** `nestedHeaders/__tests__/stateManager` - matrix-generator assertions preserved (behavior unchanged), tests adapted to the `syncVisibilityOnTree` API, level-keyed collapse restore, and `affectedColumns` ordering.
- Regression-checked across `hiddenColumns`, `hiddenRows`, `manualColumnMove`, `selection`, and `collapsibleColumns` E2E suites with no failures; full unit suite green.
### Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature or improvement (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Additional language file or change to the existing one (translations)
### Related issue(s):
1. DEV-294
2. dev-handsontable #500
### Affected project(s):
- [x] `handsontable`
- [ ] `@handsontable/angular-wrapper`
- [ ] `@handsontable/react-wrapper`
- [ ] `@handsontable/vue3`
### Checklist:
- [x] I have reviewed the guidelines about [Contributing to Handsontable](https://github.com/handsontable/handsontable/blob/master/CONTRIBUTING.md) and I confirm that my code follows the code style of this project.
- [x] I have signed the [Contributor License Agreement](https://docs.google.com/forms/d/e/1FAIpQLScpMq4swMelvw3-onxC8Jl29m0fVp5hpf7d1yQVklqVjGjWGA/viewform?c=0&w=1)
- [ ] My change requires a change to the documentation.
ClickUp task: https://app.clickup.com/t/9015210959/DEV-294
---
> [!NOTE]
> **High Risk**
> Large changes across NestedHeaders, CollapsibleColumns, selection, and column alter paths that affect header/body layout and visibility for many plugin combinations; mitigated by extensive tests but still high blast radius in the grid core.
>
> **Overview**
> Fixes **DEV-294** misalignment when nested headers share collapsible groups with **HiddenColumns** (and related plugins). Header **colspan and visibility are derived in one pass** from the column index mapper (`syncVisibilityOnTree`) instead of incremental hide/show node modifiers and the old collapsible guard that dropped external hides.
>
> **Collapse & columns:** Groups can collapse when a child is already hidden; collapse state and **`visibleWhen`** rules survive column insert/remove and settings rebuilds. **`visibleWhen`** (`collapsed` / `expanded` / `always`) controls which group columns stay visible per collapse state via a dedicated hiding map in **CollapsibleColumns**. Hidden-column indicators render only on the bottom header row adjacent to cells.
>
> **Selection:** Single-cell selection snaps to the nearest visible column/row after remove when it would land on a hidden index (wider ranges unchanged).
>
> Adds docs/examples, typings for `nestedHeaders`, Angular docs typecheck **`SKIP_LATEST`**, and large E2E/unit coverage.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4e59a4d627b8ce4991a4351eda7e446bf24ec041. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>