diff --git a/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt b/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
index 9f4c7373eecb281fffcd52c575482a5fd2564ed8..e2b6fce9c941afe923d3ea2a14202063f42d491b 100644
--- a/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
+++ b/src/main/kotlin/androidx/compose/material/OutlinedTextFieldExt.kt
@@ -108,7 +108,8 @@ fun OutlinedTextField(
             colors = colors
         )
         val message = if (showError && errorMessage != null) errorMessage else helperMessage ?: ""
-        val color = if (showError) MaterialTheme.colors.error else LocalTextStyle.current.color.copy(alpha = ContentAlpha.medium)
+        val color =
+            if (showError) MaterialTheme.colors.error else LocalTextStyle.current.color.copy(alpha = ContentAlpha.medium)
         Text(
             text = message,
             style = TextStyle(
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
index 3744c8608d45b07ebcc51d4571a22842c753ff7f..00bfb43fa69e9ceae03c74805ff25e2d74fc7c21 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
@@ -33,14 +33,15 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ER
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS
 import org.briarproject.briar.desktop.theme.Red500
+import org.briarproject.briar.desktop.ui.Constants.STARTUP_FIELDS_WIDTH
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
 
 fun main() = preview {
-    var error: ErrorViewHolder.Error by remember { mutableStateOf(RegistrationViewHolder.RegistrationError) }
+    var error: ErrorSubViewModel.Error by remember { mutableStateOf(RegistrationSubViewModel.RegistrationError) }
 
     Row(horizontalArrangement = spacedBy(8.dp)) {
-        Button(onClick = { error = RegistrationViewHolder.RegistrationError }) {
+        Button(onClick = { error = RegistrationSubViewModel.RegistrationError }) {
             Text("Registration")
         }
         for (e in StartResult.values().filterNot { it in listOf(SUCCESS, ALREADY_RUNNING) }) {
@@ -54,12 +55,12 @@ fun main() = preview {
 }
 
 @Composable
-fun ErrorScreen(viewHolder: ErrorViewHolder) =
+fun ErrorScreen(viewHolder: ErrorSubViewModel) =
     ErrorScreen(viewHolder.error, viewHolder.onBackButton)
 
 @Composable
 fun ErrorScreen(
-    error: ErrorViewHolder.Error,
+    error: ErrorSubViewModel.Error,
     onBackButton: () -> Unit,
 ) = Surface {
     IconButton(onClick = onBackButton) {
@@ -81,7 +82,7 @@ fun ErrorScreen(
         Text(i18n("sorry"), style = MaterialTheme.typography.h5)
 
         val text = when (error) {
-            is RegistrationViewHolder.RegistrationError -> i18n("startup.failed.registration")
+            is RegistrationSubViewModel.RegistrationError -> i18n("startup.failed.registration")
             is StartupViewModel.StartingError -> {
                 when (error.error) {
                     CLOCK_ERROR -> i18n("startup.failed.clock_error")
@@ -96,7 +97,7 @@ fun ErrorScreen(
         Text(
             text = text,
             style = MaterialTheme.typography.body1,
-            modifier = Modifier.widthIn(max = 400.dp)
+            modifier = Modifier.widthIn(max = STARTUP_FIELDS_WIDTH)
         )
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorViewHolder.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
similarity index 59%
rename from src/main/kotlin/org/briarproject/briar/desktop/login/ErrorViewHolder.kt
rename to src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
index 8d540501ab66a6ce11625e1c7e07ca0f508ef565..498efb6e38c1556fee397b166fb901a9e2502288 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorViewHolder.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt
@@ -1,11 +1,9 @@
 package org.briarproject.briar.desktop.login
 
-import org.briarproject.bramble.api.lifecycle.LifecycleManager
-
-class ErrorViewHolder(
+class ErrorSubViewModel(
     private val viewModel: StartupViewModel,
     val error: Error,
     val onBackButton: () -> Unit,
-) : StartupViewModel.ViewHolder {
+) : StartupViewModel.SubViewModel {
     sealed interface Error
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/FormScaffold.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/FormScaffold.kt
new file mode 100644
index 0000000000000000000000000000000000000000..95e8e4e664c7cae3db4ea647d0fc50b74fb6d3ae
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/FormScaffold.kt
@@ -0,0 +1,39 @@
+package org.briarproject.briar.desktop.login
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.requiredWidthIn
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import org.briarproject.briar.desktop.ui.Constants.STARTUP_FIELDS_WIDTH
+import java.util.Locale
+
+@Composable
+fun FormScaffold(
+    explanationText: String?,
+    buttonText: String,
+    buttonClick: () -> Unit,
+    buttonEnabled: Boolean,
+    content: @Composable () -> Unit,
+) = Column(
+    modifier = Modifier.requiredWidthIn(max = STARTUP_FIELDS_WIDTH),
+    horizontalAlignment = Alignment.CenterHorizontally
+) {
+    if (explanationText != null) {
+        Spacer(Modifier.weight(0.5f))
+        Text(explanationText, style = MaterialTheme.typography.body2, modifier = Modifier.requiredWidth(STARTUP_FIELDS_WIDTH))
+        Spacer(Modifier.weight(0.5f))
+    } else Spacer(Modifier.weight(1.0f))
+    content()
+    Spacer(Modifier.weight(1.0f))
+    Button(onClick = buttonClick, enabled = buttonEnabled, modifier = Modifier.fillMaxWidth()) {
+        Text(buttonText.uppercase(Locale.getDefault()), color = Color.Black)
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/LoadingView.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/LoadingView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a8cf0dc22faa9b92858a7ceb76a02e7a76e455e3
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/LoadingView.kt
@@ -0,0 +1,23 @@
+package org.briarproject.briar.desktop.login
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun LoadingView(text: String) =
+    Column(
+        horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.Center,
+        modifier = Modifier.fillMaxHeight()
+    ) {
+        CircularProgressIndicator(Modifier.padding(16.dp))
+        Text(text)
+    }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
index 008ffbe444b68e1cd895ee48ee2e32b778706388..5a440064029042945a4747dc2392ea54a640ca49 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginScreen.kt
@@ -15,23 +15,21 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.dp
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.COMPACTING
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.MIGRATING
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.SIGNED_OUT
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.STARTED
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.STARTING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.COMPACTING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.MIGRATING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.SIGNED_OUT
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.STARTED
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.STARTING
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
 fun LoginScreen(
-    viewHolder: LoginViewHolder,
+    viewHolder: LoginSubViewModel,
 ) = StartupScreenScaffold(i18n("startup.title.login")) {
     when (viewHolder.state.value) {
         SIGNED_OUT ->
@@ -56,8 +54,10 @@ fun LoginScreen(
     }
 
     if (viewHolder.decryptionFailedError.value) {
-        // todo: is this actually needed on Desktop?
-        // todo: use ErrorScreen to display this instead?
+        // todo: this should never be triggered,
+        //  since we don't use any keyStrengthener for now
+        //  when adding this, we could think about showing
+        //  a proper error screen instead
         AlertDialog(
             onDismissRequest = viewHolder::closeDecryptionFailedDialog,
             title = { Text(i18n("startup.error.decryption.title")) },
@@ -90,7 +90,6 @@ fun LoginForm(
         singleLine = true,
         isError = passwordInvalidError,
         errorMessage = i18n("startup.error.password_wrong"),
-        textStyle = TextStyle(color = Color.White),
         visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginViewHolder.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginSubViewModel.kt
similarity index 87%
rename from src/main/kotlin/org/briarproject/briar/desktop/login/LoginViewHolder.kt
rename to src/main/kotlin/org/briarproject/briar/desktop/login/LoginSubViewModel.kt
index 8ad23db1f64aa5b65bad1bc2da5b8919d13ffe72..74b96432d73a5f44bab54d8d151f27d847a618d7 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/LoginViewHolder.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/LoginSubViewModel.kt
@@ -7,20 +7,20 @@ import org.briarproject.bramble.api.crypto.DecryptionException
 import org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD
 import org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.COMPACTING
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.MIGRATING
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.SIGNED_OUT
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.STARTED
-import org.briarproject.briar.desktop.login.LoginViewHolder.State.STARTING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.COMPACTING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.MIGRATING
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.SIGNED_OUT
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.STARTED
+import org.briarproject.briar.desktop.login.LoginSubViewModel.State.STARTING
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.viewmodel.asState
 
-class LoginViewHolder(
+class LoginSubViewModel(
     private val viewModel: StartupViewModel,
     private val accountManager: AccountManager,
     private val briarExecutors: BriarExecutors,
     initialLifecycleState: LifecycleState,
-) : StartupViewModel.ViewHolder {
+) : StartupViewModel.SubViewModel {
 
     enum class State {
         SIGNED_OUT, STARTING, MIGRATING, COMPACTING, STARTED
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
index b981d77398344374e33b77033433d8ddd5d0fb1d..2e534ec07a8296de91d59234b204393663c1f09d 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
@@ -16,22 +16,20 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.dp
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.CREATED
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.CREATING
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.INSERT_NICKNAME
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.INSERT_PASSWORD
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.CREATED
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.CREATING
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.INSERT_NICKNAME
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.INSERT_PASSWORD
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 
 @Composable
 fun RegistrationScreen(
-    viewHolder: RegistrationViewHolder,
+    viewHolder: RegistrationSubViewModel,
 ) = StartupScreenScaffold(
     title = i18n("startup.title.registration"),
     showBackButton = viewHolder.showBackButton.value,
@@ -91,7 +89,6 @@ fun NicknameForm(
         singleLine = true,
         isError = nicknameTooLongError,
         errorMessage = i18n("startup.error.name_too_long"),
-        textStyle = TextStyle(color = Color.White),
         keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
         onEnter = onEnter
@@ -131,7 +128,6 @@ fun PasswordForm(
         isError = passwordTooWeakError,
         showErrorWhen = AFTER_FOCUS_LOST_ONCE,
         errorMessage = i18n("startup.error.password_too_weak"),
-        textStyle = TextStyle(color = Color.White),
         visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
@@ -145,7 +141,6 @@ fun PasswordForm(
         isError = passwordsDontMatchError,
         showErrorWhen = AFTER_FIRST_FOCUSSED,
         errorMessage = i18n("startup.error.passwords_not_match"),
-        textStyle = TextStyle(color = Color.White),
         visualTransformation = PasswordVisualTransformation(),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
         modifier = Modifier.fillMaxWidth(),
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationViewHolder.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationSubViewModel.kt
similarity index 87%
rename from src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationViewHolder.kt
rename to src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationSubViewModel.kt
index ca2a309376f907e11e06c0c6a754a05dc2be4cd4..990c2ca67fad549e8030c79f112b03558705e014 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationViewHolder.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationSubViewModel.kt
@@ -7,24 +7,24 @@ import org.briarproject.bramble.api.account.AccountManager
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK
 import org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.CREATING
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.INSERT_NICKNAME
-import org.briarproject.briar.desktop.login.RegistrationViewHolder.State.INSERT_PASSWORD
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.CREATING
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.INSERT_NICKNAME
+import org.briarproject.briar.desktop.login.RegistrationSubViewModel.State.INSERT_PASSWORD
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.viewmodel.asState
 
-class RegistrationViewHolder(
+class RegistrationSubViewModel(
     private val viewModel: StartupViewModel,
     private val accountManager: AccountManager,
     private val briarExecutors: BriarExecutors,
     private val passwordStrengthEstimator: PasswordStrengthEstimator,
-) : StartupViewModel.ViewHolder {
+) : StartupViewModel.SubViewModel {
 
     companion object {
         private val LOG = KotlinLogging.logger {}
     }
 
-    object RegistrationError : ErrorViewHolder.Error
+    object RegistrationError : ErrorSubViewModel.Error
 
     enum class State {
         INSERT_NICKNAME, INSERT_PASSWORD, CREATING, CREATED
@@ -59,7 +59,7 @@ class RegistrationViewHolder(
     val buttonEnabled = derivedStateOf {
         when (_state.value) {
             INSERT_NICKNAME ->
-                nickname.value.isNotEmpty() && !nicknameTooLongError.value
+                nickname.value.isNotBlank() && !nicknameTooLongError.value
             INSERT_PASSWORD ->
                 password.value.isNotEmpty() && passwordConfirmation.value.isNotEmpty() &&
                     !passwordTooWeakError.value && !passwordMatchError.value
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
index 8dead60266c91b2dd547667427ad86df602523a5..24a9d8a05f34d5379d81d54025fa5425c8f1566e 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
@@ -3,16 +3,9 @@ package org.briarproject.briar.desktop.login
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.requiredWidth
-import androidx.compose.foundation.layout.requiredWidthIn
 import androidx.compose.foundation.layout.width
-import androidx.compose.material.Button
-import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
@@ -24,21 +17,19 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment.Companion.CenterHorizontally
 import androidx.compose.ui.Alignment.Companion.CenterVertically
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import org.briarproject.briar.desktop.ui.BriarLogo
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.viewmodel.viewModel
-import java.util.Locale
 
 @Composable
 fun StartupScreen(
     viewModel: StartupViewModel = viewModel(),
 ) {
-    when (val holder = viewModel.mode.value) {
-        is LoginViewHolder -> LoginScreen(holder)
-        is RegistrationViewHolder -> RegistrationScreen(holder)
-        is ErrorViewHolder -> ErrorScreen(holder)
+    when (val holder = viewModel.currentSubViewModel.value) {
+        is LoginSubViewModel -> LoginScreen(holder)
+        is RegistrationSubViewModel -> RegistrationScreen(holder)
+        is ErrorSubViewModel -> ErrorScreen(holder)
     }
 }
 
@@ -73,37 +64,3 @@ fun HeaderLine(title: String) =
         BriarLogo(Modifier.width(100.dp))
         Text(title, style = MaterialTheme.typography.h4)
     }
-
-@Composable
-fun LoadingView(text: String) =
-    Column(
-        horizontalAlignment = CenterHorizontally,
-        verticalArrangement = Arrangement.Center,
-        modifier = Modifier.fillMaxHeight()
-    ) {
-        CircularProgressIndicator(Modifier.padding(16.dp))
-        Text(text)
-    }
-
-@Composable
-fun FormScaffold(
-    explanationText: String?,
-    buttonText: String,
-    buttonClick: () -> Unit,
-    buttonEnabled: Boolean,
-    content: @Composable () -> Unit,
-) = Column(
-    modifier = Modifier.requiredWidthIn(max = 400.dp),
-    horizontalAlignment = CenterHorizontally
-) {
-    if (explanationText != null) {
-        Spacer(Modifier.weight(0.5f))
-        Text(explanationText, style = MaterialTheme.typography.body2, modifier = Modifier.requiredWidth(400.dp))
-        Spacer(Modifier.weight(0.5f))
-    } else Spacer(Modifier.weight(1.0f))
-    content()
-    Spacer(Modifier.weight(1.0f))
-    Button(onClick = buttonClick, enabled = buttonEnabled, modifier = Modifier.fillMaxWidth()) {
-        Text(buttonText.uppercase(Locale.getDefault()), color = Color.Black)
-    }
-}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
index bf426137677d7e00f609a32bc4e4cf9c28155cc3..ef58597c77dccab334f479cfeed585918bd18a33 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt
@@ -32,45 +32,46 @@ constructor(
         private val LOG = KotlinLogging.logger {}
     }
 
-    sealed interface ViewHolder {
+    sealed interface SubViewModel {
         fun lifecycleStateChanged(s: LifecycleManager.LifecycleState) {}
     }
 
-    class StartingError(val error: LifecycleManager.StartResult):
-        ErrorViewHolder.Error
+    class StartingError(val error: LifecycleManager.StartResult) :
+        ErrorSubViewModel.Error
 
-    private val _mode = mutableStateOf(decideMode())
-    val mode = _mode.asState()
+    private val _currentSubViewModel = mutableStateOf(decideSubViewModel())
+    val currentSubViewModel = _currentSubViewModel.asState()
 
-    private fun decideMode() =
+    private fun decideSubViewModel() =
         if (accountManager.accountExists()) makeLogin()
         else makeRegistration()
 
-    private fun makeLogin() = LoginViewHolder(
+    private fun makeLogin() = LoginSubViewModel(
         this, accountManager, briarExecutors, lifecycleManager.lifecycleState
     )
 
     fun showLogin() {
-        _mode.value = makeLogin()
+        _currentSubViewModel.value = makeLogin()
     }
 
-    private fun makeRegistration() = RegistrationViewHolder(
+    private fun makeRegistration() = RegistrationSubViewModel(
         this, accountManager, briarExecutors, passwordStrengthEstimator
     )
 
     fun showRegistration() {
-        _mode.value = makeRegistration()
+        _currentSubViewModel.value = makeRegistration()
     }
 
-    private fun makeError(error: ErrorViewHolder.Error) = ErrorViewHolder(
-        this, error, onBackButton = { _mode.value = decideMode() }
+    private fun makeError(error: ErrorSubViewModel.Error) = ErrorSubViewModel(
+        this, error, onBackButton = { _currentSubViewModel.value = decideSubViewModel() }
     )
-    fun showError(error: ErrorViewHolder.Error) {
-        _mode.value = makeError(error)
+
+    fun showError(error: ErrorSubViewModel.Error) {
+        _currentSubViewModel.value = makeError(error)
     }
 
     override fun eventOccurred(e: Event) {
-        if (e is LifecycleEvent) _mode.value.lifecycleStateChanged(e.lifecycleState)
+        if (e is LifecycleEvent) _currentSubViewModel.value.lifecycleStateChanged(e.lifecycleState)
     }
 
     @IoExecutor
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt
index 0207c290cff1312566acbcaae88847fb96b795d2..9a834aad465a15630696556b6d26a1b18a554769 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt
@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRON
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK
+import org.briarproject.briar.desktop.utils.PreviewUtils
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
 
 val RED = Color(255, 0, 0)
@@ -21,7 +22,7 @@ val LIME = Color(180, 255, 0)
 val GREEN = Color(0, 255, 0)
 
 fun main() = preview(
-    "strength" to 0f
+    "strength" to PreviewUtils.FloatSlider(0f, 0f, 1f)
 ) {
     StrengthMeter(getFloatParameter("strength"))
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/settings/SettingsScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/settings/SettingsScreen.kt
index 500fcc851a188e8fb7ed3cd6ddca056b581ef3b8..d47c682fdb6e36bdd580472585140a8d97810054 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/settings/SettingsScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/settings/SettingsScreen.kt
@@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import org.briarproject.briar.desktop.viewmodel.viewModel
 
 @Composable
 fun SettingsScreen(viewModel: SettingsViewModel) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
index abd377d359a150bd7fe05434ddb71a84d44ecefd..34edbf5e2619440f05f6c925c41f0fa188fd7857 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt
@@ -5,5 +5,6 @@ import androidx.compose.ui.unit.dp
 object Constants {
     val HEADER_SIZE = 56.dp
     val COLUMN_WIDTH = 275.dp
+    val STARTUP_FIELDS_WIDTH = 400.dp
     val PARAGRAPH_WIDTH = 540.dp
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
index 032a405e1adba1dc4d28844a5e66655878bb8dbf..5c0e6ef2d54316a9ed0f58b0909914627139377e 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
@@ -8,9 +8,11 @@ import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Slider
 import androidx.compose.material.Surface
 import androidx.compose.material.Text
 import androidx.compose.material.icons.Icons
@@ -119,6 +121,11 @@ object PreviewUtils {
         BasicTextField(value.value.toString(), { value.value = it.toFloat() })
     }
 
+    @Composable
+    private fun PreviewScope.addFloatSliderParameter(name: String, initial: FloatSlider) = addParameter(name, initial.initial) { value ->
+        Slider(value.value, { value.value = it }, valueRange = initial.min..initial.max, modifier = Modifier.width(400.dp))
+    }
+
     /**
      * Open an interactive preview of the composable specified by [content].
      * All [parameters] passed to this function will be changeable on the fly.
@@ -142,6 +149,7 @@ object PreviewUtils {
                             is Int -> scope.addIntParameter(name, initial)
                             is Long -> scope.addLongParameter(name, initial)
                             is Float -> scope.addFloatParameter(name, initial)
+                            is FloatSlider -> scope.addFloatSliderParameter(name, initial)
                             else -> throw IllegalArgumentException("Type ${initial::class.simpleName} is not supported for previewing.")
                         }
                     }
@@ -157,4 +165,10 @@ object PreviewUtils {
             }
         }
     }
+
+    data class FloatSlider(
+        val initial: Float,
+        val min: Float,
+        val max: Float,
+    )
 }
diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties
index 4f6b716a71c464fe9584a51a5541e481003417e4..6c35408dc9007cb3cd9a13010e16808e3243b0e6 100644
--- a/src/main/resources/strings/BriarDesktop.properties
+++ b/src/main/resources/strings/BriarDesktop.properties
@@ -141,7 +141,7 @@ startup.password_forgotten.title=Lost Password
 startup.password_forgotten.text=Your Briar account is stored encrypted on your device, not in the cloud, so we can't reset your password. Would you like to delete your account and start again?\n\nCaution: Your identities, contacts and messages will be permanently lost.
 startup.failed.registration=Briar was unable to create your account.\n\nPlease upgrade to the latest version and try again.
 startup.failed.clock_error=Briar was unable to start because your device's clock is wrong.\n\nPlease set your device's clock to the right time and try again.
-startup.failed.db_error=Briar was unable to open the database containing your account, your contacts and your messages.\n\nPlease upgrade to the latest version of the app and try again, or set up a new account by choosing 'I have forgotten my password' at the password prompt.
+startup.failed.db_error=Briar was unable to open the database containing your account, your contacts and your messages.\n\nPlease check if Briar is already running on this device. Otherwise, upgrade to the latest version of the app and try again, or set up a new account by choosing 'I have forgotten my password' at the password prompt.
 startup.failed.data_too_old_error=Your account was created with an old version of this app and cannot be opened with this version.\n\nYou must either reinstall the old version or set up a new account by choosing 'I have forgotten my password' at the password prompt.
 startup.failed.data_too_new_error=Your account was created with a newer version of this app and cannot be opened with this version.\n\nPlease upgrade to the latest version and try again.
 startup.failed.service_error=Briar was unable to start a required component.\n\nPlease upgrade to the latest version of the app and try again.