diff --git a/.gitignore b/.gitignore
index 184d4873765c80af9bc0773b09aa01bffedab19c..26f571387f1c69514b44858ce2e2f7de7ce226ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,3 +70,9 @@ UserSettings/
 /Assets/Images/frameit_logo_512x512.png.meta
 /Assets/Images/ScreenCapture_1024x500.png.meta
 UserSettings/EditorUserSettings.asset
+
+# ignore FrameIT server
+Assets/StreamingAssets/archives/
+Assets/StreamingAssets/archives.meta
+Assets/StreamingAssets/frameit-mmt.jar
+Assets/StreamingAssets/frameit-mmt.jar.meta
diff --git a/.gitmodules b/.gitmodules
index 33a218feea88a69725fc267c7c107c4a6b797ee5..a49babd763ba692ced0e3ef95f7b895a14896c13 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "Assets/Plugins/JsonSubTypes"]
 	path = Assets/Plugins/JsonSubTypes
 	url = https://github.com/manuc66/JsonSubTypes
+[submodule "Packages/bessw.unity.webview"]
+	path = Packages/bessw.unity.webview
+	url = https://gl.kwarc.info/FrameIT/unity-webview.git
diff --git a/Assets/Resources/Prefabs/Stage_Default.prefab b/Assets/Resources/Prefabs/Stage_Default.prefab
index f60a78e5214d52d0a31785273b522905fbfeb3ba..062e64483e63aec3cfa341ff8abea3eb03ffd8b4 100644
--- a/Assets/Resources/Prefabs/Stage_Default.prefab
+++ b/Assets/Resources/Prefabs/Stage_Default.prefab
@@ -2376,6 +2376,11 @@ PrefabInstance:
       propertyPath: m_AnchorMin.x
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 861183263081594858, guid: b996060e27da25c498842defc1996d84,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 9
+      objectReference: {fileID: 0}
     - target: {fileID: 947958301574655299, guid: b996060e27da25c498842defc1996d84,
         type: 3}
       propertyPath: m_IsActive
diff --git a/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab b/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab
index 8addc4b7fc81f8adcdffff90ba33456f23d06819..9dff07b6f55c9b75890a54c5f83648b1192ad7ea 100644
--- a/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab
+++ b/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab
@@ -507,7 +507,7 @@ RectTransform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 1}
   m_AnchorMax: {x: 1, y: 1}
-  m_AnchoredPosition: {x: 0, y: -180}
+  m_AnchoredPosition: {x: 0, y: 0.000030517578}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 0.5, y: 1}
 --- !u!114 &8823539307371861913
diff --git a/Assets/Resources/Prefabs/UI/FrameITUI.prefab b/Assets/Resources/Prefabs/UI/FrameITUI.prefab
index e570ddf7a13f5d0c5b183dc73e6280dbb6c15faa..a1609d901a39ae0cf6544a1861fb1e23141bf7ae 100644
--- a/Assets/Resources/Prefabs/UI/FrameITUI.prefab
+++ b/Assets/Resources/Prefabs/UI/FrameITUI.prefab
@@ -406,12 +406,12 @@ PrefabInstance:
     - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1,
         type: 3}
       propertyPath: m_AnchorMax.y
-      value: 1
+      value: 0
       objectReference: {fileID: 0}
     - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1,
         type: 3}
       propertyPath: m_AnchorMin.y
-      value: 1
+      value: 0
       objectReference: {fileID: 0}
     - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1,
         type: 3}
@@ -503,6 +503,11 @@ PrefabInstance:
       propertyPath: m_AnchoredPosition.y
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 573039479540912931, guid: 292834880e6f0e54186b873acc62d3f2,
+        type: 3}
+      propertyPath: m_PresetInfoIsWorld
+      value: 1
+      objectReference: {fileID: 0}
     - target: {fileID: 3716020922907832516, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
       propertyPath: m_SizeDelta.y
@@ -556,7 +561,7 @@ PrefabInstance:
     - target: {fileID: 6500467619489830996, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
       propertyPath: m_AnchoredPosition.y
-      value: 1080
+      value: 146554910
       objectReference: {fileID: 0}
     - target: {fileID: 7849991042685492731, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
@@ -606,7 +611,7 @@ PrefabInstance:
     - target: {fileID: 7989559431199338490, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
       propertyPath: m_AnchoredPosition.y
-      value: -59.999527
+      value: -8136900
       objectReference: {fileID: 0}
     - target: {fileID: 8004702056544321748, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
diff --git a/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab b/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab
index 31d6d0d00310add6747878f091b56d3ab22e558d..d789848f1792a669a3e2061c919f159aea604fca 100644
--- a/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab
+++ b/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab
@@ -4952,7 +4952,7 @@ PrefabInstance:
     - target: {fileID: 7989559431199338490, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
       propertyPath: m_AnchoredPosition.y
-      value: -0.000045776367
+      value: -2400
       objectReference: {fileID: 0}
     - target: {fileID: 8004702056544321748, guid: 292834880e6f0e54186b873acc62d3f2,
         type: 3}
diff --git a/Assets/Resources/Prefabs/UI/Ingame/GadgetCanvas.prefab b/Assets/Resources/Prefabs/UI/Ingame/GadgetCanvas.prefab
index f4fa3f780801052b4b41d65697dc2c72a75ef37a..b67e0e696ab7c32fac8cc4b402e330b0e01f0126 100644
--- a/Assets/Resources/Prefabs/UI/Ingame/GadgetCanvas.prefab
+++ b/Assets/Resources/Prefabs/UI/Ingame/GadgetCanvas.prefab
@@ -174,7 +174,7 @@ RectTransform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 0, y: 0}
-  m_AnchoredPosition: {x: 0, y: 0}
+  m_AnchoredPosition: {x: 940, y: 0}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 0.5, y: 0.5}
 --- !u!114 &6662492270148709753
@@ -538,7 +538,7 @@ MonoBehaviour:
   m_FallbackScreenDPI: 96
   m_DefaultSpriteDPI: 96
   m_DynamicPixelsPerUnit: 1
-  m_PresetInfoIsWorld: 0
+  m_PresetInfoIsWorld: 1
 --- !u!1001 &3122257488954702478
 PrefabInstance:
   m_ObjectHideFlags: 0
diff --git a/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab b/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab
index c6dd0370ab27d0b96ea4c9f7f7e8aacaf4e5a5f3..d46918be32052d1850dd5431f381855637b54ca6 100644
--- a/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab
+++ b/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab
@@ -35,7 +35,7 @@ RectTransform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 1, y: 1}
-  m_AnchoredPosition: {x: 0, y: 14040}
+  m_AnchoredPosition: {x: 0, y: 0}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 1, y: 1}
 --- !u!114 &6592514254180342026
@@ -109,10 +109,10 @@ RectTransform:
   m_Father: {fileID: 3853195430673864773}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
-  m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 0}
+  m_AnchorMin: {x: 0, y: 1}
+  m_AnchorMax: {x: 0, y: 1}
+  m_AnchoredPosition: {x: 568.4, y: -208.64}
+  m_SizeDelta: {x: 1136.8, y: 217.28}
   m_Pivot: {x: 0.5, y: 0.5}
 --- !u!222 &8314331323416511131
 CanvasRenderer:
@@ -248,10 +248,10 @@ RectTransform:
   m_Father: {fileID: 3716020922907832516}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
-  m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
-  m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 0}
+  m_AnchorMin: {x: 0, y: 1}
+  m_AnchorMax: {x: 0, y: 1}
+  m_AnchoredPosition: {x: 578.4, y: -208.64}
+  m_SizeDelta: {x: 1156.8, y: 417.28}
   m_Pivot: {x: 0.5, y: 0.5}
 --- !u!222 &911570484790407906
 CanvasRenderer:
@@ -531,7 +531,7 @@ RectTransform:
   m_AnchorMin: {x: 0.5, y: 0}
   m_AnchorMax: {x: 0.5, y: 0}
   m_AnchoredPosition: {x: -0.008544922, y: 0.0002421254}
-  m_SizeDelta: {x: 1156.8, y: 0}
+  m_SizeDelta: {x: 1156.8, y: 417.28}
   m_Pivot: {x: 0.5, y: 0}
 --- !u!114 &6505282139119622248
 MonoBehaviour:
@@ -991,9 +991,9 @@ RectTransform:
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
   m_AnchoredPosition: {x: 0, y: 0}
-  m_SizeDelta: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: -20}
   m_Pivot: {x: 0, y: 1}
 --- !u!222 &8004702056544321750
 CanvasRenderer:
@@ -1242,7 +1242,7 @@ RectTransform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 1}
   m_AnchorMax: {x: 0.5, y: 1}
-  m_AnchoredPosition: {x: 7478.3154, y: 0.000030517578}
+  m_AnchoredPosition: {x: 0, y: 0.000030517578}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 0.5, y: 1}
 --- !u!114 &8004702056565720367
@@ -1454,7 +1454,7 @@ RectTransform:
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
-  m_AnchorMax: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
   m_AnchoredPosition: {x: 0, y: 0}
   m_SizeDelta: {x: 20, y: 20}
   m_Pivot: {x: 0.5, y: 0.5}
@@ -1617,8 +1617,8 @@ MonoBehaviour:
   m_TargetGraphic: {fileID: 8004702056618112985}
   m_HandleRect: {fileID: 8004702056618112982}
   m_Direction: 0
-  m_Value: 1
-  m_Size: 0.4705884
+  m_Value: 0
+  m_Size: 1
   m_NumberOfSteps: 0
   m_OnValueChanged:
     m_PersistentCalls:
@@ -1924,7 +1924,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 8004702057932254677}
-  m_RootOrder: 7
+  m_RootOrder: 8
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
@@ -2160,7 +2160,7 @@ GameObject:
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
-  m_IsActive: 1
+  m_IsActive: 0
 --- !u!224 &8004702057540368335
 RectTransform:
   m_ObjectHideFlags: 0
@@ -2175,7 +2175,7 @@ RectTransform:
   m_Children:
   - {fileID: 8004702057798297439}
   m_Father: {fileID: 8004702057932254677}
-  m_RootOrder: 5
+  m_RootOrder: 6
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 0, y: 0}
@@ -2464,9 +2464,6 @@ MonoBehaviour:
   parameterDisplayPrefab: {fileID: 8358525157842135574, guid: 0651df442e07acf439dd439c86c20e93,
     type: 3}
   mmtAnswerPopUp: {fileID: 6618856106128302243}
-  currentMmtAnswer: 
-  DynamicScrollDescriptionsActive: 1
-  AutomaticHintGenerationActive: 1
 --- !u!114 &8004702057798297409
 MonoBehaviour:
   m_ObjectHideFlags: 0
@@ -2828,6 +2825,8 @@ GameObject:
   - component: {fileID: 8004702057932254678}
   - component: {fileID: 8004702057932254679}
   - component: {fileID: 8004702057932254676}
+  - component: {fileID: 3671007757941716709}
+  - component: {fileID: 582397463733288183}
   m_Layer: 5
   m_Name: HidingCanvas
   m_TagString: Untagged
@@ -2852,6 +2851,7 @@ RectTransform:
   - {fileID: 8004702056565337459}
   - {fileID: 8004702057423300230}
   - {fileID: 8004702058016740899}
+  - {fileID: 154516623474793748}
   - {fileID: 8004702057540368335}
   - {fileID: 2050004651490542596}
   - {fileID: 8004702056853212489}
@@ -2906,7 +2906,7 @@ MonoBehaviour:
   m_FallbackScreenDPI: 96
   m_DefaultSpriteDPI: 96
   m_DynamicPixelsPerUnit: 1
-  m_PresetInfoIsWorld: 1
+  m_PresetInfoIsWorld: 0
 --- !u!114 &8004702057932254676
 MonoBehaviour:
   m_ObjectHideFlags: 0
@@ -2924,6 +2924,64 @@ MonoBehaviour:
   m_BlockingMask:
     serializedVersion: 2
     m_Bits: 4294967295
+--- !u!114 &3671007757941716709
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8004702057932254674}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 90c1c0815499367409841816e1485ec7, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  ScrollShowUI: {fileID: 8004702057540368332}
+  ScrollWebView: {fileID: 5843063412347785762}
+--- !u!114 &582397463733288183
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8004702057932254674}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ae310511ad607a64e891bcb46517277b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  OnButtonClick:
+    m_PersistentCalls:
+      m_Calls: []
+  HintAvailableEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnFactAssignmentUpdated:
+    m_PersistentCalls:
+      m_Calls: []
+  OnMMTServerComunicationError:
+    m_PersistentCalls:
+      m_Calls: []
+  OnScrollDynamicInfoError:
+    m_PersistentCalls:
+      m_Calls: []
+  OnScrollApplicationError:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCancelErrorState:
+    m_PersistentCalls:
+      m_Calls: []
+  OnScrollSolved:
+    m_PersistentCalls:
+      m_Calls: []
+  OnScrollChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnScrollDynamicInfoUpdated:
+    m_PersistentCalls:
+      m_Calls: []
+  DynamicScrollDescriptionsActive: 1
+  AutomaticHintGenerationActive: 1
 --- !u!1 &8004702058016740896
 GameObject:
   m_ObjectHideFlags: 0
