diff --git a/common/helpOnePager.md b/common/helpOnePager.md
index 2f66d4b38b167b2f9ac0d93a93f0ca347da43c3d..be7927406de2cdd4130a98f2035c3894795c5115 100644
--- a/common/helpOnePager.md
+++ b/common/helpOnePager.md
@@ -4,7 +4,7 @@
 | Oblique rotation | `<shift>` + `[drag]` any of the slice views | `[rotate]` any of the slice views |
 | Zoom | `[mousewheel]` | `[pinch zoom]` |
 | Zoom | `[hover]` on any slice views > `[click]` magnifier | `[tap]` on magnifier |
-| Next slice | `<ctrl>` + `[mousewheel]` | - |
+| Next slice | `<ctrl>` + `[mousewheel]` / `[p]`revious / `[n]`ext | - |
 | Next 10 slice | `<shift>` + `[mousewheel]` | - |
 | Toggle delineation | `[q]` | - |
 | Toggle cross hair | `[a]` | - |
diff --git a/docs/releases/v2.14.0.md b/docs/releases/v2.14.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..0339fb1b060df2270fc83d5ef8cc26bead46987b
--- /dev/null
+++ b/docs/releases/v2.14.0.md
@@ -0,0 +1,10 @@
+# v2.14.0
+
+## Feature
+
+- added `[p]` and `[n]` as keyboard shortcut to navigate previous/next slice
+- experimental support for other versions of regions
+
+## Behind the scenes
+
+- Minor refactoring
diff --git a/e2e/checklist.md b/e2e/checklist.md
index 8378541a0f49d69c4ca72bd7a48d9489b2a185cb..d6f3f1dcebd8778ffc9c1880616b52546ea4964d 100644
--- a/e2e/checklist.md
+++ b/e2e/checklist.md
@@ -9,6 +9,11 @@
 - [ ] Can access front page
 - [ ] Can login to oidc v2 via top-right
 
+## Verify testing correct siibra-explorer and siibra-api versions
+
+- [ ] Git hash from `[?]` -> `About` matches with the git hash of `HEAD` of staging
+- [ ] Message `Expecting {VERSION}, but got {VERSION}, some functionalities may not work as expected` does **not** show
+
 ## Atlas data specific
 
 - [ ] Human multilevel atlas
diff --git a/mkdocs.yml b/mkdocs.yml
index 4e782a75f743f652947a8e4899fd4243710ce17e..683054e07f3097a9c1b3cc699afc0485aff729fb 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -33,6 +33,7 @@ nav:
     - Fetching datasets: 'advanced/datasets.md'
     - Display non-atlas volumes: 'advanced/otherVolumes.md'
   - Release notes:
+    - v2.14.0: 'releases/v2.14.0.md'
     - v2.13.5: 'releases/v2.13.5.md'
     - v2.13.4: 'releases/v2.13.4.md'
     - v2.13.3: 'releases/v2.13.3.md'
