diff --git a/app-launcher/common/src/commonMain/kotlin/io/ashdavies/playground/LauncherScreen.kt b/app-launcher/common/src/commonMain/kotlin/io/ashdavies/playground/LauncherScreen.kt
index 7fa0448fd..0ba20a7bd 100644
--- a/app-launcher/common/src/commonMain/kotlin/io/ashdavies/playground/LauncherScreen.kt
+++ b/app-launcher/common/src/commonMain/kotlin/io/ashdavies/playground/LauncherScreen.kt
@@ -23,6 +23,8 @@ import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBar
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -62,7 +64,7 @@ internal object LauncherScreen : Parcelable, Screen {
 internal fun LauncherScreen(
     state: LauncherScreen.State,
     modifier: Modifier = Modifier,
-    onFullyDrawn: () -> Unit,
+    reportFullyDrawn: () -> Unit,
 ) {
     val eventSink = state.eventSink
 
@@ -74,7 +76,7 @@ internal fun LauncherScreen(
             items(state.entries) { entry ->
                 LauncherItem(
                     item = entry,
-                    modifier = modifier.padding(24.dp),
+                    modifier = Modifier.padding(24.dp),
                     onClick = OnClick("launcher_goto", mapOf("screen" to entry.screen)) {
                         eventSink(NavEvent.GoTo(entry.screen))
                     },
@@ -83,8 +85,10 @@ internal fun LauncherScreen(
         }
     }
 
+    val latestReportFullyDrawn by rememberUpdatedState(reportFullyDrawn)
+
     LaunchedEffect(Unit) {
-        onFullyDrawn()
+        latestReportFullyDrawn()
     }
 }
 
diff --git a/app-launcher/desktop/src/commonMain/kotlin/io/ashdavies/playground/KeyNavigationDecoration.kt b/app-launcher/desktop/src/commonMain/kotlin/io/ashdavies/playground/KeyNavigationDecoration.kt
index c507f9a4d..3822d9f44 100644
--- a/app-launcher/desktop/src/commonMain/kotlin/io/ashdavies/playground/KeyNavigationDecoration.kt
+++ b/app-launcher/desktop/src/commonMain/kotlin/io/ashdavies/playground/KeyNavigationDecoration.kt
@@ -33,7 +33,7 @@ internal class KeyNavigationDecoration(
             args = args,
             backStackDepth = backStackDepth,
             modifier = modifier
-                .focusOnPlacement()
+                .focusOnPlacement(remember { FocusRequester() })
                 .onPreviewKeyUp(
                     predicate = predicate,
                     action = onBackInvoked,
@@ -43,13 +43,12 @@ internal class KeyNavigationDecoration(
     }
 }
 
-@Composable
-private fun Modifier.focusOnPlacement(): Modifier {
-    val focusRequester = remember { FocusRequester() }
-    return focusRequester(focusRequester).onPlaced { focusRequester.requestFocus() }
+private fun Modifier.focusOnPlacement(
+    focusRequester: FocusRequester,
+): Modifier = focusRequester(focusRequester).onPlaced {
+    focusRequester.requestFocus()
 }
 
-@Composable
 private fun Modifier.onPreviewKeyUp(
     predicate: (KeyEvent) -> Boolean = { true },
     action: () -> Unit,
diff --git a/compose-material/src/commonMain/kotlin/io/ashdavies/material/BottomSheet.kt b/compose-material/src/commonMain/kotlin/io/ashdavies/material/BottomSheet.kt
index 114736d2a..5cfde2073 100644
--- a/compose-material/src/commonMain/kotlin/io/ashdavies/material/BottomSheet.kt
+++ b/compose-material/src/commonMain/kotlin/io/ashdavies/material/BottomSheet.kt
@@ -27,12 +27,12 @@ import androidx.compose.ui.unit.dp
 @ExperimentalMaterial3Api
 public fun BottomSheetScaffold(
     sheetContent: @Composable ColumnScope.() -> Unit,
+    modifier: Modifier = Modifier,
     topBar: @Composable () -> Unit = { },
     bottomBar: @Composable () -> Unit = { },
     floatingActionButton: @Composable () -> Unit = { },
     showDragHandle: Boolean = true,
     isExpanded: Boolean = false,
-    modifier: Modifier = Modifier,
     content: @Composable (PaddingValues) -> Unit,
 ) {
     val bottomSheetState = rememberStandardBottomSheetState(SheetValue.Hidden, skipHiddenState = false)
diff --git a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/events/EventsScreen.android.kt b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/events/EventsScreen.android.kt
index 545a2979a..d7af0a5ab 100644
--- a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/events/EventsScreen.android.kt
+++ b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/events/EventsScreen.android.kt
@@ -4,14 +4,14 @@ import androidx.compose.runtime.Composable
 import androidx.paging.PagingData
 import io.ashdavies.paging.LazyPagingItems
 import io.ashdavies.paging.collectAsLazyPagingItems
-import io.ashdavies.party.tooling.DayNightPreview
+import io.ashdavies.party.tooling.PreviewDayNight
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
 
 private val DroidconEvents = listOf(AndroidMakers, DroidconBerlin, DroidconLondon)
 
 @Composable
-@DayNightPreview
+@PreviewDayNight
 internal fun EventsScreenPreview(data: List<Event> = DroidconEvents) {
     EventsScreen(EventsScreen.State(lazyPagingItems(flowOf(PagingData.from(data)))))
 }
diff --git a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/gallery/GalleryScreen.preview.kt b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/gallery/GalleryScreen.preview.kt
index 98fdfca05..2c8310f6d 100644
--- a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/gallery/GalleryScreen.preview.kt
+++ b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/gallery/GalleryScreen.preview.kt
@@ -7,11 +7,11 @@ import androidx.compose.material3.Surface
 import androidx.compose.material3.darkColorScheme
 import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
-import io.ashdavies.party.tooling.DayNightPreview
+import io.ashdavies.party.tooling.PreviewDayNight
 import kotlinx.collections.immutable.persistentListOf
 
 @Composable
-@DayNightPreview
+@PreviewDayNight
 @OptIn(ExperimentalFoundationApi::class)
 internal fun GalleryGridPreview() {
     GalleryPreviewTheme {
@@ -23,16 +23,16 @@ internal fun GalleryGridPreview() {
                     galleryScreenStateItem(state = SyncState.SYNCING),
                     galleryScreenStateItem(state = SyncState.SYNCED),
                 ),
-                isSelecting = true,
-                onSelect = { },
                 onExpand = { },
+                onSelect = { },
+                isSelecting = true,
             )
         }
     }
 }
 
 @Composable
-@DayNightPreview
+@PreviewDayNight
 internal fun GalleryBottomSheetPreview() {
     GalleryPreviewTheme {
         GallerySheetContent(eventSink = { })
diff --git a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/home/HomeScreenPreview.kt b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/home/HomeScreenPreview.kt
index 20f51f776..ead080840 100644
--- a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/home/HomeScreenPreview.kt
+++ b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/home/HomeScreenPreview.kt
@@ -8,10 +8,10 @@ import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
 import io.ashdavies.identity.IdentityState
 import io.ashdavies.party.profile.ProfileActionButton
-import io.ashdavies.party.tooling.DayNightPreview
+import io.ashdavies.party.tooling.PreviewDayNight
 
 @Composable
-@DayNightPreview
+@PreviewDayNight
 @OptIn(ExperimentalMaterial3Api::class)
 internal fun HomeTopAppBarPreview() {
     MaterialPreviewTheme {
@@ -27,10 +27,10 @@ internal fun HomeTopAppBarPreview() {
 }
 
 @Composable
-@DayNightPreview
+@PreviewDayNight
 internal fun HomeBottomSheetPreview() {
     MaterialPreviewTheme {
-        HomeBottomBar(HomeScreen)
+        HomeBottomBar()
     }
 }
 
diff --git a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/DayNightPreview.kt b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/PreviewDayNight.kt
similarity index 83%
rename from conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/DayNightPreview.kt
rename to conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/PreviewDayNight.kt
index c0b4d4785..9c1d8713d 100644
--- a/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/DayNightPreview.kt
+++ b/conferences-app/src/androidDebug/kotlin/io/ashdavies/party/tooling/PreviewDayNight.kt
@@ -5,4 +5,4 @@ import androidx.compose.ui.tooling.preview.Preview
 
 @Preview(name = "Day")
 @Preview(name = "Night", uiMode = Configuration.UI_MODE_NIGHT_YES)
-internal annotation class DayNightPreview
+internal annotation class PreviewDayNight
diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsScreen.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsScreen.kt
index 33b9d61cc..32917a73f 100644
--- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsScreen.kt
+++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsScreen.kt
@@ -92,13 +92,13 @@ internal fun EventsScreen(
 
             items(itemCount) { index ->
                 EventSection(
-                    event = state.pagingItems.getOrNull(index),
-                    modifier = Modifier.animateItemPlacement(),
                     emphasis = when (index) {
                         0 -> TextEmphasis.Significant
                         1 -> TextEmphasis.Moderate
                         else -> TextEmphasis.Standard
                     },
+                    modifier = Modifier.animateItemPlacement(),
+                    event = state.pagingItems.getOrNull(index),
                 )
             }
         }
@@ -124,9 +124,9 @@ private enum class TextEmphasis {
 @Composable
 @ExperimentalMaterialApi
 private fun EventSection(
-    event: Event?,
-    modifier: Modifier = Modifier,
     emphasis: TextEmphasis,
+    modifier: Modifier = Modifier,
+    event: Event? = null,
 ) {
     Card(
         modifier = modifier
diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryScreen.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryScreen.kt
index 7ee0f4790..951b34294 100644
--- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryScreen.kt
+++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryScreen.kt
@@ -139,7 +139,7 @@ internal fun GalleryScreen(
         when {
             state.itemList.isEmpty() -> {
                 Box(
-                    modifier = modifier.fillMaxSize(),
+                    modifier = Modifier.fillMaxSize(),
                     contentAlignment = Alignment.Center,
                     content = { Text("Empty") },
                 )
@@ -148,10 +148,10 @@ internal fun GalleryScreen(
             else -> {
                 GalleryGrid(
                     itemList = state.itemList.toImmutableList(),
-                    isSelecting = isSelecting,
-                    modifier = Modifier.padding(paddingValues),
                     onExpand = { eventSink(GalleryScreen.Event.Selection.Expand(it)) },
                     onSelect = { eventSink(GalleryScreen.Event.Selection.Toggle(it)) },
+                    modifier = Modifier.padding(paddingValues),
+                    isSelecting = isSelecting,
                 )
 
                 if (state.expandedItem != null) {
@@ -207,11 +207,11 @@ private fun GalleryExpandedItem(
 @ExperimentalFoundationApi
 internal fun GalleryGrid(
     itemList: ImmutableList<GalleryScreen.State.StandardItem>,
-    columnCount: Int = DEFAULT_COLUMN_COUNT,
-    isSelecting: Boolean = false,
-    modifier: Modifier = Modifier,
     onExpand: (Int) -> Unit,
     onSelect: (Int) -> Unit,
+    modifier: Modifier = Modifier,
+    columnCount: Int = DEFAULT_COLUMN_COUNT,
+    isSelecting: Boolean = false,
 ) {
     LazyVerticalGrid(
         columns = GridCells.Fixed(columnCount),
@@ -223,10 +223,10 @@ internal fun GalleryGrid(
         itemsIndexed(itemList) { index, item ->
             GalleryItem(
                 item = item,
-                isSelecting = isSelecting,
-                modifier = Modifier.animateItemPlacement(),
                 onSelect = { onSelect(index) },
                 onExpand = { onExpand(index) },
+                modifier = Modifier.animateItemPlacement(),
+                isSelecting = isSelecting,
             )
         }
     }
@@ -236,10 +236,10 @@ internal fun GalleryGrid(
 @ExperimentalFoundationApi
 internal fun GalleryItem(
     item: GalleryScreen.State.StandardItem,
-    isSelecting: Boolean = false,
-    modifier: Modifier = Modifier,
     onSelect: () -> Unit,
     onExpand: () -> Unit,
+    modifier: Modifier = Modifier,
+    isSelecting: Boolean = false,
 ) {
     val itemBorderRadius by animateDpAsState(if (item.isSelected) 12.dp else 8.dp)
     val itemPadding by animateDpAsState(if (item.isSelected) 12.dp else 0.dp)
@@ -306,10 +306,10 @@ internal fun GalleryItem(
 
 @Composable
 private fun SelectedIndicator(
+    modifier: Modifier = Modifier,
     surfaceColor: Color = MaterialTheme.colorScheme.surface,
     onPrimaryContainerColor: Color = MaterialTheme.colorScheme.onPrimaryContainer,
     iconPainter: Painter = rememberVectorPainter(Icons.Filled.CheckCircle),
-    modifier: Modifier = Modifier,
 ) {
     Canvas(modifier) {
         drawCircle(
@@ -327,9 +327,9 @@ private fun SelectedIndicator(
 
 @Composable
 private fun UnselectedIndicator(
+    modifier: Modifier = Modifier,
     highlightColor: Color = Color.White.copy(alpha = 0.5F),
     strokeWidth: Dp = 2.dp,
-    modifier: Modifier = Modifier,
 ) {
     Canvas(modifier) {
         drawCircle(
diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/SyncIndicator.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/SyncIndicator.kt
index 5baa1271f..6c6333e41 100644
--- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/SyncIndicator.kt
+++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/SyncIndicator.kt
@@ -15,7 +15,7 @@ import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
@@ -42,7 +42,7 @@ internal fun SyncIndicator(isSyncing: Boolean, modifier: Modifier = Modifier) {
     val tint by animateColorAsState(if (isSyncing) Color.Orange else Color.LightGreen)
     val scale by animateFloatAsState(if (isSyncing) SYNCING_SCALE else 1f)
 
-    var currentRotation by remember { mutableStateOf(0f) }
+    var currentRotation by remember { mutableFloatStateOf(0f) }
     val rotation = remember { Animatable(currentRotation) }
 
     LaunchedEffect(isSyncing) {
diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeScreen.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeScreen.kt
index 06a1359dc..de2336f6d 100644
--- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeScreen.kt
+++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeScreen.kt
@@ -20,6 +20,7 @@ import androidx.compose.material3.rememberTopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.vector.ImageVector
@@ -63,7 +64,7 @@ internal object HomeScreen : Parcelable, Screen {
 internal fun HomeScreen(
     state: HomeScreen.State,
     modifier: Modifier = Modifier,
-    onFullyDrawn: () -> Unit,
+    reportFullyDrawn: () -> Unit,
 ) {
     val isProfileEnabled by booleanConfigAsState { isProfileEnabled() }
     val isHomeEnabled by booleanConfigAsState { isHomeEnabled() }
@@ -71,6 +72,7 @@ internal fun HomeScreen(
 
     BottomSheetScaffold(
         sheetContent = { GallerySheetContent({ }) },
+        modifier = modifier,
         showDragHandle = false,
         topBar = {
             HomeTopBar(
@@ -95,7 +97,6 @@ internal fun HomeScreen(
         },
         floatingActionButton = { },
         isExpanded = false,
-        modifier = modifier,
     ) { contentPadding ->
         CircuitContent(
             screen = state.screen,
@@ -106,16 +107,18 @@ internal fun HomeScreen(
         )
     }
 
+    val latestReportFullyDrawn by rememberUpdatedState(reportFullyDrawn)
+
     LaunchedEffect(Unit) {
-        onFullyDrawn()
+        latestReportFullyDrawn()
     }
 }
 
 @Composable
 @ExperimentalMaterial3Api
 internal fun HomeTopBar(
-    title: String = "Home",
     modifier: Modifier = Modifier,
+    title: String = "Home",
     actions: @Composable RowScope.() -> Unit = { },
     scrollBehavior: TopAppBarScrollBehavior = enterAlwaysScrollBehavior(rememberTopAppBarState()),
 ) {
@@ -138,8 +141,8 @@ internal fun HomeTopBar(
 
 @Composable
 internal fun HomeBottomBar(
-    selected: Screen = HomeScreen,
     modifier: Modifier = Modifier,
+    selected: Screen = HomeScreen,
     onClick: (Screen) -> Unit = { },
 ) {
     BottomAppBar(modifier) {
diff --git a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListPresenter.kt b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListPresenter.kt
new file mode 100644
index 000000000..7853190ca
--- /dev/null
+++ b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListPresenter.kt
@@ -0,0 +1,32 @@
+package io.ashdavies.dominion
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import com.slack.circuit.runtime.Navigator
+
+@Composable
+internal fun BoxSetListPresenter(
+    navigator: Navigator,
+    boxSetStore: BoxSetStore,
+): DominionScreen.BoxSetList.State {
+    var isLoading by remember { mutableStateOf(true) }
+    val boxSetList by produceState(emptyList<BoxSet>()) {
+        value = boxSetStore()
+        isLoading = false
+    }
+
+    return DominionScreen.BoxSetList.State(
+        boxSetList = boxSetList,
+        isLoading = isLoading,
+    ) { event ->
+        when (event) {
+            is DominionScreen.BoxSetList.Event.ShowBoxSet -> {
+                navigator.goTo(DominionScreen.BoxSetDetails(event.boxSet.title))
+            }
+        }
+    }
+}
diff --git a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListScreen.kt b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListScreen.kt
index d1ebb69b4..73416df80 100644
--- a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListScreen.kt
+++ b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/BoxSetListScreen.kt
@@ -25,7 +25,6 @@ import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -36,36 +35,12 @@ import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.unit.dp
 import coil3.compose.AsyncImagePainter
 import coil3.compose.rememberAsyncImagePainter
-import com.slack.circuit.runtime.Navigator
 import io.ashdavies.analytics.OnClickWith
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.toImmutableList
 
 private const val DEFAULT_COLUMN_COUNT = 3
 
-@Composable
-internal fun BoxSetListPresenter(
-    navigator: Navigator,
-    boxSetStore: BoxSetStore,
-): DominionScreen.BoxSetList.State {
-    var isLoading by remember { mutableStateOf(true) }
-    val boxSetList by produceState(emptyList<BoxSet>()) {
-        value = boxSetStore()
-        isLoading = false
-    }
-
-    return DominionScreen.BoxSetList.State(
-        boxSetList = boxSetList,
-        isLoading = isLoading,
-    ) { event ->
-        when (event) {
-            is DominionScreen.BoxSetList.Event.ShowBoxSet -> {
-                navigator.goTo(DominionScreen.BoxSetDetails(event.boxSet.title))
-            }
-        }
-    }
-}
-
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
 internal fun BoxSetListScreen(
@@ -102,8 +77,8 @@ internal fun BoxSetListScreen(
 private fun BoxSetListScreen(
     boxSetList: ImmutableList<BoxSet>,
     contentPadding: PaddingValues,
-    columnCount: Int = DEFAULT_COLUMN_COUNT,
     modifier: Modifier = Modifier,
+    columnCount: Int = DEFAULT_COLUMN_COUNT,
     onClick: (BoxSet) -> Unit,
 ) {
     LazyVerticalGrid(
@@ -139,7 +114,7 @@ private fun BoxSetCard(
 
         if (isLoading) {
             CircularProgressIndicator(
-                modifier = modifier
+                modifier = Modifier
                     .fillMaxSize()
                     .padding(48.dp),
             )
@@ -148,7 +123,7 @@ private fun BoxSetCard(
         Image(
             painter = painter,
             contentDescription = boxSet.title,
-            modifier = modifier
+            modifier = Modifier
                 .clickable(onClick = onClick)
                 .fillMaxSize(),
             alignment = Alignment.TopCenter,
diff --git a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsPresenter.kt b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsPresenter.kt
new file mode 100644
index 000000000..14c2cb820
--- /dev/null
+++ b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsPresenter.kt
@@ -0,0 +1,41 @@
+package io.ashdavies.dominion
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import com.slack.circuit.runtime.Navigator
+
+@Composable
+internal fun DetailsPresenter(
+    navigator: Navigator,
+    cardsStore: CardsStore,
+    boxSet: BoxSet,
+): DominionScreen.BoxSetDetails.State {
+    var expandedCard by remember { mutableStateOf<Card?>(null) }
+    var isLoading by remember { mutableStateOf(false) }
+
+    val cards by produceState(emptyList<Card>()) {
+        value = cardsStore(boxSet.title)
+        isLoading = false
+    }
+
+    return DominionScreen.BoxSetDetails.State(
+        boxSet = boxSet,
+        cards = cards,
+        expandedCard = expandedCard,
+        isLoading = isLoading,
+    ) { event ->
+        when (event) {
+            is DominionScreen.BoxSetDetails.Event.ExpandCard -> {
+                expandedCard = event.card
+            }
+
+            DominionScreen.BoxSetDetails.Event.Back -> {
+                navigator.pop()
+            }
+        }
+    }
+}
diff --git a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsScreen.kt b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsScreen.kt
index 27064788a..93f517d7c 100644
--- a/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsScreen.kt
+++ b/dominion-app/src/commonMain/kotlin/io/ashdavies/dominion/DetailsScreen.kt
@@ -29,17 +29,11 @@ import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBarDefaults.enterAlwaysScrollBehavior
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.unit.dp
 import coil3.compose.rememberAsyncImagePainter
-import com.slack.circuit.runtime.Navigator
 import io.ashdavies.analytics.OnClickWith
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.toImmutableList
@@ -50,38 +44,6 @@ private const val DEFAULT_COLUMN_COUNT = 6
 private const val HORIZONTAL_CARD_SPAN = 2
 private const val VERTICAL_CARD_SPAN = 3
 
-@Composable
-internal fun DetailsPresenter(
-    navigator: Navigator,
-    cardsStore: CardsStore,
-    boxSet: BoxSet,
-): DominionScreen.BoxSetDetails.State {
-    var expandedCard by remember { mutableStateOf<Card?>(null) }
-    var isLoading by remember { mutableStateOf(false) }
-
-    val cards by produceState(emptyList<Card>()) {
-        value = cardsStore(boxSet.title)
-        isLoading = false
-    }
-
-    return DominionScreen.BoxSetDetails.State(
-        boxSet = boxSet,
-        cards = cards,
-        expandedCard = expandedCard,
-        isLoading = isLoading,
-    ) { event ->
-        when (event) {
-            is DominionScreen.BoxSetDetails.Event.ExpandCard -> {
-                expandedCard = event.card
-            }
-
-            DominionScreen.BoxSetDetails.Event.Back -> {
-                navigator.pop()
-            }
-        }
-    }
-}
-
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
 internal fun DetailsScreen(
@@ -171,9 +133,9 @@ private fun BackIconButton(onClick: () -> Unit) {
 private fun DetailsScreen(
     cards: ImmutableList<Card>,
     contentPadding: PaddingValues,
+    modifier: Modifier = Modifier,
     columnCount: Int = DEFAULT_COLUMN_COUNT,
     onClick: (Card) -> Unit,
-    modifier: Modifier = Modifier,
 ) {
     LazyVerticalGrid(
         columns = GridCells.Fixed(columnCount),
@@ -202,8 +164,8 @@ private fun BoxSetCard(
     modifier: Modifier = Modifier,
     onClick: () -> Unit = { },
 ) {
-    Box(Modifier.padding(4.dp)) {
-        Card(modifier.clickable(onClick = onClick)) {
+    Box(modifier.padding(4.dp)) {
+        Card(Modifier.clickable(onClick = onClick)) {
             Image(
                 painter = rememberAsyncImagePainter(value.image),
                 contentDescription = value.title,
diff --git a/http-client/src/commonMain/kotlin/io/ashdavies/http/State.kt b/http-client/src/commonMain/kotlin/io/ashdavies/http/State.kt
deleted file mode 100644
index dc28297d6..000000000
--- a/http-client/src/commonMain/kotlin/io/ashdavies/http/State.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.ashdavies.http
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.produceState
-import kotlinx.coroutines.CoroutineScope
-
-@Composable
-public fun <T : Any> produceStateInline(block: suspend CoroutineScope.() -> T): State<Result<T>> {
-    return produceState(Result.loading()) { value = runCatching { block() } }
-}