@@ -3020,7 +3078,6 @@ MonoBehaviour:
   ScrollButtons: []
   ScrollPrefab: {fileID: 3173330253721512196, guid: a6a9a3ebdb022e546a21d9f9ff148261,
     type: 3}
-  DetailScreen: {fileID: 8004702057798297436}
   scrollscreenContent: {fileID: 6500467619489830996}
 --- !u!114 &3490402746730127523
 MonoBehaviour:
@@ -3398,6 +3455,164 @@ MonoBehaviour:
   m_hasFontAssetChanged: 0
   m_baseMaterial: {fileID: 0}
   m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1001 &3129137578625191326
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 8004702057932254677}
+    m_Modifications:
+    - target: {fileID: 2459857651630437765, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: targetUrl
+      value: http://127.0.0.1:5500/scrollView.html
+      objectReference: {fileID: 0}
+    - target: {fileID: 2459857651630437765, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: PageScaleFactor
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_Pivot.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_Pivot.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchorMax.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchorMax.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchorMin.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchorMin.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_SizeDelta.x
+      value: 890
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_SizeDelta.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchoredPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_AnchoredPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_ConstrainProportionsScale
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 8825460134736404412, guid: 1e9d514b702f5784791a4df8d22e1866,
+        type: 3}
+      propertyPath: m_Name
+      value: WebViewComponent
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 1e9d514b702f5784791a4df8d22e1866, type: 3}
+--- !u!224 &154516623474793748 stripped
+RectTransform:
+  m_CorrespondingSourceObject: {fileID: 2974656142881083530, guid: 1e9d514b702f5784791a4df8d22e1866,
+    type: 3}
+  m_PrefabInstance: {fileID: 3129137578625191326}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &5843063412347785762 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 8825460134736404412, guid: 1e9d514b702f5784791a4df8d22e1866,
+    type: 3}
+  m_PrefabInstance: {fileID: 3129137578625191326}
+  m_PrefabAsset: {fileID: 0}
+--- !u!114 &5494409561197319113
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5843063412347785762}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 534d25977ad35154781c29672ce2bb13, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1001 &3298748675541104415
 PrefabInstance:
   m_ObjectHideFlags: 0
@@ -3438,7 +3653,7 @@ PrefabInstance:
     - target: {fileID: 3581652732795482907, guid: 49deb83b881477047bfac0ee629a7ae9,
         type: 3}
       propertyPath: m_RootOrder
-      value: 6
+      value: 7
       objectReference: {fileID: 0}
     - target: {fileID: 3581652732795482907, guid: 49deb83b881477047bfac0ee629a7ae9,
         type: 3}
@@ -3538,7 +3753,7 @@ PrefabInstance:
     - target: {fileID: 4838871000058222821, guid: 49deb83b881477047bfac0ee629a7ae9,
         type: 3}
       propertyPath: m_AnchoredPosition.y
-      value: -780
+      value: 0
       objectReference: {fileID: 0}
     m_RemovedComponents: []
   m_SourcePrefab: {fileID: 100100000, guid: 49deb83b881477047bfac0ee629a7ae9, type: 3}
diff --git a/Assets/Scenes/Worlds/RiverWorld.unity b/Assets/Scenes/Worlds/RiverWorld.unity
index 45555d2b40aae99c7444d735064ca11e2d374dd8..86592f5a4eef7b1200d4fa4abe918e488bfd798d 100644
--- a/Assets/Scenes/Worlds/RiverWorld.unity
+++ b/Assets/Scenes/Worlds/RiverWorld.unity
@@ -38,7 +38,7 @@ RenderSettings:
   m_ReflectionIntensity: 1
   m_CustomReflection: {fileID: 0}
   m_Sun: {fileID: 0}
-  m_IndirectSpecularColor: {r: 0.44084534, g: 0.48955458, b: 0.56959116, a: 1}
+  m_IndirectSpecularColor: {r: 0.4410664, g: 0.4898227, b: 0.5699191, a: 1}
   m_UseRadianceAmbientProbe: 0
 --- !u!157 &3
 LightmapSettings:
@@ -39839,6 +39839,18 @@ Transform:
     type: 3}
   m_PrefabInstance: {fileID: 1858641942}
   m_PrefabAsset: {fileID: 0}
+--- !u!114 &1860195005 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 5465760683304490972, guid: b07552db700124a4680401e6fb94c186,
+    type: 3}
+  m_PrefabInstance: {fileID: 3020720018843181009}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ae310511ad607a64e891bcb46517277b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1001 &1862008647
 PrefabInstance:
   m_ObjectHideFlags: 0
@@ -63789,6 +63801,11 @@ PrefabInstance:
       propertyPath: m_AnchoredPosition.y
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 8063809797576254706, guid: b07552db700124a4680401e6fb94c186,
+        type: 3}
+      propertyPath: activeScroll
+      value: 
+      objectReference: {fileID: 1860195005}
     - target: {fileID: 8111165885733284748, guid: b07552db700124a4680401e6fb94c186,
         type: 3}
       propertyPath: m_AnchorMax.y
diff --git a/Assets/Scripts/InteractionEngine/CommunicationEvents.cs b/Assets/Scripts/InteractionEngine/CommunicationEvents.cs
index bca75d877d64b9cf64721dd3808db7ac66663307..f9b0f0f3fed5882dc398f2fdd52b7eb6b95bfa9c 100644
--- a/Assets/Scripts/InteractionEngine/CommunicationEvents.cs
+++ b/Assets/Scripts/InteractionEngine/CommunicationEvents.cs
@@ -13,20 +13,15 @@ public static class CommunicationEvents
     public static UnityEvent<Fact> AddFactEvent = new();
     public static UnityEvent<Fact> RemoveFactEvent = new();
 
-    public static UnityEvent<REST_JSON_API.ScrollApplicationCheckingError[]> ScrollApplicationCheckingErrorEvent = new();
-    public static UnityEvent PushoutFactFailEvent = new();
-
     public static UnityEvent gameSucceededEvent = new();
     public static UnityEvent gameNotSucceededEvent = new();
-    public static UnityEvent NewAssignmentEvent = new();
     public static UnityEvent StartT0Event = new();
-
-    public static UnityEvent<string> ScrollFactHintEvent = new();
+    /** <summary>Inform ui that it should animate the given fact with the given material.</summary> */
     public static UnityEvent<string, FactWrapper.FactMaterials> AnimateExistingFactEvent = new();
+    /** <summary>Start firework and invoke AnimateExistingFactEvent.</summary> */
     public static UnityEvent<Fact, FactWrapper.FactMaterials> AnimateExistingAsSolutionEvent = new();
+    /** <summary>Show a missing fact for a short time as hint for the user.</summary> */
     public static UnityEvent<Fact> AnimateNonExistingFactEvent = new();
-    public static UnityEvent<List<string>> HintAvailableEvent = new();
-
 
 
     //------------------------------------------------------------------------------------
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/FactObjectUI.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/FactObjectUI.cs
index 7a0449d0a3cb18953becd9dbff8b867262a9b32d..e90f6cc60b78d374a3a30f15d0c3fafee9fc2516 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/FactObjectUI.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/FactObjectUI.cs
@@ -1,7 +1,8 @@
+using bessw.Unity.WebView;
 using System.Linq;
 using UnityEngine.UI;
 
-public class FactObjectUI : FactObject
+public class FactObjectUI : FactObject, BrowserDropable
 {
     public Image[] Images;
 
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs
index 6a362b1c51890dbe89f8af53b9793cc87d33d1b5..cb0f1d45327d6e11bd74542ba5cd986af97ec388 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs
@@ -1,5 +1,6 @@
 using REST_JSON_API;
 using System.Collections.Generic;
+using System.Linq;
 using TMPro;
 using UnityEngine;
 using UnityEngine.EventSystems;
@@ -66,9 +67,9 @@ protected override void FactUpdated()
         if (VerboseURI)
             Debug.Log(nameof(RenderedScrollFact) + " recieved Fact: " + URI);
 
-        NewAssignmentEvent.Invoke();
         ResetPayload();
         base.FactUpdated();
+        SwitchScrollUI.activeScrollData.AssignFact(ScrollFactURI, Fact);
     }
 
     private void ResetPayload()
@@ -97,19 +98,21 @@ protected override void _DeleteFactEvent(Fact fact)
 
     protected override void _OnEnable()
     {
-        HintAvailableEvent.AddListener(OnHintAvailable);
+        SwitchScrollUI.activeScrollData.HintAvailableEvent.AddListener(OnHintAvailable);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.AddListener(OnScrollUpdated);
         ResetPayload();
     }
 
     protected override void _OnDisable()
     {
-        HintAvailableEvent.RemoveListener(OnHintAvailable);
+        SwitchScrollUI.activeScrollData.HintAvailableEvent.RemoveListener(OnHintAvailable);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.RemoveListener(OnScrollUpdated);
     }
 
     public void Populate(Scroll scroll, string scroll_fact_uri)
     {
-        _Scroll = scroll;
-        _ScrollFactURI = scroll_fact_uri;
+        ScrollFactURI = scroll_fact_uri;
+        Scroll = scroll;
         _URI = ScrollFactURI;
 
         ResetPayload();
@@ -120,12 +123,22 @@ private void SetLabel(string label)
         LabelMesh.text = label ?? Scroll?.requiredFacts[ID].label ?? "Err";
     }
 
+    private void OnScrollUpdated(Scroll rendered) {
+        // this if has been copied during refactoring, I don't know if we need it^^
+        if(SwitchScrollUI.activeScrollData.DynamicScrollDescriptionsActive &&
+            rendered.requiredFacts.Any(rf => rf.@ref.uri == ScrollFactURI))
+        {
+            // Update ScrollParameter label (side effect of setting the property "Scroll")
+            Scroll = rendered;
+        }
+    }
+
     public void OnClickHintButton()
     {
-        ScrollFactHintEvent.Invoke(URI);
+        SwitchScrollUI.activeScrollData.ButtonClicked(new HintScrollButton(URI));
     }
 
-    public void OnHintAvailable(List<string> uris)
+    private void OnHintAvailable(IReadOnlyList<string> uris)
     {
         HintButton.SetActive(uris.Contains(ScrollFactURI));
     }
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/Facts/Fact.cs b/Assets/Scripts/InteractionEngine/FactHandling/Facts/Fact.cs
index 83aa7854ffc649a2b70af504e21d6049dd551aa2..1bafe7d92f05ca815c92640cde3a0a0a82075981 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/Facts/Fact.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/Facts/Fact.cs
@@ -262,6 +262,7 @@ public string GetLabel(FactRecorder name_space)
             ? _CustomLabel
             : generateLabel(name_space));
     }
+    [JsonProperty("Label")]
     protected internal string _LastLabel = "NaN";
 
     public bool SetLabel(string value, FactRecorder name_space)
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/GadgetBehaviour.cs b/Assets/Scripts/InteractionEngine/Gadgets/GadgetBehaviour.cs
index 57c0ebacd3cbeebd2982c0f6dfcc416775274b95..2c96847470cb283ebca9b4a9bbbed5807da99206 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/GadgetBehaviour.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/GadgetBehaviour.cs
@@ -5,7 +5,6 @@
 using UnityEngine;
 using UnityEngine.Events;
 using UnityEngine.UI;
-using static UIconfig;
 
 
 public class GadgetBehaviour : MonoBehaviour, ISerializationCallbackReceiver
diff --git a/Assets/Scripts/InteractionEngine/ShinyThings.cs b/Assets/Scripts/InteractionEngine/ShinyThings.cs
index 37993ee77fe3c40f372e5b296bf9577d3c7823af..4d33baf625a1d63c964ca9ef048aacb508b1854f 100644
--- a/Assets/Scripts/InteractionEngine/ShinyThings.cs
+++ b/Assets/Scripts/InteractionEngine/ShinyThings.cs
@@ -28,13 +28,19 @@ public GameObject
 
     private void OnEnable()
     {
-        CommunicationEvents.PushoutFactFailEvent.AddListener(LetItRain);
+        SwitchScrollUI.OnActiveScrollDataInitialized.AddListener(OnActiveScrollDataInitialized);
         CommunicationEvents.AnimateExistingAsSolutionEvent.AddListener(HighlightWithFireworks);
     }
 
