diff --git a/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss b/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss
index d538e8a16ce..f61cfde961a 100644
--- a/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss
+++ b/community-modules/styles/src/internal/base/parts/_columns-tool-panel.scss
@@ -59,7 +59,7 @@
         line-height: 1.5;
     }
 
-    .ag-column-panel .ag-standard-button.ag-column-panel-buttons-apply-button {
+    .ag-column-panel .ag-standard-button.ag-column-panel-buttons-apply-button:not(:disabled) {
         color: var(--ag-column-panel-apply-button-color);
         background-color: var(--ag-column-panel-apply-button-background-color);
     }
diff --git a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc
index 3b4f260ef4b..0f796337e53 100644
--- a/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc
+++ b/documentation/ag-grid-docs/src/content/docs/tool-panel-columns/index.mdoc
@@ -24,9 +24,19 @@ Selecting columns means different things depending on whether the grid is in piv
 -   **Pivot Mode Off**: When pivot mode is off, selecting a column toggles the visibility of the column. A selected column is visible and an unselected column is hidden. With `allowDragFromColumnsToolPanel=true`, you can drag a column from the tool panel onto the grid and it will become visible.
 -   **Pivot Mode On**: When pivot mode is on, selecting a column will trigger the column to be either aggregated, grouped or pivoted depending on what is allowed for that column.
 
-## Deferred Apply Mode
+## Deferred Updates
 
-You can configure the Columns Tool Panel to stage changes and require an explicit **Apply** action before they are committed.
+You can configure the Columns Tool Panel to stage changes and require an explicit **Apply** action before they are committed. This allows multiple configuration changes to be made and applied in a single update, avoiding unnecessary intermediate recomputations or requests.
+
+Deferred Updates are enabled by including the **Apply** button in `toolPanelParams.buttons`.
+
+The example below uses the Server-Side Row Model. Note that in the example below:
+
+-   Changes made in the Columns Tool Panel (CTP) are staged as pending changes.
+-   **Apply** commits all pending changes in a single operation.
+-   **Cancel** discards all pending changes and restores the last applied state.
+
+{% gridExampleRunner title="Deferred Updates" name="deferred-apply-mode"  exampleHeight=830 /%}
 
 ```{% frameworkTransform=true %}
 const gridOptions = {
@@ -48,18 +58,9 @@ const gridOptions = {
 };
 ```
 
-When `buttons` includes `'apply'`:
-
--   Changes made in the Columns Tool Panel are staged as pending changes.
--   The configured buttons are displayed at the bottom of the tool panel.
--   **Apply** commits pending changes.
--   **Cancel** discards pending changes.
-
-Changes made outside the Columns Tool Panel (for example via other grid entry points) continue to apply immediately.
-
-The example below uses the Server-Side Row Model.
-
-{% gridExampleRunner title="Deferred Apply Mode" name="deferred-apply-mode"  exampleHeight=830 /%}
+{% note %}
+Changes made outside the Columns Tool Panel — such as dragging columns into the Row Group or Pivot Panels, using the Column Menu, or calling the Grid / Column API — are applied immediately and clear any pending changes. Column pinning, resizing, and group expansion do not clear pending changes.
+{% /note %}
 
 ## Columns Tool Panel Sections
 
diff --git a/packages/ag-grid-community/src/validation/errorMessages/errorText.ts b/packages/ag-grid-community/src/validation/errorMessages/errorText.ts
index 0b181ec70e2..a3369dbea40 100644
--- a/packages/ag-grid-community/src/validation/errorMessages/errorText.ts
+++ b/packages/ag-grid-community/src/validation/errorMessages/errorText.ts
@@ -733,8 +733,7 @@ export const AG_GRID_ERRORS = {
         'Since v35, `api.hideOverlay()` does not hide the overlay when `activeOverlay` is set. Set `activeOverlay=null` instead.' as const,
     297: () =>
         '`api.hideOverlay()` does not hide the no matching rows overlay as it is only controlled by grid state. Set `suppressOverlays=["noMatchingRows"] to not show it.' as const,
-    298: () =>
-        `Columns Tool Panel 'buttons' requires 'apply' to enable deferred mode. Without it, other buttons have no effect.` as const,
+    298: () => `Columns Tool Panel 'buttons' requires 'apply' to enable Deferred Updates.` as const,
 };
 
 export type ErrorMap = typeof AG_GRID_ERRORS;
diff --git a/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.css b/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.css
index 6e26c0846ec..6ad4ed719b0 100644
--- a/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.css
+++ b/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.css
@@ -61,7 +61,7 @@
 }
 
 /* stylelint-disable-next-line selector-max-specificity */
