diff --git a/common/constants.js b/common/constants.js
index e92bdb8be98ac6aba5a3e1bb33607e348fb83e83..bdc389ddccba9f4b6227cec7357fc6def00443cb 100644
--- a/common/constants.js
+++ b/common/constants.js
@@ -13,6 +13,10 @@
 
     // overlay specific
     CONTEXT_MENU: `Viewer context menu`,
+    ZOOM_IN: 'Zoom in',
+    ZOOM_OUT: 'Zoom out',
+    MAXIMISE_VIEW: 'Maximise this view',
+    UNMAXIMISE_VIEW: 'Undo maximise',
 
     // sharing module
     SHARE_BTN: `Share this view`,
diff --git a/docs/releases/v2.3.0.md b/docs/releases/v2.3.0.md
index 396db42327544c2bc94914ac71ad524ec242fbdd..da2150d30807dcd67059fe4e77df3391ffb976c2 100644
--- a/docs/releases/v2.3.0.md
+++ b/docs/releases/v2.3.0.md
@@ -7,3 +7,4 @@
   - parse min and max, if these metadata are provided
   - allowing for color maps other than jet
 - Updated `README.md`
+- introduced zoom buttons
diff --git a/e2e/chromeOpts.js b/e2e/chromeOpts.js
index 8af7d2b44f19fe2945dca7fc63b8dcd7c5139c11..f5447a0912e3ef936136ed0cbc91b17ba00b3f2e 100644
--- a/e2e/chromeOpts.js
+++ b/e2e/chromeOpts.js
@@ -1,9 +1,11 @@
+const { width, height } = require('./opts')
+
 module.exports = [
   ...(process.env.DISABLE_CHROME_HEADLESS ?  [] : ['--headless']),
   '--no-sandbox',
   '--disable-gpu',
   '--disable-setuid-sandbox',
   "--disable-extensions",
-  '--window-size=800,796',
+  `--window-size=${width},${height}`,
   '--disable-application-cache'
 ]
\ No newline at end of file
diff --git a/e2e/opts.js b/e2e/opts.js
new file mode 100644
index 0000000000000000000000000000000000000000..9b75d287f6ec4ffa0100562d033d65b1eb9c4eb6
--- /dev/null
+++ b/e2e/opts.js
@@ -0,0 +1,4 @@
+module.exports = {
+  width: 800,
+  height: 796
+}
diff --git a/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js b/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..e54ee6f0d684ee588297745de1c36296f172aec6
--- /dev/null
+++ b/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js
@@ -0,0 +1,141 @@
+const { AtlasPage } = require('../util')
+const { width, height } = require('../../opts')
+const { ARIA_LABELS } = require('../../../common/constants')
+
+describe('> zoom in btns on panels', () => {
+  
+  let iavPage
+  const url = '/?templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas'
+
+  describe('> on hover overlay elements', () => {
+    beforeEach(async () => {
+      iavPage = new AtlasPage()
+      await iavPage.init()
+      await iavPage.goto(url)
+    })
+    it('> panel btns do not show', async () => {
+      
+      await iavPage.cursorMoveTo({
+        position: [width / 4, height/4]
+      })
+
+      await iavPage.wait(500)
+
+      const visibleFlags = await iavPage.areVisible(`[aria-label="${ARIA_LABELS.ZOOM_IN}"]`)
+      expect(visibleFlags.length).toBe(4)
+      expect(visibleFlags).toEqual([false, false, false, false])
+      await iavPage.clickSideNavTab()
+    })
+  })
+
+  const delta = 30
+  const arr = [
+    [ width / 2 - delta, height / 2 - delta ],
+    [ width / 2 + delta, height / 2 - delta ],
+    [ width / 2 - delta, height / 2 + delta ],
+    [ width / 2 + delta, height / 2 + delta ],
+  ]
+
+  for (const key of [ ARIA_LABELS.ZOOM_IN, ARIA_LABELS.ZOOM_OUT ]) {
+    describe(`> testing ${key}`, () => {
+
+      for (const idx in arr) {
+        describe(`> panel ${idx}`, () => {
+          beforeAll(async () => {
+            iavPage = new AtlasPage()
+            await iavPage.init()
+            await iavPage.goto(url)
+          })
+          it('> mouse over should show zoom btns', async () => {
+            
+            await iavPage.cursorMoveTo({
+              position: arr[idx]
+            })
+    
+            await iavPage.wait(500)
+            const visibleFlags = await iavPage.areVisible(`[aria-label="${key}"]`)
+            expect(visibleFlags.length).toBe(4)
+    
+            const expectedFlags = [false, false, false, false]
+            expectedFlags[idx] = true
+            expect(visibleFlags).toEqual(expectedFlags)
+          })
+    
+    
+          describe(`> "${key}" btns btn works`, () => {
+            let o, po, pz, z, p,
+            no2, npo2, npz2, nz2, np2
+            beforeAll(async () => {
+    
+              const navState = await iavPage.getNavigationState()
+    
+              o = navState.orientation
+              po = navState.perspectiveOrientation
+              pz = navState.perspectiveZoom
+              z = navState.zoom
+              p = navState.position
+    
+              const arrOut = await iavPage.areAt(`[aria-label="${key}"]`)
+    
+              const positionOut = [
+                arrOut[idx].x + arrOut[idx].width / 2,
+                arrOut[idx].y + arrOut[idx].height / 2
+              ]
+              await iavPage.cursorMoveToAndClick({
+                position: positionOut
+              })
+              await iavPage.wait(2000)
+              const newNavState2 = await iavPage.getNavigationState()
+              no2 = newNavState2.orientation
+              npo2 = newNavState2.perspectiveOrientation
+              npz2 = newNavState2.perspectiveZoom
+              nz2 = newNavState2.zoom
+              np2 = newNavState2.position
+    
+            })
+    
+            it('> expect orientation to be unchanged', () => {
+              expect(o).toEqual(no2)
+            })
+    
+            it('> expects perspective orientation to be unchanged', () => {
+              expect(po).toEqual(npo2)
+            })
+    
+            it('> expect position to be unchanged', () => {
+              expect(p).toEqual(np2)
+            })
+    
+            /**
+             * when iterating over array accessor, the key are actually string
+             */
+            if (Number(idx) === 3) {
+              it('> expects zoom to be unchanged', () => {
+                expect(z).toEqual(nz2)
+              })
+              it('> expects perspectivezoom to increase with zoom out btn', () => {
+                if (key === ARIA_LABELS.ZOOM_OUT) {
+                  expect(pz).toBeLessThan(npz2)
+                } else {
+                  expect(pz).toBeGreaterThan(npz2)
+                }
+              })
+            } else {
+              it('> expects perspective zoom to be unchanged', () => {
+                expect(pz).toEqual(npz2)
+              })
+    
+              it('> expect zoom to  increase with zoom out btn', () => {
+                if (key === ARIA_LABELS.ZOOM_OUT) {
+                  expect(z).toBeLessThan(nz2)
+                } else {
+                  expect(z).toBeGreaterThan(nz2)
+                }
+              })
+            }
+          })
+        })
+      }
+    })
+  }
+})
diff --git a/e2e/src/util.js b/e2e/src/util.js
index c96a61803847c02574306e31ca6f9b5dbaece2d7..5f346536bbb0e53e041f00153d59d7061e88cd6e 100644
--- a/e2e/src/util.js
+++ b/e2e/src/util.js
@@ -174,12 +174,36 @@ class WdBase{
     return isDisplayed
   }
 