+    private void OnActiveScrollDataInitialized()
+    {
+        SwitchScrollUI.activeScrollData.OnScrollApplicationError.AddListener(LetItRain);
+        SwitchScrollUI.OnActiveScrollDataInitialized.RemoveListener(OnActiveScrollDataInitialized);
+    }
+
     private void OnDisable()
     {
-        CommunicationEvents.PushoutFactFailEvent.RemoveListener(LetItRain);
+        SwitchScrollUI.activeScrollData.OnScrollApplicationError.RemoveListener(LetItRain);
         CommunicationEvents.AnimateExistingAsSolutionEvent.RemoveListener(HighlightWithFireworks);
     }
 
@@ -117,7 +123,7 @@ IEnumerator _BlossomAndDie()
         }
     }
 
-    public void LetItRain()
+    public void LetItRain(REST_JSON_API.ScrollApplicationCheckingError[] _)
     {
         // check if couroutine is waiting or finished 
         if (!rain_wait.MoveNext() || !rain.MoveNext())
diff --git a/Assets/Scripts/InventoryStuff/ActiveScroll.cs b/Assets/Scripts/InventoryStuff/ActiveScroll.cs
new file mode 100644
index 0000000000000000000000000000000000000000..20d585471bd178177af92e3ca9dec507ce3c934e
--- /dev/null
+++ b/Assets/Scripts/InventoryStuff/ActiveScroll.cs
@@ -0,0 +1,503 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using REST_JSON_API;
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.Networking;
+
+#nullable enable annotations
+
+/// <summary>
+/// This class keeps track of the currently active scroll and comunicates with the FrameIT Server.
+/// It represents the model of the Model-View-Controler pattern.
+/// </summary>
+[Serializable]
+public class ActiveScroll : MonoBehaviour
+{
+
+    /** <summary>Event that is invoked when a scroll button is clicked, e.g. magic button. You can use it to handle custom buttons.</summary> */
+    public UnityEvent<ScrollButton> OnButtonClick = new();
+    /** <summary>Notifies about availible hints.</summary> */
+    public UnityEvent<IReadOnlyList<string>> HintAvailableEvent = new();
+    /** <summary>Event that is invoked when a fact has been assigned to a slot and the ui needs to update.</summary> */
+    public UnityEvent<string,SlotAssignment> OnFactAssignmentUpdated = new();
+    /** <summary>Event that is invoked when the MMT server returned an error.</summary> */
+    public UnityEvent<string> OnMMTServerComunicationError = new();
+    /** <summary>Event that is invoked when the scroll dynamic info returned errors in the current scroll assignment.</summary> */
+    public UnityEvent<ScrollApplicationCheckingError[]> OnScrollDynamicInfoError = new();
+    /** <summary>Event that is invoked when the scroll application returned errors in the current assignment.</summary> */
+    public UnityEvent<ScrollApplicationCheckingError[]> OnScrollApplicationError = new();
+    /** <summary>Event that is invoked when the OnMMTServerError or OnScrollApplicationFailed error state has been resolved and e.g. the error popup can be cloesed.</summary> */
+    public UnityEvent OnCancelErrorState = new();
+    /** <summary>Event that is invoked when the scroll has been solved.</summary> */
+    public UnityEvent<IReadOnlyList<Fact>> OnScrollSolved = new();
+    /** <summary>Event that is invoked when an other scroll has been selected.</summary> */
+    public UnityEvent<Scroll> OnScrollChanged = new();
+    /** <summary>Event that is invoked when the dynamic info of the scroll has been updated e.g. because a fact has been assigned.</summary> */
+    public UnityEvent<Scroll> OnScrollDynamicInfoUpdated = new();
+
+
+    public bool DynamicScrollDescriptionsActive = true;
+    public bool AutomaticHintGenerationActive = true;
+
+    private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>()
+    {
+        // Insert ScrollURIS that are poorly optimized
+    };
+
+
+    /// <summary>
+    /// Variable used to block that only one SendView is running at a time.
+    /// </summary>
+    private bool SendingViewDone = true;
+    /// <summary>
+    /// this is true while a fact assignment is enqueued
+    /// </summary>
+    private bool DynamicScrollInQue = false;
+    /// <summary>
+    /// this is true while a scroll application via magic button is enqueued
+    /// </summary>
+    private bool MagicInQue = false;
+    private string currentMmtAnswer;
+
+    /** <summary>List of available hints for the current partial assignment.</summary> */
+    private List<Fact> LatestRenderedHints;
+    /** <summary>List of available auto comletions for the current partial assignment. Used to generate hints.</summary> */
+    private List<ScrollAssignment> LatestBackwartsCompletions;
+
+    private Scroll _Scroll;
+    /// <summary>
+    /// The currently active scroll
+    /// </summary>
+    public Scroll Scroll
+    {
+        get => _Scroll;
+        set {
+            _Scroll = value;
+            
+            // initialize the assignments dictionary with the references to the required fact slots as keys
+            // and an empty SlottAssignment
+            _Assignments = value.requiredFacts
+                .ToDictionary(fact => fact.@ref.uri, _ => new SlotAssignment());
+            
+            // apply fact assignments that are pre defined by the stage (defaults or constrains), if there are any
+            if (StageStatic.stage.solution.ScrollOverwrites
+                    .TryGetValue(value.ScrollReference, out (string, int, bool)[] population)
+            && population.Length > 0)
+            {
+                foreach ((string Id, int index, bool show) in population)
+                {
+                    var key = value.requiredFacts[index].@ref.uri;
+                    _Assignments[key].fact = FactRecorder.AllFacts[Id];
+                    _Assignments[key].IsVisible = show;
+                }
+            }
+            // notify the observers that the scroll has changed
+            OnScrollChanged.Invoke(value);
+        }
+    }
+    /// <summary>
+    /// The currently active scroll with rendered description and backward_completions (hints)
+    /// </summary>
+    public Scroll RenderedScroll { get; private set; }
+
+    public class SlotAssignment
+    {
+        public Fact? fact = null;
+        public bool IsVisible = true;
+        public bool IsSet => fact is not null;
+    }
+
+    /// <summary>
+    /// Maps the required facts (keys) to the assigned facts (values)
+    /// </summary>
+    private Dictionary<string, SlotAssignment> _Assignments = new();
+
+    /// <summary>
+    /// Maps the required facts (keys) to the assigned facts (values).
+    /// </summary>
+    public IReadOnlyDictionary<string, SlotAssignment> Assignments => _Assignments;
+
+    /// <summary>
+    /// The ScrollView should call this method when a button is clicked
+    /// </summary>
+    public void ButtonClicked(ScrollButton button)
+    {
+        switch (button)
+        {
+            // Magic button to apply the scroll
+            case MagicScrollButton:
+                if ((Scroll is not null) && ! MagicInQue) StartCoroutine(SendScrollApplication());
+                break;
+            case HintScrollButton:
+                HintButtonPressed((HintScrollButton)button);
+                break;
+            // add cases for future additional buttons here
+        }
+        // notify observers that a button was clicked
+        OnButtonClick.Invoke(button);
+    }
+
+    /// <summary>
+    /// Assigns the given fact to the given slotUri and calls the OnFactAssignmentUpdated Event
+    /// </summary>
+    /// <param name="slotUri"></param>
+    /// <param name="fact"></param>
+    /// <exception cref="ArgumentException"></exception>
+    public void AssignFact(string slotUri, Fact fact)
+    {
+        if (Assignments.ContainsKey(slotUri))
+        {
+            var slot = _Assignments[slotUri];
+
+            // return if the slot is already assigned to the given value (avoid unnecessary ui updates and frameit Server requests)
+            //if ((fact is null && !slot.IsSet) || (slot.IsSet && slot.fact.Id == fact.Id)) return;
+            if (fact is null && !slot.IsSet) return;
+
+            slot.fact = fact;
+            SendCurrentFactAssignments();
+            OnFactAssignmentUpdated.Invoke(slotUri, slot);
+        } else {
+            throw new ArgumentException($"Can't assign fact, the given slot doesn't exist on the current scroll: '{slotUri}'");
+        }
+    }
+
+    /// <summary>
+    /// If the fact is not already assigned, assign it to the next empty slot, otherwise unassign it.
+    /// And calls the OnFactAssignmentUpdated Event.
+    /// </summary>
+    /// <param name="fact"></param>
+    /// <returns></returns>
+    public string? AssignOrRemoveFactToNextEmptySlot(Fact fact)
+    {
+        // unassign the fact if it is already assigned and return
+        try {
+            var assignedSlot = _Assignments.First(pair => pair.Value.fact == fact).Key;
+            AssignFact(assignedSlot, null);
+            return null;
+        } catch (Exception) {
+            // the fact has not been assigned yet => continue
+        }
+
+        // find the first empty slot and assign the fact to it
+        try {
+            KeyValuePair<string, SlotAssignment> emptySlot = _Assignments.First(pair => !pair.Value.IsSet);
+            AssignFact(emptySlot.Key, fact);
+            return emptySlot.Key;
+        } catch (Exception) {
+            return null;
+        }
+    }
+
+
+    #region MagicButton
+    private IEnumerator SendScrollApplication()
+    {
+        // there is already a MagicButton coroutine waiting => exit
+        if (MagicInQue) yield break;
+
+        MagicInQue = true;
+        // wait if SendView is not yet done or dynamic scroll is in the queue
+        while (!SendingViewDone || DynamicScrollInQue)
+            yield return null;
+        MagicInQue = false;
+
+        yield return SendView("/scroll/apply");
+
+        if (currentMmtAnswer == null)
+        {
+            // OnMMTServerComunicationError is already invoked in SendView
+            Debug.LogError("Magic FAILED");
+            yield break;
+        }
+        else
+        {
+            if (CommunicationEvents.VerboseURI)
+                Debug.Log("Magic answers:\n" + currentMmtAnswer);
+
+            System.DateTime deserializeTime = System.DateTime.UtcNow;
+            ScrollApplicationInfo scrollApplication = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer);
+            Debug.LogFormat($"Answerd deserialized in : {(System.DateTime.UtcNow - deserializeTime).TotalMilliseconds}ms");
+
+            if (scrollApplication.acquiredFacts.Count == 0
+                || scrollApplication.errors.Length > 0)
+            {
+                OnScrollApplicationError.Invoke(scrollApplication.errors);
+            }
+            else
+            {
+                OnCancelErrorState.Invoke();
+            }
+
+            var new_facts = __GenerateUnityFactsForAcquiredMMTFacts(scrollApplication.acquiredFacts);
+            if (scrollApplication.acquiredFacts.Count >= 0 || scrollApplication.errors.Length == 0)
+            {
+                OnScrollSolved.Invoke(new_facts);
+            }
+        }
+    }
+
+    private List<Fact> __GenerateUnityFactsForAcquiredMMTFacts(List<MMTFact> pushoutFacts)
+    {
+        List<Fact> new_facts = new();
+        Dictionary<string, string> old_to_new = new();
+        System.DateTime parseTime = System.DateTime.UtcNow;
+
+        bool samestep = false;
+        for (int i = 0; i < pushoutFacts.Count; i++)
+        {
+            List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new));
+
+            if (new_list.Count == 0)
+            {
+                Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed");
+                continue;
+            }
+
+            foreach (Fact new_fact in new_list)
+            {
+                Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, Scroll.label);
+                if (!exists)
+                {
+                    new_facts.Add(added);
+                    CommunicationEvents.AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution);
+                    samestep = true;
+                }
+                else
+                {
+                    // AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder
+                    old_to_new.Add(new_fact.Id, added.Id);
+                }
+            }
+
+            //yield return null;
+        }
+
+        Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms");
+        return new_facts;
+    }
+
+    #endregion MagicButton
+
+    #region HintButton
+    
+    private void HintButtonPressed(HintScrollButton hintButton)
+    {
+        if (FactRecorder.AllFacts.ContainsKey(hintButton.SlotUri))
+        {
+            CommunicationEvents.AnimateExistingFactEvent.Invoke(
+                    hintButton.SlotUri,
+                    FactWrapper.FactMaterials.Hint
+                );
+        }
+
+        Fact hintFact = LatestRenderedHints.Find(x => x.Id == hintButton.SlotUri); // "Dictionary"
+
+        ScrollAssignment suitableCompletion =
+            LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == hintButton.SlotUri); // "Dictionary"
+
+        if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment)
+        {
+            if (FactRecorder.AllFacts.ContainsKey(assignment.uri))
+            {
+                CommunicationEvents.AnimateExistingFactEvent.Invoke(
+                    assignment.uri,
+                    FactWrapper.FactMaterials.Hint
+                );
+            }
+        }
+        else if (hintFact != null)
+        {
+            if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false))
+                // existing fact -> Animate that
+                CommunicationEvents.AnimateExistingFactEvent.Invoke(
+                    found_key,
+                    FactWrapper.FactMaterials.Hint
+                );
+
+            else
+            {   // Generate new FactRepresentation and animate it
+                CommunicationEvents.AnimateNonExistingFactEvent.Invoke(hintFact);
+                CommunicationEvents.AnimateExistingFactEvent.Invoke(
+                    hintButton.SlotUri,
+                    FactWrapper.FactMaterials.Hint
+                );
+            }
+        }
+    }
+    #endregion HintButton
+
+    #region FactAssignment
+
+    public void SendCurrentFactAssignments()
+    {
+        if (Scroll?.ScrollReference == null
+         || NoDynamicScroll.Contains(Scroll.ScrollReference))
+            return;
+
+        if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive)
+            StartCoroutine(_SendAssignments());
+    }
+
+    private IEnumerator _SendAssignments()
+    {
+        if (DynamicScrollInQue)
+            yield break; // only need next in que to finish
+
+        DynamicScrollInQue = true;
+        while (!SendingViewDone)
+            yield return null; // if we dont wait => server will crash
+        DynamicScrollInQue = false;
+
+        yield return SendView("/scroll/dynamic");
+
+        if (currentMmtAnswer == null)
+        {
+            // OnMMTServerComunicationError is already invoked in SendView
+            Debug.LogError("Dynamic Scroll FAILED");
+            yield break;
+        }
+        else
+        {
+            ScrollDynamicInfo scrollDynamicInfo;
+            try
+            {
+                scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer);
+                //scrollDynamicInfo = IJSONsavable<ScrollDynamicInfo>.postprocess(scrollDynamicInfo); // DON'T! will remove hints
+            }
+            catch (JsonSerializationException ex)
+            {
+                Debug.LogException(ex);
+                Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer);
+                OnMMTServerComunicationError.Invoke("MMT Server communication error: could not deserialize message.");
+                yield break;
+            }
+
+            ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors
+                .Where(err => err.kind != "nonTotal") // expected
+                .ToArray();
+
+            if (errors.Length > 0) OnScrollDynamicInfoError.Invoke(errors);
+
+            _ProcessScrollDynamicInfo(scrollDynamicInfo);
+        }
+    }
+
+    private void _ProcessScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo)
+    {
+        //TODO: more hints available in scrollDynamicInfo.rendered.requiredFacts
+
+        LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0
+            ? scrollDynamicInfo.backward_completions[0]
+            : new List<ScrollAssignment>();
+
+        List<string> hintUris = LatestBackwartsCompletions
+            .Select(completion => completion.fact.uri)
+            .ToList();
+
+        //Update Scroll, process data for later hints and update Uri-List for which hints are available
+        _processRenderedScroll(scrollDynamicInfo.rendered, hintUris);
+
+        if (AutomaticHintGenerationActive)
+            //Show that Hint is available for ScrollParameter
+            HintAvailableEvent.Invoke(hintUris);
+
+        return;
+
+        void _processRenderedScroll(Scroll rendered, List<string> hintUris)
+        {
+            OnScrollDynamicInfoUpdated.Invoke(rendered);
+
+            LatestRenderedHints = new();
+            foreach (var requiredFact in rendered.requiredFacts)
+            {
+                //If ScrollFact is not assigned -> enable hint button
+                if (Assignments.TryGetValue(requiredFact.@ref.uri, out SlotAssignment assignedFact) && ! assignedFact.IsSet)
+                {
+                    List<Fact> HintFactList = Fact.MMTFactory(requiredFact);
+
+                    foreach (Fact HintFact in HintFactList)
+                    {
+                        hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri
+                        LatestRenderedHints.Add(HintFact);
+                    }
+                }
+            }
+
+            return;
+        }
+    }
+
+
+    #endregion FactAssignment
+
+    /// <summary>
+    /// Only one instance of the SendView coroutine should run at a time.
+    /// </summary>
+    private IEnumerator SendView(string endpoint)
+    {
+        SendingViewDone = false;
+
+        //while (ParameterDisplays == null) // Wait for server
+        //    yield return null;
+
+        string body = prepareScrollAssignments();
+
+        using UnityWebRequest www = UnityWebRequest.Put(CommunicationEvents.ServerAdress + endpoint, body);
+        www.method = UnityWebRequest.kHttpVerbPOST;
+        www.SetRequestHeader("Content-Type", "application/json");
+
+        System.DateTime sendTime = System.DateTime.UtcNow;
+        yield return www.SendWebRequest();
+        //if (VerboseURI)
+        Debug.LogFormat("Server answerd in : {0}ms"
+            , (System.DateTime.UtcNow - sendTime).TotalMilliseconds);
+
+        if (www.result == UnityWebRequest.Result.ConnectionError
+         || www.result == UnityWebRequest.Result.ProtocolError)
+        {
+            Debug.LogWarning(www.error);
+            currentMmtAnswer = null;
+            OnMMTServerComunicationError.Invoke($"Server communication error: {www.error}");
+            SendingViewDone = true;
+            yield break;
+        }
+        else
+        {
+            while (!www.downloadHandler.isDone)
+                yield return null;
+
+            currentMmtAnswer = www.downloadHandler.text
+                .Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type
+        }
+
+        SendingViewDone = true;
+        yield break;
+
+        /// <summary>Maps the assigned slots to ScrollAssignment objects.
+        /// Returns json serialized ScrollApplication containing the list of ScrollAssignments.</summary>
+        string prepareScrollAssignments()
+        {
+            List<ScrollAssignment> assignmentList = Assignments.Where(assignment => assignment.Value.IsSet)
+                .Select(assignment => new ScrollAssignment(assignment.Key, assignment.Value.fact.ServerDefinition))
+                .ToList();
+
+            return JsonConvert.SerializeObject(new ScrollApplication(Scroll.ScrollReference, assignmentList));
+        }
+    }
+}
+
+/// <summary>
+/// Parrent class for all scroll buttons, inherit from this class to implement custom buttons.
+/// </summary>
+public abstract class ScrollButton {}
+
+public class MagicScrollButton : ScrollButton {}
+public class HintScrollButton : ScrollButton
+{
+    public readonly string SlotUri;
+    public HintScrollButton(string slotUri) => SlotUri = slotUri;
+}
\ No newline at end of file
diff --git a/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta b/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9098ea197710bb798ddbc60676934b8ee348d9d1
--- /dev/null
+++ b/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ae310511ad607a64e891bcb46517277b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
index a9ec4ddcd40579c81b51392a78755b72348b1695..d9c6616d47d2496ff554d8d38d969dc5d2d3845b 100644
--- a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
+++ b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
@@ -10,7 +10,6 @@ public class DisplayScrolls : MonoBehaviour
     static public List<REST_JSON_API.Scroll> AllowedScrolls;
     public GameObject[] ScrollButtons;
     public GameObject ScrollPrefab;
