diff --git a/android/app/build.gradle b/android/app/build.gradle
index 27a93e6dc9643f4306c4f44656ebb131510b20cb..2328355d7aa1d0dd7f1436cd5c0ed32b0159d6d1 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) {
 }
 
 android {
-    compileSdkVersion 33
+    compileSdkVersion 34
     namespace "org.benoitharrault.twister"
 
     defaultConfig {
diff --git a/android/gradle.properties b/android/gradle.properties
index 3487476dc637023e34426ce50caf3343f91e3038..1913fd1742a8e1553517f28698109845b429435c 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.23
-app.versionCode=23
+app.versionName=0.1.0
+app.versionCode=24
diff --git a/assets/images/blank.png b/assets/images/blank.png
deleted file mode 100644
index 71f46e2afe84d6655d32e85d164c463cdfc5dabc..0000000000000000000000000000000000000000
Binary files a/assets/images/blank.png and /dev/null differ
diff --git a/assets/images/left-foot.png b/assets/images/left-foot.png
deleted file mode 100644
index fd46c1a8ac871945c801a6a353ebb0b70639df7f..0000000000000000000000000000000000000000
Binary files a/assets/images/left-foot.png and /dev/null differ
diff --git a/assets/images/left-hand.png b/assets/images/left-hand.png
deleted file mode 100644
index 34d676bb3985fe6479e20d003c1be4370d20857d..0000000000000000000000000000000000000000
Binary files a/assets/images/left-hand.png and /dev/null differ
diff --git a/assets/images/right-foot.png b/assets/images/right-foot.png
deleted file mode 100644
index d0eff85495b7b8910389f948a712b02cde1909ef..0000000000000000000000000000000000000000
Binary files a/assets/images/right-foot.png and /dev/null differ
diff --git a/assets/images/right-hand.png b/assets/images/right-hand.png
deleted file mode 100644
index e730e5f4c58c8bdf3444ae7ff7ad45fe3e32e3e3..0000000000000000000000000000000000000000
Binary files a/assets/images/right-hand.png and /dev/null differ
diff --git a/assets/translations/en.json b/assets/translations/en.json
index 867a99439eb21af93fbddae51621e131d25c8aa5..7ff9b8cd6e5c2c5f802b8e884b6bfe6d27d5206f 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -6,13 +6,14 @@
   "left_foot": "left foot",
   "right_foot": "right foot",
 
-  "bottom_nav_game": "Game",
-  "bottom_nav_settings": "Settings",
-
   "settings_title": "Settings",
   "settings_label_theme": "Theme mode",
-  "settings_title_game": "Game settings",
-  "settings_label_game_timer_value": "Timer value: ",
 
-  "lang_prefix": "en"
+  "lang_prefix": "en",
+
+  "about_title": "Informations",
+  "about_content": "Twister",
+  "about_version": "Version: {version}",
+
+  "": ""
 }
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index c481788e703609e5007305c9051e498b7b15fef1..e2aafac3159564d30808cc8fd8e54a3040fd0504 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -6,13 +6,14 @@
   "left_foot": "pied gauche",
   "right_foot": "pied droit",
 
-  "bottom_nav_game": "Jeu",
-  "bottom_nav_settings": "Réglages",
-
   "settings_title": "Réglages",
   "settings_label_theme": "Thème de couleurs",
-  "settings_title_game": "Paramètres du jeu",
-  "settings_label_game_timer_value": "Durée du chrono : ",
 
-  "lang_prefix": "fr"
+  "lang_prefix": "fr",
+
+  "about_title": "Informations",
+  "about_content": "Twister.",
+  "about_version": "Version : {version}",
+
+  "": ""
 }
diff --git a/assets/ui/button_back.png b/assets/ui/button_back.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc48ffb1dbb653d9a996f139dfbe02969724bfa5
Binary files /dev/null and b/assets/ui/button_back.png differ
diff --git a/assets/ui/button_delete_saved_game.png b/assets/ui/button_delete_saved_game.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e4f217689b11e444b7163557d7e5d68f3bbfe7d
Binary files /dev/null and b/assets/ui/button_delete_saved_game.png differ
diff --git a/assets/ui/button_resume_game.png b/assets/ui/button_resume_game.png
new file mode 100644
index 0000000000000000000000000000000000000000..b2ea0a02d05e42377eb551a4b51428b511a32f5d
Binary files /dev/null and b/assets/ui/button_resume_game.png differ
diff --git a/assets/ui/button_start.png b/assets/ui/button_start.png
new file mode 100644
index 0000000000000000000000000000000000000000..6845e2f5c21598ab61f1684d2075aeec0334bf23
Binary files /dev/null and b/assets/ui/button_start.png differ
diff --git a/assets/ui/game_end.png b/assets/ui/game_end.png
new file mode 100644
index 0000000000000000000000000000000000000000..876334279c1711b349a62131a33607eecf924eb6
Binary files /dev/null and b/assets/ui/game_end.png differ
diff --git a/assets/ui/move-blank.png b/assets/ui/move-blank.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a504ec4e9ab530b04d5324bcfb4cf187a3c1b86
Binary files /dev/null and b/assets/ui/move-blank.png differ
diff --git a/assets/ui/move-left-foot.png b/assets/ui/move-left-foot.png
new file mode 100644
index 0000000000000000000000000000000000000000..1d4a90c1b42d4da8a617c0b10cad5c5dc9329b32
Binary files /dev/null and b/assets/ui/move-left-foot.png differ
diff --git a/assets/ui/move-left-hand.png b/assets/ui/move-left-hand.png
new file mode 100644
index 0000000000000000000000000000000000000000..d8e288cc61afef47b8f6342fdf109098d321dde8
Binary files /dev/null and b/assets/ui/move-left-hand.png differ
diff --git a/assets/ui/move-right-foot.png b/assets/ui/move-right-foot.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9fa2905b064ca539d6785851d89ae3e55428d4d
Binary files /dev/null and b/assets/ui/move-right-foot.png differ
diff --git a/assets/ui/move-right-hand.png b/assets/ui/move-right-hand.png
new file mode 100644
index 0000000000000000000000000000000000000000..297d74dff520bba8e4ab33b893a1fe4f49fcf197
Binary files /dev/null and b/assets/ui/move-right-hand.png differ
diff --git a/assets/ui/placeholder.png b/assets/ui/placeholder.png
new file mode 100644
index 0000000000000000000000000000000000000000..814df31be6ddc4275ebe4490c79365578dbef1f0
Binary files /dev/null and b/assets/ui/placeholder.png differ
diff --git a/fastlane/metadata/android/en-US/changelogs/24.txt b/fastlane/metadata/android/en-US/changelogs/24.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d4afd512e55b3fd8ffbfd795adb9b00832e5aaef
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/24.txt
@@ -0,0 +1 @@
+Improve/normalize game architecture.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/24.txt b/fastlane/metadata/android/fr-FR/changelogs/24.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6a9871a5eb8eb3c6e9106520f1cbf1f39f9e5ef7
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/24.txt
@@ -0,0 +1 @@
+Amélioration/normalisation de l'architecture du jeu.
diff --git a/images/build_game_images.sh b/images/build_game_images.sh
deleted file mode 100755
index 4d7f1be1f63651c82db63cbd8217cde60fa2bd3f..0000000000000000000000000000000000000000
--- a/images/build_game_images.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#! /bin/bash
-
-# Check dependencies
-command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; }
-command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; }
-command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
-
-CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
-ASSETS_DIR="${BASE_DIR}/assets"
-
-OPTIPNG_OPTIONS="-preserve -quiet -o7"
-ICON_SIZE=512
-
-#######################################################
-
-# Game images
-AVAILABLE_GAME_IMAGES="
-  blank
-  left-hand
-  right-hand
-  left-foot
-  right-foot
-"
-#######################################################
-
-# optimize svg
-function optimize_svg() {
-  SOURCE="$1"
-
-  cp ${SOURCE} ${SOURCE}.tmp
-  scour \
-      --remove-descriptive-elements \
-      --enable-id-stripping \
-      --enable-viewboxing \
-      --enable-comment-stripping \
-      --nindent=4 \
-      --quiet \
-      -i ${SOURCE}.tmp \
-      -o ${SOURCE}
-  rm ${SOURCE}.tmp
-}
-
-# build icons
-function build_icon() {
-  SOURCE="$1"
-  TARGET="$2"
-
-  echo "Building ${TARGET}"
-
-  if [ ! -f "${SOURCE}" ]; then
-    echo "Missing file: ${SOURCE}"
-    exit 1
-  fi
-
-  optimize_svg "${SOURCE}"
-
-  inkscape \
-      --export-width=${ICON_SIZE} \
-      --export-height=${ICON_SIZE} \
-      --export-filename=${TARGET} \
-      ${SOURCE}
-
-  optipng ${OPTIPNG_OPTIONS} ${TARGET}
-}
-
-#######################################################
-
-# Create output folder
-mkdir -p ${ASSETS_DIR}/images
-
-# Delete existing generated images
-find ${ASSETS_DIR}/images -type f -name "*.png" -delete
-
-# build game images
-for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES}
-do
-  build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/images/${GAME_IMAGE}.png
-done
-
diff --git a/lib/config/color_theme.dart b/lib/config/color_theme.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6b0ae12adc50d6f20ed07bc6e17a6fa4288ae284
--- /dev/null
+++ b/lib/config/color_theme.dart
@@ -0,0 +1,10 @@
+import 'package:flutter/material.dart';
+
+class TwisterColors {
+  static const Color grey = Color.fromARGB(255, 0xF0, 0xE2, 0xE7); // F0E2E7
+
+  static const Color blue = Color.fromARGB(255, 0x3F, 0x84, 0xE5); // 3F84E5
+  static const Color green = Color.fromARGB(255, 0x1D, 0xA2, 0x3C); // 1DA23C
+  static const Color red = Color.fromARGB(255, 0xE0, 0x0D, 0x3B); // E00D3B
+  static const Color yellow = Color.fromARGB(255, 0xE8, 0xD9, 0x17); // E8D917
+}
diff --git a/lib/config/colors.dart b/lib/config/colors.dart
deleted file mode 100644
index 988ca612285d28a0b3c4d68538708f8afc8f21ad..0000000000000000000000000000000000000000
--- a/lib/config/colors.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-import 'package:flutter/material.dart';
-
-class TwisterColors {
-  static const Color grey = Color.fromARGB(255, 0xF0, 0xE2, 0xE7); // F0E2E7
-
-  static const Color blue = Color.fromARGB(255, 0x3F, 0x84, 0xE5); // 3F84E5
-  static const Color green = Color.fromARGB(255, 0x3F, 0x78, 0x4C); // 3F784C
-  static const Color red = Color.fromARGB(255, 0xB2, 0x0D, 0x30); // B20D30
-  static const Color yellow = Color.fromARGB(255, 0xC1, 0x78, 0x17); // C17817
-}
diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d2d7d496e5f46f548b8efe0e89ce861589604c49
--- /dev/null
+++ b/lib/config/default_game_settings.dart
@@ -0,0 +1,33 @@
+import 'package:twister/utils/tools.dart';
+
+class DefaultGameSettings {
+  // available global parameters codes
+  static const String parameterCodeTimerValue = 'timer';
+  static const List<String> availableParameters = [
+    parameterCodeTimerValue,
+  ];
+
+  // timer value: available values
+  static const String timerValueMedium = 'medium';
+  static const List<String> allowedTimerValues = [
+    timerValueMedium,
+  ];
+  // timer value: default value
+  static const String defaultTimerValue = timerValueMedium;
+
+  // available values from parameter code
+  static List<String> getAvailableValues(String parameterCode) {
+    switch (parameterCode) {
+      case parameterCodeTimerValue:
+        return DefaultGameSettings.allowedTimerValues;
+    }
+
+    printlog('Did not find any available value for game parameter "$parameterCode".');
+    return [];
+  }
+
+  // parameters displayed with assets (instead of painter)
+  static List<String> displayedWithAssets = [
+    //
+  ];
+}
diff --git a/lib/config/default_global_settings.dart b/lib/config/default_global_settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f090d539c1c414a1043a3f2b3adb4a7d390c9506
--- /dev/null
+++ b/lib/config/default_global_settings.dart
@@ -0,0 +1,33 @@
+import 'package:twister/utils/tools.dart';
+
+class DefaultGlobalSettings {
+  // available global parameters codes
+  static const String parameterCodeSkin = 'skin';
+  static const List<String> availableParameters = [
+    parameterCodeSkin,
+  ];
+
+  // skin: available values
+  static const String skinValueColors = 'colors';
+  static const List<String> allowedSkinValues = [
+    skinValueColors,
+  ];
+  // skin: default value
+  static const String defaultSkinValue = skinValueColors;
+
+  // available values from parameter code
+  static List<String> getAvailableValues(String parameterCode) {
+    switch (parameterCode) {
+      case parameterCodeSkin:
+        return DefaultGlobalSettings.allowedSkinValues;
+    }
+
+    printlog('Did not find any available value for global parameter "$parameterCode".');
+    return [];
+  }
+
+  // parameters displayed with assets (instead of painter)
+  static List<String> displayedWithAssets = [
+    //
+  ];
+}
diff --git a/lib/config/default_settings.dart b/lib/config/default_settings.dart
deleted file mode 100644
index c3271480d414643f6c75e0b3d2cb283596c57b6f..0000000000000000000000000000000000000000
--- a/lib/config/default_settings.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-class DefaultSettings {
-  static const int defaultTimerValue = 20;
-
-  static const List<int> allowedTimerValues = [
-    10,
-    defaultTimerValue,
-    30,
-  ];
-}
diff --git a/lib/config/menu.dart b/lib/config/menu.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1af3bf32520e803d16618438ad5d3965936b723a
--- /dev/null
+++ b/lib/config/menu.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:twister/ui/screens/page_about.dart';
+import 'package:twister/ui/screens/page_game.dart';
+import 'package:twister/ui/screens/page_settings.dart';
+
+class MenuItem {
+  final Icon icon;
+  final Widget page;
+
+  const MenuItem({
+    required this.icon,
+    required this.page,
+  });
+}
+
+class Menu {
+  static const indexGame = 0;
+  static const menuItemGame = MenuItem(
+    icon: Icon(UniconsLine.home),
+    page: PageGame(),
+  );
+
+  static const indexSettings = 1;
+  static const menuItemSettings = MenuItem(
+    icon: Icon(UniconsLine.setting),
+    page: PageSettings(),
+  );
+
+  static const indexAbout = 2;
+  static const menuItemAbout = MenuItem(
+    icon: Icon(UniconsLine.info_circle),
+    page: PageAbout(),
+  );
+
+  static Map<int, MenuItem> items = {
+    indexGame: menuItemGame,
+    indexSettings: menuItemSettings,
+    indexAbout: menuItemAbout,
+  };
+
+  static bool isIndexAllowed(int pageIndex) {
+    return items.keys.contains(pageIndex);
+  }
+
+  static Widget getPageWidget(int pageIndex) {
+    return items[pageIndex]?.page ?? menuItemGame.page;
+  }
+
+  static int itemsCount = Menu.items.length;
+}
diff --git a/lib/config/theme.dart b/lib/config/theme.dart
index be390348c7868e7c63387df13e13c46de43f8a23..74f532fd5abf693979118609564d29167e902009 100644
--- a/lib/config/theme.dart
+++ b/lib/config/theme.dart
@@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: textSwatch.shade200,
-  onBackground: textSwatch.shade500,
   onSurface: textSwatch.shade500,
   surface: textSwatch.shade50,
