diff --git a/src/atlasComponents/annotations/annotation.service.ts b/src/atlasComponents/annotations/annotation.service.ts
index fd67601ee087cf32ddd230d7e42b3ca9fdaea421..3969bc420cdea13c69fef90bbc765de4b047e0de 100644
--- a/src/atlasComponents/annotations/annotation.service.ts
+++ b/src/atlasComponents/annotations/annotation.service.ts
@@ -112,12 +112,12 @@ export class AnnotationLayer {
     this.nglayer && this.nglayer.setVisible(flag)
   }
   dispose() {
-    this.nglayer = null
     AnnotationLayer.Map.delete(this.name)
     this._onHover.complete()
     while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
     try {
       this.viewer.layerManager.removeManagedLayer(this.nglayer)
+      this.nglayer = null
     // eslint-disable-next-line no-empty
     } catch (e) {
 
diff --git a/src/atlasComponents/sapi/core/base.ts b/src/atlasComponents/sapi/core/base.ts
index cb7544f04111082bcf5cb845c8bfccb7b1bd1c9d..b5fbcaefc04fc33512ae9ebb4d50cd42fdef6784 100644
--- a/src/atlasComponents/sapi/core/base.ts
+++ b/src/atlasComponents/sapi/core/base.ts
@@ -4,7 +4,7 @@ import { RouteParam } from "../typeV3"
 
 const AllFeatures = {
   CorticalProfile: "CorticalProfile",
-  // EbrainsDataFeature: "EbrainsDataFeature",
+  EbrainsDataFeature: "EbrainsDataFeature",
   RegionalConnectivity: "RegionalConnectivity",
   Tabular: "Tabular",
   // GeneExpressions: "GeneExpressions",
diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts
index 942859c6fb33390c8c36585d4b7869181f300cbc..0c5a2d0baf73fa0ba4d6e499abdbe4e3930eeb13 100644
--- a/src/atlasComponents/sapi/core/sapiRegion.ts
+++ b/src/atlasComponents/sapi/core/sapiRegion.ts
@@ -12,7 +12,7 @@ import { SxplrRegion } from "../sxplrTypes";
 const RegionFeatures = {
   // ReceptorDensityFingerprint: "ReceptorDensityFingerprint",
   // GeneExpressions: "GeneExpressions",
-  // EbrainsDataFeature: "EbrainsDataFeature",
+  EbrainsDataFeature: "EbrainsDataFeature",
   
   // ReceptorDensityProfile: "ReceptorDensityProfile",
   // BigBrainIntensityProfile: "BigBrainIntensityProfile",
diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts
index 14b6ccf20fa72d37c1d0d9d237e932b25bdf996b..78aa3e006d082b8ba95dd50716a438dd98026c0c 100644
--- a/src/atlasComponents/sapi/schemaV3.ts
+++ b/src/atlasComponents/sapi/schemaV3.ts
@@ -111,8 +111,16 @@ export interface paths {
     get: operations["get_all_gene_feature_GeneExpressions_get"]
   }
   "/feature/GeneExpressions/{feature_id}": {
-    /** Get All Gene */
-    get: operations["get_all_gene_feature_GeneExpressions__feature_id__get"]
+    /** Get Single Gene */
+    get: operations["get_single_gene_feature_GeneExpressions__feature_id__get"]
+  }
+  "/feature/EbrainsDataFeature": {
+    /** Get All Ebrains Df */
+    get: operations["get_all_ebrains_df_feature_EbrainsDataFeature_get"]
+  }
+  "/feature/EbrainsDataFeature/{feature_id}": {
+    /** Get Single Ebrains Df */
+    get: operations["get_single_ebrains_df_feature_EbrainsDataFeature__feature_id__get"]
   }
   "/feature/{feature_id}": {
     /**
@@ -753,6 +761,17 @@ export interface components {
       /** Size */
       size: number
     }
+    /** Page[SiibraEbrainsDataFeatureModel] */
+    Page_SiibraEbrainsDataFeatureModel_: {
+      /** Items */
+      items: (components["schemas"]["SiibraEbrainsDataFeatureModel"])[]
+      /** Total */
+      total: number
+      /** Page */
+      page: number
+      /** Size */
+      size: number
+    }
     /** Page[SiibraParcellationModel] */
     Page_SiibraParcellationModel_: {
       /** Items */
@@ -1011,6 +1030,24 @@ export interface components {
       /** Boundaries Mapped */
       boundaries_mapped: boolean
     }
+    /** SiibraEbrainsDataFeatureModel */
+    SiibraEbrainsDataFeatureModel: {
+      /** @Type */
+      "@type": string
+      /** Id */
+      id: string
+      /** Modality */
+      modality: string
+      /** Category */
+      category: string
+      /** Description */
+      description: string
+      /** Name */
+      name: string
+      /** Datasets */
+      datasets: (components["schemas"]["EbrainsDatasetModel"])[]
+      anchor?: components["schemas"]["SiibraAnchorModel"]
+    }
     /** SiibraParcellationModel */
     SiibraParcellationModel: {
       /** @Type */
@@ -1820,8 +1857,8 @@ export interface operations {
       }
     }
   }
-  get_all_gene_feature_GeneExpressions__feature_id__get: {
-    /** Get All Gene */
+  get_single_gene_feature_GeneExpressions__feature_id__get: {
+    /** Get Single Gene */
     parameters: {
       query: {
         parcellation_id: string
@@ -1847,6 +1884,57 @@ export interface operations {
       }
     }
   }
+  get_all_ebrains_df_feature_EbrainsDataFeature_get: {
+    /** Get All Ebrains Df */
+    parameters: {
+      query: {
+        parcellation_id: string
+        region_id: string
+        page?: number
+        size?: number
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["Page_SiibraEbrainsDataFeatureModel_"]
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  get_single_ebrains_df_feature_EbrainsDataFeature__feature_id__get: {
+    /** Get Single Ebrains Df */
+    parameters: {
+      query: {
+        parcellation_id: string
+        region_id: string
+      }
+      path: {
+        feature_id: string
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["SiibraEbrainsDataFeatureModel"]
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
   get_single_feature_feature__feature_id__get: {
     /**
      * Get Single Feature 
@@ -1864,7 +1952,7 @@ export interface operations {
       /** @description Successful Response */
       200: {
         content: {
-          "application/json": components["schemas"]["SiibraVoiModel"] | components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraRegionalConnectivityModel"] | components["schemas"]["SiibraReceptorDensityFp"] | components["schemas"]["SiibraTabularModel"]
+          "application/json": components["schemas"]["SiibraVoiModel"] | components["schemas"]["SiibraCorticalProfileModel"] | components["schemas"]["SiibraRegionalConnectivityModel"] | components["schemas"]["SiibraReceptorDensityFp"] | components["schemas"]["SiibraTabularModel"] | components["schemas"]["SiibraEbrainsDataFeatureModel"]
         }
       }
       /** @description Validation Error */
diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts
index 00135556626cd73ec440a2f48c559bb51d535d65..4abda4e69f64cc3476ea7f77a562bc19d6c3f709 100644
--- a/src/atlasComponents/sapi/translateV3.ts
+++ b/src/atlasComponents/sapi/translateV3.ts
@@ -120,8 +120,8 @@ class TranslateV3 {
       }
       const url = input[key]
       const [ transform, info ] = await Promise.all([
-        fetch(`${url}/transform.json`).then(res => res.json()) as Promise<number[][]>,
-        fetch(`${url}/info`).then(res => res.json()) as Promise<Record<string, any>>,
+        this.cFetch(`${url}/transform.json`).then(res => res.json()) as Promise<number[][]>,
+        this.cFetch(`${url}/info`).then(res => res.json()) as Promise<Record<string, any>>,
       ])
       returnObj[key] = {
         url: input[key],
diff --git a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
index fc5ba9f3f62e5a2c8923a4992b49950fd082af87..72d0e2e66fc7ff0b8da0d520a6ec4438432a88df 100644
--- a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
+++ b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
@@ -1,9 +1,12 @@
-import { Directive, Input, OnChanges, SimpleChanges } from "@angular/core";
+import { Directive, Input, OnChanges } from "@angular/core";
 import { BehaviorSubject, Observable } from "rxjs";
 import { distinctUntilChanged } from "rxjs/operators";
 import { BoundingBox, SxplrTemplate, SxplrAtlas } from "src/atlasComponents/sapi/sxplrTypes"
 
-function validateBbox(input: any): boolean {
+type Point = [number, number, number]
+type BBox = [Point, Point]
+
+function validateBbox(input: any): input is BoundingBox {
   if (!Array.isArray(input)) return false
   if (input.length !== 2) return false
   return input.every(el => Array.isArray(el) && el.length === 3 && el.every(val => typeof val === "number"))
@@ -21,9 +24,9 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
   @Input('sxplr-sapiviews-core-space-boundingbox-space')
   space: SxplrTemplate
 
-  private _bbox: BoundingBox
+  private _bbox: BBox
   @Input('sxplr-sapiviews-core-space-boundingbox-spec')
-  set bbox(val: string | BoundingBox ) {
+  set bbox(val: string | BBox ) {
 
     if (typeof val === "string") {
       try {
@@ -31,12 +34,7 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
           [number, number, number],
           [number, number, number],
         ] = JSON.parse(val)
-        this._bbox = {
-          minpoint: min,
-          maxpoint: max,
-          center: min.map((v, idx) => (v + max[idx]) / 2) as [number, number, number],
-          space: this.space
-        }
+        this._bbox = [min, max]
       } catch (e) {
         console.warn(`Parse bbox input error`)
       }
@@ -48,14 +46,14 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
     }
     this._bbox = val
   }
-  get bbox(): BoundingBox {
+  get bbox(): BBox {
     return this._bbox
   }
 
   private _bbox$: BehaviorSubject<{
     atlas: SxplrAtlas
     space: SxplrTemplate
-    bbox: BoundingBox
+    bbox: BBox
   }> = new BehaviorSubject({
     atlas: null,
     space: null,
@@ -65,11 +63,11 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
   public bbox$: Observable<{
     atlas: SxplrAtlas
     space: SxplrTemplate
-    bbox: BoundingBox
+    bbox: BBox
   }> = this._bbox$.asObservable().pipe(
     distinctUntilChanged(
-      (prev, curr) => prev.atlas?.["@id"] === curr.atlas?.['@id']
-        && prev.space?.["@id"] === curr.space?.["@id"]
+      (prev, curr) => prev.atlas?.id === curr.atlas?.id
+        && prev.space?.id === curr.space?.id
         && JSON.stringify(prev.bbox) === JSON.stringify(curr.bbox)
     )
   )
diff --git a/src/features/base.ts b/src/features/base.ts
index cda6bb56ed795abbfaecf68dcdf23e9644c2297d..c5085aabd44abacf2c9e198703b60e0d88df088f 100644
--- a/src/features/base.ts
+++ b/src/features/base.ts
@@ -1,7 +1,10 @@
-import { Input, OnChanges, Directive } from "@angular/core";
-import { BehaviorSubject } from "rxjs";
+import { Input, OnChanges, Directive, SimpleChanges } from "@angular/core";
+import { BehaviorSubject, combineLatest } from "rxjs";
+import { debounceTime, map } from "rxjs/operators";
 import { SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 
+type BBox = [[number, number, number], [number, number, number]]
+
 @Directive()
 export class FeatureBase implements OnChanges{
     
@@ -14,14 +17,36 @@ export class FeatureBase implements OnChanges{
   @Input()
   region: SxplrRegion
 
+  @Input()
+  bbox: BBox
+
   @Input()
   queryParams: Record<string, string> = {}
   
-  protected TPR$ = new BehaviorSubject<{ template?: SxplrTemplate, parcellation?: SxplrParcellation, region?: SxplrRegion }>({ template: null, parcellation: null, region: null })
-
-  ngOnChanges(): void {
-    const { template, parcellation, region } = this
-    this.TPR$.next({ template, parcellation, region })
+  #TPR$ = new BehaviorSubject<{ template?: SxplrTemplate, parcellation?: SxplrParcellation, region?: SxplrRegion }>({ template: null, parcellation: null, region: null })
+  #bbox$ = new BehaviorSubject<{ bbox?: BBox }>({ bbox: null })
+  protected TPRBbox$ = combineLatest([
+    this.#TPR$,
+    this.#bbox$.pipe(
+      debounceTime(500)
+    )
+  ]).pipe(
+    map(([ v1, v2 ]) => ({ ...v1, ...v2 }))
+  )
+
+  ngOnChanges(sc: SimpleChanges): void {
+    const { template, parcellation, region, bbox } = sc
+    if (bbox) {
+      this.#bbox$.next({ bbox: bbox.currentValue })
+    }
+    if (template || parcellation || region) {
+      const { template: t, parcellation: p, region: r } = this
+      this.#TPR$.next({
+        template: template?.currentValue || t,
+        parcellation: parcellation?.currentValue || p,
+        region: region?.currentValue || r
+      })
+    }
   }
 }
 
@@ -29,7 +54,7 @@ export class FeatureBase implements OnChanges{
 
 export const AllFeatures = {
   CorticalProfile: "CorticalProfile",
-  // EbrainsDataFeature: "EbrainsDataFeature",
+  EbrainsDataFeature: "EbrainsDataFeature",
   RegionalConnectivity: "RegionalConnectivity",
   Tabular: "Tabular",
   // GeneExpressions: "GeneExpressions",
diff --git a/src/features/category-acc.directive.ts b/src/features/category-acc.directive.ts
index d578d36dabcfa260822a9208e4e276637f32f117..661896191ba70576e8354970754efee6562aab6a 100644
--- a/src/features/category-acc.directive.ts
+++ b/src/features/category-acc.directive.ts
@@ -2,6 +2,7 @@ import { AfterContentInit, ContentChildren, Directive, OnDestroy, QueryList } fr
 import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { ListComponent } from './list/list.component';
+import { Feature } from "src/atlasComponents/sapi/sxplrTypes"
 
 @Directive({
   selector: '[sxplrCategoryAcc]',
@@ -11,6 +12,7 @@ export class CategoryAccDirective implements AfterContentInit, OnDestroy {
 
   public isBusy$ = new BehaviorSubject<boolean>(false)
   public total$ = new BehaviorSubject<number>(0)
+  public features$ = new BehaviorSubject<Feature[]>([])
 
   @ContentChildren(ListComponent, { read: ListComponent, descendants: true })
   listCmps: QueryList<ListComponent>
@@ -35,15 +37,19 @@ export class CategoryAccDirective implements AfterContentInit, OnDestroy {
 
     const listCmp = Array.from(this.listCmps)
 
-    this.#subscriptions.push(  
+    this.#subscriptions.push( 
       combineLatest(
-        listCmp.map(listCmp => listCmp.features$)
+        listCmp.map(listC => listC.features$)
+      ).subscribe(features => this.features$.next(features.flatMap(f => f))),
+      
+      combineLatest(
+        listCmp.map(listC => listC.features$)
       ).pipe(
         map(features => features.reduce((acc, curr) => acc + curr.length, 0))
       ).subscribe(total => this.total$.next(total)),
 
       combineLatest(
-        listCmp.map(listCmp => listCmp.state$)
+        listCmp.map(listC => listC.state$)
       ).pipe(
         map(states => states.some(state => state === "busy"))
       ).subscribe(flag => this.isBusy$.next(flag))
diff --git a/src/features/entry/entry.component.html b/src/features/entry/entry.component.html
index 899e7a4d061a29ca3296985f07d950cda66f5e86..ad474847c41c6ccd4a3c7d3c390b2855f9f01152 100644
--- a/src/features/entry/entry.component.html
+++ b/src/features/entry/entry.component.html
@@ -47,6 +47,7 @@
                             [template]="template"
                             [parcellation]="parcellation"
                             [region]="region"
+                            [bbox]="bbox"
                             [queryParams]="queryParams | mergeObj : { type: (feature.name | featureNamePipe) }"
                             [featureRoute]="feature.path"
                             (onClickFeature)="onClickFeature($event)"
diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts
index ad314cb5befeaefca8b22d729589e338e2d12a65..29a8f2907d2ee3a56743888dbe8d3a9b64b60018 100644
--- a/src/features/entry/entry.component.ts
+++ b/src/features/entry/entry.component.ts
@@ -1,11 +1,13 @@
-import { Component } from '@angular/core';
+import { AfterViewInit, Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
 import { Store } from '@ngrx/store';
-import { map, switchMap, tap } from 'rxjs/operators';
+import { map, scan, switchMap } from 'rxjs/operators';
 import { SAPI } from 'src/atlasComponents/sapi';
 import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
 import { FeatureBase } from '../base';
 import * as userInteraction from "src/state/userInteraction"
 import { atlasSelection } from 'src/state';
+import { CategoryAccDirective } from "../category-acc.directive"
+import { BehaviorSubject, combineLatest, merge, of, Subscription } from 'rxjs';
 
 const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
   const returnVal: Record<string, T[]> = {}
@@ -23,15 +25,61 @@ const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
 
 @Component({
   selector: 'sxplr-feature-entry',
-  templateUrl: './entry.component.html',
-  styleUrls: ['./entry.component.scss']
+  templateUrl: './entry.nestedExpPanel.component.html',
+  styleUrls: ['./entry.nestedExpPanel.component.scss'],
+  exportAs: 'featureEntryCmp'
 })
-export class EntryComponent extends FeatureBase {
+export class EntryComponent extends FeatureBase implements AfterViewInit, OnDestroy {
+
+  @ViewChildren(CategoryAccDirective)
+  catAccDirs: QueryList<CategoryAccDirective>
+
+  public totals$ = new BehaviorSubject<number>(null)
+  public features$ = new BehaviorSubject<Feature[]>([])
 
   constructor(private sapi: SAPI, private store: Store) {
     super()
   }
 
+  #subscriptions: Subscription[] = []
+
+  ngOnDestroy(): void {
+    while (this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
+  }
+  ngAfterViewInit(): void {
+    const catAccDirs$ = merge(
+      of(null),
+      this.catAccDirs.changes
+    ).pipe(
+      map(() => Array.from(this.catAccDirs))
+    )
+    this.#subscriptions.push(
+      catAccDirs$.pipe(
+        switchMap(catArrDirs => merge(
+          ...catArrDirs.map((dir, idx) => dir.total$.pipe(
+            map(val => ({ idx, val }))
+          ))
+        )),
+        
+        map(({ idx, val }) => ({ [idx.toString()]: val })),
+        scan((acc, curr) => ({ ...acc, ...curr })),
+        map(record => {
+          let tally = 0
+          for (const idx in record) {
+            tally += record[idx]
+          }
+          return tally
+        })
+      ).subscribe(num => this.totals$.next(num)),
+      catAccDirs$.pipe(
+        switchMap(catArrDirs => combineLatest(
+          catArrDirs.map(dir => dir.features$)
+        )),
+        map(features => features.flatMap(f => f))
+      ).subscribe(features => this.features$.next(features))
+    )
+  }
+
   public atlas = this.store.select(atlasSelection.selectors.selectedAtlas)
 
   private featureTypes$ = this.sapi.v3Get("/feature/_types", {}).pipe(
@@ -46,7 +94,7 @@ export class EntryComponent extends FeatureBase {
     ),
   )
 
-  public cateogryCollections$ = this.TPR$.pipe(
+  public cateogryCollections$ = this.TPRBbox$.pipe(
     switchMap(({ template, parcellation, region }) => this.featureTypes$.pipe(
       map(features => {
         const filteredFeatures = features.filter(v => {
diff --git a/src/features/entry/entry.nestedExpPanel.component.html b/src/features/entry/entry.nestedExpPanel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3522c1f48fb0f7d523237bcc9ffa838685f48fb5
--- /dev/null
+++ b/src/features/entry/entry.nestedExpPanel.component.html
@@ -0,0 +1,84 @@
+<mat-accordion>
+    <mat-expansion-panel *ngFor="let keyvalue of (cateogryCollections$ | async | keyvalue | isConnectivity : false)"
+        sxplrCategoryAcc
+        #categoryAcc="categoryAcc"
+        [ngClass]="{
+            'sxplr-d-none': !(categoryAcc.isBusy$ | async) && (categoryAcc.total$ | async) === 0
+        }">
+
+        <mat-expansion-panel-header>
+
+            <mat-panel-title>
+                {{ keyvalue.key }}
+            </mat-panel-title>
+            
+            <mat-panel-description>
+                <spinner-cmp *ngIf="categoryAcc.isBusy$ | async"></spinner-cmp>
+                <ng-template [ngIf]="categoryAcc.total$ | async" let-total>
+                    <span>
+                        {{ total }}
+                    </span>
+                </ng-template>
+            </mat-panel-description>
+        </mat-expansion-panel-header>
+
+        <mat-accordion>
+            <mat-expansion-panel class="mat-elevation-z4"
+                *ngFor="let feature of keyvalue.value"
+                [ngClass]="{
+                    'sxplr-d-none': (list.state$ | async) === 'noresult'
+                }">
+
+                <mat-expansion-panel-header>
+                    <mat-panel-title>
+                        <span class="sxplr-white-space-nowrap">
+                            {{ feature.name | featureNamePipe }}
+                        </span>
+                    </mat-panel-title>
+                </mat-expansion-panel-header>
+                
+                <spinner-cmp *ngIf="(list.state$ | async) === 'busy'"></spinner-cmp>
+                <sxplr-feature-list
+                    [template]="template"
+                    [parcellation]="parcellation"
+                    [region]="region"
+                    [bbox]="bbox"
+                    [queryParams]="queryParams | mergeObj : { type: (feature.name | featureNamePipe) }"
+                    [featureRoute]="feature.path"
+                    (onClickFeature)="onClickFeature($event)"
+                    #list="featureList"
+                    >
+                </sxplr-feature-list>
+
+            </mat-expansion-panel>
+        </mat-accordion>
+
+    </mat-expansion-panel>
+    
+
+    <ng-template [ngIf]="cateogryCollections$ | async | keyvalue | isConnectivity : true" let-connectivity>
+        <ng-template ngFor [ngForOf]="connectivity" let-conn>
+            <mat-expansion-panel sxplr-sapiviews-features-connectivity-check
+                #connectivityAccordion
+                *ngIf="conn">
+                <mat-expansion-panel-header>
+                    <mat-panel-title>
+                        {{ conn.key }}
+                    </mat-panel-title>
+                </mat-expansion-panel-header>
+
+                <sxplr-features-connectivity-browser class="pe-all flex-shrink-1" 
+                    [region]="region"
+                    [sxplr-features-connectivity-browser-atlas]="atlas | async"
+                    [sxplr-features-connectivity-browser-template]="template"
+                    [sxplr-features-connectivity-browser-parcellation]="parcellation"
+                    [accordionExpanded]="connectivityAccordion.expanded"
+                    [types]="conn.value">
+                </sxplr-features-connectivity-browser>
+
+            </mat-expansion-panel>
+        </ng-template>
+    </ng-template>
+
+
+</mat-accordion>
diff --git a/src/features/entry/entry.nestedExpPanel.component.scss b/src/features/entry/entry.nestedExpPanel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..68a9f6d331b11a5b9723e3996f827c8240c1bae5
--- /dev/null
+++ b/src/features/entry/entry.nestedExpPanel.component.scss
@@ -0,0 +1,10 @@
+mat-list-item
+{
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+sxplr-feature-list
+{
+    height: 10rem;
+}
diff --git a/src/features/feature-view/feature-view.component.ts b/src/features/feature-view/feature-view.component.ts
index bf6c7916ad45f2f0a821127bb41a4c0cc3f0fa83..61371830b8d5e81761b9de75b311523e8016d0b9 100644
--- a/src/features/feature-view/feature-view.component.ts
+++ b/src/features/feature-view/feature-view.component.ts
@@ -4,15 +4,7 @@ import { filter, map } from 'rxjs/operators';
 import { SAPI } from 'src/atlasComponents/sapi/sapi.service';
 import { Feature, TabularFeature, VoiFeature } from 'src/atlasComponents/sapi/sxplrTypes';
 import { DARKTHEME } from 'src/util/injectionTokens';
-
-function isTabularData(feature: unknown): feature is TabularFeature<number|string|number[]> {
-  return !!feature['index'] && !!feature['columns']
-}
-
-function isVoiData(feature: unknown): feature is VoiFeature {
-  return !!feature['bbox']
-}
-
+import { isTabularData, isVoiData } from "../guards"
 
 type PolarPlotData = {
   receptor: {
diff --git a/src/features/guards.ts b/src/features/guards.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b258c66e7fa67ccc5f008eef071e1c215fc161d4
--- /dev/null
+++ b/src/features/guards.ts
@@ -0,0 +1,9 @@
+import { TabularFeature, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes"
+
+export function isTabularData(feature: unknown): feature is TabularFeature<number|string|number[]> {
+  return !!feature['index'] && !!feature['columns']
+}
+
+export function isVoiData(feature: unknown): feature is VoiFeature {
+  return !!feature['bbox']
+}
diff --git a/src/features/list/list.component.ts b/src/features/list/list.component.ts
index cc06e706efab87233eaa7f6691deb832bec1c579..52dbcc1ff85cd51e16f241687bccfdd3b449a73e 100644
--- a/src/features/list/list.component.ts
+++ b/src/features/list/list.component.ts
@@ -1,5 +1,5 @@
-import { Component, EventEmitter, Input, Output } from '@angular/core';
-import { BehaviorSubject, combineLatest, NEVER, Observable, of, throwError } from 'rxjs';
+import { Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core';
+import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
 import { catchError, switchMap, tap } from 'rxjs/operators';
 import { SAPI } from 'src/atlasComponents/sapi';
 import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
@@ -27,18 +27,21 @@ export class ListComponent extends FeatureBase {
     super()
   }
 
-  ngOnChanges(): void {
-    super.ngOnChanges()
-    const featureType = (this.featureRoute || '').split("/").slice(-1)[0]
-    this.guardedRoute$.next(AllFeatures[featureType])
+  ngOnChanges(sc: SimpleChanges): void {
+    super.ngOnChanges(sc)
+    const { featureRoute } = sc
+    if (featureRoute) {
+      const featureType = (featureRoute.currentValue || '').split("/").slice(-1)[0]
+      this.guardedRoute$.next(AllFeatures[featureType])
+    }
   }
 
   public features$: Observable<Feature[]> = combineLatest([
     this.guardedRoute$,
-    this.TPR$,
+    this.TPRBbox$,
   ]).pipe(
     tap(() => this.state$.next('busy')),
-    switchMap(([route, { template, parcellation, region }]) => {
+    switchMap(([route, { template, parcellation, region, bbox }]) => {
       if (!route) {
         return throwError("noresult")
       }
@@ -46,6 +49,7 @@ export class ListComponent extends FeatureBase {
       if (template) query['space_id'] = template.id
       if (parcellation) query['parcellation_id'] = parcellation.id
       if (region) query['region_id'] = region.name
+      if (bbox) query['bbox'] = JSON.stringify(bbox)
       return this.sapi.getV3Features(route, {
         query: {
           ...this.queryParams,
diff --git a/src/features/module.ts b/src/features/module.ts
index dc9843c177063c1c7f7cc0c67a7ab94404acdc2d..37712be5079fa5b1351cc0d1029efb9f0adb0bae 100644
--- a/src/features/module.ts
+++ b/src/features/module.ts
@@ -22,6 +22,7 @@ import { MatTableModule } from "@angular/material/table";
 import { FeatureViewComponent } from "./feature-view/feature-view.component";
 import { TransformPdToDsPipe } from "./transform-pd-to-ds.pipe";
 import { NgLayerCtlModule } from "src/viewerModule/nehuba/ngLayerCtlModule/module";
+import { VoiBboxDirective } from "./voi-bbox.directive";
 
 @NgModule({
   imports: [
@@ -49,6 +50,7 @@ import { NgLayerCtlModule } from "src/viewerModule/nehuba/ngLayerCtlModule/modul
 
     FetchDirective,
     CategoryAccDirective,
+    VoiBboxDirective,
 
     FeatureNamePipe,
     TransformPdToDsPipe,
@@ -56,6 +58,7 @@ import { NgLayerCtlModule } from "src/viewerModule/nehuba/ngLayerCtlModule/modul
   exports: [
     EntryComponent,
     FeatureViewComponent,
+    VoiBboxDirective,
   ],
   schemas: [
     CUSTOM_ELEMENTS_SCHEMA,
diff --git a/src/features/voi-bbox.directive.ts b/src/features/voi-bbox.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f9b615613090ffc95adf0e4ea01fe17ddc0c79b
--- /dev/null
+++ b/src/features/voi-bbox.directive.ts
@@ -0,0 +1,155 @@
+import { Directive, Inject, Input, OnDestroy, Optional } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { concat, interval, of, Subject, Subscription } from "rxjs";
+import { debounce, distinctUntilChanged, filter, pairwise, take } from "rxjs/operators";
+import { AnnotationLayer, TNgAnnotationAABBox, TNgAnnotationPoint } from "src/atlasComponents/annotations";
+import { Feature, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes";
+import { userInteraction } from "src/state";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
+import { arrayEqual } from "src/util/array";
+import { isVoiData } from "./guards"
+
+@Directive({
+  selector: '[voiBbox]',
+})
+export class VoiBboxDirective implements OnDestroy {
+  
+  #onDestroyCb: (() => void)[] = []
+
+  static VOI_LAYER_NAME = 'voi-annotation-layer'
+  static VOI_ANNOTATION_COLOR = "#ffff00"
+
+  #voiSubs: Subscription[] = []
+  private _voiBBoxSvc: AnnotationLayer
+  get voiBBoxSvc(): AnnotationLayer {
+    if (this._voiBBoxSvc) return this._voiBBoxSvc
+    try {
+      const layer = AnnotationLayer.Get(
+        VoiBboxDirective.VOI_LAYER_NAME,
+        VoiBboxDirective.VOI_ANNOTATION_COLOR
+      )
+      this._voiBBoxSvc = layer
+      this.#voiSubs.push(
+        layer.onHover.subscribe(val => this.handleOnHoverFeature(val || {}))
+      )
+      this.#onDestroyCb.push(() => {
+        this._voiBBoxSvc.dispose()
+        this._voiBBoxSvc = null
+      })
+      return layer
+    } catch (e) {
+      return null
+    }
+  }
+  #annotationIdToFeature = new Map<string, VoiFeature>()
+  #features$ = new Subject<VoiFeature[]>()
+  #voiFeatures: VoiFeature[] = []
+  
+  @Input()
+  set features(feats: Feature[]){
+    this.#voiFeatures = feats.filter(isVoiData)
+    this.#features$.next(this.#voiFeatures)
+  }
+  get features(): VoiFeature[]{
+    return this.#voiFeatures
+  }
+
+  ngOnDestroy(): void {
+    while (this.#onDestroyCb.length > 0) this.#onDestroyCb.pop()()
+
+  }
+
+  constructor(
+    private store: Store,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+  ){
+    if (clickInterceptor) {
+      const { register, deregister } = clickInterceptor
+      const handleClick = this.handleClick.bind(this)
+      register(handleClick)
+      this.#onDestroyCb.push(() => deregister(handleClick))
+    }
+
+    const sub = concat(
+      of([] as VoiFeature[]),
+      this.#features$
+    ).pipe(
+      distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
+      pairwise(),
+      debounce(() => 
+        interval(16).pipe(
+          filter(() => !!this.voiBBoxSvc),
+          take(1),
+        )
+      ),
+    ).subscribe(([ prev, curr ]) => {
+      for (const v of prev) {
+        const box = this.#pointsToAABB(v.bbox.maxpoint, v.bbox.minpoint)
+        const point = this.#pointToPoint(v.bbox.center)
+        this.#annotationIdToFeature.delete(box.id)
+        this.#annotationIdToFeature.delete(point.id)
+        if (!this.voiBBoxSvc) continue
+        for (const ann of [box, point]) {
+          this.voiBBoxSvc.removeAnnotation({
+            id: ann.id
+          })
+        }
+      }
+      for (const v of curr) {
+        const box = this.#pointsToAABB(v.bbox.maxpoint, v.bbox.minpoint)
+        const point = this.#pointToPoint(v.bbox.center)
+        this.#annotationIdToFeature.set(box.id, v)
+        this.#annotationIdToFeature.set(point.id, v)
+        if (!this.voiBBoxSvc) {
+          throw new Error(`annotation is expected to be added, but annotation layer cannot be instantiated.`)
+        }
+        for (const ann of [box, point]) {
+          this.voiBBoxSvc.updateAnnotation(ann)
+        }
+      }
+      if (this.voiBBoxSvc) this.voiBBoxSvc.setVisible(true)
+    })
+
+    this.#onDestroyCb.push(() => sub.unsubscribe())
+    this.#onDestroyCb.push(() => this.store.dispatch(
+      userInteraction.actions.setMouseoverVoi({ feature: null })
+    ))
+  }
+
+  handleClick(){
+    if (this.#hoveredFeat) {
+      this.store.dispatch(
+        userInteraction.actions.showFeature({
+          feature: this.#hoveredFeat
+        })
+      )
+      return true
+    }
+  }
+
+  #hoveredFeat: VoiFeature
+  handleOnHoverFeature(ann: { id?: string }){
+    const { id } = ann || {}
+    const feature = this.#annotationIdToFeature.get(id)
+    this.#hoveredFeat = feature
+    this.store.dispatch(
+      userInteraction.actions.setMouseoverVoi({ feature })
+    )
+  }
+
+  #pointsToAABB(pointA: [number, number, number], pointB: [number, number, number]): TNgAnnotationAABBox{
+    return {
+      id: `${VoiBboxDirective.VOI_LAYER_NAME}:${JSON.stringify(pointA)}:${JSON.stringify(pointB)}`,
+      type: "aabbox",
+      pointA: pointA.map(v => v*1e6) as [number, number, number],
+      pointB: pointB.map(v => v*1e6) as [number, number, number],
+    }
+  }
+  #pointToPoint(point: [number, number, number]): TNgAnnotationPoint{
+    return {
+      id: `${VoiBboxDirective.VOI_LAYER_NAME}:${JSON.stringify(point)}`,
+      point: point.map(v => v*1e6) as [number, number, number],
+      type: "point"
+    }
+  }
+}
diff --git a/src/mouseoverModule/mouseOverCvt.pipe.ts b/src/mouseoverModule/mouseOverCvt.pipe.ts
index 05be3ae63bda1d1c48d6a55ae9acf1ba927fe888..35a5ca53cce95853ba60d04281f89a728e465572 100644
--- a/src/mouseoverModule/mouseOverCvt.pipe.ts
+++ b/src/mouseoverModule/mouseOverCvt.pipe.ts
@@ -15,24 +15,15 @@ function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){
       }
     })
   }
-  case 'landmark': {
+  case 'voi':
+    const { name } = value as TOnHoverObj['voi']
     return [{
       icon: {
         fontSet: 'fas',
-        fontIcon: 'fa-map-marker-alt',
+        fontIcon: 'fa-database'
       },
-      text: (value as TOnHoverObj['landmark']).landmarkName
+      text: name
     }]
-  }
-  case 'userLandmark': {
-    return [{
-      icon: {
-        fontSet: 'fas',
-        fontIcon: 'fa-map-marker-alt',
-      },
-      text: value as TOnHoverObj['userLandmark']
-    }]
-  }
   case 'annotation': {
     const { annotationType, name } = (value as TOnHoverObj['annotation'])
     let fontIcon: string
diff --git a/src/mouseoverModule/mouseover.directive.ts b/src/mouseoverModule/mouseover.directive.ts
index 9e005530107e7653a559fe108c1b4891f9bc7847..fc4beeb88c4eb39e9dbc2a810045d5a21c0abde1 100644
--- a/src/mouseoverModule/mouseover.directive.ts
+++ b/src/mouseoverModule/mouseover.directive.ts
@@ -1,11 +1,11 @@
 import { Directive } from "@angular/core"
 import { select, Store } from "@ngrx/store"
-import { merge, NEVER, Observable, of } from "rxjs"
-import { distinctUntilChanged, map, scan, shareReplay } from "rxjs/operators"
-import { LoggingService } from "src/logging"
+import { merge, Observable } from "rxjs"
+import { distinctUntilChanged, map, scan } from "rxjs/operators"
 import { TOnHoverObj, temporalPositveScanFn } from "./util"
 import { ModularUserAnnotationToolService } from "src/atlasComponents/userAnnotations/tools/service";
 import { userInteraction } from "src/state"
+import { arrayEqual } from "src/util/array"
 
 @Directive({
   selector: '[iav-mouse-hover]',
@@ -14,111 +14,50 @@ import { userInteraction } from "src/state"
 
 export class MouseHoverDirective {
 
-  public currentOnHoverObs$: Observable<TOnHoverObj>
-
-  constructor(
-    private store$: Store<any>,
-    private log: LoggingService,
-    private annotSvc: ModularUserAnnotationToolService,
-  ) {
-
-    // TODO consider moving these into a single obs serviced by a DI service
-    // can potentially net better performance
-
-    const onHoverUserLandmark$ = NEVER
-    // this.store$.pipe(
-    //   select(uiStateMouseoverUserLandmark)
-    // )
-
-    const onHoverLandmark$ = NEVER
-    // this.store$.pipe(
-    //   select(uiStateMouseOverLandmarkSelector)
-    // ).pipe(
-    //   map(landmark => {
-    //     if (landmark === null) { return null }
-    //     const idx = Number(landmark.replace('label=', ''))
-    //     if (isNaN(idx)) {
-    //       this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`)
-    //       return {
-    //         landmarkName: idx,
-    //       }
-    //     } 
-    //   }),
-    // )
-
-    const onHoverSegments$ = this.store$.pipe(
+  public currentOnHoverObs$: Observable<TOnHoverObj> = merge(
+    this.store$.pipe(
       select(userInteraction.selectors.mousingOverRegions),
-
-      // TODO fix aux mesh filtering
-
-      // withLatestFrom(
-      //   this.store$.pipe(
-      //     select(viewerStateSelectedParcellationSelector),
-      //     startWith(null as any),
-      //   ),
-      // ),
-      // map(([ arr, parcellationSelected ]) => parcellationSelected && parcellationSelected.auxillaryMeshIndices
-      //   ? arr.filter(({ segment }) => {
-      //     // if segment is not a string (i.e., not labelIndexId) return true
-      //     if (typeof segment !== 'string') { return true }
-      //     const { label: labelIndex } = deserializeSegment(segment)
-      //     return parcellationSelected.auxillaryMeshIndices.indexOf(labelIndex) < 0
-      //   })
-      //   : arr),
-    )
-
-    const onHoverAnnotation$ = this.annotSvc.hoveringAnnotations$
-
-    const mergeObs = merge(
-      onHoverSegments$.pipe(
-        distinctUntilChanged(),
-        map(regions => {
-          return { regions }
-        }),
-      ),
-      onHoverAnnotation$.pipe(
-        distinctUntilChanged(),
-        map(annotation => {
-          return { annotation }
-        }),
-      ),
-      onHoverLandmark$.pipe(
-        distinctUntilChanged(),
-        map(landmark => {
-          return { landmark }
-        }),
-      ),
-      onHoverUserLandmark$.pipe(
-        distinctUntilChanged(),
-        map(userLandmark => {
-          return { userLandmark }
-        }),
-      ),
     ).pipe(
-      shareReplay(1),
+      distinctUntilChanged(arrayEqual((o, n) => o.id === n.name)),
+      map(regions => {
+        return { regions }
+      }),
+    ),
+    this.annotSvc.hoveringAnnotations$.pipe(
+      distinctUntilChanged(),
+      map(annotation => {
+        return { annotation }
+      }),
+    ),
+    this.store$.pipe(
+      select(userInteraction.selectors.mousingOverVoiFeature),
+      distinctUntilChanged((o, n) => o?.id === n?.id),
+      map(voi => ({ voi }))
     )
-
-    this.currentOnHoverObs$ = mergeObs.pipe(
-      scan(temporalPositveScanFn, []),
-      map(arr => {
-
-        let returnObj = {
-          regions: null,
-          annotation: null,
-          landmark: null,
-          userLandmark: null,
+  ).pipe(
+    scan(temporalPositveScanFn, []),
+    map(arr => {
+
+      let returnObj: TOnHoverObj = {
+        regions: null,
+        annotation: null,
+        voi: null
+      }
+
+      for (const val of arr) {
+        returnObj = {
+          ...returnObj,
+          ...val
         }
+      }
 
-        for (const val of arr) {
-          returnObj = {
-            ...returnObj,
-            ...val
-          }
-        }
+      return returnObj
+    }),
+  )
 
-        return returnObj
-      }),
-      shareReplay(1),
-    )
+  constructor(
+    private store$: Store<any>,
+    private annotSvc: ModularUserAnnotationToolService,
+  ) {
   }
 }
diff --git a/src/mouseoverModule/util.ts b/src/mouseoverModule/util.ts
index e22552b7c770804a33c4f477056a058e164963fd..208c01ceebea6038619ee5cb781799632c14d2d7 100644
--- a/src/mouseoverModule/util.ts
+++ b/src/mouseoverModule/util.ts
@@ -1,13 +1,10 @@
-import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"
+import { SxplrRegion, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes"
 import { IAnnotationGeometry } from "src/atlasComponents/userAnnotations/tools/type"
 
 export type TOnHoverObj = {
   regions: SxplrRegion[]
   annotation: IAnnotationGeometry
-  landmark: {
-    landmarkName: number
-  }
-  userLandmark: any
+  voi: VoiFeature
 }
 
 /**
diff --git a/src/plugin/pluginBanner/pluginBanner.component.ts b/src/plugin/pluginBanner/pluginBanner.component.ts
index df682e4cfd68a3cefac4a8b9952670cdcfb49bb0..ee763c40921a60ad08b665ecbad7303ae9c6be33 100644
--- a/src/plugin/pluginBanner/pluginBanner.component.ts
+++ b/src/plugin/pluginBanner/pluginBanner.component.ts
@@ -1,10 +1,11 @@
 import { Component, TemplateRef } from "@angular/core";
 import { MatDialog } from "@angular/material/dialog";
-import { environment } from 'src/environments/environment';
 import { PluginService } from "../service";
 import { PluginManifest } from "../types";
 import { combineLatest, Observable, Subject } from "rxjs";
 import { map, scan, startWith } from "rxjs/operators";
+import { select, Store } from "@ngrx/store";
+import { userPreference } from "src/state";
 
 @Component({
   selector : 'plugin-banner',
@@ -16,9 +17,13 @@ import { map, scan, startWith } from "rxjs/operators";
 
 export class PluginBannerUI {
 
-  EXPERIMENTAL_FEATURE_FLAG = environment.EXPERIMENTAL_FEATURE_FLAG
+  experimentalFlag$ = this.store.pipe(
+    select(userPreference.selectors.showExperimental)
+  )
+  
 
   constructor(
+    private store: Store,
     private svc: PluginService,
     private matDialog: MatDialog,
   ) {
diff --git a/src/plugin/pluginBanner/pluginBanner.template.html b/src/plugin/pluginBanner/pluginBanner.template.html
index 4f9ef3ae2332b37d845eddc838c5519ce787dd2d..a45098a882a6868b92dab9a212fa4a0f83b7ce3e 100644
--- a/src/plugin/pluginBanner/pluginBanner.template.html
+++ b/src/plugin/pluginBanner/pluginBanner.template.html
@@ -7,7 +7,7 @@
     </span>
   </button>
 
-  <button mat-menu-item *ngIf="EXPERIMENTAL_FEATURE_FLAG"
+  <button mat-menu-item *ngIf="experimentalFlag$ | async"
     (click)="showTmpl(thirdPartyPluginTmpl)">
     <span>
       Add third party plugin
diff --git a/src/routerModule/router.service.ts b/src/routerModule/router.service.ts
index 37d6caec7354d1343031f1760f48f99124ec4409..dc8edb2c6158b6b3b24a13f0386e39547a298512 100644
--- a/src/routerModule/router.service.ts
+++ b/src/routerModule/router.service.ts
@@ -1,4 +1,4 @@
-import { Injectable } from "@angular/core";
+import { Injectable, NgZone } from "@angular/core";
 import { APP_BASE_HREF } from "@angular/common";
 import { Inject } from "@angular/core";
 import { NavigationEnd, Router } from '@angular/router'
@@ -40,6 +40,7 @@ export class RouterService {
     routeToStateTransformSvc: RouteStateTransformSvc,
     sapi: SAPI,
     store$: Store<any>,
+    private zone: NgZone,
     @Inject(APP_BASE_HREF) baseHref: string
   ){
 
@@ -245,7 +246,9 @@ export class RouterService {
         const newUrlUrlTree = router.parseUrl(joinedRoutes)
         
         if (currUrlUrlTree.toString() !== newUrlUrlTree.toString()) {
-          router.navigateByUrl(joinedRoutes)
+          this.zone.run(() => {
+            router.navigateByUrl(joinedRoutes)
+          })
         }
       }
     })
diff --git a/src/state/userInteraction/actions.ts b/src/state/userInteraction/actions.ts
index e6db64e75dcad04c3d2027d0a73ef17d45ab15d5..dfdaa14298ccad2e8804460d434a4b3caf960591 100644
--- a/src/state/userInteraction/actions.ts
+++ b/src/state/userInteraction/actions.ts
@@ -1,6 +1,6 @@
 import { createAction, props } from "@ngrx/store"
 import { nameSpace } from "./const"
-import { SxplrRegion, Feature, Point } from "src/atlasComponents/sapi/sxplrTypes"
+import { SxplrRegion, Feature, Point, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes"
 
 export const mouseOverAnnotations = createAction(
   `${nameSpace} mouseOverAnnotations`,
@@ -35,3 +35,10 @@ export const showFeature = createAction(
 export const clearShownFeature = createAction(
   `${nameSpace} clearShownFeature`,
 )
+
+export const setMouseoverVoi = createAction(
+  `${nameSpace} setMouseoverVoi`,
+  props<{
+    feature: VoiFeature
+  }>()
+)
diff --git a/src/state/userInteraction/selectors.ts b/src/state/userInteraction/selectors.ts
index 0f43a1a4c56fd0188d90c4fa4fb37f491daa35f3..8596e5d25827c8aceb5636a0d55a12ba54121c5b 100644
--- a/src/state/userInteraction/selectors.ts
+++ b/src/state/userInteraction/selectors.ts
@@ -18,3 +18,8 @@ export const mousingOverPosition = createSelector(
   selectStore,
   state => state.mouseoverPosition
 )
+
+export const mousingOverVoiFeature = createSelector(
+  selectStore,
+  state => state.mousedOverVoiFeature
+)
diff --git a/src/state/userInteraction/store.ts b/src/state/userInteraction/store.ts
index f6fba659973bf863161f4fd8c79e9b0eee190db6..7c562249392e2cab90c2ceca65005169c1249a9b 100644
--- a/src/state/userInteraction/store.ts
+++ b/src/state/userInteraction/store.ts
@@ -1,17 +1,19 @@
 import { createReducer, on } from "@ngrx/store";
-import { SxplrRegion, Feature, Point } from "src/atlasComponents/sapi/sxplrTypes";
+import { SxplrRegion, Feature, Point, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes";
 import * as actions from "./actions"
 
 export type UserInteraction = {
   mouseoverRegions: SxplrRegion[]
   selectedFeature: Feature
   mouseoverPosition: Point
+  mousedOverVoiFeature: VoiFeature
 }
 
 export const defaultState: UserInteraction = {
   selectedFeature: null,
   mouseoverRegions: [],
-  mouseoverPosition: null
+  mouseoverPosition: null,
+  mousedOverVoiFeature: null,
 }
 
 export const reducer = createReducer(
@@ -51,5 +53,12 @@ export const reducer = createReducer(
         mouseoverPosition: position
       }
     }
+  ),
+  on(
+    actions.setMouseoverVoi,
+    (state, { feature }) => ({
+      ...state,
+      mousedOverVoiFeature: feature
+    })
   )
 )
diff --git a/src/state/userPreference/actions.ts b/src/state/userPreference/actions.ts
index ee6f8f3062bde7c4c1e3648db385d55859134f2c..05d96e5caabe5e4067ce8903358cf78c2ac5a836 100644
--- a/src/state/userPreference/actions.ts
+++ b/src/state/userPreference/actions.ts
@@ -36,4 +36,11 @@ export const updateCsp = createAction(
     name: string
     csp: CSP
   }>()
-)
\ No newline at end of file
+)
+
+export const setShowExperimental = createAction(
+  `${nameSpace} setShowExp`,
+  props<{
+    flag: boolean
+  }>()
+)
diff --git a/src/state/userPreference/selectors.ts b/src/state/userPreference/selectors.ts
index 608264fc6ce5d007e81b53fd1fadd25ae4a16dfb..00856d0160f0af6fd391bf1c1e3b68288a981dbb 100644
--- a/src/state/userPreference/selectors.ts
+++ b/src/state/userPreference/selectors.ts
@@ -33,3 +33,8 @@ export const userCsp = createSelector(
   storeSelector,
   store => store.pluginCSP
 )
+
+export const showExperimental = createSelector(
+  storeSelector,
+  store => store.showExperimental
+)
diff --git a/src/state/userPreference/store.ts b/src/state/userPreference/store.ts
index dbf0be95561efd8992d77a54e6c33db4f6f2c7a8..2a4fa314a1999dbacca0513bea5eaf3efe877c8e 100644
--- a/src/state/userPreference/store.ts
+++ b/src/state/userPreference/store.ts
@@ -1,4 +1,5 @@
 import { createReducer, on } from "@ngrx/store"
+import { environment } from "src/environments/environment"
 import { COOKIE_VERSION, KG_TOS_VERSION, LOCAL_STORAGE_CONST } from "src/util/constants"
 import * as actions from "./actions"
 import { maxGpuLimit, CSP } from "./const"
@@ -13,6 +14,8 @@ export type UserPreference = {
 
   agreeCookie: boolean
   agreeKgTos: boolean
+
+  showExperimental: boolean
 }
 
 export const defaultState: UserPreference = {
@@ -23,6 +26,7 @@ export const defaultState: UserPreference = {
 
   agreeCookie: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_COOKIE) === COOKIE_VERSION,
   agreeKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION,
+  showExperimental: environment.EXPERIMENTAL_FEATURE_FLAG
 }
 
 export const reducer = createReducer(
@@ -89,5 +93,12 @@ export const reducer = createReducer(
         }
       }
     }
+  ),
+  on(
+    actions.setShowExperimental,
+    (state, { flag }) => ({
+      ...state,
+      showExperimental: flag
+    })
   )
 )
diff --git a/src/ui/config/configCmp/config.component.ts b/src/ui/config/configCmp/config.component.ts
index 3d1dfdce600504a6fb92b684560e61adb749d26e..b3d556c87fa882ef0029e362bcab45cd552f564c 100644
--- a/src/ui/config/configCmp/config.component.ts
+++ b/src/ui/config/configCmp/config.component.ts
@@ -28,7 +28,14 @@ export class ConfigComponent implements OnInit, OnDestroy {
   public ANIMATION_TOOLTIP = ANIMATION_TOOLTIP
   public MOBILE_UI_TOOLTIP = MOBILE_UI_TOOLTIP
 
-  public experimentalFlag = environment.EXPERIMENTAL_FEATURE_FLAG
+  /**
+   * n.b. do not use store to set experimental flag here, since this also shows the control to toggle exp control on and off
+   */
+  public environment = environment
+
+  public experimentalFlag$ = this.store.pipe(
+    select(userPreference.selectors.showExperimental)
+  )
 
   public panelModes: Record<string, userInterface.PanelMode> = {
     FOUR_PANEL: "FOUR_PANEL",
@@ -181,5 +188,13 @@ export class ConfigComponent implements OnInit, OnDestroy {
     target.classList.remove('onDragOver')
   }
 
+  public updateExperimentalFlag(event: MatSlideToggleChange) {
+    this.store.dispatch(
+      userPreference.actions.setShowExperimental({
+        flag: event.checked
+      })
+    )
+  }
+
   public stepSize: number = 10
 }
diff --git a/src/ui/config/configCmp/config.template.html b/src/ui/config/configCmp/config.template.html
index 85bbb90d53f68e253e52e99407245283417d4945..4982158f5bb83596ed47eeaf5425f17981666cc8 100644
--- a/src/ui/config/configCmp/config.template.html
+++ b/src/ui/config/configCmp/config.template.html
@@ -52,9 +52,16 @@
   </mat-tab>
 
   <!-- viewer preference -->
-  <mat-tab *ngIf="experimentalFlag" label="Viewer Preference">
+  <mat-tab *ngIf="environment.EXPERIMENTAL_FEATURE_FLAG" label="Viewer Preference">
 
     <div class="sxplr-custom-cmp text sxplr-m-2">
+      
+      <mat-slide-toggle
+        [checked]="experimentalFlag$ | async"
+        (change)="updateExperimentalFlag($event)">
+        Experimental Flag
+      </mat-slide-toggle>
+
       <div class="mat-h2">
         Rearrange Viewports
       </div>
@@ -180,7 +187,7 @@
       <!-- temporarily disabling 1-3 layout -->
 
       <!-- horizontal 1 3 card -->
-      <!-- <button
+      <button
         class="sxplr-m-2 sxplr-p-2"
         mat-flat-button
         (click)="usePanelMode(panelModes.H_ONE_THREE)"
@@ -191,10 +198,10 @@
           <div class="border chunky" cell-iii></div>
           <div class="border chunky" cell-iv></div>
         </layout-horizontal-one-three>
-      </button> -->
+      </button>
 
       <!-- vertical 1 3 card -->
-      <!-- <button
+      <button
         class="sxplr-m-2 sxplr-p-2"
         mat-flat-button
         (click)="usePanelMode(panelModes.V_ONE_THREE)"
@@ -205,7 +212,7 @@
           <div class="border chunky" cell-iii></div>
           <div class="border chunky" cell-iv></div>
         </layout-vertical-one-three>
-      </button> -->
+      </button>
 
       <!-- single -->
       <ng-template #singlePanelTmpl>
diff --git a/src/ui/topMenu/topMenuCmp/topMenu.components.ts b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
index c2ed29672c1caffcaf41257b8c1fa5448f6149bc..5933480bb7cf1c047d79d99557cc1853576ded52 100644
--- a/src/ui/topMenu/topMenuCmp/topMenu.components.ts
+++ b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
@@ -12,8 +12,9 @@ import { MatDialog, MatDialogConfig, MatDialogRef } from "@angular/material/dial
 import { MatBottomSheet } from "@angular/material/bottom-sheet";
 import { CONST, QUICKTOUR_DESC, ARIA_LABELS } from 'common/constants'
 import { IQuickTourData } from "src/ui/quickTour/constrants";
-import { environment } from 'src/environments/environment'
 import { TypeMatBtnColor, TypeMatBtnStyle } from "src/components/dynamicMaterialBtn/dynamicMaterialBtn.component";
+import { select, Store } from "@ngrx/store";
+import { userPreference } from "src/state";
 
 @Component({
   selector: 'top-menu-cmp',
@@ -26,7 +27,9 @@ import { TypeMatBtnColor, TypeMatBtnStyle } from "src/components/dynamicMaterial
 
 export class TopMenuCmp {
 
-  public EXPERIMENTAL_FEATURE_FLAG = environment.EXPERIMENTAL_FEATURE_FLAG
+  public experimentalFlag$ = this.store.pipe(
+    select(userPreference.selectors.showExperimental)
+  )
 
   public ARIA_LABELS = ARIA_LABELS
   public PINNED_DATASETS_BADGE_DESC = CONST.PINNED_DATASETS_BADGE_DESC
@@ -71,6 +74,7 @@ export class TopMenuCmp {
   }
 
   constructor(
+    private store: Store,
     private authService: AuthService,
     private dialog: MatDialog,
     public bottomSheet: MatBottomSheet,
diff --git a/src/ui/topMenu/topMenuCmp/topMenu.template.html b/src/ui/topMenu/topMenuCmp/topMenu.template.html
index f652afdd663843230c4b712d3f28a35739751dfd..319528dc8c3dae585c36cef03ab3a908c2b1a720 100644
--- a/src/ui/topMenu/topMenuCmp/topMenu.template.html
+++ b/src/ui/topMenu/topMenuCmp/topMenu.template.html
@@ -168,7 +168,7 @@
   </button>
 
   <button mat-menu-item
-    *ngIf="EXPERIMENTAL_FEATURE_FLAG"
+    *ngIf="experimentalFlag$ | async"
     [disabled]="!viewerLoaded"
     key-frame-play-now
     [matTooltip]="keyFrameText">
diff --git a/src/util/injectionTokens.ts b/src/util/injectionTokens.ts
index feb547176fb2e37b7fe5222e716d1cf8c257a5ce..250a8cce50b1279b6c4d63fbb3e8bcd517b96a33 100644
--- a/src/util/injectionTokens.ts
+++ b/src/util/injectionTokens.ts
@@ -1,12 +1,19 @@
 import { InjectionToken } from "@angular/core";
 import { Observable } from "rxjs";
 
+/**
+ * Inject click interceptor
+ */
 export const CLICK_INTERCEPTOR_INJECTOR = new InjectionToken<ClickInterceptor>('CLICK_INTERCEPTOR_INJECTOR')
 
 export type TClickInterceptorConfig = {
   last?: boolean
 }
 
+/**
+ * Register callbacks
+ * interceptorFunction may return a truthy value. If so, no futher click interceptors will be called.
+ */
 export interface ClickInterceptor{
   register: (interceptorFunction: (ev: any) => boolean, config?: TClickInterceptorConfig) => void
   deregister: (interceptorFunction: (ev: any) => any) => void
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 8961b4f440ae0298602daed8efd50ad01afdeb6d..fa2aa8e61b6fc05102c21fbbfbcca19ed4b974f2 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -10,11 +10,7 @@ import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
 import { DialogService } from "src/services/dialogService.service";
 import { SAPI } from "src/atlasComponents/sapi";
 import { Feature, SxplrAtlas, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"
-import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
-
-import { environment } from "src/environments/environment"
-// import { SapiViewsFeaturesVoiQuery } from "src/atlasComponents/sapiViews/features";
-import { SapiViewsCoreSpaceBoundingBox } from "src/atlasComponents/sapiViews/core";
+import { atlasAppearance, atlasSelection, userInteraction, userPreference } from "src/state";
 import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 
 @Component({
@@ -64,18 +60,14 @@ import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 
 export class ViewerCmp implements OnDestroy {
 
+  public experimentalFlag$ = this.store$.pipe(
+    select(userPreference.selectors.showExperimental)
+  )
   public CONST = CONST
   public ARIA_LABELS = ARIA_LABELS
 
-  @ViewChild('genericInfoVCR', { read: ViewContainerRef })
-  genericInfoVCR: ViewContainerRef
-
-  // @ViewChild('voiFeatures', { read: SapiViewsFeaturesVoiQuery })
-  // voiQueryDirective: SapiViewsFeaturesVoiQuery
-
-  @ViewChild('bbox', { read: SapiViewsCoreSpaceBoundingBox })
-  boundingBoxDirective: SapiViewsCoreSpaceBoundingBox
-
+  public showVoiFlag = true
+  
   public quickTourRegionSearch: IQuickTourData = {
     order: 7,
     description: QUICKTOUR_DESC.REGION_SEARCH,
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 72c4cfdac0921c2ea38b7079e3e015b48ef2baa7..63d30d9436668e34ab5ddd51b10ed538377e792c 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -29,17 +29,6 @@
 
         </mat-list-item>
 
-        <!-- <ng-template [ngIf]="voiQueryDirective && (voiQueryDirective.onhover | async)" let-feat>
-          <mat-list-item>
-            <mat-icon
-              fontSet="fas"
-              fontIcon="fa-database"
-              mat-list-icon>
-            </mat-icon>
-            <div matLine>{{ feat?.metadata?.fullName || 'Feature' }}</div>
-          </mat-list-item>
-        </ng-template> -->
-
       </mat-list>
       <!-- TODO Potentially implementing plugin contextual info -->
     </div>
@@ -266,17 +255,6 @@
   
   </div>
 
-  <!-- tab toggling hide/show of min search tray -->
-  <!-- <div class="tab-toggle-container d-inline-block v-align-top">
-    <ng-container *ngTemplateOutlet="tabTmpl; context: {
-      isOpen: minTrayVisSwitch.switchState$ | async,
-      regionSelected: selectedRegions$ | async,
-      click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch),
-      badge: voiQueryDirective && (voiQueryDirective.features$ | async).length || null
-    }">
-    </ng-container>
-  </div> -->
-
 </ng-template>
 
 
@@ -942,14 +920,6 @@
 
   </sxplr-feature-view>
   
-  <!-- TODO FIXME -->
-  <!-- <sxplr-sapiviews-features-entry
-    [sxplr-sapiviews-features-entry-atlas]="selectedAtlas$ | async"
-    [sxplr-sapiviews-features-entry-space]="templateSelected$ | async"
-    [sxplr-sapiviews-features-entry-parcellation]="parcellationSelected$ | async"
-    [sxplr-sapiviews-features-entry-region]="(selectedRegions$ | async)[0]"
-    [sxplr-sapiviews-features-entry-feature]="feature">
-  </sxplr-sapiviews-features-entry> -->
 </ng-template>
 
 <!-- general feature tmpl -->
@@ -975,61 +945,74 @@
 
 </ng-template>
 
+<!-- spatial search tmpls -->
 <ng-template #spatialFeatureListTmpl>
-  <sxplr-feature-entry
-    class="sxplr-pe-all sxplr-d-block mat-elevation-z8"
-    [template]="templateSelected$ | async">
-  </sxplr-feature-entry>
-</ng-template>
-
-<!-- <ng-template #spatialFeatureListViewTmpl>
-  <div *ngIf="voiQueryDirective && (voiQueryDirective.busy$ | async); else notBusyTmpl" class="fs-200">
-    <spinner-cmp></spinner-cmp>
-  </div>
-
-  <ng-template #notBusyTmpl>
-    <mat-card *ngIf="voiQueryDirective && (voiQueryDirective.features$ | async).length > 0" class="pe-all mat-elevation-z4">
+  <mat-card class="sxplr-pe-all"
+    [ngClass]="{
+      'sxplr-d-none': !showVoiFlag || (voiFeatureEntryCmp.totals$ | async) === 0
+    }">
+    <mat-card-header>
       <mat-card-title>
-        Volumes of interest
+        Image Features
       </mat-card-title>
-      <mat-card-subtitle class="overflow-hidden">
-        <ng-template let-bbox [ngIf]="boundingBoxDirective && (boundingBoxDirective.bbox$ | async | getProperty : 'bbox')" [ngIfElse]="bboxFallbackTmpl">
-          Bounding box: {{ bbox[0] | numbers | json }} - {{ bbox[1] | numbers | json }} mm
-        </ng-template>
-        <ng-template #bboxFallbackTmpl>
-          Found nearby
+      <mat-card-subtitle>
+        <div>
+          {{ templateSelected$ | async | getProperty : 'name' }}
+        </div>
+        <ng-template [ngIf]="bbox.bbox$ | async | getProperty : 'bbox'" let-bbox>
+          <div>
+            from {{ bbox[0] | numbers | addUnitAndJoin : '' }}
+          </div>
+          <div>
+            to {{ bbox[1] | numbers | addUnitAndJoin : '' }}
+          </div>
         </ng-template>
-        
       </mat-card-subtitle>
+    </mat-card-header>
+  </mat-card>
 
-      <mat-divider></mat-divider>
+  <sxplr-feature-entry
+    [ngClass]="showVoiFlag ? 'sxplr-d-block' : 'sxplr-d-none'"
+    class="sxplr-pe-all mat-elevation-z8"
+    [template]="templateSelected$ | async"
+    [bbox]="bbox.bbox$ | async | getProperty : 'bbox'"
+    #voiFeatureEntryCmp="featureEntryCmp">
+  </sxplr-feature-entry>
 
-      <ng-template [ngIf]="voiQueryDirective">
+  <button mat-raised-button
+    [ngClass]="{
+      'sxplr-d-none': (voiFeatureEntryCmp.totals$ | async) === 0
+    }"
+    class="sxplr-pe-all sxplr-w-100"
+    (click)="showVoiFlag = !showVoiFlag">
 
-        <div *ngFor="let feature of voiQueryDirective.features$ | async"
-          mat-ripple
-          (click)="showDataset(feature)"
-          class="sxplr-custom-cmp hoverable w-100 overflow-hidden text-overflow-ellipses">
-          {{ feature.metadata.fullName }}
-        </div>
-      </ng-template>
-    </mat-card>
-  </ng-template>
-</ng-template> -->
+    <ng-template [ngIf]="showVoiFlag">
+      <i class="fas fa-chevron-up"></i>
+      <span>
+        Collapse
+      </span>
+    </ng-template>
 
-<!-- TODO FIXME -->
-<!-- <div class="d-none"
-  *ngIf="VOI_QUERY_FLAG"
+    <ng-template [ngIf]="!showVoiFlag">
+      <i class="fas fa-chevron-down"></i>
+      <span>
+        Explore {{ voiFeatureEntryCmp.totals$ | async }} spatial features
+      </span>
+    </ng-template>
+  </button>
+  <div
+    *ngIf="experimentalFlag$ | async"
+    voiBbox
+    [features]="voiFeatureEntryCmp.features$ | async">
+
+  </div>
+</ng-template>
+
+<div
   sxplr-sapiviews-core-space-boundingbox
   [sxplr-sapiviews-core-space-boundingbox-atlas]="selectedAtlas$ | async"
   [sxplr-sapiviews-core-space-boundingbox-space]="templateSelected$ | async"
   [sxplr-sapiviews-core-space-boundingbox-spec]="viewerCtx$ | async | nehubaVCtxToBbox"
-  #bbox="sxplrSapiViewsCoreSpaceBoundingBox"
-  sxplr-sapiviews-features-voi-query
-  [sxplr-sapiviews-features-voi-query-atlas]="selectedAtlas$ | async"
-  [sxplr-sapiviews-features-voi-query-space]="templateSelected$ | async"
-  [sxplr-sapiviews-features-voi-query-bbox]="bbox.bbox$ | async | getProperty : 'bbox'"
-  (sxplr-sapiviews-features-voi-query-onclick)="showDataset($event)"
-  #voiFeatures="sxplrSapiViewsFeaturesVoiQuery">
-
-</div> -->
+  #bbox="sxplrSapiViewsCoreSpaceBoundingBox">
+</div>
+