-    public GameObject DetailScreen;
 
     public Transform scrollscreenContent;
 
@@ -35,13 +34,11 @@ void BuildScrollGUI()
         {
             var obj = Instantiate(ScrollPrefab, scrollscreenContent);
             obj.GetComponent<ScrollClickedScript>().scroll = AllowedScrolls[i];
-            obj.GetComponent<ScrollClickedScript>().DetailScreen = this.DetailScreen;
             obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = AllowedScrolls[i].label;
             ScrollButtons[i] = obj;
         }
 
         REST_JSON_API.Scroll preferredStartScroll = AllowedScrolls.Find(x => x.label.Equals(preferredStartScrollName));
-        if (preferredStartScroll != null)
-            ScrollDetails.Instance?.SetScroll(preferredStartScroll);
+        if (preferredStartScroll != null) SwitchScrollUI.activeScrollData.Scroll = preferredStartScroll;
     }
 }
diff --git a/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs b/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs
index 50ee8f5cc4580e4a7731308fa8ddafee21525f2b..7936ebbe44968473174c1e9e2f5707ec5a86d89d 100644
--- a/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs
+++ b/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs
@@ -5,10 +5,9 @@
 public class ScrollClickedScript : MonoBehaviour, IPointerDownHandler
 {
     public Scroll scroll;
-    public GameObject DetailScreen;
 
     public void OnPointerDown(PointerEventData eventData)
     {
-        this.DetailScreen.GetComponent<ScrollDetails>().SetScroll(this.scroll);
+        SwitchScrollUI.activeScrollData.Scroll = this.scroll;
     }
 }