diff --git a/package.json b/package.json
index a8d4d91d281340a14821537ae29b3cf76def7d3f..101d49c59e5b92380e94010ac86ee15d81cea2fe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "siibra-explorer",
-  "version": "2.13.5",
+  "version": "2.14.0",
   "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
   "scripts": {
     "lint": "eslint src --ext .ts",
diff --git a/src/atlasComponents/sapi/core/index.ts b/src/atlasComponents/sapi/core/index.ts
index ed68003d55ce542a35f5e173aaa173345202d597..f008a6194a10af74a8e653a18c3afde6fab48c4d 100644
--- a/src/atlasComponents/sapi/core/index.ts
+++ b/src/atlasComponents/sapi/core/index.ts
@@ -1,3 +1,2 @@
 export { SAPIAtlas } from "./sapiAtlas"
 export { SAPIParcellation } from "./sapiParcellation"
-export { SAPIRegion } from "./sapiRegion"
diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts
deleted file mode 100644
index 5c9db2f9fa82eed38c7c85afaebb57653bab0cee..0000000000000000000000000000000000000000
--- a/src/atlasComponents/sapi/core/sapiRegion.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { SAPI } from "..";
-import { SapiRegionModel, RouteParam } from "../typeV3";
-import { Observable, of } from "rxjs";
-import { map } from "rxjs/operators";
-import { SAPIBase } from "./base";
-import { SxplrRegion } from "../sxplrTypes";
-
-/**
- * All valid region features
- */
-const RegionFeatures = {
-  // ReceptorDensityFingerprint: "ReceptorDensityFingerprint",
-  // GeneExpressions: "GeneExpressions",
-  EbrainsDataFeature: "EbrainsDataFeature",
-  
-  // ReceptorDensityProfile: "ReceptorDensityProfile",
-  // BigBrainIntensityProfile: "BigBrainIntensityProfile",
-  // CellDensityProfile: "CellDensityProfile",
-  // LayerwiseBigBrainIntensities: "LayerwiseBigBrainIntensities",
-  // LayerwiseCellDensity: "LayerwiseCellDensity",
-  
-  Tabular: "Tabular",
-  CorticalProfile: "CorticalProfile",
-} as const
-
-type RF = keyof typeof RegionFeatures
-
-export class SAPIRegion extends SAPIBase<RF>{
-
-  static GetDisplayColor(region: SxplrRegion): [number, number, number]{
-    if (!region) {
-      throw new Error(`region must be provided!`)
-    }
-    return region.color
-    // if (region.hasAnnotation?.displayColor) {
-    //   return hexToRgb(region.hasAnnotation.displayColor)
-    // }
-    // return strToRgb(JSON.stringify(region))
-  }
-
-
-  constructor(
-    private sapi: SAPI,
-    public atlasId: string,
-    public parcId: string,
-    public id: string,
-  ){
-    super(sapi)
-  }
-
-  static Features: RF[] = Object.keys(RegionFeatures) as RF[]
-  static Features$ = of(SAPIRegion.Features)
-  public features$ = SAPIRegion.Features$
-
-  /**
-   * @param spaceId 
-   * @returns 
-   */
-  getMapInfo(spaceId: string): Observable<{min: number, max: number}> {
-    return this.sapi.v3Get("/map/statistical_map.info.json", {
-      query: {
-        parcellation_id: this.parcId,
-        region_id: this.id,
-        space_id: spaceId
-      }
-    })
-  }
-
-  /**
-   * @param spaceId 
-   * @returns 
-   */
-  getMapUrl(spaceId: string): Observable<string> {
-    return SAPI.BsEndpoint$.pipe(
-      map(endpoint => {
-        const { path, params } =  this.sapi.v3GetRoute("/map/statistical_map.nii.gz", {
-          query: {
-            parcellation_id: this.parcId,
-            region_id: this.id,
-            space_id: spaceId
-          }
-        })
-
-        const search = new URLSearchParams()
-        for (const key in params) {
-          search.set(key, params[key].toString())
-        }
-
-        return `${endpoint}${path}?${search.toString()}`
-      })
-    )
-  }
-
-  getDetail(spaceId: string): Observable<SapiRegionModel> {
-    return this.sapi.v3Get("/regions/{region_id}", {
-      path: {
-        region_id: this.id
-      },
-      query: {
-        parcellation_id: this.parcId,
-        space_id: spaceId
-      }
-    })
-  }
-
-  getMap(spaceId: string, mapType: RouteParam<"/map">['query']['map_type']) {
-    return this.sapi.v3Get("/map", {
-      query: {
-        space_id: spaceId,
-        parcellation_id: this.parcId,
-        map_type: mapType,
-      }
-    })
-  }
-}
diff --git a/src/atlasComponents/sapi/index.ts b/src/atlasComponents/sapi/index.ts
index 2ec890861ec737a5a1aec4b1f6d4d899fac532c7..fc42070f901fc363822b43855d6a245c2cf1b678 100644
--- a/src/atlasComponents/sapi/index.ts
+++ b/src/atlasComponents/sapi/index.ts
@@ -4,7 +4,6 @@ export { SAPI } from "./sapi.service"
 export {
   SAPIAtlas,
   SAPIParcellation,
-  SAPIRegion
 } from "./core"
 
 export {
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index f2b2050ff889ab7d1590ab57280cb5e050831f5d..53868ab6a8ec59cc9ff63fa6bb50b78ed69b92be 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -22,7 +22,7 @@ export const useViewer = {
 } as const
 
 export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version'
-export const EXPECTED_SIIBRA_API_VERSION = '0.3.14'
+export const EXPECTED_SIIBRA_API_VERSION = '0.3.15'
 
 let BS_ENDPOINT_CACHED_VALUE: Observable<string> = null
 
diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts
index 7e7f00636a8c72af362e4534f8aa47a0b2689a34..53b4c245caa70ddc2171bb3f75d5e4ec9dab88f2 100644
--- a/src/atlasComponents/sapi/schemaV3.ts
+++ b/src/atlasComponents/sapi/schemaV3.ts
@@ -14,6 +14,13 @@ export interface paths {
      */
     get: operations["get_single_feature_plot_feature__feature_id__plotly_get"]
   }
+  "/feature/{feature_id}/download": {
+    /**
+     * Get Single Feature Download 
+     * @description Get a zip archive of the downloadables from a feature.
+     */
+    get: operations["get_single_feature_download_feature__feature_id__download_get"]
+  }
   "/atlases": {
     /**
      * Get All Atlases 
@@ -65,17 +72,24 @@ export interface paths {
   }
   "/regions/{region_id}/features": {
     /**
-     * Get All Regions 
+     * Get All Features Region 
      * @description HTTP get all features of a single region
      */
-    get: operations["get_all_regions_regions__region_id__features_get"]
+    get: operations["get_all_features_region_regions__region_id__features_get"]
+  }
+  "/regions/{region_id}/related": {
+    /**
+     * Get Related Region 
+     * @description HTTP get_related_regions of the specified region
+     */
+    get: operations["get_related_region_regions__region_id__related_get"]
   }
   "/regions/{region_id}": {
     /**
-     * Get All Regions 
+     * Get Single Regions 
      * @description HTTP get a single region
      */
-    get: operations["get_all_regions_regions__region_id__get"]
+    get: operations["get_single_regions_regions__region_id__get"]
   }
   "/map": {
     /**
@@ -132,20 +146,6 @@ export interface paths {
      */
     get: operations["get_download_bundle_atlas_download_get"]
   }
-  "/atlas_download/{task_id}": {
-    /**
-     * Get Download Progress 
-     * @description Get download task progress with task_id
-     */
-    get: operations["get_download_progress_atlas_download__task_id__get"]
-  }
-  "/atlas_download/{task_id}/download": {
-    /**
-     * Get Download Result 
-     * @description Download the bundle
-     */
-    get: operations["get_download_result_atlas_download__task_id__download_get"]
-  }
   "/feature/_types": {
     /**
      * Get All Feature Types 
@@ -171,17 +171,17 @@ export interface paths {
   }
   "/feature/CorticalProfile": {
     /**
-     * Get All Connectivity Features 
+     * Get All Corticalprofile Features 
      * @description Get all CorticalProfile features
      */
-    get: operations["get_all_connectivity_features_feature_CorticalProfile_get"]
+    get: operations["get_all_corticalprofile_features_feature_CorticalProfile_get"]
   }
   "/feature/CorticalProfile/{feature_id}": {
     /**
-     * Get Single Connectivity Feature 
+     * Get Single Corticalprofile Feature 
      * @description Get a single CorticalProfile feature
      */
-    get: operations["get_single_connectivity_feature_feature_CorticalProfile__feature_id__get"]
+    get: operations["get_single_corticalprofile_feature_feature_CorticalProfile__feature_id__get"]
   }
   "/feature/Tabular": {
     /**
@@ -886,6 +886,19 @@ export interface components {
       /** Pages */
       pages?: number
     }
+    /** Page[RegionRelationAsmtModel] */
+    Page_RegionRelationAsmtModel_: {
+      /** Items */
+      items: (components["schemas"]["RegionRelationAsmtModel"])[]
+      /** Total */
+      total: number
+      /** Page */
+      page?: number
+      /** Size */
+      size?: number
+      /** Pages */
+      pages?: number
+    }
     /** Page[SiibraAtlasModel] */
     Page_SiibraAtlasModel_: {
       /** Items */
@@ -1059,6 +1072,14 @@ export interface components {
      * @enum {unknown}
      */
     PlotlyTemplate: "plotly" | "plotly_white" | "plotly_dark" | "ggplot2" | "seaborn" | "simple_white" | "none"
+    /**
+     * Qualification 
+     * @description Qualification
+     * 
+     * Exactly match to Qualification in siibra.core.relation_quantification.Quantification 
+     * @enum {string}
+     */
+    Qualification: "EXACT" | "OVERLAPS" | "CONTAINED" | "CONTAINS" | "APPROXIMATE" | "HOMOLOGOUS" | "OTHER_VERSION"
     /** QuantitativeOverlapItem */
     QuantitativeOverlapItem: {
       /**
@@ -1113,6 +1134,18 @@ export interface components {
       /** minValueUnit */
       minValueUnit?: Record<string, never>
     }
+    /**
+     * RegionRelationAsmtModel 
+     * @description ConfigBaseModel
+     */
+    RegionRelationAsmtModel: {
+      /** @Type */
+      "@type": string
+      qualification: components["schemas"]["Qualification"]
+      query_structure: components["schemas"]["ParcellationEntityVersionModel"]
+      assigned_structure: components["schemas"]["ParcellationEntityVersionModel"]
+      assigned_structure_parcellation: components["schemas"]["SiibraParcellationModel"]
+    }
     /** RelationAssessmentItem */
     RelationAssessmentItem: {
       /**
@@ -1170,9 +1203,9 @@ export interface components {
       /** Qualification */
       qualification: string
       /** Query Structure */
-      query_structure: components["schemas"]["LocationModel"] | components["schemas"]["ParcellationEntityVersionModel"]
+      query_structure: components["schemas"]["LocationModel"] | components["schemas"]["ParcellationEntityVersionModel"] | components["schemas"]["SiibraParcellationModel"]
       /** Assigned Structure */
-      assigned_structure: components["schemas"]["LocationModel"] | components["schemas"]["ParcellationEntityVersionModel"]
+      assigned_structure: components["schemas"]["LocationModel"] | components["schemas"]["ParcellationEntityVersionModel"] | components["schemas"]["SiibraParcellationModel"]
       /** Explanation */
       explanation: string
     }
@@ -1228,7 +1261,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1258,7 +1291,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1326,7 +1359,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1363,7 +1396,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1392,7 +1425,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1414,7 +1447,7 @@ export interface components {
       /** Id */
       id: string
       /** Modality */
-      modality: string
+      modality?: string
       /** Category */
       category: string
       /** Description */
@@ -1581,6 +1614,31 @@ export interface operations {
       }
     }
   }
+  get_single_feature_download_feature__feature_id__download_get: {
+    /**
+     * Get Single Feature Download 
+     * @description Get a zip archive of the downloadables from a feature.
+     */
+    parameters: {
+      path: {
+        feature_id: string
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": Record<string, never>
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
   get_all_atlases_atlases_get: {
     /**
      * Get All Atlases 
@@ -1762,9 +1820,9 @@ export interface operations {
       }
     }
   }
-  get_all_regions_regions__region_id__features_get: {
+  get_all_features_region_regions__region_id__features_get: {
     /**
-     * Get All Regions 
+     * Get All Features Region 
      * @description HTTP get all features of a single region
      */
     parameters: {
@@ -1792,9 +1850,39 @@ export interface operations {
       }
     }
   }
-  get_all_regions_regions__region_id__get: {
+  get_related_region_regions__region_id__related_get: {
     /**
-     * Get All Regions 
+     * Get Related Region 
+     * @description HTTP get_related_regions of the specified region
+     */
+    parameters: {
+      query: {
+        parcellation_id: string
+        page?: number
+        size?: number
+      }
+      path: {
+        region_id: string
+      }
+    }
+    responses: {
+      /** @description Successful Response */
+      200: {
+        content: {
+          "application/json": components["schemas"]["Page_RegionRelationAsmtModel_"]
+        }
+      }
+      /** @description Validation Error */
+      422: {
+        content: {
+          "application/json": components["schemas"]["HTTPValidationError"]
+        }
+      }
+    }
+  }
+  get_single_regions_regions__region_id__get: {
+    /**
+     * Get Single Regions 
      * @description HTTP get a single region
      */
     parameters: {
@@ -1988,56 +2076,7 @@ export interface operations {
         space_id: string
         parcellation_id: string
         region_id?: string
-      }
-    }
-    responses: {
-      /** @description Successful Response */
-      200: {
-        content: {
-          "application/json": Record<string, never>
-        }
-      }
-      /** @description Validation Error */
-      422: {
-        content: {
-          "application/json": components["schemas"]["HTTPValidationError"]
-        }
-      }
-    }
-  }
-  get_download_progress_atlas_download__task_id__get: {
-    /**
-     * Get Download Progress 
-     * @description Get download task progress with task_id
-     */
-    parameters: {
-      path: {
-        task_id: string
-      }
-    }
-    responses: {
-      /** @description Successful Response */
-      200: {
-        content: {
-          "application/json": Record<string, never>
-        }
-      }
-      /** @description Validation Error */
-      422: {
-        content: {
-          "application/json": components["schemas"]["HTTPValidationError"]
-        }
-      }
-    }
-  }
-  get_download_result_atlas_download__task_id__download_get: {
-    /**
-     * Get Download Result 
-     * @description Download the bundle
-     */
-    parameters: {
-      path: {
-        task_id: string
+        feature_id?: string
       }
     }
     responses: {
@@ -2141,9 +2180,9 @@ export interface operations {
       }
     }
   }
-  get_all_connectivity_features_feature_CorticalProfile_get: {
+  get_all_corticalprofile_features_feature_CorticalProfile_get: {
     /**
-     * Get All Connectivity Features 
+     * Get All Corticalprofile Features 
      * @description Get all CorticalProfile features
      */
     parameters: {
@@ -2170,9 +2209,9 @@ export interface operations {
       }
     }
   }
-  get_single_connectivity_feature_feature_CorticalProfile__feature_id__get: {
+  get_single_corticalprofile_feature_feature_CorticalProfile__feature_id__get: {
     /**
-     * Get Single Connectivity Feature 
+     * Get Single Corticalprofile Feature 
      * @description Get a single CorticalProfile feature
      */
     parameters: {
diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts
index 00a7e88ec1774c3ec0bf1164258830a2ec3b5e3b..6d08f219997b32eefa77edf5f571a3a66fb2e0b5 100644
--- a/src/atlasComponents/sapi/translateV3.ts
+++ b/src/atlasComponents/sapi/translateV3.ts
@@ -104,6 +104,13 @@ const TMP_META_REGISTRY: Record<string, MetaV1Schema> = {
       type: "image/3d"
     },
     transform: [[-0.74000001,0,0,38134608],[0,-0.26530117,-0.6908077,13562314],[0,-0.6908077,0.26530117,-3964904],[0,0,0,1]]
+  },
+  "https://neuroglancer.humanbrainproject.eu/precomputed/chenonceau_dti_rgb_200um/precomputed": {
+    version: 1,
+    data: {
+      type: "image/3d"
+    },
+    transform: [[-0.2, 0.0, 0.0, 96400000.0], [0.0, -0.2, 0.0, 96400000.0], [0.0, 0.0, -0.2, 114400000.0], [0.0, 0.0, 0.0, 1.0]]
   }
 }
 
diff --git a/src/atlasComponents/sapi/typeV3.ts b/src/atlasComponents/sapi/typeV3.ts
index 13d2d393b2cfee465f4927ed21522832b70726d9..0ae097aa9c77422881e912038c155fcce189b5ff 100644
--- a/src/atlasComponents/sapi/typeV3.ts
+++ b/src/atlasComponents/sapi/typeV3.ts
@@ -17,9 +17,9 @@ export type SapiFeatureModel = SapiSpatialFeatureModel | PathReturn<"/feature/Ta
 
 export type SapiRoute = keyof paths
 
-type SapiRouteExcludePlotly = Exclude<SapiRoute, "/feature/{feature_id}/plotly">
+type SapiRouteExcludePlotlyDownload = Exclude<SapiRoute, "/feature/{feature_id}/plotly" | "/feature/{feature_id}/download">
 
-type _FeatureType<FeatureRoute extends SapiRouteExcludePlotly> = FeatureRoute extends `/feature/${infer FT}`
+type _FeatureType<FeatureRoute extends SapiRouteExcludePlotlyDownload> = FeatureRoute extends `/feature/${infer FT}`
   ? FT extends "_types"
     ? never
     : FT extends "{feature_id}"
@@ -30,7 +30,7 @@ type _FeatureType<FeatureRoute extends SapiRouteExcludePlotly> = FeatureRoute ex
         : FT
   : never
 
-export type FeatureType = _FeatureType<SapiRouteExcludePlotly>
+export type FeatureType = _FeatureType<SapiRouteExcludePlotlyDownload>
 
 /**
  * Support types
@@ -132,3 +132,5 @@ interface EnclosedROI {
 export function isEnclosed(v: BestViewPoints[number]): v is EnclosedROI {
   return v.type === "enclosed"
 }
+
+export type Qualification = components["schemas"]["Qualification"]
diff --git a/src/atlasComponents/sapiViews/core/region/dedupRelatedRegion.pipe.ts b/src/atlasComponents/sapiViews/core/region/dedupRelatedRegion.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f655d7031766ab49876098c0ddade38d75458e8e
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/dedupRelatedRegion.pipe.ts
@@ -0,0 +1,34 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { Qualification } from "src/atlasComponents/sapi/typeV3"
+import { SxplrRegion, SxplrParcellation } from "src/atlasComponents/sapi/sxplrTypes"
+
+type InputType = {
+  qualification: Qualification
+  region: SxplrRegion,
+  parcellation: SxplrParcellation
+}
+
+@Pipe({
+  name: "dedupRelatedRegionPipe",
+  pure: true
+})
+export class DedupRelatedRegionPipe implements PipeTransform{
+  public transform(arr: InputType[]): InputType[] {
+    if (!arr) {
+      return []
+    }
+    const accumulator: InputType[] = []
+    for (const item of arr) {
+      const exists = accumulator.find(v => (
+        v.qualification === item.qualification
+        && item.region.name === v.region.name
+        && item.parcellation.id === v.parcellation.id)
+      )
+      if (exists) {
+        continue
+      }
+      accumulator.push(item)
+    }
+    return accumulator
+  }
+}
diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts
index 908761ba5a664ab5a27f1ebeac38c53cf549b829..46b01dfb024e630fd67a352ca85c35ef0aa9f3f1 100644
--- a/src/atlasComponents/sapiViews/core/region/module.ts
+++ b/src/atlasComponents/sapiViews/core/region/module.ts
@@ -16,6 +16,9 @@ import { MatListModule } from "@angular/material/list";
 import { DialogModule } from "src/ui/dialogInfo";
 import { SapiViewsCoreParcellationModule } from "../parcellation";
 import { MatTooltipModule } from "@angular/material/tooltip";
+import { TranslateQualificationPipe } from "./translateQualification.pipe";
+import { DedupRelatedRegionPipe } from "./dedupRelatedRegion.pipe";
+import { MatExpansionModule } from "@angular/material/expansion";
 
 @NgModule({
   imports: [
@@ -33,11 +36,16 @@ import { MatTooltipModule } from "@angular/material/tooltip";
     DialogModule,
     SapiViewsCoreParcellationModule,
     MatTooltipModule,
+    MatExpansionModule,
+    ExperimentalModule,
   ],
   declarations: [
     SapiViewsCoreRegionRegionListItem,
     SapiViewsCoreRegionRegionRich,
     SapiViewsCoreRegionRegionBase,
+    
+    TranslateQualificationPipe,
+    DedupRelatedRegionPipe,
   ],
   exports: [
     SapiViewsCoreRegionRegionListItem,
diff --git a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
index 59b09ba91bb2e0eddbac28add6ed03fdd4c0cade..fc5b5c1dfae5eeab36776b0adad11945e38f85fe 100644
--- a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
+++ b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts
@@ -3,8 +3,7 @@ import { SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/a
 import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
 import { rgbToHsl } from 'common/util'
 import { SAPI } from "src/atlasComponents/sapi/sapi.service";
-import { BehaviorSubject, combineLatest } from "rxjs";
-import { SAPIRegion } from "src/atlasComponents/sapi/core";
+import { BehaviorSubject, combineLatest, forkJoin, of } from "rxjs";
 import { map, switchMap } from "rxjs/operators";
 
 @Directive({
@@ -95,7 +94,7 @@ export class SapiViewsCoreRegionRegionBase {
       /**
        * color
        */
-      const rgb = SAPIRegion.GetDisplayColor(this.region) || [200, 200, 200]
+      const rgb = this.region?.color || [200, 200, 200]
       this.regionRgbString = `rgb(${rgb.join(',')})`
       const [ /* _h */, /* _s */, l] = rgbToHsl(...rgb)
       this.regionDarkmode = l < 0.4
@@ -130,6 +129,28 @@ export class SapiViewsCoreRegionRegionBase {
     ).toPromise()
   }
 
+  protected async fetchRelated(region: SxplrRegion){
+    const getPage = (page: number) => this.sapi.v3Get("/regions/{region_id}/related", {
+      path: {
+        region_id: region.name
+      },
+      query: {
+        parcellation_id: this.parcellation.id,
+        page
+      }
+    })
+    return getPage(1).pipe(
+      switchMap(resp => this.sapi.iteratePages(resp, getPage)),
+      switchMap(arr => forkJoin(
+        arr.map(({ qualification, assigned_structure, assigned_structure_parcellation }) => forkJoin({
+          qualification: of(qualification),
+          region: translateV3Entities.translateRegion(assigned_structure),
+          parcellation: translateV3Entities.translateParcellation(assigned_structure_parcellation),
+        }))
+      ))
+    ).toPromise()
+  }
+
   constructor(protected sapi: SAPI){
 
   }
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
index d774b1e05f61c2ebe3a4f676d6464417626b4cdd..ce52048ff4ac2fdab09a1efaa372d69faa66fdbb 100644
--- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts
@@ -92,4 +92,9 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase
       }
     }),
   )
+
+  public relatedRegions$ = this.ATPR$.pipe(
+    switchMap(({ region }) => this.fetchRelated(region)),
+    shareReplay(1),
+  )
 }
diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
index bfa0c7cd41ecc40131b9f23a7468fc72d0a67d2f..b48e7804e752ec61a567d11c2a415a8ab9f6f62e 100644
--- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
+++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html
@@ -62,6 +62,62 @@
             <div mat-line>{{ doi }}</div>
           </a>
         </ng-template>
+
+        <ng-template sxplrExperimentalFlag [experimental]="true"
+          #relatedRegionsExport="sxplrExperimentalFlag"
+          [ngIf]="relatedRegionsExport.show$ | async">
+
+        <ng-template [ngIf]="relatedRegions$ | async | dedupRelatedRegionPipe" let-relatedRegions>
+          <!-- only show icon if related regions length > 0 -->
+          <ng-template [ngIf]="relatedRegions.length > 0">
+            <button mat-list-item
+              [sxplr-dialog]="relatedRegionsTmpl"
+              sxplr-dialog-size="auto">
+              <mat-icon mat-list-icon fontSet="fas" fontIcon="fa-link"></mat-icon>
+              <div mat-line class="overview-content">Related Regions ({{ relatedRegions.length }})</div>
+            </button>
+          </ng-template>
+
+          <!-- dialog when user clicks related regions -->
+          <ng-template #relatedRegionsTmpl>
+            <!-- header -->
+            <h2 mat-dialog-title>Current region {{ region.name }} ...</h2>
+
+            <!-- body -->
+            <mat-dialog-content>
+
+              <!-- iterate over all related -->
+              <ng-template ngFor [ngForOf]="relatedRegions" let-related let-isLast="last">
+
+                <!-- related region body -->
+                <div class="sxplr-p-2">
+                  <div>
+                    {{ related.qualification | translateQualificationPipe }}
+                  </div>
+
+                  <div>
+                    {{ related.region.name }} in
+                  </div>
+                  <div>
+                    {{ related.parcellation.name}}
+                  </div>
+                </div>
+                
+                <!-- divider -->
+                <ng-template [ngIf]="!isLast">
+                  <mat-divider></mat-divider>
+                </ng-template>
+              </ng-template>
+            </mat-dialog-content>
+
+            <!-- footer -->
+            <mat-dialog-actions>
+              <button mat-button mat-dialog-close>close</button>
+            </mat-dialog-actions>
+          </ng-template>
+        </ng-template>
+
+        </ng-template>
       </mat-action-list>
 
       
@@ -94,6 +150,60 @@
         #featureEntryCmp="featureEntryCmp">
       </sxplr-feature-entry>
     </mat-tab>
+
+
+    <ng-template sxplrExperimentalFlag [experimental]="true"
+      #exmptRelatedFeat="sxplrExperimentalFlag"
+      [ngIf]="exmptRelatedFeat.show$ | async">
+    
+    <!-- related region features -->
+    <ng-template [ngIf]="relatedRegions$ | async | dedupRelatedRegionPipe" let-relatedRegions>
+      <ng-template [ngIf]="relatedRegions.length > 0">
+
+        <mat-tab>
+
+          <!-- tab label -->
+          <ng-template mat-tab-label>
+            <span>
+              Related Region Features
+            </span>
+          </ng-template>
+    
+          <!-- tab-content -->
+          <mat-accordion>
+            
+            <mat-expansion-panel *ngFor="let relatedRegion of relatedRegions" hideToggle>
+              <mat-expansion-panel-header>
+                <mat-panel-title class="ws-no-wrap">
+                  {{ relatedRegion.region.name }}
+                </mat-panel-title>
+                <mat-panel-description class="ws-no-wrap">
+                  {{ relatedRegion.parcellation.name }}
+                </mat-panel-description>
+              </mat-expansion-panel-header>
+
+              <ng-template matExpansionPanelContent>
+                
+                <div>
+                  Current region 
+                  <i>{{ region.name }}</i> {{ relatedRegion.qualification | translateQualificationPipe }}
+                  <i>{{ relatedRegion.region.name }}</i> in
+                  <b>{{ relatedRegion.parcellation.name }}</b>
+                </div>
+
+                <sxplr-feature-entry
+                  [parcellation]="relatedRegion.parcellation"
+                  [region]="relatedRegion.region">
+                </sxplr-feature-entry>
+
+              </ng-template>
+            </mat-expansion-panel>
+          </mat-accordion>
+        </mat-tab>
+      </ng-template>
+    </ng-template>
+
+    </ng-template>
   </mat-tab-group>
 
 </ng-template>
diff --git a/src/atlasComponents/sapiViews/core/region/translateQualification.pipe.ts b/src/atlasComponents/sapiViews/core/region/translateQualification.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62eaa9714b4294a497a2957cf9ed401a92a79347
--- /dev/null
+++ b/src/atlasComponents/sapiViews/core/region/translateQualification.pipe.ts
@@ -0,0 +1,22 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { Qualification } from "src/atlasComponents/sapi/typeV3"
+
+const QUALIFICATION_TRANSLATION: Record<Qualification, string> = {
+  APPROXIMATE: "is approximately",
+  CONTAINED: "is contained by",
+  CONTAINS: "contains",
+  EXACT: "is exactly",
+  HOMOLOGOUS: "is homologuous to",
+  OTHER_VERSION: "is another version of",
+  OVERLAPS: "overlaps with",
+}
+
+@Pipe({
+  name: "translateQualificationPipe",
+  pure: true
+})
+export class TranslateQualificationPipe implements PipeTransform{
+  public transform(value: Qualification): string {
+    return QUALIFICATION_TRANSLATION[value]
+  }
+}
diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts
index 5967e510255e45ba3eb1c980d7cf65a53018c429..3960d862437dcea9db00aea3812a0f60b420c331 100644
--- a/src/state/atlasSelection/effects.ts
+++ b/src/state/atlasSelection/effects.ts
@@ -2,7 +2,7 @@ import { Injectable } from "@angular/core";
 import { Actions, createEffect, ofType } from "@ngrx/effects";
 import { forkJoin, merge, NEVER, Observable, of } from "rxjs";
 import { catchError, filter, map, mapTo, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators";
-import { SAPI, SAPIRegion } from "src/atlasComponents/sapi";
+import { SAPI } from "src/atlasComponents/sapi";
 import * as mainActions from "../actions"
 import { select, Store } from "@ngrx/store";
 import { selectors, actions } from '.'
@@ -264,7 +264,7 @@ export class Effect {
     switchMap(([regions, layers]) => {
       const map = new Map<SxplrRegion, number[]>()
       for (const region of regions) {
-        map.set(region, SAPIRegion.GetDisplayColor(region))
+        map.set(region, region.color)
       }
       const actions = [
         ...layers.map(({ id }) =>
diff --git a/src/ui/dialogInfo/dialog.directive.ts b/src/ui/dialogInfo/dialog.directive.ts
index 3b24370b99282a6b8386e4c81b35c8b7286f552f..62640509efdc3a65f053989d50464ea81dd2285b 100644
--- a/src/ui/dialogInfo/dialog.directive.ts
+++ b/src/ui/dialogInfo/dialog.directive.ts
@@ -2,7 +2,7 @@ import { Directive, HostListener, Input, TemplateRef } from "@angular/core";
 import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
 import { DialogFallbackCmp } from "./tmpl/tmpl.component"
 
-type DialogSize = 's' | 'm' | 'l' | 'xl'
+type DialogSize = 's' | 'm' | 'l' | 'xl' | 'auto'
 
 const sizeDict: Record<DialogSize, Partial<MatDialogConfig>> = {
   's': {
@@ -20,7 +20,8 @@ const sizeDict: Record<DialogSize, Partial<MatDialogConfig>> = {
   'xl': {
     width: '90vw',
     height: '90vh'
-  }
+  },
+  'auto': {}
 }
 
 @Directive({
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index 0ac608a713e16025f5ffcbb6049162fa1d37d15f..62aca0e77422e585a95e6e31951e6301dfdff8e4 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -439,6 +439,8 @@ export class NehubaViewerUnit implements OnDestroy {
         }
       })()
       viewer.inputEventBindings.sliceView.set("at:wheel", "proxy-wheel-1")
+      viewer.inputEventBindings.sliceView.set("at:keyp", "proxy-wheel-1")
+      viewer.inputEventBindings.sliceView.set("at:keyn", "proxy-wheel-1")
       viewer.inputEventBindings.sliceView.set("at:control+shift+wheel", "proxy-wheel-10")
       viewer.display.panels.forEach(sliceView => patchSliceViewPanel(sliceView, this.exportNehuba, this.multplier))
     }
@@ -962,8 +964,18 @@ const patchSliceViewPanel = (sliceViewPanel: any, exportNehuba: any, mulitplier:
     registerActionListener(sliceViewPanel.element, `proxy-wheel-${val}`, event => {
       const e = event.detail
 
+      let keyDelta = null
+      if (e.key === "p") {
+        keyDelta = -1
+      }
+      if (e.key === "n") {
+        keyDelta = 1
+      }
       const offset = tempVec3
-      const delta = e.deltaY !== 0 ? e.deltaY : e.deltaX
+      const wheelDelta = e.deltaY !== 0 ? e.deltaY : e.deltaX
+      
+      const delta = keyDelta ?? wheelDelta
+
       offset[0] = 0
       offset[1] = 0
       offset[2] = (delta > 0 ? -1 : 1) * mulitplier[0] * val