+  async areVisible(cssSelector){
+    if (!cssSelector) throw new Error(`getText needs to define css selector`)
+    const els = await this._browser.findElements( By.css( cssSelector ) )
+    const returnArr = []
+
+    for (const el of els) {
+      returnArr.push(await el.isDisplayed())
+    }
+    return returnArr
+  }
+
   async isAt(cssSelector){
     if (!cssSelector) throw new Error(`getText needs to define css selector`)
     const { x, y, width, height } = await this._browser.findElement( By.css(cssSelector) ).getRect()
     return { x, y, width, height }
   }
 
+  async areAt(cssSelector){
+
+    if (!cssSelector) throw new Error(`getText needs to define css selector`)
+    const els = await this._browser.findElements( By.css( cssSelector ) )
+    const returnArr = []
+
+    for (const el of els) {
+      const { x, y, width, height } = await el.getRect()
+      returnArr.push({ x, y, width, height })
+    }
+    return returnArr
+  }
+
   historyBack() {
     return this._browser.navigate().back()
   }
@@ -328,6 +352,7 @@ class WdBase{
 
   // it seems if you set intercept http to be true, you might also want ot set do not automat to be true
   async goto(url = '/', { interceptHttp, doNotAutomate, forceTimeout } = {}){
+    this.__trackingNavigationState__ = false
     const actualUrl = getActualUrl(url)
     if (interceptHttp) {
       this._browser.get(actualUrl)
@@ -920,29 +945,33 @@ class WdIavPage extends WdLayoutPage{
   }
 
   async getNavigationState() {
-    const actualNav = await this._browser.executeScript(async () => {
-      let returnObj, sub
-      const getPr = () =>  new Promise(rs => {
-
-        sub = nehubaViewer.navigationState.all
-          .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
-            returnObj = {
-              orientation: Array.from(orientation),
-              perspectiveOrientation: Array.from(perspectiveOrientation),
-              perspectiveZoom,
-              zoom,
-              position: Array.from(position)
-            }
-            rs()
-          })
+    if (!this.__trackingNavigationState__) {
+      await this._browser.executeScript(async () => {
+        window.__iavE2eNavigationState__ = {}
+        
+        const getPr = () => new Promise(rs => {
+  
+          window.__iavE2eNavigationStateSubptn__ = nehubaViewer.navigationState.all
+            .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
+              window.__iavE2eNavigationState__ = {
+                orientation: Array.from(orientation),
+                perspectiveOrientation: Array.from(perspectiveOrientation),
+                perspectiveZoom,
+                zoom,
+                position: Array.from(position)
+              }
+              rs()
+            })
+        })
+  
+        await getPr()
       })
 
-      await getPr()
-      sub.unsubscribe()
+      this.__trackingNavigationState__ = true
+    }
 
-      return returnObj
-    })
-    return actualNav
+    const returnVal = await this._browser.executeScript(() => window.__iavE2eNavigationState__)
+    return returnVal
   }
 
 }
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index 1f3066bb2843b1853060526c7b1bc90932fd0a7a..ac40aad9cae7d3cb847eae167a8f698b6564ef67 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -43,6 +43,7 @@ export const NEHUBA_CLICK_OVERRIDE = 'NEHUBA_CLICK_OVERRIDE'
 
 import { MIN_REQ_EXPLAINER } from 'src/util/constants'
 import { SlServiceService } from "src/spotlight/sl-service.service";
+import { PureContantService } from "src/util";
 
 /**
  * TODO
@@ -119,6 +120,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     private store: Store<IavRootStoreInterface>,
     private widgetServices: WidgetServices,
     private constantsService: AtlasViewerConstantsServices,
+    private pureConstantService: PureContantService,
     private matDialog: MatDialog,
     private dispatcher$: ActionsSubject,
     private rd: Renderer2,
@@ -272,7 +274,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     }
 
     this.subscriptions.push(
-      this.constantsService.useMobileUI$.subscribe(bool => this.ismobile = bool),
+      this.pureConstantService.useTouchUI$.subscribe(bool => this.ismobile = bool),
     )
 
     this.subscriptions.push(
diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts
index c52d47324a1e515b89bc12d884f2386f9b2a5976..c49765262f89ea15c05488414cdc66eccd2ecc5b 100644
--- a/src/atlasViewer/atlasViewer.constantService.service.ts
+++ b/src/atlasViewer/atlasViewer.constantService.service.ts
@@ -7,8 +7,7 @@ import { LoggingService } from "src/logging";
 import { SNACKBAR_MESSAGE } from "src/services/state/uiState.store";
 import { IavRootStoreInterface } from "../services/stateStore.service";
 import { AtlasWorkerService } from "./atlasViewer.workerService.service";
-
-export const CM_THRESHOLD = `0.05`
+import { PureContantService } from "src/util";
 
 const getUniqueId = () => Math.round(Math.random() * 1e16).toString(16)
 
@@ -21,8 +20,6 @@ export class AtlasViewerConstantsServices implements OnDestroy {
   public darktheme: boolean = false
   public darktheme$: Observable<boolean>
 
-  public useMobileUI$: Observable<boolean>
-
   public citationToastDuration = 7e3
 
   /**
@@ -30,13 +27,6 @@ export class AtlasViewerConstantsServices implements OnDestroy {
    */
   private TIMEOUT = 16000
 