diff --git a/Assets/Scripts/InventoryStuff/ScrollDetails.cs b/Assets/Scripts/InventoryStuff/ScrollDetails.cs
deleted file mode 100644
index 0b0afd18ea4fd37ab8b5c33d2246f2d325bb2156..0000000000000000000000000000000000000000
--- a/Assets/Scripts/InventoryStuff/ScrollDetails.cs
+++ /dev/null
@@ -1,446 +0,0 @@
-using Newtonsoft.Json;
-using REST_JSON_API;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using TMPro;
-using UnityEngine;
-using UnityEngine.Networking;
-using static CommunicationEvents;
-
-public class ScrollDetails : MonoBehaviour
-{
-    public static ScrollDetails Instance
-    {
-        get => _Instance;
-        set
-        {
-            if (_Instance != value)
-                Destroy(_Instance);
-
-            _Instance = value;
-        }
-    }
-    private static ScrollDetails _Instance;
-
-    public WorldCursor cursor;
-    public GameObject parameterDisplayPrefab;
-    public static Scroll ActiveScroll { get; private set; }
-    public GameObject mmtAnswerPopUp;
-    private PopupBehavior Popup;
-
-    public static List<RenderedScrollFact> ParameterDisplays { get; private set; }
-    private static List<ScrollAssignment> LatestBackwartsCompletions;
-    private static List<Fact> LatestRenderedHints;
-
-    public string currentMmtAnswer;
-
-    public bool DynamicScrollDescriptionsActive = true;
-    public bool AutomaticHintGenerationActive = true;
-
-    private bool SendingViewDone = true;
-    private bool DynamicScrollInQue = false;
-    private bool MagicInQue = false;
-
-    private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>()
-    {
-        // Insert ScrollURIS that are poorly optimized
-    };
-
-    void Awake()
-    {
-        Instance = this;
-
-        if (cursor == null)
-            cursor = FindObjectOfType<WorldCursor>();
-
-        Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>();
-        Popup.gameObject.SetActive(true); // force Awake
-    }
-
-    private void OnEnable()
-    {
-        ScrollFactHintEvent.AddListener(animateHint);
-        NewAssignmentEvent.AddListener(NewAssignmentTrigger);
-    }
-
-    private void OnDisable()
-    {
-        ScrollFactHintEvent.RemoveListener(animateHint);
-        NewAssignmentEvent.RemoveListener(NewAssignmentTrigger);
-    }
-
-    private void OnDestroy()
-    {
-        _Instance = null;
-    }
-
-    public void SetScroll(Scroll scroll_to_set)
-    {
-        ActiveScroll = scroll_to_set;
-
-        Transform originalScroll = gameObject.transform.GetChild(1);
-        Transform originalScrollView = originalScroll.GetChild(1);
-        Transform originalViewport = originalScrollView.GetChild(0);
-
-        //Clear all current ScrollFacts
-        originalViewport.GetChild(0).gameObject.DestroyAllChildren();
-
-        originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = ActiveScroll.description;
-
-        //ParameterDisplays.ForEach(gameObj => Destroy(gameObj));
-        ParameterDisplays = new();
-        for (int i = 0; i < ActiveScroll.requiredFacts.Count; i++)
-        {
-            GameObject originalObj =
-                Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0));
-
-            RenderedScrollFact originalRSF =
-                originalObj.GetComponentInChildren<RenderedScrollFact>();
-
-            ParameterDisplays.Add(originalRSF);
-
-            originalRSF.Populate(ActiveScroll, ActiveScroll.requiredFacts[i].@ref.uri);
-        }
-
-        if (StageStatic.stage.solution.ScrollOverwrites
-                .TryGetValue(ActiveScroll.ScrollReference, out (string, int, bool)[] population)
-         && population.Length > 0)
-        {
-            DynamicScrollInQue = true; // block update on population
-
-            foreach ((string Id, int index, bool show) in population)
-            {
-                ParameterDisplays[index].URI = Id;
-                ParameterDisplays[index].transform.parent.gameObject.SetActive(show);
-            }
-
-            DynamicScrollInQue = false; // unblock
-        }
-
-        NewAssignmentEvent.Invoke(); // init display
-    }
-
-    public bool SetNextEmptyTo(FactObjectUI activator)
-    {
-        RenderedScrollFact check = ParameterDisplays
-            .Find(RSF => RSF != null
-                      && RSF.Payload != null
-                      && RSF.Payload.Equals(activator));
-
-        if (check != null)
-        {
-            check.URI = null;
-            return false;
-        }
-
-        RenderedScrollFact empty = ParameterDisplays
-            .Find(RSF => !RSF.IsSet);
-
-        if (empty == null)
-            return false;
-
-        empty.SetByFactObject(activator);
-        return true;
-    }
-
-    public void MagicButtonTrigger()
-    {
-        if (ActiveScroll == null)
-            return;
-
-        StartCoroutine(_MagicButton());
-
-        IEnumerator _MagicButton()
-        {
-            if (MagicInQue)
-                yield break;  // only need next in que to finish
-
-            MagicInQue = true;
-            while (!SendingViewDone || DynamicScrollInQue)
-                yield return null; // Wait for last assignment
-            MagicInQue = false;
-
-            yield return SendView("/scroll/apply");
-
-            if (currentMmtAnswer == null)   
-            {
-                Debug.LogError("Magic FAILED");
-                ScrollApplicationCheckingErrorEvent.Invoke(null);
-            }
-            else
-            {
-                if (VerboseURI)
-                    Debug.Log("Magic answers:\n" + currentMmtAnswer);
-
-                System.DateTime serializeTime = System.DateTime.UtcNow;
-                ScrollApplicationInfo pushout = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer);
-                Debug.LogFormat($"Answerd serialized in : {(System.DateTime.UtcNow - serializeTime).TotalMilliseconds}ms");
-
-                if (pushout.acquiredFacts.Count == 0
-                 || pushout.errors.Length > 0)
-                {
-                    ScrollApplicationCheckingErrorEvent.Invoke(pushout.errors);
-                    PushoutFactFailEvent.Invoke();
-                }
-                else
-                    Popup.HidePopUp(); //close error Window
-
-                yield return __GeneratePushoutFacts(pushout.acquiredFacts);
-            }
-        }
-
-        IEnumerator __GeneratePushoutFacts(List<MMTFact> pushoutFacts)
-        {
-            List<Fact> new_facts = new();
-            Dictionary<string, string> old_to_new = new();
-            System.DateTime parseTime = System.DateTime.UtcNow;
-
-            bool samestep = false;
-            for (int i = 0; i < pushoutFacts.Count; i++)
-            {
-                List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new));
-
-                if (new_list.Count == 0)
-                {
-                    Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed");
-                    continue;
-                }
-
-                foreach (Fact new_fact in new_list)
-                {
-                    Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, ActiveScroll.label);
-                    if (!exists)
-                    {
-                        new_facts.Add(added);
-                        AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution);
-                        samestep = true;
-                    }
-                    else
-                    {
-                        // AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder
-                        old_to_new.Add(new_fact.Id, added.Id);
-                    }
-                }
-
-                //yield return null;
-            }
-
-            Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms");
-            yield break;
-        }
-    }
-
-    public void NewAssignmentTrigger()
-    {
-        //return; //if you read this, delete this line!
-
-        if (ActiveScroll?.ScrollReference == null
-         || NoDynamicScroll.Contains(ActiveScroll.ScrollReference))
-            return;
-
-        if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive)
-            StartCoroutine(_NewAssignment());
-
-        IEnumerator _NewAssignment()
-        {
-            if (DynamicScrollInQue)
-                yield break; // only need next in que to finish
-
-            DynamicScrollInQue = true;
-            while (!SendingViewDone)
-                yield return null; // if we dont wait => server will crash
-            DynamicScrollInQue = false;
-
-            yield return SendView("/scroll/dynamic");
-
-            if (currentMmtAnswer == null)
-            {
-                Debug.LogError("Dynamic Scroll FAILED");
-            }
-            else
-            {
-                ScrollDynamicInfo scrollDynamicInfo;
-                try
-                {
-                    scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer);
-                    //scrollDynamicInfo = IJSONsavable<ScrollDynamicInfo>.postprocess(scrollDynamicInfo); // DON'T! will remove hints
-                }
-                catch (JsonSerializationException ex)
-                {
-                    Debug.LogException(ex);
-                    Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer);
-                    yield break;
-                }
-
-                ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors
-                    .Where(err => err.kind != "nonTotal") // expected
-                    .ToArray();
-
-                if (errors.Length > 0)
-                    ScrollApplicationCheckingErrorEvent.Invoke(errors);
-
-                processScrollDynamicInfo(scrollDynamicInfo);
-            }
-        }
-    }
-
-    private IEnumerator SendView(string endpoint)
-    {
-        SendingViewDone = false;
-
-        while (ParameterDisplays == null) // Wait for server
-            yield return null;
-
-        string body = prepareScrollAssignments();
-
-        using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body);
-        www.method = UnityWebRequest.kHttpVerbPOST;
-        www.SetRequestHeader("Content-Type", "application/json");
-
-        System.DateTime sendTime = System.DateTime.UtcNow;
-        yield return www.SendWebRequest();
-        //if (VerboseURI)
-        Debug.LogFormat("Server answerd in : {0}ms"
-            , (System.DateTime.UtcNow - sendTime).TotalMilliseconds);
-
-        if (www.result == UnityWebRequest.Result.ConnectionError
-         || www.result == UnityWebRequest.Result.ProtocolError)
-        {
-            Debug.LogWarning(www.error);
-            currentMmtAnswer = null;
-        }
-        else
-        {
-            while (!www.downloadHandler.isDone)
-                yield return null;
-
-            currentMmtAnswer = www.downloadHandler.text
-                .Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type
-        }
-
-        SendingViewDone = true;
-        yield break;
-
-        string prepareScrollAssignments()
-        {
-            List<ScrollAssignment> assignmentList = new();
-
-            for (int i = 0; i < ParameterDisplays.Count; i++)
-            {
-                Fact tempFact = ParameterDisplays[i].Fact;
-                if (tempFact != null)
-                    assignmentList.Add(new ScrollAssignment(ActiveScroll.requiredFacts[i].@ref.uri, tempFact.Id));
-            }
-
-            return JsonConvert.SerializeObject(new ScrollApplication(ActiveScroll.ScrollReference, assignmentList));
-        }
-    }
-
-    private void processScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo)
-    {
-        //TODO: more hints available in scrollDynamicInfo.rendered.requiredFacts
-
-        LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0
-            ? scrollDynamicInfo.backward_completions[0]
-            : new List<ScrollAssignment>();
-
-        List<string> hintUris = LatestBackwartsCompletions
-            .Select(completion => completion.fact.uri)
-            .ToList();
-
-        //Update Scroll, process data for later hints and update Uri-List for which hints are available
-        _processRenderedScroll(scrollDynamicInfo.rendered, hintUris);
-
-        if (AutomaticHintGenerationActive)
-            //Show that Hint is available for ScrollParameter
-            HintAvailableEvent.Invoke(hintUris);
-
-        return;
-
-        void _processRenderedScroll(Scroll rendered, List<string> hintUris)
-        {
-            if (DynamicScrollDescriptionsActive)
-            { // Update scroll-description
-                Transform scroll = gameObject.transform.GetChild(1).transform;
-                scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description;
-            }
-
-            LatestRenderedHints = new();
-            for (int i = 0; i < rendered.requiredFacts.Count; i++)
-            {
-                RenderedScrollFact RenderedScrollFact = ParameterDisplays
-                    .Find(RSF => RSF.ScrollFactURI == rendered.requiredFacts[i].@ref.uri);
-
-                if (RenderedScrollFact == null)
-                { // e.g. FUNCs
-                    //if(CommunicationEvents.VerboseScroll)
-                    //    Debug.Log($"Descrapancy between requiredFacts and displayed facts:" +
-                    //        $"Could not find display with ref: {rendered.requiredFacts[i].@ref.uri}");
-                    continue;
-                }
-
-                if (DynamicScrollDescriptionsActive)
-                    //Update ScrollParameter label
-                    RenderedScrollFact.Scroll = rendered;
-
-                //If ScrollFact is assigned -> No Hint
-                if (!RenderedScrollFact.IsSet)
-                {
-                    List<Fact> HintFactList = Fact.MMTFactory(rendered.requiredFacts[i]);
-
-                    foreach (Fact HintFact in HintFactList)
-                    {
-                        hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri
-                        LatestRenderedHints.Add(HintFact);
-                    }
-                }
-            }
-
-            return;
-        }
-    }
-
-    public void animateHint(string scrollParameterUri)
-    {
-        if (FactRecorder.AllFacts.ContainsKey(scrollParameterUri))
-            AnimateExistingFactEvent.Invoke(
-                    scrollParameterUri,
-                    FactWrapper.FactMaterials.Hint
-                );
-
-        Fact hintFact = LatestRenderedHints.Find(x => x.Id == scrollParameterUri); // "Dictionary"
-
-        ScrollAssignment suitableCompletion =
-            LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == scrollParameterUri); // "Dictionary"
-
-        if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment)
-        {
-            if (FactRecorder.AllFacts.ContainsKey(assignment.uri))
-            {
-                AnimateExistingFactEvent.Invoke(
-                    assignment.uri,
-                    FactWrapper.FactMaterials.Hint
-                );
-            }
-        }
-        else if (hintFact != null)
-        {
-            if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false))
-                // existing fact -> Animate that
-                AnimateExistingFactEvent.Invoke(
-                    found_key,
-                    FactWrapper.FactMaterials.Hint
-                );
-
-            else
-            {   // Generate new FactRepresentation and animate it
-                AnimateNonExistingFactEvent.Invoke(hintFact);
-                AnimateExistingFactEvent.Invoke(
-                    scrollParameterUri,
-                    FactWrapper.FactMaterials.Hint
-                );
-            }
-        }
-    }
-}
diff --git a/Assets/Scripts/Loading/StreamingAssetLoader.cs b/Assets/Scripts/Loading/StreamingAssetLoader.cs
index 0f5d00a1a2f6012347fa190ff88664de463e3cba..e8ad2dfa932003f47d6c0f271bbe1c622e765680 100644
--- a/Assets/Scripts/Loading/StreamingAssetLoader.cs
+++ b/Assets/Scripts/Loading/StreamingAssetLoader.cs
@@ -142,6 +142,7 @@ public static void NetworkJSON_Save()
         myObject.camRotatingSensitivity = UIconfig.camRotatingSensitivity;
         myObject.MouseKeepingInWindow = UIconfig.MouseKeepingInWindow;
         myObject.scrolldirection = UIconfig.scrolldirection;
+        myObject.scrollViewVersion = UIconfig.scrollViewVersion;
 
 
 
@@ -478,6 +479,10 @@ public static void NetworkJSON_Load_x(string path)
         {
             UIconfig.scrolldirection = myObjs.scrolldirection;
         }
+        if (! string.IsNullOrEmpty(myObjsOnlyStrings.scrollViewVersion))
+        {
+            UIconfig.scrollViewVersion = myObjs.scrollViewVersion;
+        }
 
     }
 
diff --git a/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs b/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs
index 4f1afd71217476405c1192363a2be484336b2112..f407a6c7723059f5a1123af6b129e17aaad7161a 100644
--- a/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs
+++ b/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs
@@ -109,7 +109,7 @@ private void DoOpenFactExplorer()
     }
 
     private bool DoSetActive()
-        => ScrollDetails.Instance.SetNextEmptyTo(CachedFactWrapper as FactObjectUI);
+        => SwitchScrollUI.activeScrollData.AssignOrRemoveFactToNextEmptySlot(CachedFactWrapper.Fact) is not null;
     #endregion Implementation
 }
 
diff --git a/Assets/Scripts/UI/HideUI_mobile.cs b/Assets/Scripts/UI/HideUI_mobile.cs
index 6dc8e19d776412227474451adcb734717ae06d13..face97d01a5249fd56be092a0936009bdb7020ba 100644
--- a/Assets/Scripts/UI/HideUI_mobile.cs
+++ b/Assets/Scripts/UI/HideUI_mobile.cs
@@ -3,7 +3,6 @@
 using UnityEngine.InputSystem;
 using static CommunicationEvents;
 using static UIconfig;
