diff --git a/.github/workflows/deploy-helm.yml b/.github/workflows/deploy-helm.yml
index be72a299c06c12d47079e3cf035c9015d6598c78..59796a18dd55d38db2cd5c8a5b768568d07c2b69 100644
--- a/.github/workflows/deploy-helm.yml
+++ b/.github/workflows/deploy-helm.yml
@@ -33,6 +33,7 @@ jobs:
         helm --kubeconfig=$kubecfg_path \
           upgrade \
           --history-max 3 \
+          --reuse-values \
           --set image.tag=${{ inputs.IMAGE_TAG }} \
           --set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \
           ${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/
@@ -53,8 +54,6 @@ jobs:
           upgrade \
           --history-max 3 \
           --reuse-values \
-          --set envObj.HOST_PATHNAME=/viewer-staging \
-          --set envObj.OVERWRITE_API_ENDPOINT=https://siibra-api-rc.apps.tc.humanbrainproject.eu/v3_0 \
           --set image.tag=${{ inputs.IMAGE_TAG }} \
           --set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \
           ${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/
@@ -75,9 +74,6 @@ jobs:
           upgrade \
           --history-max 3 \
           --reuse-values \
-          --set envObj.OVERWRITE_EXPERIMENTAL_FLAG_ATTR=1 \
-          --set envObj.HOST_PATHNAME=/viewer-exmpt \
-          --set envObj.OVERWRITE_API_ENDPOINT=https://siibra-api-rc.apps.tc.humanbrainproject.eu/v3_0 \
           --set image.tag=${{ inputs.IMAGE_TAG }} \
           --set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \
           ${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/
diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml
index ad5df0476fb791736a3fc94c6c3d710d29f88b9f..9fc356880df41ba0efa0a6ee86565e64d1ad0521 100644
--- a/.github/workflows/docker_img.yml
+++ b/.github/workflows/docker_img.yml
@@ -167,6 +167,19 @@ jobs:
     secrets:
       KUBECONFIG: ${{ secrets.KUBECONFIG }}
 
+  trigger-deploy-expmt-rancher:
+    if: ${{ needs.setting-vars.outputs.BRANCH_NAME == 'staging' && success() }}
+    needs:
+      - build-docker-img
+      - setting-vars
+    uses: ./.github/workflows/deploy-helm.yml
+    with:
+      DEPLOYMENT_NAME: expmt
+      IMAGE_TAG: staging
+      IMAGE_DIGEST: ${{ needs.build-docker-img.outputs.GIT_DIGEST }}
+    secrets:
+      KUBECONFIG: ${{ secrets.KUBECONFIG }}
+
   trigger-deploy-master-rancher:
     if: ${{ needs.setting-vars.outputs.BRANCH_NAME == 'master' && success() }}
     needs:
diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts
index 322d59bd25739beaf102a3870ffe014366758222..f11a937786a47d695031aca2cad5a6182f65e68e 100644
--- a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts
+++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts
@@ -2,7 +2,7 @@ import { Component, Inject, OnDestroy, Optional } from "@angular/core";
 import { ModularUserAnnotationToolService } from "../tools/service";
 import { ARIA_LABELS } from 'common/constants'
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, CONTEXT_MENU_ITEM_INJECTOR, TContextMenu } from "src/util";
-import { TContextArg } from "src/viewerModule/viewer.interface";
+import { TViewerEvtCtxData } from "src/viewerModule/viewer.interface";
 import { TContextMenuReg } from "src/contextMenuModule";
 import { MatSnackBar } from 'src/sharedModules/angularMaterial.exports'
 
@@ -26,7 +26,7 @@ export class AnnotationMode implements OnDestroy{
     private modularToolSvc: ModularUserAnnotationToolService,
     snackbar: MatSnackBar,
     @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
-    @Optional() @Inject(CONTEXT_MENU_ITEM_INJECTOR) ctxMenuInterceptor: TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>>
+    @Optional() @Inject(CONTEXT_MENU_ITEM_INJECTOR) ctxMenuInterceptor: TContextMenu<TContextMenuReg<TViewerEvtCtxData<'nehuba' | 'threeSurfer'>>>
   ) {
 
     /**
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index 1d36acf63f87a8e269d43f85a449d36fb298d9f6..002fa318906e0728864392f73d567a31360cb300 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -18,6 +18,7 @@ import { atlasSelection } from "src/state";
 import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { AnnotationLayer } from "src/atlasComponents/annotations";
 import { translateV3Entities } from "src/atlasComponents/sapi/translateV3";
+import { HOVER_INTERCEPTOR_INJECTOR, HoverInterceptor, THoverConfig } from "src/util/injectionTokens";
 
 const LOCAL_STORAGE_KEY = 'userAnnotationKey'
 const ANNOTATION_LAYER_NAME = "modular_tool_layer_name"
@@ -276,12 +277,52 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     })
   )
 
+  #hoverMsgs: THoverConfig[] = []
+
+  #dismimssHoverMsgs(){
+    if (!this.hoverInterceptor) {
+      return
+    }
+    const { remove } = this.hoverInterceptor
+    for (const msg of this.#hoverMsgs){
+      remove(msg)
+    }
+  }
+  #appendHoverMsgs(geometries: IAnnotationGeometry[]){
+    if (!this.hoverInterceptor) {
+      return
+    }
+    const { append } = this.hoverInterceptor
+    this.#hoverMsgs = geometries.map(geom => {
+      let fontIcon = 'fa-file'
+      if (geom.annotationType === 'Point') {
+        fontIcon = 'fa-circle'
+      }
+      if (geom.annotationType === 'Line') {
+        fontIcon = 'fa-slash'
+      }
+      if (geom.annotationType === 'Polygon') {
+        fontIcon = 'fa-draw-polygon'
+      }
+      return {
+        message: geom.name || `Unnamed ${geom.annotationType}`,
+        fontSet: 'fas',
+        fontIcon
+      }
+    })
+    for (const msg of this.#hoverMsgs){
+      append(msg)
+    }
+  }
+
   constructor(
     private store: Store<any>,
     private snackbar: MatSnackBar,
     @Inject(INJ_ANNOT_TARGET) annotTarget$: Observable<HTMLElement>,
     @Inject(ANNOTATION_EVENT_INJ_TOKEN) private annotnEvSubj: Subject<TAnnotationEvent<keyof IAnnotationEvents>>,
     @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit>,
+    @Optional() @Inject(HOVER_INTERCEPTOR_INJECTOR) 
+    private hoverInterceptor: HoverInterceptor,
   ){
 
     /**
@@ -323,7 +364,13 @@ export class ModularUserAnnotationToolService implements OnDestroy{
           }
         } as TAnnotationEvent<'mousedown' | 'mouseup' | 'mousemove'>
         this.annotnEvSubj.next(payload)
-      })
+      }),
+      this.hoveringAnnotations$.subscribe(ev => {
+        this.#dismimssHoverMsgs()
+        if (ev) {
+          this.#appendHoverMsgs([ev])
+        }
+      }),
     )
 
     /**
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index acbcfd87f578637a6c94ac47df9c5e46f24dff7b..e326167d14b816b5723139c7b60d44fa14cec712 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -15,7 +15,6 @@ import { Observable, Subscription, merge, timer, fromEvent } from "rxjs";
 import { filter, delay, switchMapTo, take, startWith } from "rxjs/operators";
 
 import { colorAnimation } from "./atlasViewer.animation"
-import { MouseHoverDirective } from "src/mouseoverModule";
 import { MatSnackBar } from 'src/sharedModules/angularMaterial.exports'
 import { MatDialog, MatDialogRef } from "src/sharedModules/angularMaterial.exports";
 import { CONST } from 'common/constants'
@@ -49,8 +48,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any>
 
-  @ViewChild(MouseHoverDirective) private mouseOverNehuba: MouseHoverDirective
-
   @ViewChild('idleOverlay', {read: TemplateRef}) idelTmpl: TemplateRef<any>
 
   @HostBinding('attr.ismobile')
diff --git a/src/contextMenuModule/ctxMenuHost.directive.ts b/src/contextMenuModule/ctxMenuHost.directive.ts
index 3f1b2b7b8f82a29bdb28147e3cfd652940fa42d8..71bcff9e308d7779c0c41afd4f8310437dc155ec 100644
--- a/src/contextMenuModule/ctxMenuHost.directive.ts
+++ b/src/contextMenuModule/ctxMenuHost.directive.ts
@@ -1,6 +1,6 @@
 import { AfterViewInit, Directive, HostListener, Input, OnDestroy, TemplateRef, ViewContainerRef } from "@angular/core";
 import { ContextMenuService } from "./service";
-import { TContextArg } from "src/viewerModule/viewer.interface";
+import { TViewerEvtCtxData } from "src/viewerModule/viewer.interface";
 
 @Directive({
   selector: '[ctx-menu-host]'
@@ -18,7 +18,7 @@ export class CtxMenuHost implements OnDestroy, AfterViewInit{
 
   constructor(
     private vcr: ViewContainerRef,
-    private svc: ContextMenuService<TContextArg<'nehuba' | 'threeSurfer'>>,
+    private svc: ContextMenuService<TViewerEvtCtxData<'nehuba' | 'threeSurfer'>>,
   ){
   }
 
diff --git a/src/contextMenuModule/dismissCtxMenu.directive.ts b/src/contextMenuModule/dismissCtxMenu.directive.ts
index 36576fccce8e8a52f6b7c090d5a56e5bf9ce5d1b..dd57df2ac5b3ba3263f40b9577dac31d52be29a5 100644
--- a/src/contextMenuModule/dismissCtxMenu.directive.ts
+++ b/src/contextMenuModule/dismissCtxMenu.directive.ts
@@ -1,5 +1,5 @@
 import { Directive, HostListener } from "@angular/core";
-import { TContextArg } from "src/viewerModule/viewer.interface";
+import { TViewerEvtCtxData } from "src/viewerModule/viewer.interface";
 import { ContextMenuService } from "./service";
 
 @Directive({
@@ -13,7 +13,7 @@ export class DismissCtxMenuDirective{
   }
 
   constructor(
-    private svc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>
+    private svc: ContextMenuService<TViewerEvtCtxData<'threeSurfer' | 'nehuba'>>
   ){
 
   }
diff --git a/src/features/voi-bbox.directive.ts b/src/features/voi-bbox.directive.ts
index f150225b9cf66f5ce2f68f26c7a936f1c30f6576..5408bc08c515814506312ce1643506d027216bcb 100644
--- a/src/features/voi-bbox.directive.ts
+++ b/src/features/voi-bbox.directive.ts
@@ -1,25 +1,27 @@
-import { Directive, Inject, Input, OnDestroy, Optional } from "@angular/core";
+import { Directive, Inject, Input, Optional, inject } 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 { concat, interval, of, Subject } from "rxjs";
+import { debounce, distinctUntilChanged, filter, pairwise, take, takeUntil } 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"
+import { DestroyDirective } from "src/util/directives/destroy.directive";
+import { HOVER_INTERCEPTOR_INJECTOR, HoverInterceptor, THoverConfig } from "src/util/injectionTokens";
 
 @Directive({
   selector: '[voiBbox]',
+  hostDirectives: [ DestroyDirective ]
 })
-export class VoiBboxDirective implements OnDestroy {
-  
-  #onDestroyCb: (() => void)[] = []
+export class VoiBboxDirective {
+
+  #destory$ = inject(DestroyDirective).destroyed$
 
   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
@@ -29,14 +31,15 @@ export class VoiBboxDirective implements OnDestroy {
         VoiBboxDirective.VOI_ANNOTATION_COLOR
       )
       this._voiBBoxSvc = layer
-      this.#voiSubs.push(
-        layer.onHover.subscribe(val => this.handleOnHoverFeature(val || {}))
-      )
-      this.#onDestroyCb.push(() => {
+      layer.onHover.pipe(
+        takeUntil(this.#destory$)
+      ).subscribe(val => this.handleOnHoverFeature(val || {}))
+
+      this.#destory$.subscribe(() => {
         this._voiBBoxSvc.dispose()
         this._voiBBoxSvc = null
       })
-      return layer
+      return this._voiBBoxSvc
     } catch (e) {
       return null
     }
@@ -54,25 +57,27 @@ export class VoiBboxDirective implements OnDestroy {
     return this.#voiFeatures
   }
 
-  ngOnDestroy(): void {
-    while (this.#onDestroyCb.length > 0) this.#onDestroyCb.pop()()
-  }
+  #hoverMsgs: THoverConfig[] = []
 
   constructor(
     private store: Store,
-    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR)
+    clickInterceptor: ClickInterceptor,
+    @Optional() @Inject(HOVER_INTERCEPTOR_INJECTOR) 
+    private hoverInterceptor: HoverInterceptor,
   ){
     if (clickInterceptor) {
       const { register, deregister } = clickInterceptor
       const handleClick = this.handleClick.bind(this)
       register(handleClick)
-      this.#onDestroyCb.push(() => deregister(handleClick))
+      this.#destory$.subscribe(() => deregister(handleClick))
     }
 
-    const sub = concat(
+    concat(
       of([] as VoiFeature[]),
       this.#features$
     ).pipe(
+      takeUntil(this.#destory$),
       distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)),
       pairwise(),
       debounce(() => 
@@ -109,10 +114,38 @@ export class VoiBboxDirective implements OnDestroy {
       if (this.voiBBoxSvc) this.voiBBoxSvc.setVisible(true)
     })
 
-    this.#onDestroyCb.push(() => sub.unsubscribe())
-    this.#onDestroyCb.push(() => this.store.dispatch(
-      userInteraction.actions.setMouseoverVoi({ feature: null })
-    ))
+    this.#destory$.subscribe(() => {
+      this.store.dispatch(
+        userInteraction.actions.setMouseoverVoi({ feature: null })
+      )
+      this.#dismissHoverMsg()
+    })
+  }
+
+  #dismissHoverMsg(){
+    if (!this.hoverInterceptor) {
+      return
+    }
+    
+    const { remove } = this.hoverInterceptor
+    for (const msg of this.#hoverMsgs){
+      remove(msg)
+    }
+  }
+
+  #appendHoverMsg(feats: VoiFeature[]){
+    if (!this.hoverInterceptor) {
+      return
+    }
+    const { append } = this.hoverInterceptor
+    this.#hoverMsgs = feats.map(feat => ({
+      message: `${feat?.name}`,
+      fontIcon: 'fa-database',
+      fontSet: 'fas'
+    }))
+    for (const msg of this.#hoverMsgs){
+      append(msg)
+    }
   }
 
   handleClick(){
@@ -135,6 +168,10 @@ export class VoiBboxDirective implements OnDestroy {
     this.store.dispatch(
       userInteraction.actions.setMouseoverVoi({ feature })
     )
+    this.#dismissHoverMsg()
+    if (feature) {
+      this.#appendHoverMsg([feature])
+    }
   }
 
   #pointsToAABB(pointA: [number, number, number], pointB: [number, number, number]): TNgAnnotationAABBox{
diff --git a/src/mouseoverModule/index.ts b/src/mouseoverModule/index.ts
index 8dea7b959fa47feda07047ca34a1fa0d6e204cec..a419a4a25c7904732c185080109b0762a391bfb4 100644
--- a/src/mouseoverModule/index.ts
+++ b/src/mouseoverModule/index.ts
@@ -1,3 +1,2 @@
-export { MouseHoverDirective } from './mouseover.directive'
-export { MouseoverModule } from './mouseover.module'
-export { TransformOnhoverSegmentPipe } from './transformOnhoverSegment.pipe'
\ No newline at end of file
+export { MouseOver } from "./mouseover.component"
+export { MouseOverSvc } from "./service"
diff --git a/src/mouseoverModule/mouseOverCvt.pipe.ts b/src/mouseoverModule/mouseOverCvt.pipe.ts
deleted file mode 100644
index fabd1d4899843579d939eb3ca94162b4ab85cc2e..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/mouseOverCvt.pipe.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { TOnHoverObj } from "./util";
-
-function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){
-  if (!value) return []
-  switch (key) {
-  case 'regions': {
-    return (value as TOnHoverObj['regions']).map(seg => {
-      return {
-        icon: {
-          fontSet: 'fas',
-          fontIcon: 'fa-brain',
-          cls: 'fas fa-brain',
-        },
-        text: seg?.name || "Unknown"
-      }
-    })
-  }
-  case 'voi': {
-    const { name } = value as TOnHoverObj['voi']
-    return [{
-      icon: {
-        fontSet: 'fas',
-        fontIcon: 'fa-database',
-        cls: 'fas fa-database'
-      },
-      text: name
-    }]
-  }
-  case 'annotation': {
-    const { annotationType, name } = (value as TOnHoverObj['annotation'])
-    let fontIcon: string
-    if (annotationType === 'Point') fontIcon = 'fa-circle'
-    if (annotationType === 'Line') fontIcon = 'fa-slash'
-    if (annotationType === 'Polygon') fontIcon = 'fa-draw-polygon'
-    if (!annotationType) fontIcon = 'fa-file'
-    return [{
-      icon: {
-        fontSet: 'fas',
-        fontIcon,
-        cls: `fas ${fontIcon}`,
-      },
-      text: name || `Unnamed ${annotationType}`
-    }]
-  }
-  default: {
-    return [{
-      icon: {
-        fontSet: 'fas',
-        fontIcon: 'fa-file',
-        cls: 'fas fa-file'
-      },
-      text: `Unknown hovered object`
-    }]
-  }
-  }
-}
-
-type TCvtOutput = {
-  icon: {
-    fontSet: string
-    fontIcon: string
-    cls: string
-  }
-  text: string
-}
-
-@Pipe({
-  name: 'mouseoverCvt',
-  pure: true
-})
-
-export class MouseOverConvertPipe implements PipeTransform{
-
-  public transform(dict: TOnHoverObj){
-    const output: TCvtOutput[] = []
-    for (const key in dict) {
-      output.push(
-        ...render(key as keyof TOnHoverObj, dict[key])
-      )
-    }
-    return output
-  }
-}
\ No newline at end of file
diff --git a/src/mouseoverModule/mouseover.component.ts b/src/mouseoverModule/mouseover.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4c5b58b5e28ca203e16851ce884398d16ef6747b
--- /dev/null
+++ b/src/mouseoverModule/mouseover.component.ts
@@ -0,0 +1,22 @@
+import { CommonModule } from "@angular/common";
+import { Component } from "@angular/core";
+import { AngularMaterialModule } from "src/sharedModules";
+import { MouseOverSvc } from "./service";
+
+@Component({
+  selector: 'mouseover-info',
+  templateUrl: './mouseover.template.html',
+  styleUrls: [
+    './mouseover.style.css'
+  ],
+  standalone: true,
+  imports: [
+    AngularMaterialModule,
+    CommonModule
+  ],
+})
+
+export class MouseOver {
+  constructor(private svc: MouseOverSvc) {}
+  messages$ = this.svc.messages$
+}
diff --git a/src/mouseoverModule/mouseover.directive.ts b/src/mouseoverModule/mouseover.directive.ts
deleted file mode 100644
index a69fccd7630c0be2a35d7ec73d2dd1f0ed7a6d53..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/mouseover.directive.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { Directive } from "@angular/core"
-import { select, Store } from "@ngrx/store"
-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"
-import { MouseOverSvc } from "./service"
-
-@Directive({
-  selector: '[iav-mouse-hover]',
-  exportAs: 'iavMouseHover',
-})
-
-export class MouseHoverDirective {
-
-  /**
-   * TODO move
-   * - mousing over regions
-   * - hovering annotation
-   * - hovering voi feature
-   * to use hover interceptor
-   */
-  public currentOnHoverObs$: Observable<TOnHoverObj> = merge(
-    this.store$.pipe(
-      select(userInteraction.selectors.mousingOverRegions),
-    ).pipe(
-      distinctUntilChanged(arrayEqual((o, n) => o?.name === 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 }))
-    )
-  ).pipe(
-    scan(temporalPositveScanFn, []),
-    map(arr => {
-
-      let returnObj: TOnHoverObj = {
-        regions: null,
-        annotation: null,
-        voi: null
-      }
-
-      for (const val of arr) {
-        returnObj = {
-          ...returnObj,
-          ...val
-        }
-      }
-
-      return returnObj
-    }),
-  )
-
-  constructor(
-    private store$: Store<any>,
-    private annotSvc: ModularUserAnnotationToolService,
-    private svc: MouseOverSvc,
-  ) {
-  }
-
-  messages$ = this.svc.messages$
-}
diff --git a/src/mouseoverModule/mouseover.module.ts b/src/mouseoverModule/mouseover.module.ts
deleted file mode 100644
index f4c54934842b01a3b656136c63d0706603a178c5..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/mouseover.module.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
-import { TransformOnhoverSegmentPipe } from "./transformOnhoverSegment.pipe";
-import { MouseHoverDirective } from "./mouseover.directive";
-import { MouseOverConvertPipe } from "./mouseOverCvt.pipe";
-import { HOVER_INTERCEPTOR_INJECTOR } from "src/util/injectionTokens";
-import { MouseOverSvc } from "./service";
-
-
-@NgModule({
-  imports: [
-    CommonModule,
-  ],
-  declarations: [
-    MouseHoverDirective,
-    TransformOnhoverSegmentPipe,
-    MouseOverConvertPipe,
-  ],
-  exports: [
-    MouseHoverDirective,
-    TransformOnhoverSegmentPipe,
-    MouseOverConvertPipe,
-  ],
-  providers: [
-    MouseOverSvc,
-    {
-      provide: HOVER_INTERCEPTOR_INJECTOR,
-      useFactory: (svc: MouseOverSvc) => {
-        return {
-          append: svc.append.bind(svc),
-          remove: svc.remove.bind(svc),
-        }
-      },
-      deps: [ MouseOverSvc ]
-    }
-  ]
-})
-
-export class MouseoverModule{}
diff --git a/src/mouseoverModule/mouseover.style.css b/src/mouseoverModule/mouseover.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..15f65ddebd5149c9bafcf843bba786d1e4740009
--- /dev/null
+++ b/src/mouseoverModule/mouseover.style.css
@@ -0,0 +1,11 @@
+:host
+{
+    display: inline-block;
+}
+
+.centered
+{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
diff --git a/src/mouseoverModule/mouseover.template.html b/src/mouseoverModule/mouseover.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..00d649b30be35b8820dc51e1b4eb0d9c1d315101
--- /dev/null
+++ b/src/mouseoverModule/mouseover.template.html
@@ -0,0 +1,17 @@
+<mat-list>
+    <ng-template ngFor [ngForOf]="messages$ | async" let-message>
+
+        <ng-template [ngIf]="message.fontIcon && message.fontSet" [ngIfElse]="noIconTmpl">
+            <mat-list-item class="h-auto">
+                <span [class]="message.fontSet + ' centered ' + message.fontIcon" matListItemIcon></span>
+                <span matListItemTitle>{{ message.message }}</span>
+            </mat-list-item>
+        </ng-template>
+
+        <ng-template #noIconTmpl>
+            <mat-list-item class="h-auto">
+                <span matListItemTitle>{{ message.message }}</span>
+            </mat-list-item>
+        </ng-template>
+    </ng-template>
+</mat-list>
diff --git a/src/mouseoverModule/service.ts b/src/mouseoverModule/service.ts
index eba057bcca83fab20a3e4afebee18de5c8d54efe..284faa28fa0614936ec90b97bb3c698fcc00fcec 100644
--- a/src/mouseoverModule/service.ts
+++ b/src/mouseoverModule/service.ts
@@ -1,17 +1,24 @@
 import { Injectable } from "@angular/core";
 import { BehaviorSubject } from "rxjs";
+import { debounceTime, shareReplay } from "rxjs/operators";
 import { THoverConfig } from "src/util/injectionTokens";
 
-@Injectable()
+@Injectable({
+  providedIn: 'root'
+})
 export class MouseOverSvc {
 
   #messages: THoverConfig[] = []
 
-  messages$ = new BehaviorSubject(this.#messages)
+  #messages$ = new BehaviorSubject(this.#messages)
+  messages$ = this.#messages$.pipe(
+    debounceTime(16),
+    shareReplay(1),
+  )
 
   set messages(messages: THoverConfig[]){
     this.#messages = messages
-    this.messages$.next(this.#messages)
+    this.#messages$.next(this.#messages)
   }
 
   get messages(): THoverConfig[]{
diff --git a/src/mouseoverModule/transformOnhoverSegment.pipe.ts b/src/mouseoverModule/transformOnhoverSegment.pipe.ts
deleted file mode 100644
index 5199a582a1ba2e5084d7097996652a492211a342..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/transformOnhoverSegment.pipe.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Pipe, PipeTransform, SecurityContext } from "@angular/core";
-import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
-
-@Pipe({
-  name: 'transformOnhoverSegment',
-})
-
-export class TransformOnhoverSegmentPipe implements PipeTransform {
-  constructor(private sanitizer: DomSanitizer) {
-
-  }
-
-  private sanitizeHtml(inc: string): SafeHtml {
-    return this.sanitizer.sanitize(SecurityContext.HTML, inc)
-  }
-
-  private getStatus(text: string) {
-    return ` <span class="text-muted">(${this.sanitizeHtml(text)})</span>`
-  }
-
-  public transform(segment: any | number): SafeHtml {
-    return this.sanitizer.bypassSecurityTrustHtml((
-      ( this.sanitizeHtml(segment.name) || segment) +
-      (segment.status
-        ? this.getStatus(segment.status)
-        : '')
-    ))
-  }
-}
diff --git a/src/mouseoverModule/util.spec.ts b/src/mouseoverModule/util.spec.ts
deleted file mode 100644
index 31920019b049537cadb4df833acc11a8d8faf20c..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/util.spec.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import {} from 'jasmine'
-import { forkJoin, Subject } from 'rxjs';
-import { scan, skip, take } from 'rxjs/operators';
-import { temporalPositveScanFn } from './util'
-
-const segmentsPositive = { segments: [{ hello: 'world' }] } as {segments: any}
-const segmentsNegative = { segments: [] }
-
-const userLandmarkPostive = { userLandmark: true }
-const userLandmarkNegative = { userLandmark: null }
-
-describe('temporalPositveScanFn', () => {
-  const subscriptions = []
-  afterAll(() => {
-    while (subscriptions.length > 0) { subscriptions.pop().unsubscribe() }
-  })
-
-  it('should scan obs as expected', (done) => {
-
-    const source = new Subject()
-
-    const testFirstEv = source.pipe(
-      scan(temporalPositveScanFn, []),
-      take(1),
-    )
-
-    const testSecondEv = source.pipe(
-      scan(temporalPositveScanFn, []),
-      skip(1),
-      take(1),
-    )
-
-    const testThirdEv = source.pipe(
-      scan(temporalPositveScanFn, []),
-      skip(2),
-      take(1),
-    )
-
-    const testFourthEv = source.pipe(
-      scan(temporalPositveScanFn, []),
-      skip(3),
-      take(1),
-    )
-
-    forkJoin([
-      testFirstEv,
-      testSecondEv,
-      testThirdEv,
-      testFourthEv,
-    ]).pipe(
-      take(1),
-    ).subscribe(([ arr1, arr2, arr3, arr4 ]) => {
-      expect(arr1).toEqual([ segmentsPositive ] as any)
-      expect(arr2).toEqual([ userLandmarkPostive, segmentsPositive ] as any)
-      expect(arr3).toEqual([ userLandmarkPostive ] as any)
-      expect(arr4).toEqual([])
-      done()
-    })
-
-    source.next(segmentsPositive)
-    source.next(userLandmarkPostive)
-    source.next(segmentsNegative)
-    source.next(userLandmarkNegative)
-  })
-})
diff --git a/src/mouseoverModule/util.ts b/src/mouseoverModule/util.ts
deleted file mode 100644
index 208c01ceebea6038619ee5cb781799632c14d2d7..0000000000000000000000000000000000000000
--- a/src/mouseoverModule/util.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { SxplrRegion, VoiFeature } from "src/atlasComponents/sapi/sxplrTypes"
-import { IAnnotationGeometry } from "src/atlasComponents/userAnnotations/tools/type"
-
-export type TOnHoverObj = {
-  regions: SxplrRegion[]
-  annotation: IAnnotationGeometry
-  voi: VoiFeature
-}
-
-/**
- * Scan function which prepends newest positive (i.e. defined) value
- *
- * e.g. const source = new Subject()
- * source.pipe(
- *  scan(temporalPositveScanFn, [])
- * ).subscribe(this.log.log) // outputs
- *
- *
- *
- */
-export const temporalPositveScanFn = (acc: Array<TOnHoverObj>, curr: Partial<TOnHoverObj>) => {
-
-  const keys = Object.keys(curr)
-
-  // empty array is truthy
-  const isPositive = keys.some(key => Array.isArray(curr[key])
-    ? curr[key].length > 0
-    : !!curr[key]
-  )
-
-  return isPositive
-    ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as Array<TOnHoverObj>
-    : acc.filter(item => !keys.some(key => !!item[key]))
-}
diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts
index 81d404525fd4acdf0b91171a9299d4dc0c5c3b56..2ecdfdf8b203769783b1f47623f60f3c3244ee45 100644
--- a/src/viewerModule/module.ts
+++ b/src/viewerModule/module.ts
@@ -15,14 +15,14 @@ import { QuickTourModule } from "src/ui/quickTour/module";
 import { INJ_ANNOT_TARGET } from "src/atlasComponents/userAnnotations/tools/type";
 import { NEHUBA_INSTANCE_INJTKN } from "./nehuba/util";
 import { map, switchMap } from "rxjs/operators";
-import { TContextArg } from "./viewer.interface";
+import { TViewerEvtCtxData } from "./viewer.interface";
 import { KeyFrameModule } from "src/keyframesModule/module";
 import { ViewerInternalStateSvc } from "./viewerInternalState.service";
 import { SAPI, SAPIModule } from 'src/atlasComponents/sapi';
 import { NehubaVCtxToBbox } from "./pipes/nehubaVCtxToBbox.pipe";
 import { SapiViewsModule, SapiViewsUtilModule } from "src/atlasComponents/sapiViews";
 import { DialogModule } from "src/ui/dialogInfo/module";
-import { MouseoverModule } from "src/mouseoverModule";
+import { MouseOver, MouseOverSvc } from "src/mouseoverModule";
 import { LogoContainer } from "src/ui/logoContainer/logoContainer.component";
 import { FloatingMouseContextualContainerDirective } from "src/util/directives/floatingMouseContextualContainer.directive";
 import { ShareModule } from "src/share";
@@ -40,6 +40,8 @@ import { Store } from "@ngrx/store";
 import { atlasSelection, userPreference } from "src/state";
 import { TabComponent } from "src/components/tab/tab.components";
 import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.directive";
+import { HOVER_INTERCEPTOR_INJECTOR } from "src/util/injectionTokens";
+import { ViewerWrapper } from "./viewerWrapper/viewerWrapper.component";
 
 @NgModule({
   imports: [
@@ -59,7 +61,6 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
     SapiViewsModule,
     SapiViewsUtilModule,
     DialogModule,
-    MouseoverModule,
     ShareModule,
     ATPSelectorModule,
     FeatureModule,
@@ -69,6 +70,7 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
     BottomMenuModule,
     TabComponent,
     
+    MouseOver,
     ExperimentalFlagDirective,
     
     ...(environment.ENABLE_LEAP_MOTION ? [LeapModule] : [])
@@ -78,6 +80,7 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
     NehubaVCtxToBbox,
     LogoContainer,
     FloatingMouseContextualContainerDirective,
+    ViewerWrapper,
   ],
   providers: [
     {
@@ -93,11 +96,11 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
     },
     {
       provide: CONTEXT_MENU_ITEM_INJECTOR,
-      useFactory: (svc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>) => {
+      useFactory: (svc: ContextMenuService<TViewerEvtCtxData<'threeSurfer' | 'nehuba'>>) => {
         return {
           register: svc.register.bind(svc),
           deregister: svc.deregister.bind(svc)
-        } as TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>>
+        } as TContextMenu<TContextMenuReg<TViewerEvtCtxData<'nehuba' | 'threeSurfer'>>>
       },
       deps: [ ContextMenuService ]
     },
@@ -141,6 +144,17 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
       ),
       deps: [ Store, SAPI ]
     },
+    
+    {
+      provide: HOVER_INTERCEPTOR_INJECTOR,
+      useFactory: (svc: MouseOverSvc) => {
+        return {
+          append: svc.append.bind(svc),
+          remove: svc.remove.bind(svc),
+        }
+      },
+      deps: [ MouseOverSvc ]
+    }
   ],
   exports: [
     ViewerCmp,
diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts
index f7e7026c186bf4aa75d663587ffc1c25053ed1f6..d6fb5813fc69c786158e2ac560541bd206ceb0a1 100644
--- a/src/viewerModule/nehuba/module.ts
+++ b/src/viewerModule/nehuba/module.ts
@@ -12,7 +12,6 @@ import { NehubaGlueCmp } from "./nehubaViewerGlue/nehubaViewerGlue.component";
 import { UtilModule } from "src/util";
 import { ComponentsModule } from "src/components";
 import { AngularMaterialModule } from "src/sharedModules";
-import { MouseoverModule } from "src/mouseoverModule";
 import { StatusCardComponent } from "./statusCard/statusCard.component";
 import { ShareModule } from "src/share";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
@@ -40,7 +39,6 @@ import { ExperimentalFlagDirective } from "src/experimental/experimental-flag.di
     UtilModule,
     AngularMaterialModule,
     ComponentsModule,
-    MouseoverModule,
     ShareModule,
     WindowResizeModule,
     NehubaUserLayerModule,
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
index 10d189784c311b6de0c8557e212e86a05774a2ea..263982f2d3c530a3368155b347bd20079b2aba9c 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
@@ -169,85 +169,5 @@ describe('> nehubaViewerGlue.component.ts', () => {
     expect(fixture.componentInstance).toBeTruthy()
   })
 
-  describe('> selectHoveredRegion', () => {
-    let dispatchSpy: jasmine.Spy
-    let clickIntServ: ClickInterceptorService
-    beforeEach(() => {
-      dispatchSpy = spyOn(mockStore, 'dispatch')
-      clickIntServ = TestBed.inject(ClickInterceptorService)
-    })
-    afterEach(() => {
-      dispatchSpy.calls.reset()
-    })
-
-    describe('> if on hover is empty array', () => {
-      let fallbackSpy: jasmine.Spy
-      beforeEach(() => {
-        fallbackSpy = spyOn(clickIntServ, 'fallback')
-        TestBed.createComponent(NehubaGlueCmp)
-        clickIntServ.callRegFns(null)
-      })
-      it('> dispatch not called', () => {
-        expect(dispatchSpy).not.toHaveBeenCalled()
-      })
-      it('> fallback called', () => {
-        expect(fallbackSpy).toHaveBeenCalled()
-      })
-    })
-
-    describe('> if on hover is non object array', () => {
-      let fallbackSpy: jasmine.Spy
-
-      const testObj0 = {
-        segment: 'hello world'
-      }
-      const testObj1 = 'hello world'
-      beforeEach(() => {
-        fallbackSpy = spyOn(clickIntServ, 'fallback')
-        TestBed.createComponent(NehubaGlueCmp)
-        clickIntServ.callRegFns(null)
-      })
-      it('> dispatch not called', () => {
-        expect(dispatchSpy).not.toHaveBeenCalled()
-      })
-      it('> fallback called', () => {
-        expect(fallbackSpy).toHaveBeenCalled()
-      })
-    })
-
-    describe('> if on hover array containing at least 1 obj, only dispatch the first obj', () => {
-      let fallbackSpy: jasmine.Spy
-      const testObj0 = {
-        segment: 'hello world'
-      }
-      const testObj1 = {
-        segment: {
-          foo: 'baz'
-        }
-      }
-      const testObj2 = {
-        segment: {
-          hello: 'world'
-        }
-      }
-      beforeEach(() => {
-        fallbackSpy = spyOn(clickIntServ, 'fallback')
-
-      })
-      afterEach(() => {
-        fallbackSpy.calls.reset()
-      })
-      it('> dispatch called with obj1', () => {
-        TestBed.createComponent(NehubaGlueCmp)
-        clickIntServ.callRegFns(null)
-        const { segment } = testObj1
-      })
-      it('> fallback called (does not intercept)', () => {
-        TestBed.createComponent(NehubaGlueCmp)
-        clickIntServ.callRegFns(null)
-        expect(fallbackSpy).toHaveBeenCalled()
-      })
-    })
-  })
 
 })
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index 1f47b5680c88ac1c21faeca6a89104d4f3e54ce7..a32c784f1ef2af37c0699e5669cec5d2e09ca9d5 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -1,15 +1,10 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { distinctUntilChanged } from "rxjs/operators";
+import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, Output } from "@angular/core";
 import { IViewer, TViewerEvent } from "../../viewer.interface";
 import { NehubaMeshService } from "../mesh.service";
 import { NehubaLayerControlService, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
 import { EXTERNAL_LAYER_CONTROL, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY } from "../layerCtrl.service/layerCtrl.util";
-import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes";
 import { NehubaConfig } from "../config.service";
 import { SET_MESHES_TO_LOAD } from "../constants";
-import { atlasSelection, userInteraction } from "src/state";
 
 
 @Component({
@@ -58,7 +53,6 @@ import { atlasSelection, userInteraction } from "src/state";
 
 export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy {
 
-  private onhoverSegments: SxplrRegion[] = []
   private onDestroyCb: (() => void)[] = []
 
   public nehubaConfig: NehubaConfig
@@ -70,53 +64,4 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy {
   @Output()
   public viewerEvent = new EventEmitter<TViewerEvent<'nehuba'>>()
 
-  constructor(
-    private store$: Store<any>,
-    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
-  ){
-
-    /**
-     * define onclick behaviour
-     */
-    if (clickInterceptor) {
-      const { deregister, register } = clickInterceptor
-      const selOnhoverRegion = this.selectHoveredRegion.bind(this)
-      register(selOnhoverRegion, { last: true })
-      this.onDestroyCb.push(() => deregister(selOnhoverRegion))
-    }
-
-    /**
-     * on hover segment
-     */
-    const onhovSegSub = this.store$.pipe(
-      select(userInteraction.selectors.mousingOverRegions),
-      distinctUntilChanged(),
-    ).subscribe(arr => {
-      this.onhoverSegments = arr
-    })
-    this.onDestroyCb.push(() => onhovSegSub.unsubscribe())
-  }
-
-  private selectHoveredRegion(ev: PointerEvent): boolean{
-    /**
-     * If label indicies are not defined by the ontology, it will be a string in the format of `{ngId}#{labelIndex}`
-     */
-    const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object')
-    if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return true
-
-    if (ev.ctrlKey) {
-      this.store$.dispatch(
-        atlasSelection.actions.toggleRegion({
-          region: trueOnhoverSegments[0]
-        })
-      )
-    } else {
-      this.store$.dispatch(
-        atlasSelection.actions.selectRegion({
-          region: trueOnhoverSegments[0]
-        })
-      )
-    }
-    return true
-  }
 }
diff --git a/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts b/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts
index e3ceaad4673e198e11eba7ca257ed0a334bb1ac6..37a68c4d3aef4acd4ef3474f2e0e4ad4fae1c5cb 100644
--- a/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts
+++ b/src/viewerModule/pipes/nehubaVCtxToBbox.pipe.ts
@@ -1,4 +1,4 @@
-import { TContextArg } from './../viewer.interface';
+import { TViewerEvtCtxData } from './../viewer.interface';
 import { Pipe, PipeTransform } from "@angular/core";
 
 type Point = [number, number, number]
@@ -12,7 +12,7 @@ const MAGIC_RADIUS = 256
 })
 
 export class NehubaVCtxToBbox implements PipeTransform{
-  public transform(event: TContextArg<'nehuba' | 'threeSurfer'>, unit: string = "mm"): BBox{
+  public transform(event: TViewerEvtCtxData<'nehuba' | 'threeSurfer'>, unit: string = "mm"): BBox{
     if (!event) {
       return null
     }
@@ -23,7 +23,7 @@ export class NehubaVCtxToBbox implements PipeTransform{
     if (unit === "mm") {
       divisor = 1e6
     }
-    const { payload } = event as TContextArg<'nehuba'>
+    const { payload } = event as TViewerEvtCtxData<'nehuba'>
     
     if (!payload.nav) return null
 
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
index 9a44a02e1cd012f11d351e66d8b7b5cc195a4336..4a5c1238064d88be6953b2e65411e7c00e25862d 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
@@ -1,12 +1,10 @@
-import { Component, Output, EventEmitter, ElementRef, OnDestroy, AfterViewInit, Inject, Optional, ChangeDetectionStrategy } from "@angular/core";
+import { Component, Output, EventEmitter, ElementRef, OnDestroy, AfterViewInit, Optional, ChangeDetectionStrategy } from "@angular/core";
 import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
 import { BehaviorSubject, combineLatest, concat, forkJoin, from, merge, NEVER, Observable, of, Subject } from "rxjs";
 import { catchError, debounceTime, distinctUntilChanged, filter, map, scan, shareReplay, startWith, switchMap, tap, withLatestFrom } from "rxjs/operators";
 import { ComponentStore, LockError } from "src/viewerModule/componentStore";
 import { select, Store } from "@ngrx/store";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
 import { MatSnackBar } from "src/sharedModules/angularMaterial.exports"
-import { CONST } from 'common/constants'
 import { getUuid, switchMapWaitFor } from "src/util/fn";
 import { AUTO_ROTATE, TInteralStatePayload, ViewerInternalStateSvc } from "src/viewerModule/viewerInternalState.service";
 import { atlasAppearance, atlasSelection } from "src/state";
@@ -400,7 +398,6 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit
     private sapi: SAPI,
     private snackbar: MatSnackBar,
     @Optional() intViewerStateSvc: ViewerInternalStateSvc,
-    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
   ){
     if (intViewerStateSvc) {
       const {
@@ -430,37 +427,6 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit
       this.onDestroyCb.push(() => done())
     }
 
-    /**
-     * intercept click and act
-     */
-    if (clickInterceptor) {
-      const handleClick = (ev: MouseEvent) => {
-
-        // if does not click inside container, ignore
-        if (!(el.nativeElement as HTMLElement).contains(ev.target as HTMLElement)) {
-          return true
-        }
-        
-        if (this.mouseoverRegions.length === 0) return true
-        if (this.mouseoverRegions.length > 1) {
-          this.snackbar.open(CONST.DOES_NOT_SUPPORT_MULTI_REGION_SELECTION, 'Dismiss', {
-            duration: 3000
-          })
-          return true
-        }
-
-        const regions = this.mouseoverRegions.slice(0, 1) as any[]
-        this.store$.dispatch(
-          atlasSelection.actions.setSelectedRegions({ regions })
-        )
-        return true
-      }
-      const { register, deregister } = clickInterceptor
-      register(handleClick)
-      this.onDestroyCb.push(
-        () => { deregister(register) }
-      )
-    }
     
     this.domEl = el.nativeElement
 
diff --git a/src/viewerModule/viewer.interface.ts b/src/viewerModule/viewer.interface.ts
index cbc85aca0e3e2620a0903b4e332dcb92386e8db0..488f8ee120168bb732a2df71742d1ddfe795990c 100644
--- a/src/viewerModule/viewer.interface.ts
+++ b/src/viewerModule/viewer.interface.ts
@@ -35,7 +35,9 @@ export interface IViewerCtx {
   'threeSurfer': TThreeSurferContextInfo
 }
 
-export type TContextArg<K extends keyof IViewerCtx> = ({
+export type ViewerType = "nehuba" | "threeSurfer"
+
+export type TViewerEvtCtxData<K extends ViewerType=ViewerType> = ({
   viewerType: K
   payload: RecursivePartial<IViewerCtx[K]>
 })
@@ -45,25 +47,40 @@ export enum EnumViewerEvt {
   VIEWER_CTX,
 }
 
-type TViewerEventViewerLoaded = {
+export type TViewerEventViewerLoaded = {
   type: EnumViewerEvt.VIEWERLOADED
   data: boolean
 }
 
-export type TViewerEvent<T extends keyof IViewerCtx> = TViewerEventViewerLoaded |
-  {
-    type: EnumViewerEvt.VIEWER_CTX
-    data: TContextArg<T>
-  }
+type TViewerEventCtx<T extends ViewerType=ViewerType> = {
+  type: EnumViewerEvt.VIEWER_CTX
+  data: TViewerEvtCtxData<T>
+}
+
+export type TViewerEvent<
+  T extends ViewerType=ViewerType
+> = TViewerEventViewerLoaded | TViewerEventCtx<T>
+
+export function isViewerCtx(ev: TViewerEvent): ev is TViewerEventCtx {
+  return ev.type === EnumViewerEvt.VIEWER_CTX
+}
+
+export function isNehubaVCtxEvt(ev: TViewerEvent): ev is TViewerEventCtx<"nehuba"> {
+  return ev.type === EnumViewerEvt.VIEWER_CTX && ev.data.viewerType === "nehuba"
+}
+
+export function isThreeSurferVCtxEvt(ev: TViewerEvent): ev is TViewerEventCtx<"threeSurfer"> {
+  return ev.type === EnumViewerEvt.VIEWER_CTX && ev.data.viewerType === "threeSurfer"
+}
 
-export type TSupportedViewers = keyof IViewerCtx
+export type TSupportedViewers = ViewerType
 
-export interface IViewer<K extends keyof IViewerCtx> {
+export interface IViewer<K extends ViewerType> {
   viewerCtrlHandler?: IViewerCtrl
   viewerEvent: EventEmitter<TViewerEvent<K>>
 }
 
 export interface IGetContextInjArg {
-  register: (fn: (contextArg: TContextArg<TSupportedViewers>) => void) => void
-  deregister: (fn: (contextArg: TContextArg<TSupportedViewers>) => void) => void
+  register: (fn: (contextArg: TViewerEvtCtxData<TSupportedViewers>) => void) => void
+  deregister: (fn: (contextArg: TViewerEvtCtxData<TSupportedViewers>) => void) => void
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index fdb4f3c31be246d79ff6aace1d01583633304556..55094ebf67b51781234625188b8b95805de995c2 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,11 +1,11 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, inject } from "@angular/core";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, TemplateRef, ViewChild, inject } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { BehaviorSubject, combineLatest, Observable, of, Subscription } from "rxjs";
+import { BehaviorSubject, combineLatest, Observable, of } from "rxjs";
 import { debounceTime, distinctUntilChanged, map, shareReplay, switchMap, takeUntil } from "rxjs/operators";
 import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
 import { animate, state, style, transition, trigger } from "@angular/animations";
 import { IQuickTourData } from "src/ui/quickTour";
-import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface";
+import { EnumViewerEvt, TViewerEvtCtxData, TSupportedViewers, TViewerEvent } from "../viewer.interface";
 import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
 import { DialogService } from "src/services/dialogService.service";
 import { SAPI } from "src/atlasComponents/sapi";
@@ -55,7 +55,7 @@ interface HasName {
   ]
 })
 
-export class ViewerCmp implements OnDestroy {
+export class ViewerCmp {
 
   public readonly destroy$ = inject(DestroyDirective).destroyed$
 
@@ -74,8 +74,6 @@ export class ViewerCmp implements OnDestroy {
     description: QUICKTOUR_DESC.ATLAS_SELECTOR,
   }
 
-  private subscriptions: Subscription[] = []
-  private onDestroyCb: (() => void)[]  = []
   public viewerLoaded: boolean = false
 
   private selectedATP = this.store$.pipe(
@@ -238,7 +236,7 @@ export class ViewerCmp implements OnDestroy {
 
   constructor(
     private store$: Store<any>,
-    private ctxMenuSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>,
+    private ctxMenuSvc: ContextMenuService<TViewerEvtCtxData<'threeSurfer' | 'nehuba'>>,
     private dialogSvc: DialogService,
     private cdr: ChangeDetectorRef,
     private sapi: SAPI,
@@ -293,51 +291,51 @@ export class ViewerCmp implements OnDestroy {
       this.#fullNavBarSwitch$.next(flag)
     })
 
-    this.subscriptions.push(
-      this.templateSelected$.subscribe(
-        t => this.templateSelected = t
-      ),
-      combineLatest([
-        this.templateSelected$,
-        this.parcellationSelected$,
-        this.selectedAtlas$,
-      ]).pipe(
-        debounceTime(160)
-      ).subscribe(async ([tmpl, parc, atlas]) => {
-        const regex = /pre.?release/i
-        const checkPrerelease = (obj: any) => {
-          if (obj?.name) return regex.test(obj.name)
-          return false
-        }
-        const message: string[] = []
-        if (checkPrerelease(atlas)) {
-          message.push(`- _${atlas.name}_`)
-        }
-        if (checkPrerelease(tmpl)) {
-          message.push(`- _${tmpl.name}_`)
-        }
-        if (checkPrerelease(parc)) {
-          message.push(`- _${parc.name}_`)
-        }
-        if (message.length > 0) {
-          message.unshift(`The following have been tagged pre-release, and may be updated frequently:`)
-          try {
-            await this.dialogSvc.getUserConfirm({
-              title: `Pre-release warning`,
-              markdown: message.join('\n\n'),
-              confirmOnly: true
-            })
-          // eslint-disable-next-line no-empty
-          } catch (e) {
-
-          }
-        }
-      })
+    this.templateSelected$.pipe(
+      takeUntil(this.destroy$)
+    ).subscribe(
+      t => this.templateSelected = t
     )
-  }
 
-  ngAfterViewInit(): void{
-    const cb: TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>> = ({ append, context }) => {
+    combineLatest([
+      this.templateSelected$,
+      this.parcellationSelected$,
+      this.selectedAtlas$,
+    ]).pipe(
+      takeUntil(this.destroy$),
+      debounceTime(160),
+    ).subscribe(async ([tmpl, parc, atlas]) => {
+      const regex = /pre.?release/i
+      const checkPrerelease = (obj: any) => {
+        if (obj?.name) return regex.test(obj.name)
+        return false
+      }
+      const message: string[] = []
+      if (checkPrerelease(atlas)) {
+        message.push(`- _${atlas.name}_`)
+      }
+      if (checkPrerelease(tmpl)) {
+        message.push(`- _${tmpl.name}_`)
+      }
+      if (checkPrerelease(parc)) {
+        message.push(`- _${parc.name}_`)
+      }
+      if (message.length > 0) {
+        message.unshift(`The following have been tagged pre-release, and may be updated frequently:`)
+        try {
+          await this.dialogSvc.getUserConfirm({
+            title: `Pre-release warning`,
+            markdown: message.join('\n\n'),
+            confirmOnly: true
+          })
+        // eslint-disable-next-line no-empty
+        } catch (e) {
+
+        }
+      }
+    })
+    
+    const cb: TContextMenuReg<TViewerEvtCtxData<'nehuba' | 'threeSurfer'>> = ({ append, context }) => {
 
       if (this.#lastSelectedPoint && this.lastViewedPointTmpl) {
         const { point, template, face, vertices } = this.#lastSelectedPoint
@@ -373,14 +371,14 @@ export class ViewerCmp implements OnDestroy {
        */
       let hoveredRegions = []
       if (context.viewerType === 'nehuba') {
-        hoveredRegions = ((context as TContextArg<'nehuba'>).payload.nehuba || []).reduce(
+        hoveredRegions = ((context as TViewerEvtCtxData<'nehuba'>).payload.nehuba || []).reduce(
           (acc, curr) => acc.concat(...curr.regions),
           []
         )
       }
 
       if (context.viewerType === 'threeSurfer') {
-        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload.regions
+        hoveredRegions = (context as TViewerEvtCtxData<'threeSurfer'>).payload.regions
       }
 
       if (hoveredRegions.length > 0) {
@@ -397,14 +395,11 @@ export class ViewerCmp implements OnDestroy {
       return true
     }
     this.ctxMenuSvc.register(cb)
-    this.onDestroyCb.push(
-      () => this.ctxMenuSvc.deregister(cb)
-    )
-  }
 
-  ngOnDestroy(): void {
-    while (this.subscriptions.length) this.subscriptions.pop().unsubscribe()
-    while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+    this.destroy$.subscribe(() => {
+      this.ctxMenuSvc.deregister(cb)
+    })
+
   }
 
   public clearRoi(): void{
@@ -484,48 +479,6 @@ export class ViewerCmp implements OnDestroy {
     )
   }
 
-  public handleViewerEvent(event: TViewerEvent<'nehuba' | 'threeSurfer'>): void{
-    switch(event.type) {
-    case EnumViewerEvt.VIEWERLOADED:
-      this.viewerLoaded = event.data
-      this.cdr.detectChanges()
-      break
-    case EnumViewerEvt.VIEWER_CTX:
-      this.ctxMenuSvc.deepMerge(event.data)
-      if (event.data.viewerType === "nehuba") {
-        const { nehuba, nav } = (event.data as TContextArg<"nehuba">).payload
-        if (nehuba) {
-          const mousingOverRegions = (nehuba || []).reduce((acc, { regions }) => acc.concat(...regions), [])
-          this.store$.dispatch(
-            userInteraction.actions.mouseoverRegions({
-              regions: mousingOverRegions
-            })
-          )
-        }
-        if (nav) {
-          this.store$.dispatch(
-            userInteraction.actions.mouseoverPosition({
-              position: {
-                loc: nav.position as [number, number, number],
-                space: this.templateSelected,
-                spaceId: this.templateSelected.id,
-              }
-            })
-          )
-        }
-      }
-      if (event.data.viewerType === "threeSurfer") {
-        const { regions=[] } = (event.data as TContextArg<"threeSurfer">).payload
-        this.store$.dispatch(
-          userInteraction.actions.mouseoverRegions({
-            regions: regions as SxplrRegion[]
-          })
-        )
-      }
-      break
-    default:
-    }
-  }
 
   public disposeCtxMenu(): void{
     this.ctxMenuSvc.dismissCtxMenu()
@@ -598,4 +551,15 @@ export class ViewerCmp implements OnDestroy {
   nameEql(a: HasName, b: HasName){
     return a.name === b.name
   }
+
+  handleViewerCtxEvent(event: TViewerEvent) {
+    if (event.type === EnumViewerEvt.VIEWERLOADED) {
+      this.viewerLoaded = event.data
+      this.cdr.detectChanges()
+      return
+    }
+    if (event.type === EnumViewerEvt.VIEWER_CTX) {
+      this.ctxMenuSvc.deepMerge(event.data)
+    }
+  }
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
index d72febea43178d6447b9c113f66ccb4ca905e2e3..05cfce23ca6b00d6251b825dec446b216d2da77a 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.style.css
+++ b/src/viewerModule/viewerCmp/viewerCmp.style.css
@@ -93,13 +93,13 @@ mat-drawer
   display: block;
 }
 
-mat-list.contextual-block
+.contextual-block
 {
   display: inline-block;
   background-color:rgba(200,200,200,0.8);
 }
 
-:host-context([darktheme="true"]) mat-list.contextual-block
+:host-context([darktheme="true"]) .contextual-block
 {
   background-color : rgba(30,30,30,0.8);
 }
@@ -155,13 +155,6 @@ sxplr-sapiviews-core-region-region-list-item
   align-items: center;
 }
 
-.centered
-{
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
 .leave-me-alone
 {
   margin-top: 0.5rem;
@@ -194,4 +187,11 @@ sxplr-tab
 {
   display: inline-flex;
   flex-direction: column;
-}
\ No newline at end of file
+}
+
+viewer-wrapper
+{
+  width: 100%;
+  height: 100%;
+  display: block;
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 9528a948def9fa6eeb3a66124b147409c050af1a..4e15b6b1c9644acb5357bc8ad01376bab9e6e144 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -12,30 +12,9 @@
     
 
     <div *ngIf="(media.mediaBreakPoint$ | async) < 2"
-      floatingMouseContextualContainerDirective    
-      iav-mouse-hover
-      #iavMouseHoverContextualBlock="iavMouseHover">
-
-      <mat-list class="contextual-block">
-        <mat-list-item *ngFor="let cvtOutput of iavMouseHoverContextualBlock.currentOnHoverObs$ | async | mouseoverCvt"
-          class="h-auto">
-          <span class="centered" matListItemIcon [class]="cvtOutput.icon.cls"></span>
-          <span matListItemTitle>{{ cvtOutput.text }}</span>
-        </mat-list-item>
-
-        <mat-list-item *ngFor="let message of iavMouseHoverContextualBlock.messages$ | async"
-          class="h-auto">
-          <ng-template [ngIf]="message.fontIcon && message.fontSet">
-            <mat-icon matListItemIcon
-              [fontSet]="message.fontSet"
-              [fontIcon]="message.fontIcon">
-            </mat-icon>
-          </ng-template>
-          <span matListItemTitle>{{ message.message }}</span>
-        </mat-list-item>
-      </mat-list>
+      floatingMouseContextualContainerDirective>
+      <mouseover-info class="contextual-block"></mouseover-info>
     </div>
-
   </div>
 </div>
 
@@ -429,44 +408,9 @@
   <div class="position-absolute w-100 h-100 z-index-1"
     ctx-menu-host
     [ctx-menu-host-tmpl]="viewerCtxMenuTmpl">
-    
-    <ng-container [ngSwitch]="useViewer$ | async">
-
-      <!-- nehuba viewer -->
-      <iav-cmp-viewer-nehuba-glue class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
-        *ngSwitchCase="'nehuba'"
-        (viewerEvent)="handleViewerEvent($event)"
-        #iavCmpViewerNehubaGlue="iavCmpViewerNehubaGlue">
-      </iav-cmp-viewer-nehuba-glue>
-
-      <!-- three surfer (free surfer viewer) -->
-      <tmp-threesurfer-lifecycle class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
-        *ngSwitchCase="'threeSurfer'"
-        (viewerEvent)="handleViewerEvent($event)">
-      </tmp-threesurfer-lifecycle>
-
-      <!-- if not supported, show not supported message -->
-      <div *ngSwitchCase="'notsupported'">Template not supported by any of the viewers</div>
-
-      <!-- by default, show splash screen -->
-      <div class="sxplr-h-100" *ngSwitchDefault>
-        <ng-template [ngIf]="(selectedAtlas$ | async)" [ngIfElse]="splashScreenTmpl">
-          <div class="center-a-div">
-            <div class="loading-atlas-text-container">
-              <spinner-cmp class="fs-200"></spinner-cmp>
-              <span>
-                Loading 
-                {{ (selectedAtlas$ | async).name }}
-              </span>
-            </div>
-          </div>
-        </ng-template>
-        <ng-template #splashScreenTmpl>
-          <ui-splashscreen class="position-absolute left-0 tosxplr-p-0">
-          </ui-splashscreen>
-        </ng-template>
-      </div>
-    </ng-container>
+    <viewer-wrapper
+      (viewer-event)="handleViewerCtxEvent($event)">
+    </viewer-wrapper>
 
   </div>
 </ng-template>
diff --git a/src/viewerModule/viewerWrapper/viewerWrapper.component.ts b/src/viewerModule/viewerWrapper/viewerWrapper.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e7e7cc5ea32de230faa01a7f60c3c779574972c
--- /dev/null
+++ b/src/viewerModule/viewerWrapper/viewerWrapper.component.ts
@@ -0,0 +1,204 @@
+import { Component, ElementRef, Inject, Optional, Output, inject } from "@angular/core";
+import { Observable, Subject, merge } from "rxjs";
+import { TSupportedViewers, TViewerEvent, isNehubaVCtxEvt, isThreeSurferVCtxEvt, isViewerCtx } from "../viewer.interface";
+import { Store, select } from "@ngrx/store";
+import { MainState, atlasAppearance, atlasSelection, userInteraction } from "src/state";
+import { distinctUntilChanged, filter, finalize, map, shareReplay, takeUntil } from "rxjs/operators";
+import { arrayEqual } from "src/util/array";
+import { DestroyDirective } from "src/util/directives/destroy.directive";
+import { CLICK_INTERCEPTOR_INJECTOR, ClickInterceptor, HOVER_INTERCEPTOR_INJECTOR, HoverInterceptor, THoverConfig } from "src/util/injectionTokens";
+import { SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
+
+@Component({
+  selector: 'viewer-wrapper',
+  templateUrl: './viewerWrapper.template.html',
+  styleUrls: [
+    './viewerWrapper.style.css'
+  ],
+  hostDirectives: [
+    DestroyDirective
+  ]
+})
+export class ViewerWrapper {
+  
+  #destroy$ = inject(DestroyDirective).destroyed$
+
+  @Output('viewer-event')
+  viewerEvent$ = new Subject<
+    TViewerEvent
+  >()
+
+  selectedAtlas$ = this.store$.pipe(
+    select(atlasSelection.selectors.selectedAtlas)
+  )
+
+  useViewer$: Observable<TSupportedViewers | 'notsupported'> = this.store$.pipe(
+    select(atlasAppearance.selectors.useViewer),
+    map(useviewer => {
+      if (useviewer === "NEHUBA") return "nehuba"
+      if (useviewer === "THREESURFER") return "threeSurfer"
+      if (useviewer === "NOT_SUPPORTED") return "notsupported"
+      return null
+    })
+  )
+
+  constructor(
+    el: ElementRef,
+    private store$: Store<MainState>,
+    @Optional() @Inject(HOVER_INTERCEPTOR_INJECTOR)
+    hoverInterceptor: HoverInterceptor,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR)
+    clickInterceptor: ClickInterceptor,
+  ){
+
+    this.store$.pipe(
+      select(atlasSelection.selectors.selectedTemplate),
+      takeUntil(this.#destroy$)
+    ).subscribe(tmpl => {
+      this.#selectedTemplate = tmpl
+    })
+
+    /**
+     * handling nehuba event
+     */
+    this.#nehubaViewerCtxEv$.pipe(
+      takeUntil(this.#destroy$)
+    ).subscribe(ev => {
+      const { nehuba, nav } = ev.data.payload
+      if (nehuba) {
+        const mousingOverRegions = (nehuba || []).reduce((acc, { regions }) => acc.concat(...regions), [])
+        this.store$.dispatch(
+          userInteraction.actions.mouseoverRegions({
+            regions: mousingOverRegions
+          })
+        )
+      }
+      if (nav) {
+        this.store$.dispatch(
+          userInteraction.actions.mouseoverPosition({
+            position: {
+              loc: nav.position as [number, number, number],
+              space: this.#selectedTemplate,
+              spaceId: this.#selectedTemplate.id,
+            }
+          })
+        )
+      }
+    })
+
+    /**
+     * handling threesurfer event
+     */
+    this.#threeSurferViewerCtxEv$.pipe(
+      takeUntil(this.#destroy$)
+    ).subscribe(ev => {
+      const { regions = [] } = ev.data.payload
+      this.store$.dispatch(
+        userInteraction.actions.mouseoverRegions({
+          regions: regions as SxplrRegion[]
+        })
+      )
+    })
+
+    if (hoverInterceptor) {
+      let hoverRegionMessages: THoverConfig[] = []
+      const { append, remove } = hoverInterceptor
+      this.#hoveredRegions$.pipe(
+        takeUntil(this.#destroy$),
+        finalize(() => {
+          for (const msg of hoverRegionMessages) {
+            remove(msg)
+          }
+        })
+      ).subscribe(regions => {
+        
+        for (const msg of hoverRegionMessages) {
+          remove(msg)
+        }
+
+        hoverRegionMessages = regions.map(region => ({
+          message: region.name || 'Unknown Region',
+          fontIcon: 'fa-brain',
+          fontSet: 'fas'
+        }))
+
+        for (const msg of hoverRegionMessages){
+          append(msg)
+        }
+      })
+    }
+
+    if (clickInterceptor) {
+      const { register, deregister } = clickInterceptor
+      let hoveredRegions: SxplrRegion[]
+      this.#hoveredRegions$.subscribe(reg => {
+        hoveredRegions = reg as SxplrRegion[]
+      })
+      const handleClick = (ev: PointerEvent) => {
+        if (!el?.nativeElement?.contains(ev.target)) {
+          return true
+        }
+        if (hoveredRegions.length === 0) {
+          return true
+        }
+        if (ev.ctrlKey) {
+          this.store$.dispatch(
+            atlasSelection.actions.toggleRegion({
+              region: hoveredRegions[0]
+            })
+          )
+        } else {
+          this.store$.dispatch(
+            atlasSelection.actions.selectRegion({
+              region: hoveredRegions[0]
+            })
+          )
+        }
+        return true
+      }
+      register(handleClick, { last: true })
+      this.#destroy$.subscribe(() => {
+        deregister(handleClick)
+      })
+    }
+  }
+
+  public handleViewerEvent(event: TViewerEvent): void{
+    this.viewerEvent$.next(event)
+  }
+  
+  #viewerCtxEvent$ = this.viewerEvent$.pipe(
+    filter(isViewerCtx),
+    shareReplay(1),
+  )
+
+  #nehubaViewerCtxEv$ = this.#viewerCtxEvent$.pipe(
+    filter(isNehubaVCtxEvt)
+  )
+
+  #threeSurferViewerCtxEv$ = this.#viewerCtxEvent$.pipe(
+    filter(isThreeSurferVCtxEvt)
+  )
+
+  #hoveredRegions$ = merge(
+    this.#nehubaViewerCtxEv$.pipe(
+      filter(ev => !!ev.data.payload.nehuba),
+      map(ev => {
+        const { nehuba } = ev.data.payload
+        return nehuba.map(n => n.regions).flatMap(v => v)
+      })
+    ),
+    this.#threeSurferViewerCtxEv$.pipe(
+      map(ev => {
+        const { regions = [] } = ev.data.payload
+        return regions
+      })
+    )
+  ).pipe(
+    distinctUntilChanged(
+      arrayEqual((o, n) => o.name === n.name)
+    )
+  )
+  
+  #selectedTemplate: SxplrTemplate = null
+}
diff --git a/src/viewerModule/viewerWrapper/viewerWrapper.style.css b/src/viewerModule/viewerWrapper/viewerWrapper.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/viewerWrapper/viewerWrapper.template.html b/src/viewerModule/viewerWrapper/viewerWrapper.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..5ea7ec0ce2d26719a83a9dd0f1cb175a5f1f286b
--- /dev/null
+++ b/src/viewerModule/viewerWrapper/viewerWrapper.template.html
@@ -0,0 +1,37 @@
+<ng-container [ngSwitch]="useViewer$ | async">
+
+    <!-- nehuba viewer -->
+    <iav-cmp-viewer-nehuba-glue class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
+      *ngSwitchCase="'nehuba'"
+      (viewerEvent)="handleViewerEvent($event)"
+      #iavCmpViewerNehubaGlue="iavCmpViewerNehubaGlue">
+    </iav-cmp-viewer-nehuba-glue>
+
+    <!-- three surfer (free surfer viewer) -->
+    <tmp-threesurfer-lifecycle class="d-block w-100 h-100 position-absolute left-0 tosxplr-p-0"
+      *ngSwitchCase="'threeSurfer'"
+      (viewerEvent)="handleViewerEvent($event)">
+    </tmp-threesurfer-lifecycle>
+
+    <!-- if not supported, show not supported message -->
+    <div *ngSwitchCase="'notsupported'">Template not supported by any of the viewers</div>
+
+    <!-- by default, show splash screen -->
+    <div class="sxplr-h-100" *ngSwitchDefault>
+      <ng-template [ngIf]="(selectedAtlas$ | async)" [ngIfElse]="splashScreenTmpl" let-atlas>
+        <div class="center-a-div">
+          <div class="loading-atlas-text-container">
+            <spinner-cmp class="fs-200"></spinner-cmp>
+            <span>
+              Loading 
+              {{ atlas.name }}
+            </span>
+          </div>
+        </div>
+      </ng-template>
+      <ng-template #splashScreenTmpl>
+        <ui-splashscreen class="position-absolute left-0 tosxplr-p-0">
+        </ui-splashscreen>
+      </ng-template>
+    </div>
+  </ng-container>