-:where(.ag-column-panel) .ag-column-panel-buttons-apply-button {
+:where(.ag-column-panel) .ag-column-panel-buttons-apply-button:not(:disabled) {
     color: var(--ag-column-panel-apply-button-color);
     background-color: var(--ag-column-panel-apply-button-background-color);
 }
diff --git a/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.ts b/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.ts
index 18abd4fe0b8..166401da7c3 100644
--- a/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.ts
+++ b/packages/ag-grid-enterprise/src/columnToolPanel/columnToolPanel.ts
@@ -148,11 +148,13 @@ export class ColumnToolPanel extends Component implements IColumnToolPanel, IToo
             );
         }
 
-        if (mergedParams.buttons?.length) {
+        if (mergedParams.buttons) {
             if (!mergedParams.buttons.includes('apply')) {
                 _warn(298);
             }
-            this.initDeferredButtons(mergedParams.buttons);
+            if (mergedParams.buttons.length) {
+                this.initDeferredButtons(mergedParams.buttons);
+            }
         }
 
         this.initialised = true;
diff --git a/packages/ag-grid-enterprise/src/columnToolPanel/toolPanelColumnComp.ts b/packages/ag-grid-enterprise/src/columnToolPanel/toolPanelColumnComp.ts
index 416f4d03029..1d0b3c91597 100644
--- a/packages/ag-grid-enterprise/src/columnToolPanel/toolPanelColumnComp.ts
+++ b/packages/ag-grid-enterprise/src/columnToolPanel/toolPanelColumnComp.ts
@@ -231,6 +231,10 @@ export class ToolPanelColumnComp extends Component {
         const beans = this.beans;
         const { gos, eventSvc, dragAndDrop } = beans;
 
+        if (isDeferredMode(this.params)) {
+            eDragHandle.setAttribute('data-column-tool-panel-deferred', '');
+        }
+
         let hideColumnOnExit = !gos.get('suppressDragLeaveHidesColumns');
         const dragSource: GridDragSource = {
             type: DragSourceType.ToolPanel,
@@ -251,7 +255,7 @@ export class ToolPanelColumnComp extends Component {
                 });
             },
             onGridEnter: (dragItem: DragItem | null) => {
-                if (hideColumnOnExit) {
+                if (hideColumnOnExit && !isDeferredMode(this.params)) {
                     // when dragged into the grid, restore the state that was active pre-drag
                     updateColumns(beans, {
                         columns: [this.column],
@@ -263,8 +267,8 @@ export class ToolPanelColumnComp extends Component {
                 }
             },
             onGridExit: () => {
-                if (hideColumnOnExit) {
-                    // when dragged outside of the grid, mimic what happens when checkbox is disabled
+                if (hideColumnOnExit && !isDeferredMode(this.params)) {
+                    // when dragged outside of the grid, copy what happens when checkbox is disabled
                     // this handles the behaviour for pivot which is different to just hiding a column.
                     this.onChangeCommon(false);
                 }
diff --git a/packages/ag-grid-enterprise/src/columnToolPanel/updates/columnStateUpdateExecutionStrategy.ts b/packages/ag-grid-enterprise/src/columnToolPanel/updates/columnStateUpdateExecutionStrategy.ts
index 5cb0ff17fc5..8da932948a5 100644
--- a/packages/ag-grid-enterprise/src/columnToolPanel/updates/columnStateUpdateExecutionStrategy.ts
+++ b/packages/ag-grid-enterprise/src/columnToolPanel/updates/columnStateUpdateExecutionStrategy.ts
@@ -265,7 +265,7 @@ class DeferredColumnStateUpdateStrategy implements ColumnStateConcreteUpdateStra
                     (patch.hide !== undefined && patch.hide !== !column.isVisible()) ||
                     (patch.rowGroup !== undefined && !!patch.rowGroup !== column.isRowGroupActive()) ||
                     (patch.pivot !== undefined && !!patch.pivot !== column.isPivotActive()) ||
-                    (patch.aggFunc !== undefined && patch.aggFunc !== column.getAggFunc())
+                    (patch.aggFunc !== undefined && (patch.aggFunc ?? null) !== (column.getAggFunc() ?? null))
                 ) {
                     return true;
                 }
@@ -646,11 +646,16 @@ class DeferredColumnStateUpdateStrategy implements ColumnStateConcreteUpdateStra
             return [];
         }
 
+        const livePivotColumns = this.beans.pivotColsSvc?.columns;
+        const fallbackColumns = livePivotColumns?.length
+            ? livePivotColumns
+            : getDraftColumns(this.beans, this.lastPivotColIds);
+
         return getDraftColumns(
             this.beans,
             getDraftFunctionColumnIds(
                 this.state.pivot?.colIds,
-                this.beans.pivotColsSvc?.columns,
+                fallbackColumns,
                 this.state.columnState?.patches,
                 (patch) => (patch.pivot == null ? undefined : !!patch.pivot)
             )
diff --git a/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/baseDropZonePanel.ts b/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/baseDropZonePanel.ts
index 23713ce8666..dceb5b89def 100644
--- a/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/baseDropZonePanel.ts
+++ b/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/baseDropZonePanel.ts
@@ -9,8 +9,6 @@ import { DropZoneColumnComp } from './dropZoneColumnComp';
 
 export type TDropZone = 'rowGroup' | 'pivot' | 'aggregation';
 
-const DEFERRED_TOOL_PANEL_CLASS = 'ag-column-panel-deferred';
-
 export abstract class BaseDropZonePanel extends PillDropZonePanel<DropZoneColumnComp, AgColumn> {
     constructor(
         horizontal: boolean,
@@ -49,7 +47,7 @@ export abstract class BaseDropZonePanel extends PillDropZonePanel<DropZoneColumn
             return true;
         }
 
-        return !sourceElement.closest(`.${DEFERRED_TOOL_PANEL_CLASS}`);
+        return !sourceElement.hasAttribute('data-column-tool-panel-deferred');
     }
 
     protected override minimumAllowedNewInsertIndex(): number {
diff --git a/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/dropZoneColumnComp.ts b/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/dropZoneColumnComp.ts
index 4d08405a8c7..fc296910511 100644
--- a/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/dropZoneColumnComp.ts
+++ b/packages/ag-grid-enterprise/src/rowGrouping/columnDropZones/dropZoneColumnComp.ts
@@ -10,7 +10,7 @@ import type {
 } from 'ag-grid-community';
 import { Component, DragSourceType, KeyCode, RefPlaceholder, _createElement } from 'ag-grid-community';
 
-import { isDeferredMode } from '../../columnToolPanel/toolPanelDeferredUiUtils';
+import { isDeferredMode, refreshDeferredToolPanelUi } from '../../columnToolPanel/toolPanelDeferredUiUtils';
 import type { ColumnStateUpdateParams } from '../../columnToolPanel/updates/columnStateUpdateTypes';
 import { PillDragComp } from '../../widgets/pillDragComp';
 import { VirtualList } from '../../widgets/virtualList';
@@ -61,6 +61,10 @@ export class DropZoneColumnComp extends PillDragComp<AgColumn> {
 
         super.postConstruct();
 
+        if (this.deferApply) {
+            this.eDragHandle.setAttribute('data-column-tool-panel-deferred', '');
+        }
+
         if (sortSvc) {
             this.setupSort();
 
@@ -178,6 +182,7 @@ export class DropZoneColumnComp extends PillDragComp<AgColumn> {
                 this.beans.columnStateUpdateStrategy.progressSortFromEvent(this.deferApply, column, event);
                 eSortIndicator.refresh();
                 this.setupAria();
+                refreshDeferredToolPanelUi(this.beans, this.updateParams);
             };
 
             this.addGuiEventListener('click', performSort);
@@ -360,6 +365,7 @@ export class DropZoneColumnComp extends PillDragComp<AgColumn> {
                 eText.textContent = this.getDisplayValue();
             }
             this.setupAria();
+            refreshDeferredToolPanelUi(this.beans, this.updateParams);
         };
 
         const localeTextFunc = this.getLocaleTextFunc();
diff --git a/packages/ag-grid-enterprise/src/widgets/pillDragComp.ts b/packages/ag-grid-enterprise/src/widgets/pillDragComp.ts
index 3a76621c493..031c8b5a8e5 100644
--- a/packages/ag-grid-enterprise/src/widgets/pillDragComp.ts
+++ b/packages/ag-grid-enterprise/src/widgets/pillDragComp.ts
@@ -38,7 +38,7 @@ const PillDragCompElement: ElementParams = {
 };
 export abstract class PillDragComp<TItem> extends Component<PillDragCompEvent> {
     private readonly eText: HTMLElement = RefPlaceholder;
-    private readonly eDragHandle: HTMLElement = RefPlaceholder;
+    protected readonly eDragHandle: HTMLElement = RefPlaceholder;
     private readonly eButton: HTMLElement = RefPlaceholder;
 
     public abstract getItem(): TItem;
diff --git a/scripts/deployments/validateVersionModules.js b/scripts/deployments/validateVersionModules.js
new file mode 100644
index 00000000000..1ff04765022
--- /dev/null
+++ b/scripts/deployments/validateVersionModules.js
@@ -0,0 +1,224 @@
+// Note: Assumes working directory is the root of the mono-repo
+//
+// Validates that all files updated by versionModules.js contain the expected
+// grid version, charts version, and environment values.
+
+const fs = require('fs');
+const path = require('path');
+
+const getPackageInformation = require('./utils/utils').getPackageInformation;
+
+const ROOT_PACKAGE_JSON = '../../package.json';
+const packageDirectories = require(ROOT_PACKAGE_JSON).workspaces.packages.filter((d) => !d.startsWith('external/'));
+const SKIPPED_DEPS = new Set(['ag-grid-testing']);
+
+if (process.argv.length < 4) {
+    console.log(
+        'Usage: node scripts/deployments/validateVersionModules.js [Grid Version] [Charts Version] [Environment]'
+    );
+    console.log('For example: node scripts/deployments/validateVersionModules.js 33.0.0 11.0.0 production');
+    console.log('Note: This script should be run from the root of the monorepo');
+    process.exit(1);
+}
+
+const [exec, scriptPath, gridVersion, chartsVersion, environment] = process.argv;
+
+if (!gridVersion || !chartsVersion || !environment) {
+    console.error('ERROR: Grid version, charts version, and environment are all required');
+    console.error('node scripts/deployments/validateVersionModules.js <grid version> <charts version> <environment>');
+    process.exit(1);
+}
+
+if (!['production', 'archive', 'local'].includes(environment)) {
+    console.error('ERROR: Invalid environment supplied. Must be one of: production|archive|local');
+    process.exit(1);
+}
+
+console.log('************************************************************************************************');
+console.log(`Validating Grid Version: ${gridVersion}, Charts Version: ${chartsVersion}, Environment: ${environment}`);
+console.log('************************************************************************************************');
+
+const errors = [];
+
+function error(message) {
+    errors.push(message);
+    console.error(`  ERROR: ${message}`);
+}
+
+// 1. Validate root .env file
+function validateEnvFile() {
+    console.log('\nChecking .env file...');
+
+    const envPath = './.env';
+    if (!fs.existsSync(envPath)) {
+        error('.env file does not exist');
+        return;
+    }
+
+    const content = fs.readFileSync(envPath, 'utf-8');
+    const lines = content.split('\n');
+
+    const envVars = {};
+    for (const line of lines) {
+        const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
+        if (match) {
+            envVars[match[1]] = match[2];
+        }
+    }
+
+    const expected = {
+        BUILD_GRID_VERSION: gridVersion,
+        BUILD_CHARTS_VERSION: chartsVersion,
+        ENV: environment,
+    };
+
+    for (const [key, expectedValue] of Object.entries(expected)) {
+        if (!(key in envVars)) {
+            error(`.env is missing ${key}`);
+        } else if (envVars[key] !== expectedValue) {
+            error(`.env has ${key}=${envVars[key]} but expected ${expectedValue}`);
+        }
+    }
+}
+
+// 2. Validate root package.json version
+function validateRootPackageJson() {
+    console.log('\nChecking root package.json...');
+
+    const resolvedPath = path.resolve(__dirname, ROOT_PACKAGE_JSON);
+    const packageJson = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));
+
+    if (packageJson.version !== gridVersion) {
+        error(`root package.json version is ${packageJson.version} but expected ${gridVersion}`);
+    }
+}
+
+// 3. Validate workspace package.json files (version + ag deps)
+function validatePackageJsonFiles() {
+    console.log('\nChecking workspace package.json files...');
+
+    const allPackages = getPackageInformation();
+
+    for (const [_, pkg] of Object.entries(allPackages)) {
+        const { projectRoot, version } = pkg;
+
+        // versionModules.js sets every package version to gridVersion unconditionally
+        if (version !== gridVersion) {
+            error(`${projectRoot}/package.json version is ${version} but expected ${gridVersion}`);
+        }
+
+        // Check ag-grid dependencies
+        for (const [dep, depVersion] of Object.entries(pkg.agGridDeps)) {
+            if (SKIPPED_DEPS.has(dep)) {
+                continue;
+            }
+            if (depVersion !== gridVersion) {
+                error(`${projectRoot}/package.json dependency ${dep} is ${depVersion} but expected ${gridVersion}`);
+            }
+        }
+
+        // Check ag-grid peer dependencies
+        for (const [dep, depVersion] of Object.entries(pkg.agGridPeerDeps)) {
+            if (SKIPPED_DEPS.has(dep)) {
+                continue;
+            }
+            if (depVersion !== gridVersion) {
+                error(`${projectRoot}/package.json peerDependency ${dep} is ${depVersion} but expected ${gridVersion}`);
+            }
+        }
+
+        // Check ag-grid optional dependencies
+        for (const [dep, depVersion] of Object.entries(pkg.agGridOptionalDeps)) {
+            if (SKIPPED_DEPS.has(dep)) {
+                continue;
+            }
+            if (depVersion !== gridVersion) {
+                error(
+                    `${projectRoot}/package.json optionalDependency ${dep} is ${depVersion} but expected ${gridVersion}`
+                );
+            }
+        }
+
+        // Check ag-charts dependencies
+        for (const [dep, depVersion] of Object.entries(pkg.agChartDeps)) {
+            if (depVersion !== chartsVersion) {
+                error(`${projectRoot}/package.json dependency ${dep} is ${depVersion} but expected ${chartsVersion}`);
+            }
+        }
+
+        // Check ag-charts optional dependencies
+        for (const [dep, depVersion] of Object.entries(pkg.agChartOptionalDeps)) {
+            if (depVersion !== chartsVersion) {
+                error(
+                    `${projectRoot}/package.json optionalDependency ${dep} is ${depVersion} but expected ${chartsVersion}`
+                );
+            }
+        }
+
+        // Check angular sub-project
+        if (pkg.agSubAngularVersion) {
+            if (pkg.agSubAngularVersion !== gridVersion) {
+                error(
+                    `${projectRoot} angular sub-project version is ${pkg.agSubAngularVersion} but expected ${gridVersion}`
+                );
+            }
+        }
+
+        if (pkg.agSubAngularGridDeps) {
+            for (const [dep, depVersion] of Object.entries(pkg.agSubAngularGridDeps)) {
+                if (depVersion !== gridVersion) {
+                    error(
+                        `${projectRoot} angular sub-project peerDependency ${dep} is ${depVersion} but expected ${gridVersion}`
+                    );
+                }
+            }
+        }
+
+        if (pkg.agSubAngularChartDeps) {
+            for (const [dep, depVersion] of Object.entries(pkg.agSubAngularChartDeps)) {
+                if (depVersion !== chartsVersion) {
+                    error(
+                        `${projectRoot} angular sub-project peerDependency ${dep} is ${depVersion} but expected ${chartsVersion}`
+                    );
+                }
+            }
+        }
+    }
+}
+
+// 4. Validate version.ts files
+function validateVersionFiles() {
+    console.log('\nChecking version.ts files...');
+
+    const CWD = process.cwd();
+    const regex = /export const VERSION = '(.*)';/;
+
+    packageDirectories.forEach((packageDirectory) => {
+        const versionFile = `${CWD}/${packageDirectory}/src/version.ts`;
+        if (!fs.existsSync(versionFile)) {
+            return;
+        }
+
+        const contents = fs.readFileSync(versionFile, 'utf8');
+        const match = contents.match(regex);
+        if (!match) {
+            error(`${packageDirectory}/src/version.ts does not contain a VERSION export`);
+        } else if (match[1] !== gridVersion) {
+            error(`${packageDirectory}/src/version.ts has VERSION '${match[1]}' but expected '${gridVersion}'`);
+        }
+    });
+}
+
+// Run all validations
+validateEnvFile();
+validateRootPackageJson();
+validatePackageJsonFiles();
+validateVersionFiles();
+
+console.log('\n************************************************************************************************');
+if (errors.length > 0) {
+    console.error(`FAILED: ${errors.length} error(s) found`);
+    process.exit(1);
+} else {
+    console.log('SUCCESS: All versions are correct');
+}
diff --git a/scripts/deployments/versionModules.js b/scripts/deployments/versionModules.js
index 987287bbb47..e44f9b90917 100644
--- a/scripts/deployments/versionModules.js
+++ b/scripts/deployments/versionModules.js
@@ -9,6 +9,7 @@ const pipe =
 
 const ROOT_PACKAGE_JSON = '../../package.json';
 const packageDirectories = require(ROOT_PACKAGE_JSON).workspaces.packages.filter((d) => !d.startsWith('external/'));
+const SKIPPED_DEPS = new Set(['ag-grid-testing']);
 
 if (process.argv.length < 4) {
     console.log('Usage: node scripts/deployments/versionModules.js [Grid Version] [Charts Version]');
@@ -190,7 +191,7 @@ function updateDependency(fileContents, property, dependencyVersion, chartsDepen
     };
     Object.entries(dependencyContents)
         .filter(([key, value]) => gridDependency(key) || chartDependency(key))
-        .filter(([key, value]) => key !== 'ag-grid-testing')
+        .filter(([key, value]) => !SKIPPED_DEPS.has(key))
         .forEach(([key, value]) => {
             if (chartsDependencyVersion) {
                 dependencyContents[key] = chartDependency(key) ? chartsDependencyVersion : dependencyVersion;
diff --git a/testing/behavioural/src/columnToolPanel/deferred-pivot-mode.test.ts b/testing/behavioural/src/columnToolPanel/deferred-pivot-mode.test.ts
index 35187c97c8d..57f54a87f30 100644
--- a/testing/behavioural/src/columnToolPanel/deferred-pivot-mode.test.ts
+++ b/testing/behavioural/src/columnToolPanel/deferred-pivot-mode.test.ts
@@ -838,6 +838,91 @@ describe('deferred column tool panel pivot mode', () => {
         expect(headerDropZones.pivotComp.isInterestedIn(DragSourceType.ToolPanel, dragHandle)).toBe(false);
     });
 
+    test('dragging a pill from a deferred CTP drop zone into header drop zones should be prohibited even when detached', async () => {
+        const { gridApi, toolPanel } = await createDeferredPivotModeGrid();
+        const country = gridApi.getColumn('country')! as any;
+        const HeaderDropZones = AgGridHeaderDropZonesSelector.component as any;
+        const headerDropZones = country.createBean(new HeaderDropZones()) as any;
+
+        // Get a pill drag handle from the CTP's row group drop zone
+        const pillDragHandle = toolPanel.rowGroupDropZonePanel
+            .getGui()
+            .querySelector('.ag-column-drop-cell-drag-handle') as Element;
+        expect(pillDragHandle).toBeTruthy();
+
+        // While attached: should be blocked
+        expect(headerDropZones.rowGroupComp.isInterestedIn(DragSourceType.ToolPanel, pillDragHandle)).toBe(false);
+
+        // Simulate what happens during drag: the pill gets detached from the DOM
+        // (source panel's onDragLeave -> removeItems -> refreshGui -> destroyGui)
+        const parent = pillDragHandle.parentElement!;
+        parent.removeChild(pillDragHandle);
+        expect(pillDragHandle.isConnected).toBe(false);
+
+        // Even when detached, should still be blocked (pill has data-column-tool-panel-deferred attribute)
+        expect(headerDropZones.rowGroupComp.isInterestedIn(DragSourceType.ToolPanel, pillDragHandle)).toBe(false);
+        expect(headerDropZones.pivotComp.isInterestedIn(DragSourceType.ToolPanel, pillDragHandle)).toBe(false);
+    });
+
+    test('dragging a CTP column to the header pivot panel in deferred mode should not apply changes', async () => {
+        const { gridApi, toolPanel, toolPanelGui } = await createDeferredPivotModeGrid();
+
+        // Athlete is not a pivot column initially
+        expect(gridApi.getPivotColumns().map((col) => col.getColId())).toEqual(['year']);
+
+        // Get the header (horizontal) pivot drop zone GUI from the grid DOM
+        const gridEl = getGridElement(gridApi)!;
+        const headerPivotDropZone = gridEl.querySelector('.ag-column-drop-horizontal-pivot') as HTMLElement;
+        expect(headerPivotDropZone).toBeTruthy();
+
+        // Simulate full drag from CTP column list to header pivot panel
+        await dragRenderedPrimaryColumnToRowGroups(toolPanel, toolPanelGui, 'Athlete', headerPivotDropZone);
+
+        // Grid pivot columns should remain unchanged (no immediate apply)
+        expect(gridApi.getPivotColumns().map((col) => col.getColId())).toEqual(['year']);
+
+        // Deferred state should also remain unchanged (drag should be fully rejected)
+        expect(
+            getUpdateStrategy(toolPanel)
+                .getPivotColumns(true)
+                .map((col) => col.getColId())
+        ).toEqual(['year']);
+    });
+
+    test('onGridExit and onGridEnter drag callbacks should be no-ops in deferred mode', async () => {
+        const { gridApi, toolPanel } = await createDeferredPivotModeGrid();
+
+        // Country is an active row group
+        expect(gridApi.getRowGroupColumns().map((col) => col.getColId())).toEqual(['country', 'sport']);
+
+        // Create a Country column comp (which registers drag source with onGridExit/onGridEnter)
+        const countryComp = createPrimaryColumnComp(toolPanel, 'Country');
+        const onChangeCommonSpy = vi.spyOn(countryComp, 'onChangeCommon');
+
+        // Find the drag source via dragSourceAndParamsList
+        const dragAndDrop = countryComp.beans.dragAndDrop;
+        const entry = dragAndDrop['dragSourceAndParamsList'].find(
+            (e: any) => e.dragSource.eElement === countryComp.eDragHandle
+        );
+        expect(entry).toBeTruthy();
+        const dragSource = entry.dragSource;
+
+        // Trigger onGridExit — should not call onChangeCommon in deferred mode
+        dragSource.onGridExit(null);
+
+        expect(onChangeCommonSpy).not.toHaveBeenCalled();
+
+        // Deferred state should remain unchanged
+        expect(
+            getUpdateStrategy(toolPanel)
+                .getRowGroupColumns(true)
+                .map((col) => col.getColId())
+        ).toEqual(['country', 'sport']);
+
+        // Grid state should remain unchanged
+        expect(gridApi.getRowGroupColumns().map((col) => col.getColId())).toEqual(['country', 'sport']);
+    });
+
     test('dragging from the non-deferred tool panel into external header drop zones should remain allowed', async () => {
         const { gridApi, toolPanel } = await createNonDeferredPivotModeGrid();
         const country = gridApi.getColumn('country')! as any;
@@ -1424,6 +1509,34 @@ describe('deferred column tool panel pivot mode', () => {
         expect(gridApi.getPivotColumns().map((col) => col.getColId())).toEqual(['year']);
     });
 
+    test('column labels section shows previous pivot columns after toggling pivot mode off, applying, then back on', async () => {
+        const { gridApi, toolPanel, toolPanelGui } = await createDeferredPivotModeGrid();
+
+        // Initially Year is a pivot column
+        expect(
+            getUpdateStrategy(toolPanel)
+                .getPivotColumns(true)
+                .map((col) => col.getColId())
+        ).toEqual(['year']);
+
+        // Toggle pivot off and apply
+        getPivotModeToggle(toolPanelGui).click();
+        getApplyButton(toolPanelGui).click();
+        await waitForNoLoadingRows(gridApi);
+
+        expect(gridApi.isPivotMode()).toBe(false);
+
+        // Toggle pivot back on (before Apply)
+        getPivotModeToggle(toolPanelGui).click();
+
+        // Deferred state should show Year in pivot columns
+        expect(
+            getUpdateStrategy(toolPanel)
+                .getPivotColumns(true)
+                .map((col) => col.getColId())
+        ).toEqual(['year']);
+    });
+
     test('turning pivot mode off and applying should remove year header group text and update the grid option', async () => {
         const { gridApi, toolPanelGui } = await createDeferredPivotModeGrid();
 
@@ -1509,4 +1622,67 @@ describe('deferred column tool panel pivot mode', () => {
 
         expect(getApplyButton(toolPanelGui).disabled).toBe(true);
     });
+
+    test('apply button is disabled after reverting a staged column visibility change via checkbox', async () => {
+        const { toolPanel, toolPanelGui } = await createDeferredNonPivotGrid();
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+
+        // Toggle Athlete checkbox off (hide column)
+        const athleteComp = createPrimaryColumnComp(toolPanel, 'Athlete');
+        athleteComp['onChangeCommon'](false);
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(false);
+
+        // Toggle Athlete checkbox back on (show column — revert to original state)
+        const athleteComp2 = createPrimaryColumnComp(toolPanel, 'Athlete');
+        athleteComp2['onChangeCommon'](true);
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+    });
+
+    test('apply button is disabled after reverting a staged pivot column change via checkbox', async () => {
+        const { toolPanel, toolPanelGui } = await createDeferredPivotModeGrid();
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+
+        // Toggle Athlete checkbox on (add to row group in pivot mode)
+        const athleteComp = createPrimaryColumnComp(toolPanel, 'Athlete');
+        athleteComp['onChangeCommon'](true);
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(false);
+
+        // Toggle Athlete checkbox back off (revert)
+        const athleteComp2 = createPrimaryColumnComp(toolPanel, 'Athlete');
+        athleteComp2['onChangeCommon'](false);
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+    });
+
+    test('apply button becomes enabled when a row group pill sort direction is changed', async () => {
+        const { toolPanel, toolPanelGui } = await createDeferredPivotModeGrid();
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+
+        // Click the Country pill in the row group drop zone to change sort direction
+        const countryPill = Array.from(
+            toolPanel.rowGroupDropZonePanel.getGui().querySelectorAll<HTMLElement>('.ag-column-drop-cell')
+        ).find((el) => el.textContent?.includes('Country'));
+        expect(countryPill).toBeTruthy();
+        countryPill!.click();
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(false);
+    });
+
+    test('apply button becomes enabled when a value column aggregation function is changed', async () => {
+        const { gridApi, toolPanel, toolPanelGui } = await createDeferredPivotModeGrid();
+        const silver = gridApi.getColumn('silver')! as AgColumn;
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(true);
+
+        getUpdateStrategy(toolPanel).setColumnAggFunc(true, silver, 'avg', 'toolPanelDragAndDrop');
+        toolPanel.refreshDeferredUi();
+
+        expect(getApplyButton(toolPanelGui).disabled).toBe(false);
+    });
 });