-using static updateMouseCursor;
 
 public class HideUI_mobile : MonoBehaviour
 {
diff --git a/Assets/Scripts/UI/InGame/PopupBehavior.cs b/Assets/Scripts/UI/InGame/PopupBehavior.cs
index 6685ced25ec25e476f35190ac058fce7e44508f6..6ef35929273be86489540383caf4c3b4ff452e16 100644
--- a/Assets/Scripts/UI/InGame/PopupBehavior.cs
+++ b/Assets/Scripts/UI/InGame/PopupBehavior.cs
@@ -24,10 +24,25 @@ public string MessageText
     private string errorMessage;
 
     #region UnityMethods
-    void Awake()
+
+    void OnEnable()
     {
-        CommunicationEvents.ScrollApplicationCheckingErrorEvent.AddListener(OnFailedScrollInput);
+        SwitchScrollUI.activeScrollData.OnMMTServerComunicationError.AddListener(OnShowErrorMessage);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoError.AddListener(OnFailedScrollInput);
+        SwitchScrollUI.activeScrollData.OnScrollApplicationError.AddListener(OnFailedScrollInput);
+        SwitchScrollUI.activeScrollData.OnCancelErrorState.AddListener(HidePopUp);
+    }
 
+    void OnDisable()
+    {
+        SwitchScrollUI.activeScrollData.OnMMTServerComunicationError.RemoveListener(OnShowErrorMessage);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoError.RemoveListener(OnFailedScrollInput);
+        SwitchScrollUI.activeScrollData.OnScrollApplicationError.RemoveListener(OnFailedScrollInput);
+        SwitchScrollUI.activeScrollData.OnCancelErrorState.RemoveListener(HidePopUp);
+    }
+    
+    void Awake()
+    {
         CloseButton.onClick.RemoveAllListeners();
         CloseButton.onClick.AddListener(HidePopUp);
 
@@ -60,6 +75,15 @@ public void OnFailedScrollInput(ScrollApplicationCheckingError[] errorInfo)
         MessageText = generateHelpfulMessageAndAnimateScrollParam(errorInfo);
         ShowTimedPopUp();
     }
+    /// <summary>
+    /// this method gets an error message as parameter and shows it in a popup.
+    /// </summary>
+    /// <param name="errorMsg"></param>
+    public void OnShowErrorMessage(string errorMsg)
+    {
+        MessageText = errorMsg;
+        ShowTimedPopUp();
+    }
 
     private string generateHelpfulMessageAndAnimateScrollParam(ScrollApplicationCheckingError[] errorInfo)
     {
@@ -141,7 +165,7 @@ private MMTFact parseFactFromError(ScrollApplicationCheckingError error)
         factUri += "?" + factLabel;
 
         //find the required fact in the active scroll thats invalidly assigned
-        return ScrollDetails.ActiveScroll?.requiredFacts
+        return SwitchScrollUI.activeScrollData.Scroll.requiredFacts
             .Find(decl => decl.@ref.uri == error.fact.uri);
     }
 }