-  surfaceVariant: Colors.white,
+  surfaceContainerHighest: Colors.white,
   shadow: textSwatch.shade900.withOpacity(.1),
 );
 
@@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: const Color(0xFF171724),
-  onBackground: textSwatch.shade400,
   onSurface: textSwatch.shade300,
   surface: const Color(0xFF262630),
-  surfaceVariant: const Color(0xFF282832),
+  surfaceContainerHighest: const Color(0xFF282832),
   shadow: textSwatch.shade900.withOpacity(.2),
 );
 
@@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith(
     ),
   ),
 );
-
-final ThemeData appTheme = darkTheme;
diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart
deleted file mode 100644
index c633c6515b0fcb4e19a521e45b579f178d605970..0000000000000000000000000000000000000000
--- a/lib/cubit/bottom_nav_cubit.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-import 'package:hydrated_bloc/hydrated_bloc.dart';
-
-class BottomNavCubit extends HydratedCubit<int> {
-  BottomNavCubit() : super(0);
-
-  int pagesCount = 2;
-
-  void updateIndex(int index) {
-    if (isIndexAllowed(index)) {
-      emit(index);
-    } else {
-      goToHomePage();
-    }
-  }
-
-  bool isIndexAllowed(int index) {
-    return (index >= 0) && (index < pagesCount);
-  }
-
-  void goToHomePage() => emit(0);
-
-  @override
-  int? fromJson(Map<String, dynamic> json) {
-    return 0;
-  }
-
-  @override
-  Map<String, dynamic>? toJson(int state) {
-    return <String, int>{'pageIndex': state};
-  }
-}
diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart
index 325c872e69f892da4db10b86182d8b343c10bead..f0eed2c2ac1f4b92c277e702a7dc4eda2f86b9b1 100644
--- a/lib/cubit/game_cubit.dart
+++ b/lib/cubit/game_cubit.dart
@@ -1,53 +1,118 @@
+import 'package:audioplayers/audioplayers.dart';
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:twister/models/move.dart';
+import 'package:twister/models/game/game.dart';
+import 'package:twister/models/game/move.dart';
+import 'package:twister/models/settings/settings_game.dart';
+import 'package:twister/models/settings/settings_global.dart';
 
 part 'game_state.dart';
 
 class GameCubit extends HydratedCubit<GameState> {
-  GameCubit() : super(const GameState());
+  GameCubit()
+      : super(GameState(
+          currentGame: Game.createEmpty(),
+        ));
 
-  Move getMove() {
-    return state.move ?? Move.createNull();
+  void updateState(Game game) {
+    emit(GameState(
+      currentGame: game,
+    ));
   }
 
-  void setValues({
-    Move? move,
+  void refresh() {
+    final Game game = Game(
+      // Settings
+      gameSettings: state.currentGame.gameSettings,
+      globalSettings: state.currentGame.globalSettings,
+      // State
+      isRunning: state.currentGame.isRunning,
+      isStarted: state.currentGame.isStarted,
+      isFinished: state.currentGame.isFinished,
+      animationInProgress: state.currentGame.animationInProgress,
+      // Base data
+      move: state.currentGame.move,
+      // Game data
+      history: state.currentGame.history,
+    );
+    // game.dump();
+
+    updateState(game);
+  }
+
+  void startNewGame({
+    required GameSettings gameSettings,
+    required GlobalSettings globalSettings,
   }) {
-    List<Move> history = state.history ?? [];
-    if (move != null) {
-      history.add(move);
-    }
+    final Game newGame = Game.createNew(
+      // Settings
+      gameSettings: gameSettings,
+      globalSettings: globalSettings,
+    );
 
-    emit(GameState(
-      move: move ?? state.move,
-      history: history,
-    ));
+    newGame.dump();
+
+    updateState(newGame);
+    refresh();
+  }
+
+  void quitGame() {
+    state.currentGame.isRunning = false;
+    refresh();
+  }
+
+  void resumeSavedGame() {
+    state.currentGame.isRunning = true;
+    refresh();
+  }
+
+  void deleteSavedGame() {
+    state.currentGame.isRunning = false;
+    state.currentGame.isFinished = true;
+    refresh();
+  }
+
+  void setMove(Move move) {
+    state.currentGame.isStarted = true;
+    state.currentGame.move = move;
+    state.currentGame.history.add(move);
+    refresh();
   }
 
   void deleteHistory() {
-    List<Move> history = [];
-    emit(GameState(
-      move: state.move,
-      history: history,
-    ));
+    state.currentGame.history = [];
+    state.currentGame.isStarted = false;
+    refresh();
+  }
+
+  void setAnimationIsRunning(bool isRunning) {
+    state.currentGame.animationInProgress = isRunning;
+    refresh();
+  }
+
+  void pickNewMove() {
+    Move newMove = Move.pickRandom();
+    setMove(newMove);
+
+    final player = AudioPlayer();
+    player.play(AssetSource(newMove.toSoundAsset()));
   }
 
   @override
   GameState? fromJson(Map<String, dynamic> json) {
-    Move move = json['move'] as Move;
+    final Game currentGame = json['currentGame'] as Game;
 
     return GameState(
-      move: move,
+      currentGame: currentGame,
     );
   }
 
   @override
   Map<String, dynamic>? toJson(GameState state) {
     return <String, dynamic>{
-      'move': state.move?.toJson(),
+      'currentGame': state.currentGame.toJson(),
     };
   }
 }
diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart
index 204801effc156a3a761d130cfe37bcd50f81aa47..00e211668c3269255926939324355792abd61c41 100644
--- a/lib/cubit/game_state.dart
+++ b/lib/cubit/game_state.dart
@@ -3,21 +3,13 @@ part of 'game_cubit.dart';
 @immutable
 class GameState extends Equatable {
   const GameState({
-    this.move,
-    this.history,
+    required this.currentGame,
   });
 
-  final Move? move;
-  final List<Move>? history;
+  final Game currentGame;
 
   @override
   List<dynamic> get props => <dynamic>[
-        move,
-        history,
+        currentGame,
       ];
-
-  Map<String, dynamic> get values => <String, dynamic>{
-        'move': move,
-        'history': history,
-      };
 }
diff --git a/lib/cubit/nav_cubit.dart b/lib/cubit/nav_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c998a4438fd478e9211b086f8a3390d220a1d94a
--- /dev/null
+++ b/lib/cubit/nav_cubit.dart
@@ -0,0 +1,37 @@
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:twister/config/menu.dart';
+
+class NavCubit extends HydratedCubit<int> {
+  NavCubit() : super(0);
+
+  void updateIndex(int index) {
+    if (Menu.isIndexAllowed(index)) {
+      emit(index);
+    } else {
+      goToGamePage();
+    }
+  }
+
+  void goToGamePage() {
+    emit(Menu.indexGame);
+  }
+
+  void goToSettingsPage() {
+    emit(Menu.indexSettings);
+  }
+
+  void goToAboutPage() {
+    emit(Menu.indexAbout);
+  }
+
+  @override
+  int fromJson(Map<String, dynamic> json) {
+    return Menu.indexGame;
+  }
+
+  @override
+  Map<String, dynamic>? toJson(int state) {
+    return <String, int>{'pageIndex': state};
+  }
+}
diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_cubit.dart
deleted file mode 100644
index 200abf9d0734a0ea58fdda0c87207677e54c64c9..0000000000000000000000000000000000000000
--- a/lib/cubit/settings_cubit.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-import 'package:equatable/equatable.dart';
-import 'package:flutter/material.dart';
-import 'package:hydrated_bloc/hydrated_bloc.dart';
-
-import 'package:twister/config/default_settings.dart';
-
-part 'settings_state.dart';
-
-class SettingsCubit extends HydratedCubit<SettingsState> {
-  SettingsCubit() : super(const SettingsState());
-
-  int getTimerValue() {
-    return state.timerValue ?? DefaultSettings.defaultTimerValue;
-  }
-
-  void setValues({
-    int? timerValue,
-  }) {
-    emit(SettingsState(
-      timerValue: timerValue ?? state.timerValue,
-    ));
-  }
-
-  @override
-  SettingsState? fromJson(Map<String, dynamic> json) {
-    int timerValue = json['timerValue'] as int;
-
-    return SettingsState(
-      timerValue: timerValue,
-    );
-  }
-
-  @override
-  Map<String, dynamic>? toJson(SettingsState state) {
-    return <String, dynamic>{
-      'timerValue': state.timerValue ?? DefaultSettings.defaultTimerValue,
-    };
-  }
-}
diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e9a16c0f8c158bd34b380c11944e3904749a5823
--- /dev/null
+++ b/lib/cubit/settings_game_cubit.dart
@@ -0,0 +1,61 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:twister/config/default_game_settings.dart';
+import 'package:twister/models/settings/settings_game.dart';
+
+part 'settings_game_state.dart';
+
+class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
+  GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault()));
+
+  void setValues({
+    String? timerValue,
+  }) {
+    emit(
+      GameSettingsState(
+        settings: GameSettings(
+          timerValue: timerValue ?? state.settings.timerValue,
+        ),
+      ),
+    );
+  }
+
+  String getParameterValue(String code) {
+    switch (code) {
+      case DefaultGameSettings.parameterCodeTimerValue:
+        return GameSettings.getTimerValueFromUnsafe(state.settings.timerValue);
+    }
+
+    return '';
+  }
+
+  void setParameterValue(String code, String value) {
+    final String timerValue = code == DefaultGameSettings.parameterCodeTimerValue
+        ? value
+        : getParameterValue(DefaultGameSettings.parameterCodeTimerValue);
+
+    setValues(
+      timerValue: timerValue,
+    );
+  }
+
+  @override
+  GameSettingsState? fromJson(Map<String, dynamic> json) {
+    final String timerValue = json[DefaultGameSettings.parameterCodeTimerValue] as String;
+
+    return GameSettingsState(
+      settings: GameSettings(
+        timerValue: timerValue,
+      ),
+    );
+  }
+
+  @override
+  Map<String, dynamic>? toJson(GameSettingsState state) {
+    return <String, dynamic>{
+      DefaultGameSettings.parameterCodeTimerValue: state.settings.timerValue,
+    };
+  }
+}
diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5acd85b44ba541e1c5e9c26af1c4be26a385b9ed
--- /dev/null
+++ b/lib/cubit/settings_game_state.dart
@@ -0,0 +1,15 @@
+part of 'settings_game_cubit.dart';
+
+@immutable
+class GameSettingsState extends Equatable {
+  const GameSettingsState({
+    required this.settings,
+  });
+
+  final GameSettings settings;
+
+  @override
+  List<dynamic> get props => <dynamic>[
+        settings,
+      ];
+}
diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings_global_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1742cfb4e3e0e1a75ca2334115c1b16521ee6b09
--- /dev/null
+++ b/lib/cubit/settings_global_cubit.dart
@@ -0,0 +1,60 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:twister/config/default_global_settings.dart';
+import 'package:twister/models/settings/settings_global.dart';
+
+part 'settings_global_state.dart';
+
+class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
+  GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault()));
+
+  void setValues({
+    String? skin,
+  }) {
+    emit(
+      GlobalSettingsState(
+        settings: GlobalSettings(
+          skin: skin ?? state.settings.skin,
+        ),
+      ),
+    );
+  }
+
+  String getParameterValue(String code) {
+    switch (code) {
+      case DefaultGlobalSettings.parameterCodeSkin:
+        return GlobalSettings.getSkinValueFromUnsafe(state.settings.skin);
+    }
+    return '';
+  }
+
+  void setParameterValue(String code, String value) {
+    final String skin = (code == DefaultGlobalSettings.parameterCodeSkin)
+        ? value
+        : getParameterValue(DefaultGlobalSettings.parameterCodeSkin);
+
+    setValues(
+      skin: skin,
+    );
+  }
+
+  @override
+  GlobalSettingsState? fromJson(Map<String, dynamic> json) {
+    final String skin = json[DefaultGlobalSettings.parameterCodeSkin] as String;
+
+    return GlobalSettingsState(
+      settings: GlobalSettings(
+        skin: skin,
+      ),
+    );
+  }
+
+  @override
+  Map<String, dynamic>? toJson(GlobalSettingsState state) {
+    return <String, dynamic>{
+      DefaultGlobalSettings.parameterCodeSkin: state.settings.skin,
+    };
+  }
+}
diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings_global_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ebcddd700f252257223ca8e16c85202b04f3ff24
--- /dev/null
+++ b/lib/cubit/settings_global_state.dart
@@ -0,0 +1,15 @@
+part of 'settings_global_cubit.dart';
+
+@immutable
+class GlobalSettingsState extends Equatable {
+  const GlobalSettingsState({
+    required this.settings,
+  });
+
+  final GlobalSettings settings;
+
+  @override
+  List<dynamic> get props => <dynamic>[
+        settings,
+      ];
+}
diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_state.dart
deleted file mode 100644
index 6048256f247aeb898ba1b3b10ca2daae3468a7c9..0000000000000000000000000000000000000000
--- a/lib/cubit/settings_state.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-part of 'settings_cubit.dart';
-
-@immutable
-class SettingsState extends Equatable {
-  const SettingsState({
-    this.timerValue,
-  });
-
-  final int? timerValue;
-
-  @override
-  List<dynamic> get props => <dynamic>[
-        timerValue,
-      ];
-
-  Map<String, dynamic> get values => <String, dynamic>{
-        'timerValue': timerValue,
-      };
-}
diff --git a/lib/main.dart b/lib/main.dart
index 78da985f11b9c86e0ff45fae91b2aaec7eed261d..d77b5291375acef85340d62ed350c5f0cbbed943 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -2,20 +2,22 @@ import 'dart:io';
 
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hive/hive.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 import 'package:path_provider/path_provider.dart';
 
 import 'package:twister/config/theme.dart';
-import 'package:twister/cubit/bottom_nav_cubit.dart';
 import 'package:twister/cubit/game_cubit.dart';