-  /* TODO to be replaced by @id: Landmark/UNIQUE_ID in KG in the future */
-  public testLandmarksChanged: (prevLandmarks: any[], newLandmarks: any[]) => boolean = (prevLandmarks: any[], newLandmarks: any[]) => {
-    return prevLandmarks.every(lm => typeof lm.name !== 'undefined') &&
-      newLandmarks.every(lm => typeof lm.name !== 'undefined') &&
-      prevLandmarks.length === newLandmarks.length
-  }
-
   // instead of using window.location.href, which includes query param etc
   public backendUrl = (BACKEND_URL && `${BACKEND_URL}/`.replace(/\/\/$/, '/')) || `${window.location.origin}${window.location.pathname}`
 
@@ -270,7 +260,8 @@ Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress
     private store$: Store<IavRootStoreInterface>,
     private http: HttpClient,
     private log: LoggingService,
-    private workerService: AtlasWorkerService
+    private workerService: AtlasWorkerService,
+    private pureConstantService: PureContantService
   ) {
 
     this.darktheme$ = this.store$.pipe(
@@ -283,18 +274,12 @@ Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress
       shareReplay(1),
     )
 
-    this.useMobileUI$ = this.store$.pipe(
-      select('viewerConfigState'),
-      select('useMobileUI'),
-      shareReplay(1),
-    )
-
     this.subscriptions.push(
       this.darktheme$.subscribe(flag => this.darktheme = flag),
     )
 
     this.subscriptions.push(
-      this.useMobileUI$.subscribe(bool => {
+      this.pureConstantService.useTouchUI$.subscribe(bool => {
         if (bool) {
           this.showHelpSliceViewMap = this.showHelpSliceViewMobile
           this.showHelpGeneralMap = this.showHelpGeneralMobile
@@ -325,8 +310,6 @@ Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress
     })
   }
 
-  public cyclePanelMessage: string = `[spacebar] to cycle through views`
-
   private dissmissUserLayerSnackbarMessageDesktop = `You can dismiss extra layers with [ESC]`
   private dissmissUserLayerSnackbarMessageMobile = `You can dismiss extra layers in the 🌏 menu`
   public dissmissUserLayerSnackbarMessage: string = this.dissmissUserLayerSnackbarMessageDesktop
diff --git a/src/components/components.module.ts b/src/components/components.module.ts
index f3c9c22e3ae90235868d04fb125dbb98ff0984fe..7125e8b0f63c309f2a4c985b7fedb0cce971ddd4 100644
--- a/src/components/components.module.ts
+++ b/src/components/components.module.ts
@@ -7,7 +7,7 @@ import {  MarkdownDom } from './markdown/markdown.component';
 
 import { CommonModule } from '@angular/common';
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module';
-import { UtilModule } from 'src/util/util.module';
+import { UtilModule } from 'src/util';
 import { SearchResultPaginationPipe } from '../util/pipes/pagination.pipe';
 import { SafeHtmlPipe } from '../util/pipes/safeHtml.pipe'
 import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe';
diff --git a/src/main.module.ts b/src/main.module.ts
index c402df53fcadec932037d8cba0d05cfb40605bb9..9db1a64985d3929e55f47a4fb91cddb4e49f8d18 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -34,7 +34,7 @@ import { DragDropDirective } from "./util/directives/dragDrop.directive";
 import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive";
 import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive";
 import { NewViewerDisctinctViewToLayer } from "./util/pipes/newViewerDistinctViewToLayer.pipe";
-import { UtilModule } from "./util/util.module";
+import { UtilModule } from "src/util";
 import { SpotLightModule } from 'src/spotlight/spot-light.module'
 import { TryMeComponent } from "./ui/tryme/tryme.component";
 
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
index 1a85cdcb3d3d3aa03c6dd4d7a57f569710acc27d..d721aa6feffda9c762c2acecead9e7deb17164ad 100644
--- a/src/services/state/ngViewerState.store.ts
+++ b/src/services/state/ngViewerState.store.ts
@@ -2,13 +2,13 @@ import { Injectable, OnDestroy } from '@angular/core';
 import { Observable, combineLatest, fromEvent, Subscription, from, of } from 'rxjs';
 import { Effect, Actions, ofType } from '@ngrx/effects';
 import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo, debounceTime, catchError, skip, throttleTime } from 'rxjs/operators';
-import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service';
 import { SNACKBAR_MESSAGE } from './uiState.store';
 import { getNgIds, IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.service';
 import { Action, select, Store } from '@ngrx/store'
-import { BACKENDURL } from 'src/util/constants';
+import { BACKENDURL, CYCLE_PANEL_MESSAGE } from 'src/util/constants';
 import { HttpClient } from '@angular/common/http';
 import { INgLayerInterface, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from './ngViewerState.store.helper'
+import { PureContantService } from 'src/util';
 
 export const FOUR_PANEL = 'FOUR_PANEL'
 export const V_ONE_THREE = 'V_ONE_THREE'
@@ -185,7 +185,7 @@ export class NgViewerUseEffect implements OnDestroy {
   constructor(
     private actions: Actions,
     private store$: Store<IavRootStoreInterface>,
-    private constantService: AtlasViewerConstantsServices,
+    private pureConstantService: PureContantService,
     private http: HttpClient,
   ){
 
@@ -343,14 +343,14 @@ export class NgViewerUseEffect implements OnDestroy {
 
     this.toggleMaximiseCycleMessage$ = combineLatest(
       this.toggleMaximiseMode$,
-      this.constantService.useMobileUI$,
+      this.pureConstantService.useTouchUI$,
     ).pipe(
       filter(([_, useMobileUI]) => !useMobileUI),
       map(([toggleMaximiseMode, _]) => toggleMaximiseMode),
       filter(({ payload }) => payload.panelMode && payload.panelMode === SINGLE_PANEL),
       mapTo({
         type: SNACKBAR_MESSAGE,
-        snackbarMessage: this.constantService.cyclePanelMessage,
+        snackbarMessage: CYCLE_PANEL_MESSAGE,
       }),
     )
 
diff --git a/src/services/state/viewerConfig.store.helper.ts b/src/services/state/viewerConfig.store.helper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98977be73c31198196532a55f6e58dcf8b369b2f
--- /dev/null
+++ b/src/services/state/viewerConfig.store.helper.ts
@@ -0,0 +1,6 @@
+export const VIEWER_CONFIG_FEATURE_KEY = 'viewerConfigState'
+export interface IViewerConfigState {
+  gpuLimit: number
+  animation: boolean
+  useMobileUI: boolean
+}
diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts
index 5df3cfdcba9ebfcfea66f43a5787122729a562da..74d2a657288cab2f4469a5eb8eb6c6f85dddf47b 100644
--- a/src/services/state/viewerConfig.store.ts
+++ b/src/services/state/viewerConfig.store.ts
@@ -1,11 +1,8 @@
 import { Action } from "@ngrx/store";
 import { LOCAL_STORAGE_CONST } from "src/util/constants";
 
-export interface StateInterface {
-  gpuLimit: number
-  animation: boolean
-  useMobileUI: boolean
-}
+import { IViewerConfigState as StateInterface } from './viewerConfig.store.helper'
+export { StateInterface }
 
 interface ViewerConfigurationAction extends Action {
   config: Partial<StateInterface>
diff --git a/src/ui/config/config.component.ts b/src/ui/config/config.component.ts
index 4eef2b4403cc18034229655a65f8befc97d866a2..78526889780b6a5be9ec9905cd8dc10ffdf71b94 100644
--- a/src/ui/config/config.component.ts
+++ b/src/ui/config/config.component.ts
@@ -2,13 +2,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
 import { select, Store } from '@ngrx/store';
 import { combineLatest, Observable, Subscription } from 'rxjs';
 import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
-import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service';
 import { NG_VIEWER_ACTION_TYPES, SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store';
 import { VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store'
 import { IavRootStoreInterface } from 'src/services/stateStore.service';
 import { isIdentityQuat } from '../nehubaContainer/util';
 import {MatSlideToggleChange} from "@angular/material/slide-toggle";
 import {MatSliderChange} from "@angular/material/slider";
+import { PureContantService } from 'src/util';
 
 const GPU_TOOLTIP = `Higher GPU usage can cause crashes on lower end machines`
 const ANIMATION_TOOLTIP = `Animation can cause slowdowns in lower end machines`
@@ -53,10 +53,10 @@ export class ConfigComponent implements OnInit, OnDestroy {
 
   constructor(
     private store: Store<IavRootStoreInterface>,
-    private constantService: AtlasViewerConstantsServices,
+    private pureConstantService: PureContantService,
   ) {
 
-    this.useMobileUI$ = this.constantService.useMobileUI$
+    this.useMobileUI$ = this.pureConstantService.useTouchUI$
 
     this.gpuLimit$ = this.store.pipe(
       select('viewerConfigState'),
diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts
index 2db92219d762414eb9d6caadf68c44c7321c638f..0463b9b61ac5447782159317ade99c2d59a02f7c 100644
--- a/src/ui/databrowserModule/databrowser.module.ts
+++ b/src/ui/databrowserModule/databrowser.module.ts
@@ -4,7 +4,7 @@ import { FormsModule } from "@angular/forms";
 import { ComponentsModule } from "src/components/components.module";
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
 import { DoiParserPipe } from "src/util/pipes/doiPipe.pipe";
-import { UtilModule } from "src/util/util.module";
+import { UtilModule } from "src/util";
 import { DataBrowser } from "./databrowser/databrowser.component";
 import { KgSingleDatasetService } from "./kgSingleDatasetService.service"
 import { ModalityPicker, SortModalityAlphabeticallyPipe } from "./modalityPicker/modalityPicker.component";
diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts
index 7187071b9950bcfc6df08d956f3fbd7dbecb67f2..6b74bfde7680c71da8cc14b92a14119a78564a92 100644
--- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts
+++ b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts
@@ -3,7 +3,12 @@ import { select, Store } from "@ngrx/store";
 import { Observable } from "rxjs";
 import { distinctUntilChanged, map } from "rxjs/operators";
 import { SINGLE_PANEL } from "src/services/state/ngViewerState.store";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
+import { ARIA_LABELS } from 'common/constants'
+
+const {
+  MAXIMISE_VIEW,
+  UNMAXIMISE_VIEW,
+} = ARIA_LABELS
 
 @Component({
   selector: 'maximise-panel-button',
@@ -15,6 +20,9 @@ import { IavRootStoreInterface } from "src/services/stateStore.service";
 
 export class MaximmisePanelButton {
 
+  public ARIA_LABEL_MAXIMISE_VIEW = MAXIMISE_VIEW
+  public ARIA_LABEL_UNMAXIMISE_VIEW = UNMAXIMISE_VIEW
+
   @Input() public panelIndex: number
 
   private panelMode$: Observable<string>
@@ -23,7 +31,7 @@ export class MaximmisePanelButton {
   public isMaximised$: Observable<boolean>
 
   constructor(
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
   ) {
     this.panelMode$ = this.store$.pipe(
       select('ngViewerState'),
diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html
index 62e7289790c7ab96f694036a9ec94706fa0dff50..03cf3cd01e57071b42cb3234dfadfe6eab5f34b1 100644
--- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html
+++ b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html
@@ -1,10 +1,17 @@
-<button
-  [matTooltip]="(isMaximised$ | async) ? 'Restore four panel view' : 'Maximise this panel'"
-  mat-icon-button
-  color="primary">
-  <i *ngIf="isMaximised$ | async; else expandIconTemplate" class="fas fa-compress"></i>
-</button>
+<ng-content *ngTemplateOutlet="maximiseBtnTmpl; context: { $implicit: (isMaximised$ | async) }">
+</ng-content>
 
-<ng-template #expandIconTemplate>
-  <i class="fas fa-expand"></i>
-</ng-template>
\ No newline at end of file
+<ng-template #maximiseBtnTmpl let-ismaximised>
+
+  <button
+    [matTooltip]="ismaximised ? 'Restore four panel view' : 'Maximise this panel'"
+    mat-icon-button
+    [attr.aria-label]="ismaximised ? ARIA_LABEL_UNMAXIMISE_VIEW : ARIA_LABEL_MAXIMISE_VIEW"
+    color="primary">
+    <i *ngIf="ismaximised; else expandIconTemplate" class="fas fa-compress"></i>
+  </button>
+
+  <ng-template #expandIconTemplate>
+    <i class="fas fa-expand"></i>
+  </ng-template>
+</ng-template>
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8724619c41adae065f2fa0a7f865d077ccd5ce37
--- /dev/null
+++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
@@ -0,0 +1,52 @@
+import { TestBed } from "@angular/core/testing"
+import { NehubaContainer } from "./nehubaContainer.component"
+import { provideMockStore } from "@ngrx/store/testing"
+
+/**
+ * access defaultState before initialization error
+ * TODO figure out why
+ */
+
+describe('> nehubaContainer.component.ts', () => {
+  // describe('> NehubaContainer', () => {
+  //   beforeEach(() => {
+  //     TestBed.configureTestingModule({
+  //       imports: [
+
+  //       ],
+  //       declarations: [
+  //         NehubaContainer
+  //       ],
+  //       providers: [
+  //         provideMockStore({
+  //           initialState: {}
+  //         })
+  //       ]
+  //     }).compileComponents()
+  //   })
+  //   it('> can be init', () => {
+  //     const fixture = TestBed.createComponent(NehubaContainer)
+    // })
+    // describe('> loading indicator', () => {
+    //   it('> appears on init', () => {
+    //     const fixture = TestBed.createComponent(NehubaContainer)
+    //     fixture.componentInstance.viewerLoaded = true
+    //     fixture.detectChanges()
+    //     const els = fixture.debugElement.queryAll( By.css(`.loadingIndicator`) )
+    //     expect(els.length).toBe(3)
+    //   })
+    // })
+
+    // describe('> panel control', () => {
+    //   it('> appears on mouse over', () => {
+    //     /**
+    //      * TODO implement mouse over
+    //      */
+    //   })
+
+    //   it('> on click of zoom btns, calls appropriate fn', () => {
+
+    //   })
+    // })
+  // })
+})
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
index 1ee340abf89b2ba8295496df670f52e0bb0c9c33..11f4c43b96e4f78ff48254ad76d2fd6632175472 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.ts
@@ -23,14 +23,21 @@ import {
 import { LoggingService } from "src/logging";
 import { FOUR_PANEL, H_ONE_THREE, NG_VIEWER_ACTION_TYPES, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store";
 import { SELECT_REGIONS_WITH_ID, VIEWERSTATE_ACTION_TYPES } from "src/services/state/viewerState.store";
-import { ADD_NG_LAYER, generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, getNgIds, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, isDefined, MOUSE_OVER_LANDMARK, NgViewerStateInterface, REMOVE_NG_LAYER, safeFilter, ViewerStateInterface, IavRootStoreInterface } from "src/services/stateStore.service";
+import { ADD_NG_LAYER, generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, getNgIds, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, isDefined, MOUSE_OVER_LANDMARK, NgViewerStateInterface, REMOVE_NG_LAYER, safeFilter } from "src/services/stateStore.service";
 import { getExportNehuba, isSame } from "src/util/fn";
 import { AtlasViewerAPIServices, IUserLandmark } from "src/atlasViewer/atlasViewer.apiService.service";
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
 import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
 import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, calculateSliceZoomFactor } from "./util";
 import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive";
 import { ITunableProp } from "./mobileOverlay/mobileOverlay.component";
+import { compareLandmarksChanged } from "src/util/constants";
+import { PureContantService } from "src/util";
+import { ARIA_LABELS } from 'common/constants'
+
+const { 
+  ZOOM_IN,
+  ZOOM_OUT,
+} = ARIA_LABELS
 
 const isFirstRow = (cell: HTMLElement) => {
   const { parentElement: row } = cell
@@ -74,6 +81,9 @@ const scanFn: (acc: [boolean, boolean, boolean], curr: CustomEvent) => [boolean,
 
 export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
 
+  public ARIA_LABEL_ZOOM_IN = ZOOM_IN
+  public ARIA_LABEL_ZOOM_OUT = ZOOM_OUT
+
   @ViewChild(NehubaViewerContainerDirective,{static: true})
   public nehubaContainerDirective: NehubaViewerContainerDirective
 
@@ -87,10 +97,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
 
   public viewerLoaded: boolean = false
 
-  private sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]>
-  public sliceViewLoading0$: Observable<boolean>
-  public sliceViewLoading1$: Observable<boolean>
-  public sliceViewLoading2$: Observable<boolean>
+  public sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]>
   public perspectiveViewLoading$: Observable<string|null>
 
   private templateSelected$: Observable<any>
@@ -135,22 +142,21 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
   public viewPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement] = [null, null, null, null]
   public panelMode$: Observable<string>
 
-  private panelOrder: string
   public panelOrder$: Observable<string>
   private redrawLayout$: Observable<[string, string]>
 
   public hoveredPanelIndices$: Observable<number>
 
   constructor(
-    private constantService: AtlasViewerConstantsServices,
+    private pureConstantService: PureContantService,
     private apiService: AtlasViewerAPIServices,
-    private store: Store<IavRootStoreInterface>,
+    private store: Store<any>,
     private elementRef: ElementRef,
     private log: LoggingService,
     private cdr: ChangeDetectorRef
   ) {
 
-    this.useMobileUI$ = this.constantService.useMobileUI$
+    this.useMobileUI$ = this.pureConstantService.useTouchUI$
 
     this.panelMode$ = this.store.pipe(
       select('ngViewerState'),
@@ -164,7 +170,6 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
       select('panelOrder'),
       distinctUntilChanged(),
       shareReplay(1),
-      tap(panelOrder => this.panelOrder = panelOrder),
     )
 
     this.redrawLayout$ = this.store.pipe(
@@ -215,7 +220,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
       select('dataStore'),
       safeFilter('fetchedSpatialData'),
       map(state => state.fetchedSpatialData),
-      distinctUntilChanged(this.constantService.testLandmarksChanged),
+      distinctUntilChanged(compareLandmarksChanged),
       debounceTime(300),
     )
 
@@ -227,24 +232,10 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
 
     this.sliceViewLoadingMain$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe(
       scan(scanFn, [null, null, null]),
+      startWith([true, true, true] as [boolean, boolean, boolean]),
       shareReplay(1),
     )
 
-    this.sliceViewLoading0$ = this.sliceViewLoadingMain$
-      .pipe(
-        map(arr => arr[0]),
-      )
-
-    this.sliceViewLoading1$ = this.sliceViewLoadingMain$
-      .pipe(
-        map(arr => arr[1]),
-      )
-
-    this.sliceViewLoading2$ = this.sliceViewLoadingMain$
-      .pipe(
-        map(arr => arr[2]),
-      )
-
     /* missing chunk perspective view */
     this.perspectiveViewLoading$ = fromEvent(this.elementRef.nativeElement, 'perpspectiveRenderEvent')
       .pipe(
@@ -719,10 +710,6 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
     this.subscriptions.forEach(s => s.unsubscribe())
   }
 
-  public test(){
-    console.log('test')
-  }
-
   public toggleMaximiseMinimise(index: number) {
     this.store.dispatch({
       type: NG_VIEWER_ACTION_TYPES.TOGGLE_MAXIMISE,
@@ -944,6 +931,22 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
     }
   }
 
+  public zoomNgView(panelIndex: number, factor: number) {
+    const ngviewer = this.nehubaViewer?.nehubaViewer?.ngviewer
+    if (!ngviewer) throw new Error(`ngviewer not defined!`)
+
+    /**
+     * panelIndex < 3 === slice view
+     */
+    if (panelIndex < 3) {
+      /**
+       * factor > 1 === zoom out
+       */
+      ngviewer.navigationState.zoomBy(factor)
+    } else {
+      ngviewer.perspectiveNavigationState.zoomBy(factor)
+    }
+  }
 }
 
 export const identifySrcElement = (element: HTMLElement) => {
diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css
index 795c4084b41770f293712671d0709ccb87c23938..14afcb960a6748c0f06936e8fc2cc569cad470dc 100644
--- a/src/ui/nehubaContainer/nehubaContainer.style.css
+++ b/src/ui/nehubaContainer/nehubaContainer.style.css
@@ -19,6 +19,9 @@ current-layout
 {
   top: 0;
   left: 0;
+  /** z index of current layout has to be higher than that of layout-floating-container, or status container will cover panel control */
+  /** TODO decide which behaviour is more nature */
+  z-index: 6;
 }
 
 div[landmarkMasterContainer]
@@ -117,36 +120,41 @@ div#scratch-pad
   bottom: 0;
 }
 
-maximise-panel-button
+.overlay-btn-container
 {
-  transition: opacity 170ms ease-in-out,
-    transform 250ms ease-in-out;
-
   position: absolute;
   bottom: 0;
   right: 0;
 }
 
+.status-card-container
+{
+  position:absolute;
+  left:1em;
+  bottom:1em;
+  width : 20em;
+  pointer-events: all;
+}
+
 /* if not mobile, then show on hover */
-maximise-panel-button
+
+.opacity-crossfade
+{
+  transition: opacity 170ms ease-in-out,
+    transform 250ms ease-in-out;
+}
+
+.opacity-crossfade
 {
+
   opacity: 0.0;
   pointer-events: none;
 }
 
-maximise-panel-button.onHover,
-maximise-panel-button:hover,
-:host-context([ismobile="true"]) maximise-panel-button
+.opacity-crossfade.onHover,
+.opacity-crossfade:hover,
+:host-context([ismobile="true"]) .opacity-crossfade
 {
   opacity: 1.0 !important;
   pointer-events: all !important;
 }
-
-.status-card-container
-{
-  position:absolute;
-  left:1em;
-  bottom:1em;
-  width : 20em;
-  pointer-events: all;
-}
diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html
index 5d4fd613d2ea4e032bfba9a1deb596a81bcc3392..01662e13500a7fd8d5f2704f6f63670ab213db75 100644
--- a/src/ui/nehubaContainer/nehubaContainer.template.html
+++ b/src/ui/nehubaContainer/nehubaContainer.template.html
@@ -10,20 +10,20 @@
 
 <current-layout *ngIf="viewerLoaded" class="position-absolute w-100 h-100 d-block pe-none">
   <div class="w-100 h-100 position-relative" cell-i>
-    <ng-content *ngTemplateOutlet="overlayi"></ng-content>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: 0 }"></ng-content>
   </div>
   <div class="w-100 h-100 position-relative" cell-ii>
-    <ng-content *ngTemplateOutlet="overlayii"></ng-content>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: 1 }"></ng-content>
   </div>
   <div class="w-100 h-100 position-relative" cell-iii>
-    <ng-content *ngTemplateOutlet="overlayiii"></ng-content>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: 2 }"></ng-content>
   </div>
   <div class="w-100 h-100 position-relative" cell-iv>
-    <ng-content *ngTemplateOutlet="overlayiv"></ng-content>
+    <ng-content *ngTemplateOutlet="overlayPerspectiveTmpl"></ng-content>
   </div>
 </current-layout>
 
-<layout-floating-container *ngIf="viewerLoaded">
+<layout-floating-container *ngIf="viewerLoaded" [zIndex]="5">
 
   <!-- StatusCard container-->
   <div class="status-card-container muted-7">
@@ -65,84 +65,48 @@
 </ng-template>
 
 <!-- overlay templates -->
-<!-- inserted using ngTemplateOutlet -->
-<ng-template #overlayi>
-  <layout-floating-container pos00 landmarkContainer>
-    <nehuba-2dlandmark-unit *ngFor="let spatialData of (selectedPtLandmarks$ | async)"
-      (mouseenter)="handleMouseEnterLandmark(spatialData)"
-      (mouseleave)="handleMouseLeaveLandmark(spatialData)"
-      [highlight]="spatialData.highlight ? spatialData.highlight : false"
-      [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'"
-      [positionX]="getPositionX(0,spatialData)"
-      [positionY]="getPositionY(0,spatialData)"
-      [positionZ]="getPositionZ(0,spatialData)">
-    </nehuba-2dlandmark-unit>
+
+<!-- perspective view tmpl -->
+<ng-template #overlayPerspectiveTmpl>
+  <layout-floating-container landmarkContainer>
 
     <!-- maximise/minimise button -->
-    <maximise-panel-button
-      (click)="toggleMaximiseMinimise(0)"
-      [ngClass]="{ onHover: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === 0 }"
-      [touch-side-class]="0"
-      class="pe-all">
-    </maximise-panel-button>
+    <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { panelIndex: 3, visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === 3 }">
+    </ng-container>
     
-    <div *ngIf="sliceViewLoading0$ | async" class="loadingIndicator">
-      <div class="spinnerAnimationCircle">
+    <div *ngIf="perspectiveViewLoading$ | async" class="loadingIndicator">
+      <div class="spinnerAnimationCircle"></div>
 
+      <div perspectiveLoadingText>
+        {{ perspectiveViewLoading$ | async }}
       </div>
     </div>
   </layout-floating-container>
 </ng-template>
 
-<ng-template #overlayii>
-  <layout-floating-container pos01 landmarkContainer>
-    <nehuba-2dlandmark-unit *ngFor="let spatialData of (selectedPtLandmarks$ | async)"
-      (mouseenter)="handleMouseEnterLandmark(spatialData)"
-      (mouseleave)="handleMouseLeaveLandmark(spatialData)"
-      [highlight]="spatialData.highlight ? spatialData.highlight : false"
-      [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'"
-      [positionX]="getPositionX(1,spatialData)"
-      [positionY]="getPositionY(1,spatialData)"
-      [positionZ]="getPositionZ(1,spatialData)">
-    </nehuba-2dlandmark-unit>
-
-    <!-- maximise/minimise button -->
-    <maximise-panel-button
-      (click)="toggleMaximiseMinimise(1)"
-      [ngClass]="{ onHover: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === 1 }"
-      [touch-side-class]="1"
-      class="pe-all">
-    </maximise-panel-button>
+<!-- slice view overlay tmpl -->
+<ng-template #ngPanelOverlayTmpl let-panelIndex="panelIndex">
 
-    <div *ngIf="sliceViewLoading1$ | async" class="loadingIndicator">
-      <div class="spinnerAnimationCircle">
+  <!-- nb this slice view is not suitable for perspective view! -->
+  <layout-floating-container *ngIf="panelIndex < 3" landmarkContainer>
 
-      </div>
-    </div>
-  </layout-floating-container>
-</ng-template>
+    <!-- only show landmarks in slice views -->
 
-<ng-template #overlayiii>
-  <layout-floating-container pos10 landmarkContainer>
     <nehuba-2dlandmark-unit *ngFor="let spatialData of (selectedPtLandmarks$ | async)"
       (mouseenter)="handleMouseEnterLandmark(spatialData)"
       (mouseleave)="handleMouseLeaveLandmark(spatialData)"
       [highlight]="spatialData.highlight ? spatialData.highlight : false"
       [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'"
-      [positionX]="getPositionX(2,spatialData)"
-      [positionY]="getPositionY(2,spatialData)"
-      [positionZ]="getPositionZ(2,spatialData)">
+      [positionX]="getPositionX(panelIndex, spatialData)"
+      [positionY]="getPositionY(panelIndex, spatialData)"
+      [positionZ]="getPositionZ(panelIndex, spatialData)">
     </nehuba-2dlandmark-unit>
 
     <!-- maximise/minimise button -->
-    <maximise-panel-button
-      (click)="toggleMaximiseMinimise(2)"
-      [ngClass]="{ onHover: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === 2 }"
-      [touch-side-class]="2"
-      class="pe-all">
-    </maximise-panel-button>
+    <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { panelIndex: panelIndex, visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === panelIndex }">
+    </ng-container>
 
-    <div *ngIf="sliceViewLoading2$ | async" class="loadingIndicator">
+    <div *ngIf="(sliceViewLoadingMain$ | async)[panelIndex]" class="loadingIndicator">
       <div class="spinnerAnimationCircle">
 
       </div>
@@ -150,23 +114,30 @@
   </layout-floating-container>
 </ng-template>
 
-<ng-template #overlayiv>
-  <layout-floating-container pos11 landmarkContainer>
+<!-- panel control template -->
+<ng-template #panelCtrlTmpl let-panelIndex="panelIndex" let-visible="visible">
+
+  <div class="opacity-crossfade pe-all overlay-btn-container"
+    [ngClass]="{ onHover: visible }">
+
+    <!-- factor < 1.0 === zoom in -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 0.9)"
+      [attr.aria-label]="ARIA_LABEL_ZOOM_IN">
+      <i class="fas fa-search-plus"></i>
+    </button>
+
+    <!-- factor > 1.0 === zoom out -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 1.1)"
+      [attr.aria-label]="ARIA_LABEL_ZOOM_OUT">
+      <i class="fas fa-search-minus"></i>
+    </button>
 
-    <!-- maximise/minimise button -->
     <maximise-panel-button
-      (click)="toggleMaximiseMinimise(3)"
-      [ngClass]="{ onHover: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async  )) === 3 }"
-      [touch-side-class]="3"
-      class="pe-all">
+      (click)="toggleMaximiseMinimise(panelIndex)"
+      [touch-side-class]="panelIndex">
     </maximise-panel-button>
-    
-    <div *ngIf="perspectiveViewLoading$ | async" class="loadingIndicator">
-      <div class="spinnerAnimationCircle"></div>
+  </div>
 
-      <div perspectiveLoadingText>
-        {{ perspectiveViewLoading$ | async }}
-      </div>
-    </div>
-  </layout-floating-container>
 </ng-template>
diff --git a/src/ui/searchSideNav/searchSideNav.component.spec.ts b/src/ui/searchSideNav/searchSideNav.component.spec.ts
index cb7d73c8762e5563526ee66839e877b2106f6460..95dd9421966970e7504bf6d85f6e9562485d53c1 100644
--- a/src/ui/searchSideNav/searchSideNav.component.spec.ts
+++ b/src/ui/searchSideNav/searchSideNav.component.spec.ts
@@ -1,16 +1,11 @@
 import { async, TestBed } from '@angular/core/testing'
 import { AngularMaterialModule } from '../../ui/sharedModules/angularMaterial.module'
-// import { UIModule } from '../ui.module'
 import { SearchSideNav } from './searchSideNav.component'
 import { provideMockStore } from '@ngrx/store/testing'
-// import { defaultRootState } from 'src/services/stateStore.service'
-import { By } from '@angular/platform-browser'
-import { CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling'
 import { COLIN, JUBRAIN_COLIN_CH123_LEFT, JUBRAIN_COLIN_CH123_RIGHT, JUBRAIN_COLIN, HttpMockRequestInterceptor } from 'spec/util'
 import { HTTP_INTERCEPTORS } from '@angular/common/http'
 import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
-import { UtilModule } from 'src/util/util.module'
-// import { ViewerStateController } from '../viewerStateController/viewerStateCFull/viewerState.component'
+import { UtilModule } from 'src/util'
 import { TemplateParcellationHasMoreInfo } from 'src/util/pipes/templateParcellationHasMoreInfo.pipe'
 import { AppendtooltipTextPipe } from 'src/util/pipes/appendTooltipText.pipe'
 import { BinSavedRegionsSelectionPipe } from '../viewerStateController/viewerState.pipes'
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index b58d1b66231b6966622ac15e7fd9f6227a310053..a112f1863088c36bff712de6cdc72ae49b7a7498 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -29,7 +29,7 @@ import { AppendtooltipTextPipe } from "src/util/pipes/appendTooltipText.pipe";
 import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe";
 import { GetFileExtension } from "src/util/pipes/getFileExt.pipe";
 import { GetFilenamePipe } from "src/util/pipes/getFilename.pipe";
-import { UtilModule } from "src/util/util.module";
+import { UtilModule } from "src/util";
 import { DownloadDirective } from "../util/directives/download.directive";
 import { SpatialLandmarksToDataBrowserItemPipe } from "../util/pipes/spatialLandmarksToDatabrowserItem.pipe";
 import { ConfigComponent } from './config/config.component'
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
index 0c33aa73b4406e23c3053c188d314a0979ea3e4a..eb73aa54adbbd2cd1a40dd6a66f74af18501950e 100644
--- a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
+++ b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
@@ -3,7 +3,6 @@ import { FormControl } from "@angular/forms";
 import { select, Store } from "@ngrx/store";
 import { combineLatest, Observable } from "rxjs";
 import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, take, tap } from "rxjs/operators";
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
 import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect";
 import { ADD_TO_REGIONS_SELECTION_WITH_IDS, CHANGE_NAVIGATION, SELECT_REGIONS } from "src/services/state/viewerState.store";
 import { generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, IavRootStoreInterface } from "src/services/stateStore.service";
@@ -11,6 +10,7 @@ import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerState.base";
 import { LoggingService } from "src/logging";
 import { MatDialog } from "@angular/material/dialog";
 import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
+import { PureContantService } from "src/util";
 
 const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase().includes(searchTerm.toLowerCase())
   || (region.relatedAreas && region.relatedAreas.some(relatedArea => relatedArea.name && relatedArea.name.toLowerCase().includes(searchTerm.toLowerCase())))
@@ -44,11 +44,11 @@ export class RegionTextSearchAutocomplete {
   constructor(
     private store$: Store<IavRootStoreInterface>,
     private dialog: MatDialog,
-    private constantService: AtlasViewerConstantsServices,
+    private pureConstantService: PureContantService,
     private log: LoggingService
   ) {
 
-    this.useMobileUI$ = this.constantService.useMobileUI$
+    this.useMobileUI$ = this.pureConstantService.useTouchUI$
 
     const viewerState$ = this.store$.pipe(
       select('viewerState'),
diff --git a/src/util/constants.ts b/src/util/constants.ts
index 0a0b993c6cfa54b69687f37479bf4ea704ace688..91a7cc447b197bc7d90160e9932eca612487d41b 100644
--- a/src/util/constants.ts
+++ b/src/util/constants.ts
@@ -106,3 +106,11 @@ export const PMAP_DEFAULT_CONFIG = {
   lowThreshold: 0.05,
   removeBg: true
 }
+
+export const compareLandmarksChanged: (prevLandmarks: any[], newLandmarks: any[]) => boolean = (prevLandmarks: any[], newLandmarks: any[]) => {
+  return prevLandmarks.every(lm => typeof lm.name !== 'undefined') &&
+    newLandmarks.every(lm => typeof lm.name !== 'undefined') &&
+    prevLandmarks.length === newLandmarks.length
+}
+
+export const CYCLE_PANEL_MESSAGE = `[spacebar] to cycle through views`
diff --git a/src/util/index.ts b/src/util/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2a2a8efd93d7369454b862d11509288af3c9994
--- /dev/null
+++ b/src/util/index.ts
@@ -0,0 +1,2 @@
+export { UtilModule } from './util.module'
+export { PureContantService } from './pureConstant.service'
diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff91b97cc921743853eb2d3052feaa77c9fdffd0
--- /dev/null
+++ b/src/util/pureConstant.service.ts
@@ -0,0 +1,23 @@
+import { Injectable } from "@angular/core";
+import { Store, createSelector, select } from "@ngrx/store";
+import { Observable } from "rxjs";
+import { VIEWER_CONFIG_FEATURE_KEY, IViewerConfigState } from "src/services/state/viewerConfig.store.helper";
+import { shareReplay } from "rxjs/operators";
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class PureContantService{
+  public useTouchUI$: Observable<boolean>
+  private useTouchUiSelector = createSelector(
+    state => state[VIEWER_CONFIG_FEATURE_KEY],
+    (state: IViewerConfigState) => state.useMobileUI
+  )
+  constructor(private store: Store<any>){
+    this.useTouchUI$ = this.store.pipe(
+      select(this.useTouchUiSelector),
+      shareReplay(1)
+    )
+  }
+}