diff --git a/Assets/Scripts/UI/InGame/ScrollDetails.cs b/Assets/Scripts/UI/InGame/ScrollDetails.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0ff048e7ede4f73f4ce5a498980fd295a013edf3
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/ScrollDetails.cs
@@ -0,0 +1,121 @@
+using REST_JSON_API;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using TMPro;
+using UnityEngine;
+
+public class ScrollDetails : ScrollView
+{
+    public static ScrollDetails Instance
+    {
+        get => _Instance;
+        set
+        {
+            if (_Instance != value)
+                Destroy(_Instance);
+
+            _Instance = value;
+        }
+    }
+    private static ScrollDetails _Instance;
+
+    public WorldCursor cursor;
+    public GameObject parameterDisplayPrefab;
+    public GameObject mmtAnswerPopUp;
+    private PopupBehavior Popup;
+
+    public static List<RenderedScrollFact> ParameterDisplays { get; private set; }
+
+    void Awake()
+    {
+        Instance = this;
+
+        if (cursor == null) cursor = FindObjectOfType<WorldCursor>();
+
+        Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>();
+        Popup.gameObject.SetActive(true); // force Awake
+    }
+
+    private void OnEnable()
+    {
+        SwitchScrollUI.activeScrollData.OnScrollChanged.AddListener(ShowScroll);
+        SwitchScrollUI.activeScrollData.OnFactAssignmentUpdated.AddListener(OnFactAssignmentUpdated);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.AddListener(UpdateScrollDescription);
+    }
+
+    private void OnDisable()
+    {
+        SwitchScrollUI.activeScrollData.OnScrollChanged.RemoveListener(ShowScroll);
+        SwitchScrollUI.activeScrollData.OnFactAssignmentUpdated.RemoveListener(OnFactAssignmentUpdated);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.RemoveListener(UpdateScrollDescription);
+    }
+
+    private void OnDestroy()
+    {
+        _Instance = null;
+    }
+
+    /// <summary>
+    /// Update the scroll details screen to display the currently active scroll
+    /// </summary>
+    /// <param name="activeScroll"></param>
+    private void ShowScroll(Scroll newScroll)
+    {
+        Transform originalScroll = gameObject.transform.GetChild(1);
+        Transform originalScrollView = originalScroll.GetChild(1);
+        Transform originalViewport = originalScrollView.GetChild(0);
+
+        //Clear all current ScrollFacts
+        originalViewport.GetChild(0).gameObject.DestroyAllChildren();
+
+        var description = newScroll.description;
+        // if description is a html description, extract the alternative text representation
+        if (Regex.IsMatch(newScroll.description, ".*<scroll-description.*"))
+        {
+            description = Regex.Match(newScroll.description, "<scroll-description [^>]* alt=((?>\\\")|(?>\\'))([^>]*?)\\1[^>]*>").Groups[2].Value;
+        }
+        originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = description;
+
+        //ParameterDisplays.ForEach(gameObj => Destroy(gameObj));
+        ParameterDisplays = new();
+        // generate parameter display slots
+        foreach (var slot in SwitchScrollUI.activeScrollData.Assignments)
+        {
+            GameObject originalObj =
+                Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0));
+
+            RenderedScrollFact originalRSF =
+                originalObj.GetComponentInChildren<RenderedScrollFact>();
+
+            ParameterDisplays.Add(originalRSF);
+
+            originalRSF.Populate(newScroll, slot.Key);
+            originalRSF.transform.parent.gameObject.SetActive(slot.Value.IsVisible);
+        }
+    }
+
+    private void UpdateScrollDescription(Scroll rendered)
+    {
+        if (SwitchScrollUI.activeScrollData.DynamicScrollDescriptionsActive)
+        { // Update scroll-description
+            Transform scroll = gameObject.transform.GetChild(1).transform;
+            scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description;
+        }
+    }
+
+    private void OnFactAssignmentUpdated(string slotUri, ActiveScroll.SlotAssignment slotAssignment)
+    {
+        RenderedScrollFact changed = ParameterDisplays.Find(RSF => RSF.ScrollFactURI == slotUri);
+
+        // check if the RenderedScrollFact already contains the assigend fact
+        // this is to prevent an endless loop because updating the Fact property will trigger the NewAssignmentEvent
+        // TODO: BE: find an other solution beacause this results in requesting the scroll dynamic update twice from the server
+        if (changed != null && (
+            (!slotAssignment.IsSet && changed.IsSet) ||
+            (slotAssignment.IsSet && changed.URI != slotAssignment.fact?.Id)
+            ))
+        {
+            changed.Fact = slotAssignment.fact;
+        }
+    }
+}
diff --git a/Assets/Scripts/InventoryStuff/ScrollDetails.cs.meta b/Assets/Scripts/UI/InGame/ScrollDetails.cs.meta
similarity index 100%
rename from Assets/Scripts/InventoryStuff/ScrollDetails.cs.meta
rename to Assets/Scripts/UI/InGame/ScrollDetails.cs.meta
diff --git a/Assets/Scripts/UI/InGame/ScrollView.cs b/Assets/Scripts/UI/InGame/ScrollView.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e3e95c89aa3afbb1265ac06b89ee31f05f2de1c7
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/ScrollView.cs
@@ -0,0 +1,5 @@
+using UnityEngine;
+
+public abstract class ScrollView : MonoBehaviour
+{
+}
diff --git a/Assets/Scripts/UI/InGame/ScrollView.cs.meta b/Assets/Scripts/UI/InGame/ScrollView.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..29da970a91abe10cbf192b20ffd7a0cbc15be4bf
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/ScrollView.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 01651b93334ae6f46a3b05f7cfe34543
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/UI/InGame/SwitchScrollUI.cs b/Assets/Scripts/UI/InGame/SwitchScrollUI.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2d6c39b947c32a078e722f40158686a7b5697c62
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/SwitchScrollUI.cs
@@ -0,0 +1,38 @@
+using UnityEngine;
+using UnityEngine.Events;
+
+public class SwitchScrollUI : MonoBehaviour
+{
+    /// <summary>
+    /// Reference to the data of the active scroll displayed in the scroll UI
+    /// </summary>
+    public static ActiveScroll activeScrollData;
+    public static UnityEvent OnActiveScrollDataInitialized = new();
+
+    public GameObject ScrollShowUI;
+    public GameObject ScrollWebView;
+
+    private void Awake()
+    {
+        activeScrollData = GetComponent<ActiveScroll>();
+        OnActiveScrollDataInitialized.Invoke();
+    }
+    void OnEnable()
+    {
+        switch (UIconfig.scrollViewVersion)
+        {
+            case UIconfig.ScrollViewVersion.ScrollShow:
+                ScrollWebView.SetActive(false);
+                ScrollShowUI.SetActive(true);
+                break;
+            case UIconfig.ScrollViewVersion.ScrollWebView:
+                ScrollShowUI.SetActive(false);
+                ScrollWebView.SetActive(true);
+                break;
+            default:
+                ScrollShowUI.SetActive(false);
+                ScrollWebView.SetActive(true);
+                break;
+        }
+    }
+}
diff --git a/Assets/Scripts/UI/InGame/SwitchScrollUI.cs.meta b/Assets/Scripts/UI/InGame/SwitchScrollUI.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4adc3448cdf51e8610c52c7b028dc37f257f3f7c
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/SwitchScrollUI.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 90c1c0815499367409841816e1485ec7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/UI/InGame/WebViewController.cs b/Assets/Scripts/UI/InGame/WebViewController.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f6b3c582fc82d249e0b533dc74a78104e5614c46
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/WebViewController.cs
@@ -0,0 +1,265 @@
+using bessw.Unity.WebView;
+using bessw.Unity.WebView.ChromeDevTools;
+using bessw.Unity.WebView.ChromeDevTools.Protocol.DOM;
+using MoreLinq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using REST_JSON_API;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnityEngine;
+
+public class WebViewController : ScrollView
+{
+    private WebViewComponent webViewComponent;
+    private DomNodeWrapper[] dropzones;
+
+    private void Awake()
+    {
+        Debug.LogWarning("awake Webview Controller");
+        webViewComponent = GetComponent<WebViewComponent>();
+        WebViewComponent.serializerSettings.Converters.Add(new FactObjectUIConverter());
+        webViewComponent.targetUrl = Path.Join(CommunicationEvents.Get_DataPath(), "scrollView.html");
+        webViewComponent.OnWebViewComponentReady += OnWebViewComponentReady;
+
+        // TODO: handle webViewComponent.onDomDocumentUpdated
+    }
+    private void OnWebViewComponentReady()
+    {
+        webViewComponent.onDomDocumentUpdated += DocumentUpdatedHandler;
+        SwitchScrollUI.activeScrollData.OnScrollChanged.AddListener(SetScrollContent);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.AddListener(SetScrollContent);
+        SwitchScrollUI.activeScrollData.HintAvailableEvent.AddListener(OnHintAvailable);
+        webViewComponent.tab.AddJSBinding("applyScroll", ApplyScrollHandler);
+        webViewComponent.tab.AddJSBinding("getHint", GetHintHandler);
+        if (SwitchScrollUI.activeScrollData.Scroll is not null)
+        {
+            SetScrollContent(SwitchScrollUI.activeScrollData.Scroll);
+        }
+        RegisterBrowserEventhandlers();
+    }
+    private void OnDisable()
+    {
+        webViewComponent.onDomDocumentUpdated -= DocumentUpdatedHandler;
+        SwitchScrollUI.activeScrollData.OnScrollChanged.RemoveListener(SetScrollContent);
+        SwitchScrollUI.activeScrollData.OnScrollDynamicInfoUpdated.RemoveListener(SetScrollContent);
+        webViewComponent.tab.RemoveJSBinding("applyScroll");
+        webViewComponent.tab.RemoveJSBinding("getHint");
+        DeRegisterBrowserEventhandlers();
+    }
+
+    private void DocumentUpdatedHandler(documentUpdatedEvent _)
+    {
+        // all old DomNodeWrapper objects are invalid, because the whole document has been updated
+        dropzones = null;
+        RegisterBrowserEventhandlers();
+    }
+
+    private async void RegisterBrowserEventhandlers()
+    {
+        // register js event handlers in the browser
+        _ = webViewComponent.tab.Evaluate("addDropZoneEventListeners()");
+        
+        // register c# event handlers
+        // get the dropzones from the scroll and add the event handler to track when something is dropped
+        DomNodeWrapper document = await webViewComponent.tab.Document;
+        dropzones = await document.querySelectorAllAsync("[dropzone='copy']");
+        dropzones.ForEach(dropzone => dropzone.OnAttributeModified += DropzoneAttributeModifiedHandler);
+    }
+
+    private void DeRegisterBrowserEventhandlers()
+    {
+        dropzones?.ForEach(dropzone => dropzone.OnAttributeModified -= DropzoneAttributeModifiedHandler);
+    }
+
+    /// <summary>
+    /// sets or updates the content of the scroll container dom element
+    /// </summary>
+    /// <param name="scrollHTML"></param>
+    private async void SetScrollContent(Scroll scroll)
+    {
+        // update scroll container content
+        DomNodeWrapper document = await webViewComponent.tab.Document;
+        DomNodeWrapper scrollContainer = await document.querySelectorAsync("#scrollContainer");
+
+        var description = scroll.description;
+        if (Regex.IsMatch(description, ".*<scroll-description.*"))
+        {
+            // replace slot templates
+            description = Regex.Replace(description, "<scroll-slot([^>]*)>([^<]*)</scroll-slot>", match =>
+            {
+                var extraAttributes = match.Groups[1].Value;
+                var slotId = match.Groups[2].Value;
+                var assignment = SwitchScrollUI.activeScrollData.Assignments[slotId];
+                /** label of the assigned fact, or the slot label if nothing assigned */
+                var label = assignment.IsSet ? assignment.fact.GetLabel(StageStatic.stage.factState) : scroll.requiredFacts.Find(fact => fact.@ref.uri == slotId).label;
+                return $"<mi dropzone='copy' data-slot-id='{slotId}' {(assignment.IsSet ? $"data-fact-id='{assignment.fact.Id}'" : "")} {extraAttributes}>{label}</mi>";
+            });
+            // replace solution templates
+            description = Regex.Replace(description, "<scroll-solution([^>]*)>([^<]*)</scroll-solution>", match =>
+            {
+                var extraAttributes = match.Groups[1].Value;
+                var solutionId = match.Groups[2].Value;
+                return $"<mi data-solution-id='{solutionId}' {extraAttributes}>{scroll.acquiredFacts.Find(fact => fact.@ref.uri == solutionId).label}</mi>";
+            });
+        }
+        else
+        {
+            // scroll is a lagacy plain text scroll, generate html with slots for the required facts
+            var factSlots = SwitchScrollUI.activeScrollData.Assignments
+                .Where(pair => pair.Value.IsVisible)
+                .Select(pair =>
+                    @$"<span class='lagacySlot' dropzone='copy' data-slot-id='{pair.Key}' {(pair.Value.IsSet ? $"data-fact-id='{pair.Value.fact.Id}'" : "")}>
+                        {(pair.Value.IsSet ?
+                            pair.Value.fact.GetLabel(StageStatic.stage.factState) :
+                            scroll.requiredFacts.Find(fact => fact.@ref.uri == pair.Key).label )}
+                    </span>");
+
+            description = $"<scroll-description><p>{scroll.description}</p><div id='lagacySlots'>{String.Join("", factSlots)}</div></scroll-description>";
+        }
+        // display the scroll description
+        dropzones = null;
+        await scrollContainer.setOuterHtmlAsync($"<div id='scrollContainer'>{description}</div>");
+
+        RegisterBrowserEventhandlers();
+    }
+
+    private async void DropzoneAttributeModifiedHandler(attributeModifiedEvent attributeModifiedEvent)
+    {
+        Debug.LogWarning($"dropzoneAtrributeModifiedHandler: '{attributeModifiedEvent.name}'");
+
+        // call the onFactAssignment event if the data-fact-id attribute was modified
+        if (attributeModifiedEvent.name == "data-fact-id")
+        {
+            // get the slot id
+            var node = await webViewComponent.tab.GetNode(attributeModifiedEvent.nodeId);
+            if (! (node.Node.attributes.TryGetValue("data-slot-id", out string slot)
+                || (await node.getAttributesAsync()).TryGetValue("data-slot-id", out slot)))
+            {
+                Debug.LogError($"dropzoneAtrributeModifiedHandler: data-slot-id attribute not found on dropzone");
+                throw new Exception("data-slot-id attribute not found on dropzone");
+            }
+
+            // if fact has been unasigned from the scroll
+            if (attributeModifiedEvent.value == "null")
+            {
+                // remove fact from slot
+                SwitchScrollUI.activeScrollData.AssignFact(slot, null);
+                return;
+            }
+
+            // get the fact from the fact id
+            if (!FactRecorder.AllFacts.TryGetValue(attributeModifiedEvent.value, out Fact fact))
+            {
+                Debug.LogError($"dropzoneAtrributeModifiedHandler: fact with id '{attributeModifiedEvent.value}' not found");
+                throw new Exception($"fact with id '{attributeModifiedEvent.value}' not found");
+            }
+
+            // assign fact to slot
+            SwitchScrollUI.activeScrollData.AssignFact(slot, fact);
+        }
+    }
+
+    private async void OnHintAvailable(IReadOnlyList<string> url)
+    {
+        dropzones = await (await webViewComponent.tab.Document).querySelectorAllAsync("[dropzone='copy']");
+        foreach (var dropzone in dropzones)
+        {
+            if (url.Contains(dropzone.Node.attributes["data-slot-id"]))
+            {
+                _ = dropzone.setAttributeValue("data-hint-available", "true");
+            }
+            else if (dropzone.Node.attributes.ContainsKey("data-hint-available"))
+            {
+                _ = dropzone.removeAttributeAsync("data-hint-available");
+            }
+        }
+    }
+
+    private void ApplyScrollHandler(string button)
+    {
+        SwitchScrollUI.activeScrollData.ButtonClicked(new MagicScrollButton());
+    }
+
+    private void GetHintHandler(string url)
+    {
+        SwitchScrollUI.activeScrollData.ButtonClicked(new HintScrollButton(url));
+    }
+
+    public string[] GetFactAssignments()
+    {
+        return dropzones.Select(dropzone => dropzone.Node.attributes.GetValueOrDefault("data-fact-id", null)).ToArray();
+    }
+
+    [Obsolete()]
+    private async void GetDropzoneStateAsync()
+    {
+        // alternative way to get the dropzone state
+        DomNodeWrapper document = await DomNodeWrapper.getDocumentAsync(webViewComponent.tab);
+        Debug.LogWarning($"dropzone 1: '{document}'");
+        var dropzones = await document.querySelectorAllAsync("[dropzone='copy']");
+
+        Debug.LogWarning($"dropzone 2: '{dropzones.Index().Aggregate("", (currentString, dropzone) => $"{currentString}, {dropzone.Key}: {dropzone.Value.Node.attributes.GetValueOrDefault("data-fact-id")}")}'");
+
+        string[] factIDs = new string[dropzones.Length];
+
+        // get attributes for each dropzone
+        //for (int i = 0; i < dropzones.Length; i++)
+        //{
+        //    factIDs[i] = ( await dropzones[i].getAttributesAsync() ).GetValueOrDefault("data-fact-id", null);
+        //}
+        //Debug.LogWarning($"dropzone 3: '{string.Join(", ", factIDs)}'");
+    }
+
+
+    /// <summary>
+    /// First try to get the dropzone state using coroutines.
+    /// Doesn't work because coroutines can't access method local temporary variables.
+    /// </summary>
+    /// <returns></returns>
+    [Obsolete("Use getDropzoneStateAsync instead")]
+    private IEnumerator GetDropzoneState()
+    {
+        Debug.LogWarning($"dropzone pre");
+
+        DomNodeWrapper doc = null;
+        yield return DomNodeWrapper.getDocument(webViewComponent.tab, (document) => {
+            Debug.LogWarning($"dropzone 1: '{document}'");
+            doc = document;
+
+            StartCoroutine(document.querySelectorAll("[dropzone='copy']", (dropzones) => {
+                foreach (var dropzone in dropzones)
+                {
+                    Debug.LogWarning($"dropzone 2: Node is Null?: '{dropzone.Node == null}'");
+                    StartCoroutine(dropzone.getAttributes((attributes) =>
+                    {
+                        Debug.LogWarning($"dropzone 3 getAttributes: '{string.Join(", ", attributes.Values)}'");
+                    }));
+                }
+            }));
+        });
+        Debug.LogWarning($"dropzone post: '{doc}'");
+    }
+}
+
+public class FactObjectUIConverter : JsonConverter<FactObjectUI>
+{
+    public override void WriteJson(JsonWriter writer, FactObjectUI value, JsonSerializer serializer)
+    {
+        //serializer.Serialize(writer, value.Fact);
+        JObject o = JObject.FromObject(value.Fact, WebViewComponent.serializer);
+        o.AddFirst(new JProperty("id", value.Fact.Id));
+
+        o.WriteTo(writer);
+    }
+    public override FactObjectUI ReadJson(JsonReader reader, Type objectType, FactObjectUI existingValue, bool hasExistingValue, JsonSerializer serializer)
+    {
+        var factObject = new FactObjectUI();
+        factObject.Fact = serializer.Deserialize<Fact>(reader);
+        return factObject;
+    }
+}
diff --git a/Assets/Scripts/UI/InGame/WebViewController.cs.meta b/Assets/Scripts/UI/InGame/WebViewController.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..89c76d258b39805e0515ab474f6103502c953732
--- /dev/null
+++ b/Assets/Scripts/UI/InGame/WebViewController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 534d25977ad35154781c29672ce2bb13
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/UI/NetwMenu/StartMenu_mobile.cs b/Assets/Scripts/UI/NetwMenu/StartMenu_mobile.cs
index c844693e5a414033e8be2d232be6935e7e9de6fe..264b5dd9454b4ab36f0dfb4ddaf799615e93480b 100644
--- a/Assets/Scripts/UI/NetwMenu/StartMenu_mobile.cs
+++ b/Assets/Scripts/UI/NetwMenu/StartMenu_mobile.cs
@@ -119,7 +119,7 @@ void start2_CheckOS_CheckConfig()
         NetworkJSON_Load();
 
         if (!checkDataPath() && false)
-            Debug.Log("Todo: Pr�fen auf Bugs bzl DataPath");
+            Debug.Log("Todo: Pr�fen auf Bugs bzl DataPath");
 
         if (!checkDataPath() || true)
         {
@@ -210,11 +210,19 @@ void changeSettingsToOS()
         {
             case OperationSystem.Windows:
                 UIconfig.controlMode = ControlMode.Keyboard;
-                if (autoSettingsAdaption == 2) { UIconfig.FrameITUIversion = 1; }
+                if (autoSettingsAdaption == 2)
+                {
+                    UIconfig.FrameITUIversion = 1;
+                    UIconfig.scrollViewVersion = ScrollViewVersion.ScrollWebView;
+                }
                 break;
             case OperationSystem.Android:
                 UIconfig.controlMode = ControlMode.Mobile;
-                if (autoSettingsAdaption == 2) { UIconfig.FrameITUIversion = 2; }
+                if (autoSettingsAdaption == 2)
+                {
+                    UIconfig.FrameITUIversion = 2;
+                    UIconfig.scrollViewVersion = ScrollViewVersion.ScrollShow;
+                }
                 break;
             default:
                 break;
diff --git a/Assets/Scripts/UI/UIconfig.cs b/Assets/Scripts/UI/UIconfig.cs
index 4250cb104355a82048fce2cb3426eb7b6b717595..a9fe37caa108cfe4f9566939e55be91bdab26ceb 100644
--- a/Assets/Scripts/UI/UIconfig.cs
+++ b/Assets/Scripts/UI/UIconfig.cs
@@ -6,6 +6,12 @@ public static class UIconfig
 {
 
     public static int FrameITUIversion = 2; // 1= FrameITUI; 2= FrameITUI_mobil
+    public static ScrollViewVersion scrollViewVersion = ScrollViewVersion.ScrollShow;
+    public enum ScrollViewVersion
+    {
+        ScrollShow = 1,
+        ScrollWebView = 2,
+    }
     public static int InputManagerVersion = 1; // 1= InputManager, 2=InputSystemPackage ; 3=overConfigfile
     public static int EventSystemModule = 2;// 1= InputManager, 2=InputSystemPackage ; 3=overConfigfile
     public static int GameplayMode = 5; //5=First Person, 4=third, 3=third+, 2=Escaperoom, 1=Sidescroller, 0=Sidescroller+
@@ -125,6 +131,7 @@ public class NetworkJSON
         public float camRotatingSensitivity; //Sensitivity to inputs.
         public bool MouseKeepingInWindow;
         public int scrolldirection;
+        public ScrollViewVersion scrollViewVersion;
 
     }
     public class NetworkJSONonlyString
@@ -150,6 +157,7 @@ public class NetworkJSONonlyString
         public string camRotatingSensitivity; //Sensitivity to inputs.
         public string MouseKeepingInWindow;
         public string scrolldirection;
+        public string scrollViewVersion;
 
     }
     //------------------------------------------------------------------------------------------------------
diff --git a/Assets/Standard Assets/Utility/AutoMobileShaderSwitch.cs b/Assets/Standard Assets/Utility/AutoMobileShaderSwitch.cs
index db3c18145b6b6e0263df206e343b33e2f793cb5d..e430a2d455510cee4ef301713157d624957ac468 100644
--- a/Assets/Standard Assets/Utility/AutoMobileShaderSwitch.cs	
+++ b/Assets/Standard Assets/Utility/AutoMobileShaderSwitch.cs	
@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using UnityEngine;
 #if UNITY_EDITOR
 using UnityEditor;
diff --git a/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html
new file mode 100644
index 0000000000000000000000000000000000000000..28a9b5d18221a4fcba5a049a209417c0d31b41b1
--- /dev/null
+++ b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html
@@ -0,0 +1,315 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <title>Scroll View</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <style>
+    [dropzone] {
+      background-color: grey;
+      margin: 1px;
+      border-width: 1px;
+      border-style: dashed;
+    }
+    [data-fact-id] {
+        background-color: lightgray;
+    }
+    [data-hint-available] {
+      background-color: yellow
+    }
+    math {
+      display: inline-flex;
+      align-items: center;
+      flex-wrap: wrap;
+    }
+    .lagacySlot {
+      width: 100px;
+      height: 100px;
+      margin: 50px;
+      border-width: 1px;
+    }
+    #rectangle {
+      width: 10px;
+      height: 10px;
+      position: absolute;
+      background-color: red;
+    }
+  </style>
+</head>
+<body>
+  <div id="scrollContainer">
+    <div>No scroll selected</div>
+    <math>
+      <mi>E</mi>
+      <mo>=</mo>
+      <mfenced>
+      <mtable>
+        <mtr>
+          <mtd>
+            <mi dropzone="copy" data-allowed-types="PointFact,AngleFact" data-slot-id="http://mathhub.info/FrameIT/frameworld?Example?A">a</mi>
+          </mtd>
+          <mtd>
+              <mi dropzone="copy" data-slot-id="http://mathhub.info/FrameIT/frameworld?Example?B">b</mi>
+          </mtd>
+          <mtd>
+            <mn>0</mn>
+          </mtd>
+        </mtr>
+        <mtr>
+          <mtd>
+            <mn>0</mn>
+          </mtd>
+          <mtd>
+            <mn>1</mn>
+          </mtd>
+          <mtd>
+            <mn>0</mn>
+          </mtd>
+        </mtr>
+        <mtr>
+          <mtd>
+            <mn>0</mn>
+          </mtd>
+          <mtd>
+            <mn>0</mn>
+          </mtd>
+          <mtd>
+            <mn>1</mn>
+          </mtd>
+        </mtr>
+      </mtable>
+    </math>
+  </div>
+  <button type="button" title="Apply the scroll" onclick="applyScroll('')">Magic</button><br>
+  <p>You can right click on yellow slots to get a hint.</p>
+</body>
+  <script>
+    /**
+   * @typedef Point
+   * @type {object}
+   * @property {number} x
+   * @property {number} y
+   * @property {number} z
+   */
+  
+  /**
+   * @property {Point} point
+   * @property {Point} normal
+   * @property {string} s_type
+   * @property {string} label
+   * @property {string|null} _CustomLabel
+   * @property {boolean} hasCustomLabel
+   * @property {number} labelId
+   */
+  
+  class Fact {
+      /** @property {string} id Fact id */
+      id = ""
+      /** @property {string} s_type Fact type */
+      s_type = ""
+      /** @property {string} label used in unity */
+      label = ""
+      /** @property {string | null} _CustomLabel Custom label */
+      _CustomLabel = null
+      /** @property {boolean} hasCustomLabel */
+      hasCustomLabel = false
+      /** @property {number} labelId */
+      labelId = 0
+  }
+  
+  class Point {
+      /** @property {number} x */
+      x = 0
+      /** @property {number} y */
+      y = 0
+      /** @property {number} z */
+      z = 0
+  }
+  /**
+  s_type: PointFact,
+  label: A,
+  _CustomLabel: null,
+  hasCustomLabel: false,
+  labelId: 0,
+  point: {x: -1.66086578, y: -0.00494432449, z: -2.17682648},
+  normal: {x: 0, y: 1, z: 0}
+   */
+  class PointFact extends Fact {
+      s_type = "PointFact";
+      /** @property {Point} point */
+      point = new Point()
+      /** @property {Point} normal */
+      normal = new Point()
+  }
+  
+  /**
+   * pid1: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact252,
+  pid2: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact254,
+  pid3: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact256,
+  s_type: AngleFact,
+  label: ∠BDF,
+  _CustomLabel: null,
+  is_right_angle: false,
+  hasCustomLabel: false,
+  labelId: 0
+   */
+  class AngleFact extends Fact {
+      s_type = "AngleFact";
+      pid1 = "";
+      pid2 = "";
+      pid3 = "";
+      is_right_angle = false;
+  }
+  
+  /**
+   * s_type: LineFact,
+  pid1: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact255,
+  pid2: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact256,
+  dir: [object Object],
+  label: [EF],
+  _CustomLabel: null,
+  hasCustomLabel: false,
+  labelId: 0
+   */
+  class LineFact extends Fact {
+      s_type = "LineFact";
+      pid1 = "";
+      pid2 = "";
+      dir = new Point();
+  }
+  
+  /**
+   * s_type: RayFact,
+  pid1: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact256,
+  pid2: http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact252,
+  dir: [object Object],
+  label: FB,
+  _CustomLabel: null,
+  hasCustomLabel: false,
+  labelId: 0
+   */
+  class RayFact extends Fact {
+      s_type = "RayFact";
+      pid1 = "";
+      pid2 = "";
+      dir = new Point();
+  }
+  
+  
+  
+  /** @param {DragEvent} event  */
+  function dropHandler(event) {
+    event.preventDefault()
+    console.log("dropHandler", event)
+
+    const data = event.dataTransfer.getData("application/json")
+    //const data = event.dataTransfer.getData("text/plain")
+    console.log(`Droped data: '${event.dataTransfer.types}'`, event.dataTransfer.types)
+
+    /** @type {Fact} */
+    const fact = JSON.parse(data)
+    console.warn(`Droped Fact '${fact.label}':`, fact)
+
+    /** @type {HTMLElement} */
+    const element = event.target
+
+    if (element.hasAttribute("data-allowed-types")) {
+      //const allowedTypesStr = element.dataset["allowed-types"]
+      const allowedTypesStr = element.getAttribute("data-allowed-types")
+      const allowedTypes = allowedTypesStr.split(",")
+
+      if (!allowedTypes.includes(fact.s_type)) {
+        console.warn(`The drop target data-type '${fact.s_type}' is not a member of the allowed types '${allowedTypesStr}'`)
+        return
+      }
+    }
+
+    element.innerHTML = fact.label
+    element.fact = fact
+    element.dataset.factId = fact.id
+  }
+
+  /** @param {MouseEvent} event  */
+  function clickHandler(event) {
+    event.preventDefault()
+    console.log("clickHandler")
+    console.log("clickHandler", event)
+    switch (event.button) {
+      case 0:
+        console.log("Left button clicked")
+        event.target.fact = null
+        event.target.dataset.factId = null
+        break
+      case 1:
+        console.log("Middle button clicked")
+        break
+      case 2:
+        console.log("Right button clicked")
+        getHint(event.target.dataset.slotId)
+        break
+      default:
+        console.log("Unknown button clicked")
+    }
+  }
+
+  /*
+  * register the drag event listeners
+  */
+  //document.addEventListener("dragstart", ev => printHandler(ev, "DragStartEvent"))
+  //document.addEventListener("dragenter", ev => printHandler(ev, "DragEnterEvent"))
+  //document.addEventListener("drag", ev => printHandler(ev, "DragEvent"))
+  document.addEventListener("dragover", ev => ev.preventDefault())
+  //document.addEventListener("dragover", ev => printPreventDefaultHandler(ev, "DragOverEvent"))
+  //document.addEventListener("dragleave", ev => printHandler(ev, "DragLeaveEvent"))
+  //document.addEventListener("dragend", ev => printHandler(ev, "DragEndEvent"))
+  
+  // select all dropzones and add drop event listeners
+  //let dropZones = document.querySelectorAll('[dropzone="copy"]')
+  //dropZones.forEach( dropZone => dropZone.addEventListener("drop", ev => dropHandler(ev, "DropEvent")) )
+  
+  /** select all dropzones and add drop event listeners */
+  function addDropZoneEventListeners() {
+    console.log("addDropZoneEventListeners")
+    document.querySelectorAll('[dropzone="copy"]')
+      .forEach( dropZone => {
+        dropZone.removeEventListener("drop", dropHandler)
+        dropZone.removeEventListener("click", clickHandler)
+        dropZone.removeEventListener("contextmenu", clickHandler)
+        //dropZone.addEventListener("dragover", ev => ev.preventDefault())
+        dropZone.addEventListener("drop", dropHandler)
+        dropZone.addEventListener("click", clickHandler)
+        dropZone.addEventListener("contextmenu", clickHandler)
+      })
+  }
+  //addDropZoneEventListeners()
+  </script>
+
+
+
+
+  <!--<script src="visualiseCursor.mjs" defer type="module"></script>-->
+  <!--<script>
+  /* mouse and pointer event handling */
+  const rectangle = document.createElement("div");
+  rectangle.id = "rectangle";
+  
+  console.log("appendChild to", document.body)
+  document.body.appendChild(rectangle);
+  
+  function followCursor(event) {
+  const cursorX = event.clientX;
+  const cursorY = event.clientY +5;
+  //console.log(`MouseEvent X: ${cursorX}, Y: ${cursorY}, Event type: ${event.type}`, event);
+  rectangle.style.left = `${cursorX}px`;
+  rectangle.style.top = `${cursorY}px`;
+  }
+  
+  document.addEventListener("mousemove", followCursor);
+  document.addEventListener("pointermove", followCursor);
+  
+  document.addEventListener("mouseover", followCursor);
+  document.addEventListener("pointerover", followCursor);
+  
+  document.addEventListener("mouseout", followCursor);
+  document.addEventListener("pointerout", followCursor);
+  </script>-->
+</html>
diff --git a/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html.meta b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html.meta
new file mode 100644
index 0000000000000000000000000000000000000000..02831017e860ae19925171910bdd1ab72f79f6a7
--- /dev/null
+++ b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 1d3e2132e24f19e41970733210b72ac6
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/StreamingAssets/StreamToPersistentDataPath/Config/Network.JSON b/Assets/StreamingAssets/StreamToPersistentDataPath/Config/Network.JSON
index eb50d80634abf076304e51be7fcbd575c0dea88c..14c02347d1ddf2ecac930d239269c7f639c98a4c 100644
--- a/Assets/StreamingAssets/StreamToPersistentDataPath/Config/Network.JSON
+++ b/Assets/StreamingAssets/StreamToPersistentDataPath/Config/Network.JSON
@@ -19,5 +19,6 @@
     "cursorSize": 0.03125,
     "camRotatingSensitivity": 2.0,
     "MouseKeepingInWindow": true,
-    "scrolldirection": -1
+    "scrolldirection": -1,
+    "scrollViewVersion": 2
 }
\ No newline at end of file
diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json
index 46042a32bf9677b1bb0405eadd44bf65e83997ad..339a6304c6f32a083fd1b546b57dde5d7467bd13 100644
--- a/Packages/packages-lock.json
+++ b/Packages/packages-lock.json
@@ -1,5 +1,11 @@
 {
   "dependencies": {
+    "bessw.unity.webview": {
+      "version": "file:bessw.unity.webview",
+      "depth": 0,
+      "source": "embedded",
+      "dependencies": {}
+    },
     "com.github-glitchenzo.nugetforunity": {
       "version": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity",
       "depth": 0,