-import 'package:twister/cubit/settings_cubit.dart';
+import 'package:twister/cubit/nav_cubit.dart';
+import 'package:twister/cubit/settings_game_cubit.dart';
+import 'package:twister/cubit/settings_global_cubit.dart';
 import 'package:twister/cubit/theme_cubit.dart';
 import 'package:twister/ui/skeleton.dart';
 
 void main() async {
-  /// Initialize packages
+  // Initialize packages
   WidgetsFlutterBinding.ensureInitialized();
   await EasyLocalization.ensureInitialized();
   final Directory tmpDir = await getTemporaryDirectory();
@@ -24,18 +26,17 @@ void main() async {
     storageDirectory: tmpDir,
   );
 
-  runApp(
-    EasyLocalization(
-      path: 'assets/translations',
-      supportedLocales: const <Locale>[
-        Locale('en'),
-        Locale('fr'),
-      ],
-      fallbackLocale: const Locale('en'),
-      useFallbackTranslations: true,
-      child: const MyApp(),
-    ),
-  );
+  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
+      .then((value) => runApp(EasyLocalization(
+            path: 'assets/translations',
+            supportedLocales: const <Locale>[
+              Locale('en'),
+              Locale('fr'),
+            ],
+            fallbackLocale: const Locale('en'),
+            useFallbackTranslations: true,
+            child: const MyApp(),
+          )));
 }
 
 class MyApp extends StatelessWidget {
@@ -43,31 +44,62 @@ class MyApp extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
+    final List<String> assets = getImagesAssets();
+    for (String asset in assets) {
+      precacheImage(AssetImage(asset), context);
+    }
+
     return MultiBlocProvider(
       providers: [
-        BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
-        BlocProvider<GameCubit>(create: (context) => GameCubit()),
-        BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
+        BlocProvider<NavCubit>(create: (context) => NavCubit()),
         BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
+        BlocProvider<GameCubit>(create: (context) => GameCubit()),
+        BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
+        BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()),
       ],
       child: BlocBuilder<ThemeCubit, ThemeModeState>(
-          builder: (BuildContext context, ThemeModeState state) {
-        return MaterialApp(
-          title: 'Twister',
-          home: const SkeletonScreen(),
+        builder: (BuildContext context, ThemeModeState state) {
+          return MaterialApp(
+            title: 'Twister',
+            home: const SkeletonScreen(),
 
-          // Theme stuff
-          theme: lightTheme,
-          darkTheme: darkTheme,
-          themeMode: state.themeMode,
+            // Theme stuff
+            theme: lightTheme,
+            darkTheme: darkTheme,
+            themeMode: state.themeMode,
 
-          // Localization stuff
-          localizationsDelegates: context.localizationDelegates,
-          supportedLocales: context.supportedLocales,
-          locale: context.locale,
-          debugShowCheckedModeBanner: false,
-        );
-      }),
+            // Localization stuff
+            localizationsDelegates: context.localizationDelegates,
+            supportedLocales: context.supportedLocales,
+            locale: context.locale,
+            debugShowCheckedModeBanner: false,
+          );
+        },
+      ),
     );
   }
+
+  List<String> getImagesAssets() {
+    final List<String> assets = [];
+
+    const List<String> gameImages = [
+      'button_back',
+      'button_delete_saved_game',
+      'button_resume_game',
+      'button_start',
+      'game_end',
+      'move-blank',
+      'move-left-foot',
+      'move-left-hand',
+      'move-right-foot',
+      'move-right-hand',
+      'placeholder',
+    ];
+
+    for (String image in gameImages) {
+      assets.add('assets/ui/$image.png');
+    }
+
+    return assets;
+  }
 }
diff --git a/lib/models/game/game.dart b/lib/models/game/game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3ba1cd24665862989d7d57fe0d10d87b2bfe9f97
--- /dev/null
+++ b/lib/models/game/game.dart
@@ -0,0 +1,116 @@
+import 'package:twister/models/game/move.dart';
+import 'package:twister/models/settings/settings_game.dart';
+import 'package:twister/models/settings/settings_global.dart';
+import 'package:twister/utils/tools.dart';
+
+class Game {
+  Game({
+    // Settings
+    required this.gameSettings,
+    required this.globalSettings,
+
+    // State
+    this.isRunning = false,
+    this.isStarted = false,
+    this.isFinished = false,
+    this.animationInProgress = false,
+
+    // Base data
+    required this.move,
+
+    // Game data
+    required this.history,
+  });
+
+  // Settings
+  final GameSettings gameSettings;
+  final GlobalSettings globalSettings;
+
+  // State
+  bool isRunning;
+  bool isStarted;
+  bool isFinished;
+  bool animationInProgress;
+
+  // Base data
+  Move? move;
+
+  // Game data
+  List<Move> history;
+
+  factory Game.createEmpty() {
+    return Game(
+      // Settings
+      gameSettings: GameSettings.createDefault(),
+      globalSettings: GlobalSettings.createDefault(),
+      // Base data
+      move: Move.createEmpty(),
+      // Game data
+      history: [],
+    );
+  }
+
+  factory Game.createNew({
+    GameSettings? gameSettings,
+    GlobalSettings? globalSettings,
+  }) {
+    final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
+    final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
+
+    return Game(
+      // Settings
+      gameSettings: newGameSettings,
+      globalSettings: newGlobalSettings,
+      // State
+      isRunning: true,
+      // Base data
+      move: Move.createEmpty(),
+      // Game data
+      history: [],
+    );
+  }
+
+  bool get canBeResumed => isStarted && !isFinished;
+
+  void dump() {
+    printlog('');
+    printlog('## Current game dump:');
+    printlog('');
+    printlog('$Game:');
+    printlog('  Settings');
+    gameSettings.dump();
+    globalSettings.dump();
+    printlog('  State');
+    printlog('    isRunning: $isRunning');
+    printlog('    isStarted: $isStarted');
+    printlog('    isFinished: $isFinished');
+    printlog('    animationInProgress: $animationInProgress');
+    printlog('  Base data');
+    printlog('    move: $move');
+    printlog('  Game data');
+    printlog('    history: $history');
+    printlog('');
+  }
+
+  @override
+  String toString() {
+    return '$Game(${toJson()})';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      // Settings
+      'gameSettings': gameSettings.toJson(),
+      'globalSettings': globalSettings.toJson(),
+      // State
+      'isRunning': isRunning,
+      'isStarted': isStarted,
+      'isFinished': isFinished,
+      'animationInProgress': animationInProgress,
+      // Base data
+      'move': move,
+      // Game data
+      'history': history,
+    };
+  }
+}
diff --git a/lib/models/move.dart b/lib/models/game/move.dart
similarity index 84%
rename from lib/models/move.dart
rename to lib/models/game/move.dart
index 0d47b0f03892a1434f703125799e181fc81d642c..a026fbccd8c23f3946cc4c10b5eb191be2e33d87 100644
--- a/lib/models/move.dart
+++ b/lib/models/game/move.dart
@@ -1,6 +1,7 @@
 import 'package:easy_localization/easy_localization.dart';
-import 'package:twister/models/twister_color.dart';
-import 'package:twister/models/twister_member.dart';
+
+import 'package:twister/models/game/twister_color.dart';
+import 'package:twister/models/game/twister_member.dart';
 
 class Move {
   final TwisterColor? color;
@@ -11,7 +12,7 @@ class Move {
     required this.member,
   });
 
-  factory Move.createNull() {
+  factory Move.createEmpty() {
     return Move(
       color: null,
       member: null,
@@ -34,7 +35,7 @@ class Move {
 
   @override
   String toString() {
-    return 'Move(${toJson()})';
+    return '$Move(${toJson()})';
   }
 
   String toSoundAsset() {
diff --git a/lib/models/twister_color.dart b/lib/models/game/twister_color.dart
similarity index 100%
rename from lib/models/twister_color.dart
rename to lib/models/game/twister_color.dart
diff --git a/lib/models/twister_member.dart b/lib/models/game/twister_member.dart
similarity index 100%
rename from lib/models/twister_member.dart
rename to lib/models/game/twister_member.dart
diff --git a/lib/models/settings/settings_game.dart b/lib/models/settings/settings_game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..36331fff5aa8a2dcc27f892df35252aff5b2b0d6
--- /dev/null
+++ b/lib/models/settings/settings_game.dart
@@ -0,0 +1,41 @@
+import 'package:twister/config/default_game_settings.dart';
+import 'package:twister/utils/tools.dart';
+
+class GameSettings {
+  final String timerValue;
+
+  GameSettings({
+    required this.timerValue,
+  });
+
+  static String getTimerValueFromUnsafe(String timerValue) {
+    if (DefaultGameSettings.allowedTimerValues.contains(timerValue)) {
+      return timerValue;
+    }
+
+    return DefaultGameSettings.defaultTimerValue;
+  }
+
+  factory GameSettings.createDefault() {
+    return GameSettings(
+      timerValue: DefaultGameSettings.defaultTimerValue,
+    );
+  }
+
+  void dump() {
+    printlog('$GameSettings:');
+    printlog('  ${DefaultGameSettings.parameterCodeTimerValue}: $timerValue');
+    printlog('');
+  }
+
+  @override
+  String toString() {
+    return '$GameSettings(${toJson()})';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      DefaultGameSettings.parameterCodeTimerValue: timerValue,
+    };
+  }
+}
diff --git a/lib/models/settings/settings_global.dart b/lib/models/settings/settings_global.dart
new file mode 100644
index 0000000000000000000000000000000000000000..8805c92e17340be871f9127cfd3035ee203828cc
--- /dev/null
+++ b/lib/models/settings/settings_global.dart
@@ -0,0 +1,41 @@
+import 'package:twister/config/default_global_settings.dart';
+import 'package:twister/utils/tools.dart';
+
+class GlobalSettings {
+  String skin;
+
+  GlobalSettings({
+    required this.skin,
+  });
+
+  static String getSkinValueFromUnsafe(String skin) {
+    if (DefaultGlobalSettings.allowedSkinValues.contains(skin)) {
+      return skin;
+    }
+
+    return DefaultGlobalSettings.defaultSkinValue;
+  }
+
+  factory GlobalSettings.createDefault() {
+    return GlobalSettings(
+      skin: DefaultGlobalSettings.defaultSkinValue,
+    );
+  }
+
+  void dump() {
+    printlog('$GlobalSettings:');
+    printlog('  ${DefaultGlobalSettings.parameterCodeSkin}: $skin');
+    printlog('');
+  }
+
+  @override
+  String toString() {
+    return '$GlobalSettings(${toJson()})';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      DefaultGlobalSettings.parameterCodeSkin: skin,
+    };
+  }
+}
diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ea2b71bfb00916b904d7fe36752568144083868f
--- /dev/null
+++ b/lib/ui/game/game_end.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/models/game/game.dart';
+import 'package:twister/ui/widgets/actions/button_game_quit.dart';
+
+class GameEndWidget extends StatelessWidget {
+  const GameEndWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        const Image decorationImage = Image(
+          image: AssetImage('assets/ui/game_end.png'),
+          fit: BoxFit.fill,
+        );
+
+        return Container(
+          margin: const EdgeInsets.all(2),
+          padding: const EdgeInsets.all(2),
+          child: Table(
+            defaultColumnWidth: const IntrinsicColumnWidth(),
+            children: [
+              TableRow(
+                children: [
+                  const Column(
+                    children: [decorationImage],
+                  ),
+                  Column(
+                    children: [
+                      currentGame.animationInProgress == true
+                          ? decorationImage
+                          : const QuitGameButton()
+                    ],
+                  ),
+                  const Column(
+                    children: [decorationImage],
+                  ),
+                ],
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/app_titles.dart b/lib/ui/helpers/app_titles.dart
similarity index 52%
rename from lib/ui/widgets/app_titles.dart
rename to lib/ui/helpers/app_titles.dart
index 93541243613b0f20e76485520a275d1527a6fcc9..b98107b12fabc3114ebfbec994166b588abcf1ad 100644
--- a/lib/ui/widgets/app_titles.dart
+++ b/lib/ui/helpers/app_titles.dart
@@ -1,8 +1,8 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-class AppTitle extends StatelessWidget {
-  const AppTitle({super.key, required this.text});
+class AppHeader extends StatelessWidget {
+  const AppHeader({super.key, required this.text});
 
   final String text;
 
@@ -11,37 +11,22 @@ class AppTitle extends StatelessWidget {
     return Text(
       tr(text),
       textAlign: TextAlign.start,
-      style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2),
+      style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
     );
   }
 }
 
-class AppTitle1 extends StatelessWidget {
-  const AppTitle1({super.key, required this.text});
+class AppTitle extends StatelessWidget {
+  const AppTitle({super.key, required this.text});
 
   final String text;
 
   @override
   Widget build(BuildContext context) {
     return Text(
-      text,
+      tr(text),
       textAlign: TextAlign.start,
       style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2),
     );
   }
 }
-
-class AppTitle2 extends StatelessWidget {
-  const AppTitle2({super.key, required this.text});
-
-  final String text;
-
-  @override
-  Widget build(BuildContext context) {
-    return Text(
-      text,
-      textAlign: TextAlign.start,
-      style: Theme.of(context).textTheme.titleMedium!.apply(fontWeightDelta: 2),
-    );
-  }
-}
diff --git a/lib/ui/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart
new file mode 100644
index 0000000000000000000000000000000000000000..dedfb1b71763d45e0be793cbd6fddd10c3848ed8
--- /dev/null
+++ b/lib/ui/helpers/outlined_text_widget.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/material.dart';
+
+import 'package:twister/utils/color_extensions.dart';
+
+class OutlinedText extends StatelessWidget {
+  const OutlinedText({
+    super.key,
+    required this.text,
+    required this.fontSize,
+    required this.textColor,
+    this.outlineColor,
+  });
+
+  final String text;
+  final double fontSize;
+  final Color textColor;
+  final Color? outlineColor;
+
+  @override
+  Widget build(BuildContext context) {
+    final double delta = fontSize / 30;
+
+    return Text(
+      text,
+      style: TextStyle(
+        inherit: true,
+        fontSize: fontSize,
+        fontWeight: FontWeight.w600,
+        color: textColor,
+        shadows: [
+          Shadow(
+            offset: Offset(-delta, -delta),
+            color: outlineColor ?? textColor.darken(),
+          ),
+          Shadow(
+            offset: Offset(delta, -delta),
+            color: outlineColor ?? textColor.darken(),
+          ),
+          Shadow(
+            offset: Offset(delta, delta),
+            color: outlineColor ?? textColor.darken(),
+          ),
+          Shadow(
+            offset: Offset(-delta, delta),
+            color: outlineColor ?? textColor.darken(),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/layouts/game_layout.dart b/lib/ui/layouts/game_layout.dart
new file mode 100644
index 0000000000000000000000000000000000000000..0c6cf4707c051884b87acb362914632da18414fa
--- /dev/null
+++ b/lib/ui/layouts/game_layout.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/models/game/game.dart';
+import 'package:twister/ui/game/game_end.dart';
+import 'package:twister/ui/widgets/game/game_board.dart';
+
+class GameLayout extends StatelessWidget {
+  const GameLayout({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        return Container(
+          alignment: AlignmentDirectional.topCenter,
+          padding: const EdgeInsets.all(4),
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              const GameBoardWidget(),
+              const Expanded(child: SizedBox.shrink()),
+              currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/layouts/parameters_layout.dart b/lib/ui/layouts/parameters_layout.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7ccf9b8e8c630c0618c3c1dbfeac837224e99347
--- /dev/null
+++ b/lib/ui/layouts/parameters_layout.dart
@@ -0,0 +1,154 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/config/default_game_settings.dart';
+import 'package:twister/config/default_global_settings.dart';
+import 'package:twister/cubit/settings_game_cubit.dart';
+import 'package:twister/cubit/settings_global_cubit.dart';
+import 'package:twister/ui/parameters/parameter_image.dart';
+import 'package:twister/ui/parameters/parameter_painter.dart';
+import 'package:twister/ui/widgets/actions/button_delete_saved_game.dart';
+import 'package:twister/ui/widgets/actions/button_game_start_new.dart';
+import 'package:twister/ui/widgets/actions/button_resume_saved_game.dart';
+
+class ParametersLayout extends StatelessWidget {
+  const ParametersLayout({super.key, required this.canResume});
+
+  final bool canResume;
+
+  final double separatorHeight = 8.0;
+
+  @override
+  Widget build(BuildContext context) {
+    final List<Widget> lines = [];
+
+    // Game settings
+    for (String code in DefaultGameSettings.availableParameters) {
+      lines.add(Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: buildParametersLine(
+          code: code,
+          isGlobal: false,
+        ),
+      ));
+
+      lines.add(SizedBox(height: separatorHeight));
+    }
+
+    lines.add(SizedBox(height: separatorHeight));
+
+    if (canResume == false) {
+      // Start new game
+      lines.add(const Expanded(
+        child: StartNewGameButton(),
+      ));
+    } else {
+      // Resume game
+      lines.add(const Expanded(
+        child: ResumeSavedGameButton(),
+      ));
+      // Delete saved game
+      lines.add(SizedBox.square(
+        dimension: MediaQuery.of(context).size.width / 4,
+        child: const DeleteSavedGameButton(),
+      ));
+    }
+
+    lines.add(SizedBox(height: separatorHeight));
+
+    // Global settings
+    for (String code in DefaultGlobalSettings.availableParameters) {
+      lines.add(Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: buildParametersLine(
+          code: code,
+          isGlobal: true,
+        ),
+      ));
+
+      lines.add(SizedBox(height: separatorHeight));
+    }
+
+    return Column(
+      children: lines,
+    );
+  }
+
+  List<Widget> buildParametersLine({
+    required String code,
+    required bool isGlobal,
+  }) {
+    final List<Widget> parameterButtons = [];
+
+    final List<String> availableValues = isGlobal
+        ? DefaultGlobalSettings.getAvailableValues(code)
+        : DefaultGameSettings.getAvailableValues(code);
+
+    if (availableValues.length <= 1) {
+      return [];
+    }
+
+    for (String value in availableValues) {
+      final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
+        builder: (BuildContext context, GameSettingsState gameSettingsState) {
+          return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
+            builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
+              final GameSettingsCubit gameSettingsCubit =
+                  BlocProvider.of<GameSettingsCubit>(context);
+              final GlobalSettingsCubit globalSettingsCubit =
+                  BlocProvider.of<GlobalSettingsCubit>(context);
+
+              final String currentValue = isGlobal
+                  ? globalSettingsCubit.getParameterValue(code)
+                  : gameSettingsCubit.getParameterValue(code);
+
+              final bool isActive = (value == currentValue);
+
+              final double displayWidth = MediaQuery.of(context).size.width;
+              final double itemWidth = displayWidth / availableValues.length - 26;
+
+              final bool displayedWithAssets =
+                  DefaultGlobalSettings.displayedWithAssets.contains(code) ||
+                      DefaultGameSettings.displayedWithAssets.contains(code);
+
+              return TextButton(
+                child: Container(
+                  child: displayedWithAssets
+                      ? SizedBox.square(
+                          dimension: itemWidth,
+                          child: ParameterImage(
+                            code: code,
+                            value: value,
+                            isSelected: isActive,
+                          ),
+                        )
+                      : CustomPaint(
+                          size: Size(itemWidth, itemWidth),
+                          willChange: false,
+                          painter: ParameterPainter(
+                            code: code,
+                            value: value,
+                            isSelected: isActive,
+                            gameSettings: gameSettingsState.settings,
+                            globalSettings: globalSettingsState.settings,
+                          ),
+                          isComplex: true,
+                        ),
+                ),
+                onPressed: () {
+                  isGlobal
+                      ? globalSettingsCubit.setParameterValue(code, value)
+                      : gameSettingsCubit.setParameterValue(code, value);
+                },
+              );
+            },
+          );
+        },
+      );
+
+      parameterButtons.add(parameterButton);
+    }
+
+    return parameterButtons;
+  }
+}
diff --git a/lib/ui/parameters/parameter_image.dart b/lib/ui/parameters/parameter_image.dart
new file mode 100644
index 0000000000000000000000000000000000000000..fc4b576f85b01158b74548400d11a4d027c57fbe
--- /dev/null
+++ b/lib/ui/parameters/parameter_image.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+class ParameterImage extends StatelessWidget {
+  const ParameterImage({
+    super.key,
+    required this.code,
+    required this.value,
+    required this.isSelected,
+  });
+
+  final String code;
+  final String value;
+  final bool isSelected;
+
+  static const Color buttonBackgroundColor = Colors.white;
+  static const Color buttonBorderColorActive = Colors.blue;
+  static const Color buttonBorderColorInactive = Colors.white;
+  static const double buttonBorderWidth = 8.0;
+  static const double buttonBorderRadius = 8.0;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(
+        color: buttonBackgroundColor,
+        borderRadius: BorderRadius.circular(buttonBorderRadius),
+        border: Border.all(
+          color: isSelected ? buttonBorderColorActive : buttonBorderColorInactive,
+          width: buttonBorderWidth,
+        ),
+      ),
+      child: Image(
+        image: AssetImage('assets/ui/${code}_$value.png'),
+        fit: BoxFit.fill,
+      ),
+    );
+  }
+}
diff --git a/lib/ui/parameters/parameter_painter.dart b/lib/ui/parameters/parameter_painter.dart
new file mode 100644
index 0000000000000000000000000000000000000000..774c21dffbdbf04d2f8003359d746cb1655d7a07
--- /dev/null
+++ b/lib/ui/parameters/parameter_painter.dart
@@ -0,0 +1,90 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+
+import 'package:twister/models/settings/settings_game.dart';
+import 'package:twister/models/settings/settings_global.dart';
+import 'package:twister/utils/tools.dart';
+
+class ParameterPainter extends CustomPainter {
+  const ParameterPainter({
+    required this.code,
+    required this.value,
+    required this.isSelected,
+    required this.gameSettings,
+    required this.globalSettings,
+  });
+
+  final String code;
+  final String value;
+  final bool isSelected;
+  final GameSettings gameSettings;
+  final GlobalSettings globalSettings;
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    // force square
+    final double canvasSize = min(size.width, size.height);
+
+    const Color borderColorEnabled = Colors.blue;
+    const Color borderColorDisabled = Colors.white;
+
+    // "enabled/disabled" border
+    final paint = Paint();
+    paint.style = PaintingStyle.stroke;
+    paint.color = isSelected ? borderColorEnabled : borderColorDisabled;
+    paint.strokeJoin = StrokeJoin.round;
+    paint.strokeWidth = 10;
+    canvas.drawRect(
+        Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint);
+
+    // content
+    switch (code) {
+      default:
+        printlog('Unknown parameter: $code/$value');
+        paintUnknownParameterItem(value, canvas, canvasSize);
+    }
+  }
+
+  @override
+  bool shouldRepaint(CustomPainter oldDelegate) {
+    return false;
+  }
+
+  // "unknown" parameter -> simple block with text
+  void paintUnknownParameterItem(
+    final String value,
+    final Canvas canvas,
+    final double size,
+  ) {
+    final paint = Paint();
+    paint.strokeJoin = StrokeJoin.round;
+    paint.strokeWidth = 3;
+
+    paint.color = Colors.grey;
+    paint.style = PaintingStyle.fill;
+    canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
+
+    final textSpan = TextSpan(
+      text: '?\n$value',
+      style: const TextStyle(
+        color: Colors.black,
+        fontSize: 18,
+        fontWeight: FontWeight.bold,
+      ),
+    );
+    final textPainter = TextPainter(
+      text: textSpan,
+      textDirection: TextDirection.ltr,
+      textAlign: TextAlign.center,
+    );
+    textPainter.layout();
+    textPainter.paint(
+      canvas,
+      Offset(
+        (size - textPainter.width) * 0.5,
+        (size - textPainter.height) * 0.5,
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/home.dart b/lib/ui/screens/home.dart
deleted file mode 100644
index f9adc5a400ff233aeb14dde37d097048037ba2df..0000000000000000000000000000000000000000
--- a/lib/ui/screens/home.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:twister/ui/widgets/game.dart';
-import 'package:unicons/unicons.dart';
-
-class ScreenHome extends StatelessWidget {
-  const ScreenHome({super.key});
-
-  static Icon navBarIcon = const Icon(UniconsLine.home);
-  static String navBarText = 'bottom_nav_game';
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: ListView(
-        padding: const EdgeInsets.symmetric(horizontal: 4),
-        physics: const BouncingScrollPhysics(),
-        children: const <Widget>[
-          SizedBox(height: 8),
-          Game(),
-          SizedBox(height: 36),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/ui/screens/page_about.dart b/lib/ui/screens/page_about.dart
new file mode 100644
index 0000000000000000000000000000000000000000..65c1e5da608aab8417d60db738d86d07cd3418e8
--- /dev/null
+++ b/lib/ui/screens/page_about.dart
@@ -0,0 +1,41 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+
+import 'package:twister/ui/helpers/app_titles.dart';
+
+class PageAbout extends StatelessWidget {
+  const PageAbout({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(horizontal: 8),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: <Widget>[
+          const SizedBox(height: 8),
+          const AppTitle(text: 'about_title'),
+          const Text('about_content').tr(),
+          FutureBuilder<PackageInfo>(
+            future: PackageInfo.fromPlatform(),
+            builder: (context, snapshot) {
+              switch (snapshot.connectionState) {
+                case ConnectionState.done:
+                  return const Text('about_version').tr(
+                    namedArgs: {
+                      'version': snapshot.data!.version,
+                    },
+                  );
+                default:
+                  return const SizedBox();
+              }
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6ad44e4c3ca7b5e669428681dc2bab87764a2b53
--- /dev/null
+++ b/lib/ui/screens/page_game.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/models/game/game.dart';
+import 'package:twister/ui/layouts/game_layout.dart';
+import 'package:twister/ui/layouts/parameters_layout.dart';
+
+class PageGame extends StatelessWidget {
+  const PageGame({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        return currentGame.isRunning
+            ? const GameLayout()
+            : ParametersLayout(canResume: currentGame.canBeResumed);
+      },
+    );
+  }
+}
diff --git a/lib/ui/screens/page_settings.dart b/lib/ui/screens/page_settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..aa5bcc79ac4d4ce4aece824fe1c557158d1b9091
--- /dev/null
+++ b/lib/ui/screens/page_settings.dart
@@ -0,0 +1,26 @@
+import 'package:flutter/material.dart';
+
+import 'package:twister/ui/helpers/app_titles.dart';
+import 'package:twister/ui/settings/settings_form.dart';
+
+class PageSettings extends StatelessWidget {
+  const PageSettings({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return const Padding(
+      padding: EdgeInsets.symmetric(horizontal: 8),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: <Widget>[
+          SizedBox(height: 8),
+          AppTitle(text: 'settings_title'),
+          SizedBox(height: 8),
+          SettingsForm(),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/settings.dart b/lib/ui/screens/settings.dart
deleted file mode 100644
index b0ecbd5132f60407d9a434f960a2423ecc10c296..0000000000000000000000000000000000000000
--- a/lib/ui/screens/settings.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import 'package:twister/ui/widgets/app_titles.dart';
-import 'package:twister/ui/widgets/settings_form.dart';
-import 'package:unicons/unicons.dart';
-
-class ScreenSettings extends StatelessWidget {
-  const ScreenSettings({super.key});
-
-  static Icon navBarIcon = const Icon(UniconsLine.setting);
-  static String navBarText = 'bottom_nav_settings';
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: ListView(
-        padding: const EdgeInsets.symmetric(horizontal: 4),
-        physics: const BouncingScrollPhysics(),
-        children: <Widget>[
-          const SizedBox(height: 8),
-          AppTitle1(text: tr('settings_title')),
-          const SettingsForm(),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/ui/settings/settings_form.dart b/lib/ui/settings/settings_form.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e29459055cea1d394791f6dffddb4c54987af8c3
--- /dev/null
+++ b/lib/ui/settings/settings_form.dart
@@ -0,0 +1,63 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:twister/ui/settings/theme_card.dart';
+
+class SettingsForm extends StatefulWidget {
+  const SettingsForm({super.key});
+
+  @override
+  State<SettingsForm> createState() => _SettingsFormState();
+}
+
+class _SettingsFormState extends State<SettingsForm> {
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.max,
+      children: <Widget>[
+        // Light/dark theme
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: <Widget>[
+            const Text('settings_label_theme').tr(),
+            const Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                ThemeCard(
+                  mode: ThemeMode.system,
+                  icon: UniconsLine.cog,
+                ),
+                ThemeCard(
+                  mode: ThemeMode.light,
+                  icon: UniconsLine.sun,
+                ),
+                ThemeCard(
+                  mode: ThemeMode.dark,
+                  icon: UniconsLine.moon,
+                )
+              ],
+            ),
+          ],
+        ),
+
+        const SizedBox(height: 16),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/settings/theme_card.dart b/lib/ui/settings/theme_card.dart
new file mode 100644
index 0000000000000000000000000000000000000000..15f343d405a9c5977f872598b9534853ada91d9e
--- /dev/null
+++ b/lib/ui/settings/theme_card.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/theme_cubit.dart';
+
+class ThemeCard extends StatelessWidget {
+  const ThemeCard({
+    super.key,
+    required this.mode,
+    required this.icon,
+  });
+
+  final IconData icon;
+  final ThemeMode mode;
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<ThemeCubit, ThemeModeState>(
+      builder: (BuildContext context, ThemeModeState state) {
+        return Card(
+          elevation: 2,
+          shadowColor: Theme.of(context).colorScheme.shadow,
+          color: state.themeMode == mode
+              ? Theme.of(context).colorScheme.primary
+              : Theme.of(context).colorScheme.surface,
+          shape: const RoundedRectangleBorder(
+            borderRadius: BorderRadius.all(Radius.circular(12)),
+          ),
+          margin: const EdgeInsets.all(5),
+          child: InkWell(
+            onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme(
+              ThemeModeState(themeMode: mode),
+            ),
+            borderRadius: const BorderRadius.all(Radius.circular(12)),
+            child: Icon(
+              icon,
+              size: 32,
+              color: state.themeMode != mode
+                  ? Theme.of(context).colorScheme.primary
+                  : Colors.white,
+            ),
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
index ecc81e1c3f615625b6a7303ecd08a84ef6d03bba..3238f96a07b26708eacb7ffe643cbbd8593d993a 100644
--- a/lib/ui/skeleton.dart
+++ b/lib/ui/skeleton.dart
@@ -1,40 +1,34 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import 'package:twister/cubit/bottom_nav_cubit.dart';
-import 'package:twister/ui/screens/home.dart';
-import 'package:twister/ui/screens/settings.dart';
-import 'package:twister/ui/widgets/app_bar.dart';
-import 'package:twister/ui/widgets/bottom_nav_bar.dart';
+import 'package:twister/config/menu.dart';
+import 'package:twister/cubit/nav_cubit.dart';
+import 'package:twister/ui/widgets/global_app_bar.dart';
 
-class SkeletonScreen extends StatefulWidget {
+class SkeletonScreen extends StatelessWidget {
   const SkeletonScreen({super.key});
 
-  @override
-  State<SkeletonScreen> createState() => _SkeletonScreenState();
-}
-
-class _SkeletonScreenState extends State<SkeletonScreen> {
   @override
   Widget build(BuildContext context) {
-    List<Widget> pageNavigation = <Widget>[
-      const ScreenHome(),
-      const ScreenSettings(),
-    ];
-
     return Scaffold(
-      appBar: const StandardAppBar(),
+      appBar: const GlobalAppBar(),
       extendBodyBehindAppBar: false,
-      body: BlocBuilder<BottomNavCubit, int>(
-        builder: (BuildContext context, int state) {
-          return AnimatedSwitcher(
-            duration: const Duration(milliseconds: 300),
-            child: pageNavigation.elementAt(state),
-          );
-        },
+      body: Material(
+        color: Theme.of(context).colorScheme.surface,
+        child: BlocBuilder<NavCubit, int>(
+          builder: (BuildContext context, int pageIndex) {
+            return Padding(
+              padding: const EdgeInsets.only(
+                top: 8,
+                left: 2,
+                right: 2,
+              ),
+              child: Menu.getPageWidget(pageIndex),
+            );
+          },
+        ),
       ),
-      backgroundColor: Theme.of(context).colorScheme.background,
-      bottomNavigationBar: const BottomNavBar(),
+      backgroundColor: Theme.of(context).colorScheme.surface,
     );
   }
 }
diff --git a/lib/ui/widgets/actions/button_delete_saved_game.dart b/lib/ui/widgets/actions/button_delete_saved_game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4036c2897361be391fe5776f9a851fef870c250b
--- /dev/null
+++ b/lib/ui/widgets/actions/button_delete_saved_game.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+
+class DeleteSavedGameButton extends StatelessWidget {
+  const DeleteSavedGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_delete_saved_game.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).deleteSavedGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_game_quit.dart b/lib/ui/widgets/actions/button_game_quit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..58cde3ebf3da0f79515b024969e3cb9ff6c546e4
--- /dev/null
+++ b/lib/ui/widgets/actions/button_game_quit.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+
+class QuitGameButton extends StatelessWidget {
+  const QuitGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_back.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).quitGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart
new file mode 100644
index 0000000000000000000000000000000000000000..fd246a8b19ac62f25580fbd7471e7cd128ed8b06
--- /dev/null
+++ b/lib/ui/widgets/actions/button_game_start_new.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/cubit/settings_game_cubit.dart';
+import 'package:twister/cubit/settings_global_cubit.dart';
+
+class StartNewGameButton extends StatelessWidget {
+  const StartNewGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameSettingsCubit, GameSettingsState>(
+      builder: (BuildContext context, GameSettingsState gameSettingsState) {
+        return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
+          builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
+            return TextButton(
+              child: const Image(
+                image: AssetImage('assets/ui/button_start.png'),
+                fit: BoxFit.fill,
+              ),
+              onPressed: () {
+                BlocProvider.of<GameCubit>(context).startNewGame(
+                  gameSettings: gameSettingsState.settings,
+                  globalSettings: globalSettingsState.settings,
+                );
+              },
+            );
+          },
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_resume_saved_game.dart b/lib/ui/widgets/actions/button_resume_saved_game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..45dc8ffcb32c315e069ddea47af100ffc347695d
--- /dev/null
+++ b/lib/ui/widgets/actions/button_resume_saved_game.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+
+class ResumeSavedGameButton extends StatelessWidget {
+  const ResumeSavedGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_resume_game.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).resumeSavedGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart
deleted file mode 100644
index b777c21f0cfbd5e5abf11e28e89f4cbb0b22c6ee..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/app_bar.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:twister/ui/widgets/app_titles.dart';
-
-class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
-  const StandardAppBar({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return AppBar(
-      title: const AppTitle(text: 'app_name'),
-      actions: const [],
-    );
-  }
-
-  @override
-  Size get preferredSize => const Size.fromHeight(50);
-}
diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart
deleted file mode 100644
index 24e9a838033600975936ce691d5368be7ec999f5..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/bottom_nav_bar.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:twister/cubit/bottom_nav_cubit.dart';
-import 'package:twister/ui/screens/settings.dart';
-import 'package:twister/ui/screens/home.dart';
-
-class BottomNavBar extends StatelessWidget {
-  const BottomNavBar({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Card(
-      margin: const EdgeInsets.only(top: 1, right: 4, left: 4),
-      elevation: 4,
-      shadowColor: Theme.of(context).colorScheme.shadow,
-      color: Theme.of(context).colorScheme.surfaceVariant,
-      shape: const RoundedRectangleBorder(
-        borderRadius: BorderRadius.only(
-          topLeft: Radius.circular(16),
-          topRight: Radius.circular(16),
-        ),
-      ),
-      child: BlocBuilder<BottomNavCubit, int>(builder: (BuildContext context, int state) {
-        return BottomNavigationBar(
-          currentIndex: state,
-          onTap: (int index) => context.read<BottomNavCubit>().updateIndex(index),
-          type: BottomNavigationBarType.fixed,
-          elevation: 0,
-          backgroundColor: Colors.transparent,
-          selectedItemColor: Theme.of(context).colorScheme.primary,
-          unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color,
-          items: <BottomNavigationBarItem>[
-            BottomNavigationBarItem(
-              icon: ScreenHome.navBarIcon,
-              label: tr(ScreenHome.navBarText),
-            ),
-            BottomNavigationBarItem(
-              icon: ScreenSettings.navBarIcon,
-              label: tr(ScreenSettings.navBarText),
-            ),
-          ],
-        );
-      }),
-    );
-  }
-}
diff --git a/lib/ui/widgets/game.dart b/lib/ui/widgets/game.dart
deleted file mode 100644
index fadc9253e4151c0d040b76d86c77cd72e86dc8e9..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/game.dart
+++ /dev/null
@@ -1,159 +0,0 @@
-import 'dart:async';
-import 'dart:math';
-
-import 'package:audioplayers/audioplayers.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:twister/cubit/game_cubit.dart';
-import 'package:twister/models/move.dart';
-import 'package:twister/models/twister_color.dart';
-import 'package:twister/models/twister_member.dart';
-import 'package:twister/ui/widgets/show_move.dart';
-
-class Game extends StatefulWidget {
-  const Game({super.key});
-
-  @override
-  State<Game> createState() => _GameState();
-}
-
-class _GameState extends State<Game> {
-  final player = AudioPlayer();
-  bool shuffling = false;
-  Move? shuffledMove;
-
-  void animate() {
-    const interval = Duration(milliseconds: 200);
-    int iterationsLeft = 10;
-
-    shuffling = true;
-    shuffledMove = null;
-
-    setState(() {});
-    Timer.periodic(
-      interval,
-      (Timer timer) {
-        if (iterationsLeft > 1) {
-          shuffledMove = Move.pickRandom();
-        }
-
-        if (iterationsLeft == 1) {
-          shuffledMove = null;
-        }
-
-        if (iterationsLeft == 0) {
-          shuffledMove = null;
-          pickNewMove();
-
-          timer.cancel();
-          shuffling = false;
-        }
-
-        setState(() {});
-        iterationsLeft--;
-      },
-    );
-  }
-
-  void pickNewMove() {
-    Move newMove = Move.pickRandom();
-
-    BlocProvider.of<GameCubit>(context).setValues(
-      move: newMove,
-    );
-
-    player.play(AssetSource(newMove.toSoundAsset()));
-  }
-
-  Widget buildCurrentStateWidget(List<Move>? history) {
-    Map<String, TwisterColor?> currentState = {};
-
-    history?.forEach((move) {
-      currentState[move.member.toString()] = move.color;
-    });
-
-    TwisterMember leftHand = TwisterMember(value: TwisterAllowedMembers.leftHand);
-    TwisterMember rightHand = TwisterMember(value: TwisterAllowedMembers.rightHand);
-    TwisterMember leftFoot = TwisterMember(value: TwisterAllowedMembers.leftFoot);
-    TwisterMember rightFoot = TwisterMember(value: TwisterAllowedMembers.rightFoot);
-
-    Move leftHandMove = Move.createFrom(
-      member: leftHand,
-      color: currentState[leftHand.toString()],
-    );
-    Move rightHandMove = Move.createFrom(
-      member: rightHand,
-      color: currentState[rightHand.toString()],
-    );
-    Move leftFootMove = Move.createFrom(
-      member: leftFoot,
-      color: currentState[leftFoot.toString()],
-    );
-    Move rightFootMove = Move.createFrom(
-      member: rightFoot,
-      color: currentState[rightFoot.toString()],
-    );
-
-    return Padding(
-      padding: const EdgeInsets.all(30),
-      child: Table(
-        children: [
-          TableRow(
-            children: [
-              Padding(
-                padding: const EdgeInsets.all(10),
-                child: ShowMove(move: leftHandMove),
-              ),
-              Padding(
-                padding: const EdgeInsets.all(10),
-                child: ShowMove(move: rightHandMove),
-              ),
-            ],
-          ),
-          TableRow(
-            children: [
-              Padding(
-                padding: const EdgeInsets.all(10),
-                child: ShowMove(move: leftFootMove),
-              ),
-              Padding(
-                padding: const EdgeInsets.all(10),
-                child: ShowMove(move: rightFootMove),
-              ),
-            ],
-          )
-        ],
-      ),
-    );
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<GameCubit, GameState>(
-      builder: (BuildContext context, GameState gameState) {
-        return Column(
-          children: [
-            GestureDetector(
-              child: shuffling
-                  ? Transform.rotate(
-                      angle: 2 * pi * Random().nextDouble(),
-                      child: ShowMove(move: shuffledMove ?? Move.createNull()),
-                    )
-                  : ShowMove(move: gameState.move ?? Move.createNull()),
-              onTap: () {
-                animate();
-              },
-            ),
-            GestureDetector(
-              child: buildCurrentStateWidget(gameState.history),
-              onTap: () {
-                BlocProvider.of<GameCubit>(context).deleteHistory();
-              },
-            ),
-          ],
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart
new file mode 100644
index 0000000000000000000000000000000000000000..0fcdef29991b9fa9811978d0de8a07d134387fa9
--- /dev/null
+++ b/lib/ui/widgets/game/game_board.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+
+import 'package:twister/ui/widgets/game/game_current_move.dart';
+import 'package:twister/ui/widgets/game/game_moves_history.dart';
+
+class GameBoardWidget extends StatelessWidget {
+  const GameBoardWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return const Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        GameCurrentMoveWidget(),
+        GameMovesHistoryWidget(),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/game_current_move.dart b/lib/ui/widgets/game/game_current_move.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ae0c7c40c8caca693c9f1f04a98760a909b1d87c
--- /dev/null
+++ b/lib/ui/widgets/game/game_current_move.dart
@@ -0,0 +1,80 @@
+import 'dart:async';
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/models/game/move.dart';
+import 'package:twister/ui/widgets/game/move.dart';
+
+class GameCurrentMoveWidget extends StatefulWidget {
+  const GameCurrentMoveWidget({super.key});
+
+  @override
+  State<GameCurrentMoveWidget> createState() => _GameCurrentMoveWidgetState();
+}
+
+class _GameCurrentMoveWidgetState extends State<GameCurrentMoveWidget> {
+  Move? shuffledMove;
+
+  void animate() {
+    final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
+    if (gameCubit.state.currentGame.animationInProgress == true) {
+      return;
+    }
+
+    const interval = Duration(milliseconds: 200);
+    int iterationsLeft = 10;
+
+    gameCubit.setAnimationIsRunning(true);
+    shuffledMove = null;
+
+    setState(() {});
+    Timer.periodic(
+      interval,
+      (Timer timer) {
+        // animate with random move
+        if (iterationsLeft > 1) {
+          shuffledMove = Move.pickRandom();
+        }
+
+        // last iteration => clear
+        if (iterationsLeft == 1) {
+          shuffledMove = null;
+        }
+
+        // end: pick and keep random move
+        if (iterationsLeft == 0) {
+          shuffledMove = null;
+          timer.cancel();
+
+          gameCubit.pickNewMove();
+          gameCubit.setAnimationIsRunning(false);
+        }
+
+        setState(() {});
+        iterationsLeft--;
+      },
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        return GestureDetector(
+          child: gameState.currentGame.animationInProgress
+              ? Transform.rotate(
+                  angle: 2 * pi * Random().nextDouble(),
+                  child: MoveWidget(move: shuffledMove ?? Move.createEmpty()),
+                )
+              : MoveWidget(move: gameState.currentGame.move ?? Move.createEmpty()),
+          onTap: () {
+            animate();
+          },
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/game_moves_history.dart b/lib/ui/widgets/game/game_moves_history.dart
new file mode 100644
index 0000000000000000000000000000000000000000..67eb3e7a04f4a0e59fcbaad493b100ba9a44d72e
--- /dev/null
+++ b/lib/ui/widgets/game/game_moves_history.dart
@@ -0,0 +1,88 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/models/game/move.dart';
+import 'package:twister/models/game/twister_color.dart';
+import 'package:twister/models/game/twister_member.dart';
+import 'package:twister/ui/widgets/game/move.dart';
+
+class GameMovesHistoryWidget extends StatelessWidget {
+  const GameMovesHistoryWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        return GestureDetector(
+          child: currentGlobalState(gameState.currentGame.history),
+          onTap: () {
+            BlocProvider.of<GameCubit>(context).deleteHistory();
+          },
+        );
+      },
+    );
+  }
+
+  Widget currentGlobalState(List<Move>? history) {
+    Map<String, TwisterColor?> currentState = {};
+
+    history?.forEach((move) {
+      currentState[move.member.toString()] = move.color;
+    });
+
+    TwisterMember leftHand = TwisterMember(value: TwisterAllowedMembers.leftHand);
+    TwisterMember rightHand = TwisterMember(value: TwisterAllowedMembers.rightHand);
+    TwisterMember leftFoot = TwisterMember(value: TwisterAllowedMembers.leftFoot);
+    TwisterMember rightFoot = TwisterMember(value: TwisterAllowedMembers.rightFoot);
+
+    Move leftHandMove = Move.createFrom(
+      member: leftHand,
+      color: currentState[leftHand.toString()],
+    );
+    Move rightHandMove = Move.createFrom(
+      member: rightHand,
+      color: currentState[rightHand.toString()],
+    );
+    Move leftFootMove = Move.createFrom(
+      member: leftFoot,
+      color: currentState[leftFoot.toString()],
+    );
+    Move rightFootMove = Move.createFrom(
+      member: rightFoot,
+      color: currentState[rightFoot.toString()],
+    );
+
+    return Padding(
+      padding: const EdgeInsets.all(30),
+      child: Table(
+        children: [
+          TableRow(
+            children: [
+              Padding(
+                padding: const EdgeInsets.all(10),
+                child: MoveWidget(move: leftHandMove),
+              ),
+              Padding(
+                padding: const EdgeInsets.all(10),
+                child: MoveWidget(move: rightHandMove),
+              ),
+            ],
+          ),
+          TableRow(
+            children: [
+              Padding(
+                padding: const EdgeInsets.all(10),
+                child: MoveWidget(move: leftFootMove),
+              ),
+              Padding(
+                padding: const EdgeInsets.all(10),
+                child: MoveWidget(move: rightFootMove),
+              ),
+            ],
+          )
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/show_move.dart b/lib/ui/widgets/game/move.dart
similarity index 54%
rename from lib/ui/widgets/show_move.dart
rename to lib/ui/widgets/game/move.dart
index c40dd3a6db62039285ae784c6683094bfc1224f8..48846a90524e8c667ffb6d4e31f2d2d2a9d94595 100644
--- a/lib/ui/widgets/show_move.dart
+++ b/lib/ui/widgets/game/move.dart
@@ -1,14 +1,14 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import 'package:twister/config/colors.dart';
-import 'package:twister/models/move.dart';
-import 'package:twister/models/twister_color.dart';
-import 'package:twister/models/twister_member.dart';
+import 'package:twister/config/color_theme.dart';
+import 'package:twister/models/game/move.dart';
+import 'package:twister/models/game/twister_color.dart';
+import 'package:twister/models/game/twister_member.dart';
 import 'package:twister/utils/color_extensions.dart';
 
-class ShowMove extends StatelessWidget {
-  const ShowMove({super.key, required this.move});
+class MoveWidget extends StatelessWidget {
+  const MoveWidget({super.key, required this.move});
 
   final Move move;
 
@@ -27,12 +27,6 @@ class ShowMove extends StatelessWidget {
     }
   }
 
-  Widget getImageWidget(Move move) {
-    String imageAsset = 'assets/images/${move.member?.toString() ?? 'blank'}.png';
-
-    return Image.asset(imageAsset);
-  }
-
   Widget getTextWidget(Move move) {
     TextStyle style = const TextStyle(
       color: Colors.black,
@@ -54,38 +48,34 @@ class ShowMove extends StatelessWidget {
     }
   }
 
-  Widget buildWidget(Move move, double maxWidth) {
-    Color color = getColor(move);
-
-    double containerSize = maxWidth * 0.8;
-
-    return AnimatedSwitcher(
-      duration: const Duration(milliseconds: 200),
-      transitionBuilder: (Widget child, Animation<double> animation) {
-        return ScaleTransition(scale: animation, child: child);
-      },
-      child: Container(
-        width: containerSize,
-        height: containerSize,
-        decoration: BoxDecoration(
-          color: color,
-          borderRadius: BorderRadius.all(Radius.circular(containerSize)),
-          border: Border.all(
-            color: color.darken(15),
-            width: 15,
-          ),
-        ),
-        child: getImageWidget(move),
-      ),
-    );
-  }
-
   @override
   Widget build(BuildContext context) {
     return LayoutBuilder(
       builder: (context, constraints) {
         final double maxWidth = constraints.maxWidth;
-        return buildWidget(move, maxWidth);
+        Color color = getColor(move);
+
+        double containerSize = maxWidth * 0.8;
+
+        return AnimatedSwitcher(
+          duration: const Duration(milliseconds: 200),
+          transitionBuilder: (Widget child, Animation<double> animation) {
+            return ScaleTransition(scale: animation, child: child);
+          },
+          child: Container(
+            width: containerSize,
+            height: containerSize,
+            decoration: BoxDecoration(
+              color: color,
+              borderRadius: BorderRadius.all(Radius.circular(containerSize)),
+              border: Border.all(
+                color: color.darken(15),
+                width: 15,
+              ),
+            ),
+            child: Image.asset('assets/ui/move-${move.member?.toString() ?? 'blank'}.png'),
+          ),
+        );
       },
     );
   }
diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6b6c07efb0ede67ab7a37ec509214873b9baebcf
--- /dev/null
+++ b/lib/ui/widgets/global_app_bar.dart
@@ -0,0 +1,83 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:twister/config/menu.dart';
+import 'package:twister/cubit/game_cubit.dart';
+import 'package:twister/cubit/nav_cubit.dart';
+import 'package:twister/models/game/game.dart';
+import 'package:twister/ui/helpers/app_titles.dart';
+
+class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
+  const GlobalAppBar({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        return BlocBuilder<NavCubit, int>(
+          builder: (BuildContext context, int pageIndex) {
+            final Game currentGame = gameState.currentGame;
+
+            final List<Widget> menuActions = [];
+
+            if (currentGame.isRunning && !currentGame.isFinished) {
+              menuActions.add(TextButton(
+                child: const Image(
+                  image: AssetImage('assets/ui/button_back.png'),
+                  fit: BoxFit.fill,
+                ),
+                onPressed: () {},
+                onLongPress: () {
+                  BlocProvider.of<GameCubit>(context).quitGame();
+                },
+              ));
+            } else {
+              if (pageIndex == Menu.indexGame) {
+                // go to Settings page
+                menuActions.add(ElevatedButton(
+                  onPressed: () {
+                    context.read<NavCubit>().goToSettingsPage();
+                  },
+                  style: ElevatedButton.styleFrom(
+                    shape: const CircleBorder(),
+                  ),
+                  child: Menu.menuItemSettings.icon,
+                ));
+
+                // go to About page
+                menuActions.add(ElevatedButton(
+                  onPressed: () {
+                    context.read<NavCubit>().goToAboutPage();
+                  },
+                  style: ElevatedButton.styleFrom(
+                    shape: const CircleBorder(),
+                  ),
+                  child: Menu.menuItemAbout.icon,
+                ));
+              } else {
+                // back to Home page
+                menuActions.add(ElevatedButton(
+                  onPressed: () {
+                    context.read<NavCubit>().goToGamePage();
+                  },
+                  style: ElevatedButton.styleFrom(
+                    shape: const CircleBorder(),
+                  ),
+                  child: Menu.menuItemGame.icon,
+                ));
+              }
+            }
+
+            return AppBar(
+              title: const AppHeader(text: 'app_name'),
+              actions: menuActions,
+            );
+          },
+        );
+      },
+    );
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(50);
+}
diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/widgets/settings_form.dart
deleted file mode 100644
index 81fac9e07e4174ad613b3d84e0047f6aaead6ba4..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/settings_form.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:unicons/unicons.dart';
-
-import 'package:twister/config/default_settings.dart';
-import 'package:twister/cubit/settings_cubit.dart';
-import 'package:twister/ui/widgets/app_titles.dart';
-import 'package:twister/ui/widgets/theme_card.dart';
-
-class SettingsForm extends StatefulWidget {
-  const SettingsForm({super.key});
-
-  @override
-  State<SettingsForm> createState() => _SettingsFormState();
-}
-
-class _SettingsFormState extends State<SettingsForm> {
-  int timerValue = DefaultSettings.defaultTimerValue;
-
-  List<bool> _selectedTimerValue = [];
-
-  @override
-  void didChangeDependencies() {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
-    timerValue = settings.getTimerValue();
-
-    _selectedTimerValue =
-        DefaultSettings.allowedTimerValues.map((e) => (e == timerValue)).toList();
-
-    super.didChangeDependencies();
-  }
-
-  @override
-  void dispose() {
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    void saveSettings() {
-      BlocProvider.of<SettingsCubit>(context).setValues(
-        timerValue: timerValue,
-      );
-    }
-
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.start,
-      crossAxisAlignment: CrossAxisAlignment.start,
-      mainAxisSize: MainAxisSize.max,
-      children: <Widget>[
-        const SizedBox(height: 8),
-
-        // Light/dark theme
-        Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: <Widget>[
-            const Text('settings_label_theme').tr(),
-            const Row(
-              mainAxisAlignment: MainAxisAlignment.end,
-              crossAxisAlignment: CrossAxisAlignment.center,
-              children: [
-                ThemeCard(
-                  mode: ThemeMode.system,
-                  icon: UniconsLine.cog,
-                ),
-                ThemeCard(
-                  mode: ThemeMode.light,
-                  icon: UniconsLine.sun,
-                ),
-                ThemeCard(
-                  mode: ThemeMode.dark,
-                  icon: UniconsLine.moon,
-                )
-              ],
-            ),
-          ],
-        ),
-
-        const SizedBox(height: 16),
-
-        AppTitle2(text: tr('settings_title_game')),
-
-        // Timer value
-        Row(
-          mainAxisAlignment: MainAxisAlignment.end,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            const Text('settings_label_game_timer_value').tr(),
-            ToggleButtons(
-              onPressed: (int index) {
-                setState(() {
-                  timerValue = DefaultSettings.allowedTimerValues[index];
-                  for (int i = 0; i < _selectedTimerValue.length; i++) {
-                    _selectedTimerValue[i] = i == index;
-                  }
-                });
-                saveSettings();
-              },
-              borderRadius: const BorderRadius.all(Radius.circular(8)),
-              constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
-              isSelected: _selectedTimerValue,
-              children:
-                  DefaultSettings.allowedTimerValues.map((e) => Text(e.toString())).toList(),
-            ),
-          ],
-        ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/theme_card.dart b/lib/ui/widgets/theme_card.dart
deleted file mode 100644
index bd935ab73dd26ff465d628f7fb82f3b1bdbad85c..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/theme_card.dart
+++ /dev/null
@@ -1,45 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:twister/cubit/theme_cubit.dart';
-
-class ThemeCard extends StatelessWidget {
-  const ThemeCard({
-    super.key,
-    required this.mode,
-    required this.icon,
-  });
-
-  final IconData icon;
-  final ThemeMode mode;
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<ThemeCubit, ThemeModeState>(
-        builder: (BuildContext context, ThemeModeState state) {
-      return Card(
-        elevation: 2,
-        shadowColor: Theme.of(context).colorScheme.shadow,
-        color: state.themeMode == mode
-            ? Theme.of(context).colorScheme.primary
-            : Theme.of(context).colorScheme.surface,
-        shape: const RoundedRectangleBorder(
-          borderRadius: BorderRadius.all(Radius.circular(12)),
-        ),
-        margin: const EdgeInsets.all(5),
-        child: InkWell(
-          onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme(
-            ThemeModeState(themeMode: mode),
-          ),
-          borderRadius: const BorderRadius.all(Radius.circular(12)),
-          child: Icon(
-            icon,
-            size: 32,
-            color:
-                state.themeMode != mode ? Theme.of(context).colorScheme.primary : Colors.white,
-          ),
-        ),
-      );
-    });
-  }
-}
diff --git a/pubspec.lock b/pubspec.lock
index fe15305da7d49aa628fe3a2241790bfa31038787..fc3ef495ce9e62261fac63f9fe041f119618b637 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,10 +5,10 @@ packages:
     dependency: transitive
     description:
       name: args
-      sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
+      sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.2"
+    version: "2.5.0"
   async:
     dependency: transitive
     description:
@@ -21,66 +21,66 @@ packages:
     dependency: "direct main"
     description:
       name: audioplayers
-      sha256: c05c6147124cd63e725e861335a8b4d57300b80e6e92cea7c145c739223bbaef
+      sha256: "752039d6aa752597c98ec212e9759519061759e402e7da59a511f39d43aa07d2"
       url: "https://pub.dev"
     source: hosted
-    version: "5.2.1"
+    version: "6.0.0"
   audioplayers_android:
     dependency: transitive
     description:
       name: audioplayers_android
-      sha256: b00e1a0e11365d88576320ec2d8c192bc21f1afb6c0e5995d1c57ae63156acb5
+      sha256: de576b890befe27175c2f511ba8b742bec83765fa97c3ce4282bba46212f58e4
       url: "https://pub.dev"
     source: hosted
-    version: "4.0.3"
+    version: "5.0.0"
   audioplayers_darwin:
     dependency: transitive
     description:
       name: audioplayers_darwin
-      sha256: "3034e99a6df8d101da0f5082dcca0a2a99db62ab1d4ddb3277bed3f6f81afe08"
+      sha256: e507887f3ff18d8e5a10a668d7bedc28206b12e10b98347797257c6ae1019c3b
       url: "https://pub.dev"
     source: hosted
-    version: "5.0.2"
+    version: "6.0.0"
   audioplayers_linux:
     dependency: transitive
     description:
       name: audioplayers_linux
-      sha256: "60787e73fefc4d2e0b9c02c69885402177e818e4e27ef087074cf27c02246c9e"
+      sha256: "3d3d244c90436115417f170426ce768856d8fe4dfc5ed66a049d2890acfa82f9"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.0"
+    version: "4.0.0"
   audioplayers_platform_interface:
     dependency: transitive
     description:
       name: audioplayers_platform_interface
-      sha256: "365c547f1bb9e77d94dd1687903a668d8f7ac3409e48e6e6a3668a1ac2982adb"
+      sha256: "6834dd48dfb7bc6c2404998ebdd161f79cd3774a7e6779e1348d54a3bfdcfaa5"
       url: "https://pub.dev"
     source: hosted
-    version: "6.1.0"
+    version: "7.0.0"
   audioplayers_web:
     dependency: transitive
     description:
       name: audioplayers_web
-      sha256: "22cd0173e54d92bd9b2c80b1204eb1eb159ece87475ab58c9788a70ec43c2a62"
+      sha256: db8fc420dadf80da18e2286c18e746fb4c3b2c5adbf0c963299dde046828886d
       url: "https://pub.dev"
     source: hosted
-    version: "4.1.0"
+    version: "5.0.0"
   audioplayers_windows:
     dependency: transitive
     description:
       name: audioplayers_windows
-      sha256: "9536812c9103563644ada2ef45ae523806b0745f7a78e89d1b5fb1951de90e1a"
+      sha256: "8605762dddba992138d476f6a0c3afd9df30ac5b96039929063eceed416795c2"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.0"
+    version: "4.0.0"
   bloc:
     dependency: transitive
     description:
       name: bloc
-      sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
+      sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
       url: "https://pub.dev"
     source: hosted
-    version: "8.1.3"
+    version: "8.1.4"
   characters:
     dependency: transitive
     description:
@@ -117,10 +117,10 @@ packages:
     dependency: "direct main"
     description:
       name: easy_localization
-      sha256: c145aeb6584aedc7c862ab8c737c3277788f47488bfdf9bae0fe112bd0a4789c
+      sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.5"
+    version: "3.0.7"
   easy_logger:
     dependency: transitive
     description:
@@ -170,18 +170,18 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_bloc
-      sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
+      sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
       url: "https://pub.dev"
     source: hosted
-    version: "8.1.4"
+    version: "8.1.6"
   flutter_lints:
     dependency: "direct dev"
     description:
       name: flutter_lints
-      sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
+      sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.1"
+    version: "4.0.0"
   flutter_localizations:
     dependency: transitive
     description: flutter
@@ -220,34 +220,26 @@ packages:
     dependency: "direct main"
     description:
       name: hydrated_bloc
-      sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d"
+      sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c
       url: "https://pub.dev"
     source: hosted
-    version: "9.1.4"
+    version: "9.1.5"
   intl:
     dependency: transitive
     description:
       name: intl
-      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
       url: "https://pub.dev"
     source: hosted
-    version: "0.18.1"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
-      url: "https://pub.dev"
-    source: hosted
-    version: "0.6.7"
+    version: "0.19.0"
   lints:
     dependency: transitive
     description:
       name: lints
-      sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
+      sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.0"
+    version: "4.0.0"
   material_color_utilities:
     dependency: transitive
     description:
@@ -260,10 +252,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
+      sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
       url: "https://pub.dev"
     source: hosted
-    version: "1.11.0"
+    version: "1.12.0"
   nested:
     dependency: transitive
     description:
@@ -272,6 +264,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.0.0"
+  package_info_plus:
+    dependency: "direct main"
+    description:
+      name: package_info_plus
+      sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.0.0"
+  package_info_plus_platform_interface:
+    dependency: transitive
+    description:
+      name: package_info_plus_platform_interface
+      sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.0"
   path:
     dependency: transitive
     description:
@@ -284,26 +292,26 @@ packages:
     dependency: "direct main"
     description:
       name: path_provider
-      sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
+      sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.3"
   path_provider_android:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
+      sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.2"
+    version: "2.2.5"
   path_provider_foundation:
     dependency: transitive
     description:
       name: path_provider_foundation
-      sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
+      sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.2"
+    version: "2.4.0"
   path_provider_linux:
     dependency: transitive
     description:
@@ -332,10 +340,10 @@ packages:
     dependency: transitive
     description:
       name: platform
-      sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
+      sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.4"
+    version: "3.1.5"
   plugin_platform_interface:
     dependency: transitive
     description:
@@ -356,26 +364,26 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences
-      sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
+      sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.2"
+    version: "2.2.3"
   shared_preferences_android:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
+      sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.3"
   shared_preferences_foundation:
     dependency: transitive
     description:
       name: shared_preferences_foundation
-      sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
+      sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.5"
+    version: "2.4.0"
   shared_preferences_linux:
     dependency: transitive
     description:
@@ -473,10 +481,10 @@ packages:
     dependency: transitive
     description:
       name: uuid
-      sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
+      sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
       url: "https://pub.dev"
     source: hosted
-    version: "4.3.3"
+    version: "4.4.0"
   vector_math:
     dependency: transitive
     description:
@@ -489,18 +497,18 @@ packages:
     dependency: transitive
     description:
       name: web
-      sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
+      sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
       url: "https://pub.dev"
     source: hosted
-    version: "0.5.0"
+    version: "0.5.1"
   win32:
     dependency: transitive
     description:
       name: win32
-      sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
+      sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
       url: "https://pub.dev"
     source: hosted
-    version: "5.2.0"
+    version: "5.5.1"
   xdg_directories:
     dependency: transitive
     description:
@@ -510,5 +518,5 @@ packages:
     source: hosted
     version: "1.0.4"
 sdks:
-  dart: ">=3.3.0 <4.0.0"
-  flutter: ">=3.19.0"
+  dart: ">=3.4.0 <4.0.0"
+  flutter: ">=3.22.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 4efd07ac9876b2eaaba71abd4e1e91cbfc531db4..16ca4fa71f59c870321d1572528a85c75a16b4eb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,33 +1,37 @@
 name: twister
-description: twister game companion
+description: Twister game companion
 
-publish_to: 'none'
+publish_to: "none"
 
-version: 0.0.23+23
+version: 0.1.0+24
 
 environment:
-  sdk: '^3.0.0'
+  sdk: "^3.0.0"
 
 dependencies:
   flutter:
     sdk: flutter
 
-  audioplayers: ^5.2.1
+  # base
   easy_localization: ^3.0.1
   equatable: ^2.0.5
   flutter_bloc: ^8.1.1
   hive: ^2.2.3
-  path_provider: ^2.0.11
   hydrated_bloc: ^9.0.0
+  package_info_plus: ^8.0.0
+  path_provider: ^2.0.11
   unicons: ^2.1.1
 
+  # specific
+  audioplayers: ^6.0.0
+
 dev_dependencies:
-  flutter_lints: ^3.0.1
+  flutter_lints: ^4.0.0
 
 flutter:
-  uses-material-design: false
+  uses-material-design: true
   assets:
-    - assets/images/
+    - assets/ui/
     - assets/translations/
     - assets/voices/
 
@@ -42,3 +46,4 @@ flutter:
           weight: 400
         - asset: assets/fonts/Nunito-Light.ttf
           weight: 300
+
diff --git a/icons/build_repository_icons.sh b/resources/app/build_application_resources.sh
similarity index 98%
rename from icons/build_repository_icons.sh
rename to resources/app/build_application_resources.sh
index 27dbe2647fe4e6d562fbd99451716d1b7d448570..6d67b8f4f9eca701d1aed7331ef41dfb0bd44f20 100755
--- a/icons/build_repository_icons.sh
+++ b/resources/app/build_application_resources.sh
@@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins
 command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
 
 CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
 
 SOURCE_ICON="${CURRENT_DIR}/icon.svg"
 SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg"
diff --git a/icons/featureGraphic.svg b/resources/app/featureGraphic.svg
similarity index 100%
rename from icons/featureGraphic.svg
rename to resources/app/featureGraphic.svg
diff --git a/icons/icon.svg b/resources/app/icon.svg
similarity index 100%
rename from icons/icon.svg
rename to resources/app/icon.svg
diff --git a/resources/build_resources.sh b/resources/build_resources.sh
new file mode 100755
index 0000000000000000000000000000000000000000..68bc1d0e887903cab672cd6466b1e8c503f36148
--- /dev/null
+++ b/resources/build_resources.sh
@@ -0,0 +1,7 @@
+#! /bin/bash
+
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+${CURRENT_DIR}/app/build_application_resources.sh
+${CURRENT_DIR}/ui/build_ui_resources.sh
+${CURRENT_DIR}/tts/generate_sounds.sh
diff --git a/tts/generate_sounds.sh b/resources/tts/generate_sounds.sh
similarity index 98%
rename from tts/generate_sounds.sh
rename to resources/tts/generate_sounds.sh
index 660e16f0120363d85d126f85c4a751010b843b7a..a5fe84c770936cea126c2562c6be08e6b2f8e079 100755
--- a/tts/generate_sounds.sh
+++ b/resources/tts/generate_sounds.sh
@@ -4,7 +4,7 @@
 command -v pico2wave >/dev/null 2>&1 || { echo >&2 "I require pico2wave (libttspico-utils) but it's not installed. Aborting."; exit 1; }
 
 CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
 
 OUTPUT_BASE_FOLDER="${BASE_DIR}/assets/voices"
 mkdir -p "${OUTPUT_BASE_FOLDER}"
diff --git a/resources/ui/build_ui_resources.sh b/resources/ui/build_ui_resources.sh
new file mode 100755
index 0000000000000000000000000000000000000000..93344c8ec66c5b8ea5d9a08a9cc0ca8f657700d1
--- /dev/null
+++ b/resources/ui/build_ui_resources.sh
@@ -0,0 +1,111 @@
+#! /bin/bash
+
+# Check dependencies
+command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; }
+command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; }
+command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
+
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
+ASSETS_DIR="${BASE_DIR}/assets"
+
+OPTIPNG_OPTIONS="-preserve -quiet -o7"
+ICON_SIZE=512
+ICON_SIZE=192
+
+#######################################################
+
+# Game images (svg files found in `images` folder)
+AVAILABLE_GAME_IMAGES=""
+if [ -d "${CURRENT_DIR}/images" ]; then
+  AVAILABLE_GAME_IMAGES="$(find "${CURRENT_DIR}/images" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort)"
+fi
+
+# Skins (subfolders found in `skins` folder)
+AVAILABLE_SKINS=""
+if [ -d "${CURRENT_DIR}/skins" ]; then
+  AVAILABLE_SKINS="$(find "${CURRENT_DIR}/skins" -mindepth 1 -type d | awk -F/ '{print $NF}')"
+fi
+
+# Images per skin (svg files found recursively in `skins` folder and subfolders)
+SKIN_IMAGES=""
+if [ -d "${CURRENT_DIR}/skins" ]; then
+  SKIN_IMAGES="$(find "${CURRENT_DIR}/skins" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort | uniq)"
+fi
+
+#######################################################
+
+# optimize svg
+function optimize_svg() {
+  SOURCE="$1"
+
+  cp ${SOURCE} ${SOURCE}.tmp
+  scour \
+      --remove-descriptive-elements \
+      --enable-id-stripping \
+      --enable-viewboxing \
+      --enable-comment-stripping \
+      --nindent=4 \
+      --quiet \
+      -i ${SOURCE}.tmp \
+      -o ${SOURCE}
+  rm ${SOURCE}.tmp
+}
+
+# build icons
+function build_image() {
+  SOURCE="$1"
+  TARGET="$2"
+
+  echo "Building ${TARGET}"
+
+  if [ ! -f "${SOURCE}" ]; then
+    echo "Missing file: ${SOURCE}"
+    exit 1
+  fi
+
+  optimize_svg "${SOURCE}"
+
+  mkdir -p "$(dirname "${TARGET}")"
+
+  inkscape \
+      --export-width=${ICON_SIZE} \
+      --export-height=${ICON_SIZE} \
+      --export-filename=${TARGET} \
+      "${SOURCE}"
+
+  optipng ${OPTIPNG_OPTIONS} "${TARGET}"
+}
+
+function build_image_for_skin() {
+  SKIN_CODE="$1"
+
+  # skin images
+  for SKIN_IMAGE in ${SKIN_IMAGES}
+  do
+    build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png
+  done
+}
+
+#######################################################
+
+# Delete existing generated images
+if [ -d "${ASSETS_DIR}/ui" ]; then
+  find ${ASSETS_DIR}/ui -type f -name "*.png" -delete
+fi
+if [ -d "${ASSETS_DIR}/skins" ]; then
+  find ${ASSETS_DIR}/skins -type f -name "*.png" -delete
+fi
+
+# build game images
+for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES}
+do
+  build_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png
+done
+
+# build skins images
+for SKIN in ${AVAILABLE_SKINS}
+do
+  build_image_for_skin "${SKIN}"
+done
+
diff --git a/resources/ui/images/button_back.svg b/resources/ui/images/button_back.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2622a578dba53ce582afabfc587c2a85a1fb6eaa
--- /dev/null
+++ b/resources/ui/images/button_back.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#e41578" stroke="#fff" stroke-width=".238"/><path d="m59.387 71.362c1.1248 1.1302 4.0012 1.1302 4.0012 0v-45.921c0-1.1316-2.8832-1.1316-4.0121 0l-37.693 20.918c-1.1289 1.1248-1.1479 2.9551-0.02171 4.084z" fill="#fefeff" stroke="#930e4e" stroke-linecap="round" stroke-linejoin="round" stroke-width="8.257"/><path d="m57.857 68.048c0.96243 0.96706 3.4236 0.96706 3.4236 0v-39.292c0-0.96825-2.467-0.96825-3.4329 0l-32.252 17.898c-0.96594 0.96243-0.9822 2.5285-0.01858 3.4945z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.314"/></svg>
diff --git a/resources/ui/images/button_delete_saved_game.svg b/resources/ui/images/button_delete_saved_game.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ac7eefef476f761903fe781b8c86d0c94323550a
--- /dev/null
+++ b/resources/ui/images/button_delete_saved_game.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#ee7d49" stroke="#fff" stroke-width=".238"/><path d="m61.07 35.601-1.7399 27.837c-0.13442 2.1535-1.9205 3.8312-4.0781 3.8312h-16.84c-2.1576 0-3.9437-1.6777-4.0781-3.8312l-1.7399-27.837h-2.6176c-0.84621 0-1.5323-0.68613-1.5323-1.5323 0-0.84655 0.68613-1.5323 1.5323-1.5323h33.711c0.84621 0 1.5323 0.68578 1.5323 1.5323 0 0.84621-0.68613 1.5323-1.5323 1.5323zm-3.2617 0h-21.953l1.4715 26.674c0.05985 1.0829 0.95531 1.9305 2.0403 1.9305h14.929c1.085 0 1.9804-0.84757 2.0403-1.9305zm-10.977 3.0647c0.78977 0 1.4301 0.6403 1.4301 1.4301v19.614c0 0.78977-0.6403 1.4301-1.4301 1.4301s-1.4301-0.6403-1.4301-1.4301v-19.614c0-0.78977 0.6403-1.4301 1.4301-1.4301zm-6.1293 0c0.80004 0 1.4588 0.62935 1.495 1.4286l0.89647 19.719c0.03182 0.70016-0.50998 1.2933-1.2101 1.3255-0.01915 7.02e-4 -0.03831 1e-3 -0.05781 1e-3 -0.74462 0-1.3596-0.58215-1.4003-1.3261l-1.0757-19.719c-0.0407-0.74701 0.53188-1.3852 1.2786-1.4259 0.02462-0.0014 0.04926-2e-3 0.07388-2e-3zm12.259 0c0.74804 0 1.3541 0.60609 1.3541 1.3541 0 0.02462-3.28e-4 0.04926-0.0017 0.07388l-1.0703 19.618c-0.04379 0.80106-0.70597 1.4281-1.5081 1.4281-0.74804 0-1.3541-0.60609-1.3541-1.3541 0-0.02462 3.49e-4 -0.04925 0.0017-0.07388l1.0703-19.618c0.04379-0.80106 0.70597-1.4281 1.5081-1.4281zm-10.216-12.259h8.1728c2.2567 0 4.086 1.8293 4.086 4.086v2.0433h-16.344v-2.0433c0-2.2567 1.8293-4.086 4.086-4.086zm0.20453 3.0647c-0.67725 0-1.2259 0.54863-1.2259 1.2259v1.8388h10.215v-1.8388c0-0.67725-0.54863-1.2259-1.2259-1.2259z" fill="#fff" fill-rule="evenodd" stroke="#bd4812" stroke-width=".75383"/></svg>
diff --git a/resources/ui/images/button_resume_game.svg b/resources/ui/images/button_resume_game.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6ad8b64202d0e70f898c16c520e756fe8a934add
--- /dev/null
+++ b/resources/ui/images/button_resume_game.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m39.211 31.236c-0.84086-0.84489-2.9911-0.84489-2.9911 0v34.329c0 0.84594 2.1554 0.84594 2.9993 0l28.178-15.637c0.84392-0.84086 0.85812-2.2091 0.01623-3.053z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="6.1726"/><path d="m40.355 33.714c-0.71948-0.72294-2.5594-0.72294-2.5594 0v29.373c0 0.72383 1.8442 0.72383 2.5663 0l24.11-13.38c0.7221-0.71948 0.73426-1.8902 0.01389-2.6124z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.225"/><path d="m28.369 66.919v-37.591" fill="#105ca2" stroke="#105ca2" stroke-linecap="round" stroke-width="4.0337"/></svg>
diff --git a/resources/ui/images/button_start.svg b/resources/ui/images/button_start.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e9d49d2172b9a0305db82779971e3c1e12f34a70
--- /dev/null
+++ b/resources/ui/images/button_start.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m34.852 25.44c-1.1248-1.1302-4.0012-1.1302-4.0012 0v45.921c0 1.1316 2.8832 1.1316 4.0121 0l37.693-20.918c1.1289-1.1248 1.1479-2.9551 0.02171-4.084z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="8.257"/><path d="m36.382 28.754c-0.96243-0.96706-3.4236-0.96706-3.4236 0v39.292c0 0.96825 2.467 0.96825 3.4329 0l32.252-17.898c0.96594-0.96243 0.9822-2.5285 0.01858-3.4945z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.314"/></svg>
diff --git a/resources/ui/images/game_end.svg b/resources/ui/images/game_end.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fe20923864d0c5d39168eced03038b65106a596b
--- /dev/null
+++ b/resources/ui/images/game_end.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.17604 0 0 .17604 7.9341 1.7716)"><path d="m101.92 496.35c-1.8555 0-3.7109-0.69532-5.1484-2.0898-2.9297-2.8438-3-7.5234-0.15234-10.453l9.1875-9.4648c2.8438-2.9297 7.5234-3 10.453-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4648c-1.4492 1.4961-3.375 2.2461-5.3047 2.2461z" fill="#ff4e61"/><path d="m201.65 133.26c-1.8516 0-3.7109-0.69531-5.1445-2.0898-2.9297-2.8438-3-7.5234-0.15625-10.449l9.1914-9.4688c2.8438-2.9297 7.5195-3 10.449-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#ff4e61"/><path d="m413.8 100.39c-1.8555 0-3.7109-0.69141-5.1484-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4688c2.8438-2.9258 7.5234-2.9961 10.453-0.15234 2.9297 2.8398 3 7.5234 0.15625 10.449l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#5c73bc"/><path d="m413.8 463.77c-1.8555 0-3.7109-0.69532-5.1484-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4688c2.8438-2.9258 7.5234-3 10.453-0.15625s3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m63.07 112.91c-1.8516 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1914-9.4687c2.8438-2.9258 7.5234-2.9961 10.453-0.15234 2.9258 2.8438 2.9961 7.5234 0.15234 10.449l-9.1914 9.4688c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m12.309 278.82c-1.8516 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l9.1875-9.4688c2.8438-2.9297 7.5234-3 10.453-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-9.1914 9.4688c-1.4453 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#2dc471"/><path d="m216.29 278.49-23.996 12.996c-6.2226 3.3711-13.496-2.0742-12.309-9.2148l4.582-27.523c0.47266-2.8359-0.4375-5.7266-2.4375-7.7344l-19.414-19.496c-5.0352-5.0547-2.2578-13.863 4.7031-14.906l26.824-4.0156c2.7656-0.41407 5.1524-2.1992 6.3867-4.7812l12-25.043c3.1133-6.4922 12.102-6.4922 15.215 0l11.996 25.043c1.2383 2.582 3.625 4.3672 6.3867 4.7812l26.828 4.0156c6.957 1.043 9.7344 9.8516 4.6992 14.906l-19.41 19.496c-2 2.0078-2.9141 4.8984-2.4414 7.7344l4.582 27.523c1.1914 7.1406-6.082 12.586-12.305 9.2148l-23.996-12.996c-2.4727-1.3398-5.4258-1.3398-7.8945 0z" fill="#ffd02f"/><path d="m220.24 512c-4.082 0-7.3906-3.3086-7.3906-7.3906v-115.59c0-4.082 3.3086-7.3945 7.3906-7.3945s7.3906 3.3125 7.3906 7.3945v115.59c0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#5c73bc"/><path d="m220.3 357.42h-0.11328c-4.082 0-7.3945-3.3125-7.3945-7.3945s3.3086-7.3906 7.3945-7.3906h0.11328c4.082 0 7.3906 3.3086 7.3906 7.3906s-3.3086 7.3945-7.3906 7.3945z" fill="#5c73bc"/><path d="m220.3 332h-0.14838c-4.082-0.0156-7.375-3.3398-7.3594-7.4219 0.0195-4.0742 3.3242-7.3594 7.3906-7.3594h0.14848c4.082 0.0156 7.375 3.3398 7.3594 7.4219-0.0156 4.0703-3.3242 7.3594-7.3906 7.3594z" fill="#fa0"/><path d="m87.234 230.89c-1.9297 0-3.8555-0.75-5.3047-2.2422l-79.34-81.738c-2.8438-2.9297-2.7773-7.6094 0.15234-10.449 2.9297-2.8438 7.6094-2.7734 10.453 0.15235l79.344 81.738c2.8438 2.9258 2.7734 7.6094-0.15625 10.449-1.4375 1.3945-3.293 2.0898-5.1484 2.0898z" fill="#ff4e61"/><path d="m113.95 258.5c-1.8633 0-3.7266-0.69922-5.1641-2.1055-2.9219-2.8516-2.9766-7.5312-0.125-10.453l0.082-0.082c2.8516-2.918 7.5312-2.9766 10.453-0.12109 2.9219 2.8516 2.9766 7.5312 0.12109 10.453l-0.0781 0.082c-1.4492 1.4805-3.3672 2.2266-5.2891 2.2266z" fill="#fa0"/><path d="m131.4 276.48c-1.8555 0-3.7109-0.69531-5.1484-2.0898-2.9258-2.8438-2.9961-7.5234-0.15235-10.449l0.0781-0.0859c2.8476-2.9297 7.5273-2.9961 10.453-0.15235 2.9297 2.8438 3 7.5234 0.15625 10.453l-0.082 0.082c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#5c73bc"/><path d="m353.24 227.99c-1.8555 0-3.7109-0.69141-5.1445-2.0859-2.9297-2.8438-3-7.5234-0.15625-10.453l79.34-81.734c2.8438-2.9297 7.5234-3 10.453-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-79.344 81.734c-1.4492 1.4922-3.375 2.2422-5.3047 2.2422z" fill="#fa0"/><path d="m326.52 255.6c-1.9141 0-3.8242-0.73828-5.2695-2.2109l-0.082-0.082c-2.8633-2.9141-2.8203-7.5938 0.0899-10.453 2.9141-2.8633 7.5938-2.8203 10.453 0.0898l0.082 0.082c2.8594 2.9141 2.8203 7.5938-0.0937 10.453-1.4375 1.4141-3.3086 2.1211-5.1797 2.1211z" fill="#ff4e61"/><path d="m309.07 273.58c-1.9297 0-3.8555-0.75-5.3047-2.2422l-0.082-0.082c-2.8398-2.9297-2.7734-7.6094 0.15625-10.453s7.6094-2.7734 10.453 0.15234l0.082 0.082c2.8398 2.9297 2.7734 7.6094-0.15625 10.453-1.4375 1.3945-3.293 2.0898-5.1484 2.0898z" fill="#fa0"/><path d="m300.65 116.69c-1.2422 0-2.5-0.3125-3.6523-0.97266-3.5469-2.0234-4.7812-6.5391-2.7578-10.082l56.863-99.652c2.0234-3.543 6.5352-4.7773 10.082-2.7539 3.5469 2.0234 4.7812 6.5391 2.7578 10.082l-56.863 99.652c-1.3633 2.3867-3.8594 3.7266-6.4297 3.7266z" fill="#62d38f"/><path d="m281.52 150.33c-1.293 0-2.5977-0.33593-3.7891-1.0469l-0.0976-0.0586c-3.5-2.0938-4.6445-6.6328-2.5469-10.137 2.0938-3.5078 6.6328-4.6445 10.137-2.5508l0.0977 0.0586c3.5039 2.0938 4.6445 6.6328 2.5508 10.137-1.3867 2.3164-3.8359 3.5976-6.3516 3.5976z" fill="#fa0"/><path d="m269.02 172.25c-1.3008 0-2.6172-0.34375-3.8086-1.0625l-0.0977-0.0586c-3.4961-2.1094-4.6211-6.6523-2.5156-10.148 2.1094-3.4961 6.6523-4.6172 10.148-2.5117l0.0976 0.0586c3.4961 2.1094 4.6211 6.6523 2.5117 10.148-1.3867 2.3008-3.832 3.5742-6.3359 3.5742z" fill="#2dc471"/><path d="m139.96 116.69c-2.5703 0-5.0664-1.3398-6.4297-3.7305l-56.863-99.648c-2.0234-3.5469-0.78906-8.0586 2.7539-10.082 3.5469-2.0234 8.0625-0.79297 10.086 2.7539l56.863 99.648c2.0234 3.5469 0.78906 8.0625-2.7539 10.086-1.1562 0.66016-2.4141 0.97266-3.6562 0.97266z" fill="#5c73bc"/><path d="m159.09 150.33c-2.5078 0-4.957-1.2773-6.3438-3.582-2.1016-3.5-0.96875-8.043 2.5273-10.145l0.10157-0.0586c3.5-2.1016 8.0391-0.97266 10.141 2.5273 2.1055 3.5 0.97266 8.0391-2.5273 10.145l-0.0977 0.0586c-1.1914 0.71484-2.5039 1.0547-3.8008 1.0547z" fill="#ff4e61"/><path d="m171.6 172.25c-2.5 0-4.9375-1.2656-6.3281-3.5625-2.1172-3.4922-1-8.0352 2.4883-10.152l0.0977-0.0586c3.4961-2.1133 8.0391-1 10.156 2.4922 2.1133 3.4922 1 8.0352-2.4922 10.152l-0.0977 0.0586c-1.1992 0.72656-2.5195 1.0703-3.8242 1.0703z" fill="#fa0"/><path d="m402.14 357.28-15.523 11.602c-4.0234 3.0117-9.6523-0.043-9.5234-5.1641l0.5039-19.75c0.0508-2.0352-0.87109-3.9648-2.4688-5.1641l-15.508-11.621c-4.0234-3.0156-2.9453-9.4726 1.8242-10.93l18.391-5.6094c1.8906-0.57812 3.3906-2.082 4-4.0156l5.9375-18.785c1.5391-4.875 7.8359-5.8125 10.652-1.5898l10.863 16.285c1.1211 1.6758 2.9688 2.6797 4.9414 2.6797l19.18 0.0117c4.9766 4e-3 7.7891 5.8828 4.7578 9.9492l-11.676 15.672c-1.2031 1.6172-1.5586 3.7383-0.94922 5.6719l5.918 18.797c1.5312 4.875-3.0273 9.4453-7.7148 7.7344l-18.078-6.5977c-1.8594-0.67969-3.9258-0.37109-5.5273 0.82422z" fill="#ffd02f"/><path d="m261.51 512c-4.082 0-7.3906-3.3086-7.3906-7.3906 0-57.23 22.832-95.922 41.984-118.3 20.828-24.332 41.613-35.023 42.488-35.469 3.6406-1.8477 8.0898-0.39063 9.9336 3.2539 1.8438 3.6367 0.39453 8.0781-3.2422 9.9297-0.3125 0.16016-19.5 10.164-38.367 32.395-25.227 29.719-38.016 66.121-38.016 108.2 0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#ff4e61"/><path d="m102.86 397.35 11.766 15.605c3.0547 4.0469 9.2852 2.7305 10.547-2.2266l4.8633-19.113c0.5-1.9648 1.9102-3.5547 3.7695-4.2461l18.039-6.707c4.6797-1.7383 5.3906-8.25 1.207-11.016l-16.141-10.672c-1.6602-1.1016-2.6914-2.9726-2.7578-5.0039l-0.61719-19.75c-0.15625-5.1211-5.9492-7.832-9.7969-4.5859l-14.84 12.516c-1.5312 1.2891-3.5781 1.7227-5.4726 1.1562l-18.422-5.5c-4.7773-1.4258-9.0703 3.4102-7.2617 8.1836l6.9688 18.41c0.71875 1.8945 0.48438 4.0352-0.625 5.7188l-10.77 16.348c-2.793 4.2422 0.34375 9.9375 5.3125 9.6445l19.145-1.1406c1.9727-0.11719 3.875 0.77343 5.0859 2.3789z" fill="#ffd02f"/><path d="m179.02 512c-4.082 0-7.3906-3.3086-7.3906-7.3906 0-30.059-6.6797-57.559-19.852-81.734-1.9531-3.5859-0.62891-8.0742 2.957-10.027 3.5859-1.9531 8.0742-0.62891 10.027 2.9531 14.363 26.375 21.648 56.254 21.648 88.809 0 4.082-3.3086 7.3906-7.3906 7.3906z" fill="#fa0"/><path d="m268.93 55.898c0-11.285-8.8828-20.434-19.836-20.434-10.957 0-19.836 9.1484-19.836 20.434 0 11.285 8.8789 20.438 19.836 20.438 10.953 0 19.836-9.1523 19.836-20.438z" fill="#ffd02f"/><path d="m373.08 446.81c0-11.285-8.8789-20.434-19.832-20.434-10.957 0-19.836 9.1484-19.836 20.434s8.8789 20.434 19.836 20.434c10.953 0 19.832-9.1484 19.832-20.434z" fill="#5c73bc"/><path d="m44.129 450.86c0-9.0508-7.1211-16.387-15.91-16.387-8.7852 0-15.906 7.3359-15.906 16.387 0 9.0547 7.1211 16.391 15.906 16.391 8.7891 0 15.91-7.3359 15.91-16.391z" fill="#62d38f"/><path d="m88.172 288.35c0-9.0508-7.1211-16.387-15.91-16.387-8.7852 0-15.906 7.3359-15.906 16.387s7.1211 16.391 15.906 16.391c8.7891 0 15.91-7.3398 15.91-16.391z" fill="#5c73bc"/><g fill="#ff4e61"><path d="m210.84 16.391c0-9.0547-7.1211-16.391-15.906-16.391-8.7891 0-15.91 7.3359-15.91 16.391 0 9.0508 7.1211 16.387 15.91 16.387 8.7852 0 15.906-7.3359 15.906-16.387z"/><path d="m365.23 152.88c0-9.0508-7.125-16.391-15.91-16.391-8.7852 0-15.91 7.3398-15.91 16.391s7.125 16.387 15.91 16.387c8.7852 0 15.91-7.3359 15.91-16.387z"/><path d="m139.96 32.746c-1.8555 0-3.7109-0.69141-5.1484-2.0898-2.9297-2.8438-3-7.5195-0.15625-10.449l9.1914-9.4688c2.8438-2.9297 7.5234-3 10.449-0.15625 2.9297 2.8438 3 7.5234 0.15625 10.453l-9.1875 9.4688c-1.4492 1.4922-3.3789 2.2422-5.3047 2.2422z"/></g></g></svg>
diff --git a/images/blank.svg b/resources/ui/images/move-blank.svg
similarity index 100%
rename from images/blank.svg
rename to resources/ui/images/move-blank.svg
diff --git a/images/left-foot.svg b/resources/ui/images/move-left-foot.svg
similarity index 100%
rename from images/left-foot.svg
rename to resources/ui/images/move-left-foot.svg
diff --git a/images/left-hand.svg b/resources/ui/images/move-left-hand.svg
similarity index 100%
rename from images/left-hand.svg
rename to resources/ui/images/move-left-hand.svg
diff --git a/images/right-foot.svg b/resources/ui/images/move-right-foot.svg
similarity index 100%
rename from images/right-foot.svg
rename to resources/ui/images/move-right-foot.svg
diff --git a/images/right-hand.svg b/resources/ui/images/move-right-hand.svg
similarity index 100%
rename from images/right-hand.svg
rename to resources/ui/images/move-right-hand.svg
diff --git a/resources/ui/images/placeholder.svg b/resources/ui/images/placeholder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..23ace81fbb82a8409cc0710c0f7bddd6381f7256
--- /dev/null
+++ b/resources/ui/images/placeholder.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"/>