diff --git a/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingPreviews.kt b/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingPreviews.kt index 5938a39..0fe79fc 100644 --- a/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingPreviews.kt +++ b/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingPreviews.kt @@ -54,26 +54,26 @@ import dev.privacyllc.period.designsystem.art.WelcomeIllustration private fun IllustrationPreviews() { PeriodTheme { Column( - Modifier.fillMaxWidth().padding(16.dp), + Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Label("1 · Welcome") - WelcomeIllustration(size = 110.dp) + WelcomeIllustration(size = 150.dp) Label("2 · Last period start") - LastPeriodIllustration(size = 110.dp) + LastPeriodIllustration(size = 150.dp) Label("3 · Period end") - PeriodEndIllustration(size = 110.dp) + PeriodEndIllustration(size = 150.dp) Label("4 · Learning, and Insights empty") - LearningIllustration(size = 110.dp) + LearningIllustration(size = 150.dp) Label("5 · Privacy promise") - PrivacyIllustration(size = 110.dp) + PrivacyIllustration(size = 150.dp) Label("6 · Reminder privacy") - NotificationPrivacyIllustration(size = 110.dp) + NotificationPrivacyIllustration(size = 150.dp) Label("7 · First forecast") - ForecastIllustration(size = 110.dp) + ForecastIllustration(size = 150.dp) Label("Today empty state") - EmptyStateIllustration(size = 110.dp) + EmptyStateIllustration(size = 150.dp) } } } diff --git a/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingScreen.kt b/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingScreen.kt index e4e43cb..3b7cf8c 100644 --- a/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingScreen.kt +++ b/app/src/main/kotlin/dev/privacyllc/period/feature/onboarding/OnboardingScreen.kt @@ -2,6 +2,7 @@ package dev.privacyllc.period.feature.onboarding import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -34,6 +35,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.semantics.Role import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -77,11 +79,14 @@ fun OnboardingScreen( modifier = Modifier.fillMaxWidth(), ) + // Vertical padding only. The hero illustration is full-bleed and Compose + // has no negative padding, so horizontal inset belongs to the content + // inside each step — see StepBody — rather than to this column. Column( Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) - .padding(24.dp), + .padding(vertical = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { when (state.step) { @@ -94,23 +99,68 @@ fun OnboardingScreen( Step.FIRST_FORECAST -> FirstForecast(state) { viewModel.complete(onFinished) } } - state.message?.let { - Spacer(Modifier.height(16.dp)) - Card { Text(it, Modifier.padding(12.dp), style = MaterialTheme.typography.bodyMedium) } - TextButton(onClick = viewModel::messageShown) { Text("OK") } - } + // These sit outside the step composables, so they carry the inset + // the parent column no longer applies. + Column( + Modifier.padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + state.message?.let { + Spacer(Modifier.height(16.dp)) + Card { Text(it, Modifier.padding(12.dp), style = MaterialTheme.typography.bodyMedium) } + TextButton(onClick = viewModel::messageShown) { Text("OK") } + } - if (state.step != Step.WELCOME && state.step != Step.FIRST_FORECAST) { - Spacer(Modifier.height(8.dp)) - TextButton(onClick = viewModel::back) { Text("Back") } + if (state.step != Step.WELCOME && state.step != Step.FIRST_FORECAST) { + Spacer(Modifier.height(8.dp)) + TextButton(onClick = viewModel::back) { Text("Back") } + } } } } } +/** + * One onboarding step: a full-bleed hero, then inset content. + * + * Every step had hand-rolled this — `Spacer / Illustration(size) / Spacer / + * Heading / controls` — in three different spacer rhythms with a reason recorded + * for only two of them. The rhythm lives here now, and a step declares what is + * different about it rather than restating what is the same. + * + * [artHeight] stays per-step and is deliberately not uniform: the steps carrying + * three or four controls under the art take a smaller hero so the primary button + * is reachable without scrolling. `Illustration` caps whatever is passed at a + * third of the screen, which is what makes landscape survivable. + * + * The hero is emitted OUTSIDE the inset column, because it is full-bleed and the + * screen's horizontal padding would otherwise box it in. + */ +@Composable +private fun StepBody( + artHeight: Dp, + art: @Composable (Dp) -> Unit, + title: String, + body: String? = null, + content: @Composable ColumnScope.() -> Unit, +) { + art(artHeight) + Spacer(Modifier.height(24.dp)) + Column( + Modifier.fillMaxWidth().padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Heading(title, body) + content() + } +} + @Composable private fun Heading(title: String, body: String? = null) { - Text(title, style = MaterialTheme.typography.headlineMedium, textAlign = TextAlign.Center) + // headlineLarge, not headlineMedium: these titles sit under a hero that now + // fills a third of the screen, and 28sp under 280dp of artwork reads as a + // caption rather than a heading. + Text(title, style = MaterialTheme.typography.headlineLarge, textAlign = TextAlign.Center) if (body != null) { Spacer(Modifier.height(12.dp)) Text( @@ -125,21 +175,24 @@ private fun Heading(title: String, body: String? = null) { @Composable private fun Welcome(onNext: () -> Unit) { - Spacer(Modifier.height(32.dp)) - WelcomeIllustration() - Spacer(Modifier.height(32.dp)) - Heading("Know what's coming.", "Track your period and get predictions that learn your cycle.") - Button(onClick = onNext, modifier = Modifier.fillMaxWidth()) { Text("Get Started") } + StepBody( + artHeight = 280.dp, + art = { WelcomeIllustration(size = it) }, + title = "Know what's coming.", + body = "Track your period and get predictions that learn your cycle.", + ) { + Button(onClick = onNext, modifier = Modifier.fillMaxWidth()) { Text("Get Started") } + } } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun LastPeriod(state: OnboardingUiState, viewModel: OnboardingViewModel) { - Spacer(Modifier.height(24.dp)) - LastPeriodIllustration(size = 128.dp) - Spacer(Modifier.height(24.dp)) - Heading("When did your last period start?") - + StepBody( + artHeight = 280.dp, + art = { LastPeriodIllustration(size = it) }, + title = "When did your last period start?", + ) { DateField( label = state.lastPeriodStart?.pretty() ?: "Choose a date", selected = state.lastPeriodStart, @@ -153,17 +206,19 @@ private fun LastPeriod(state: OnboardingUiState, viewModel: OnboardingViewModel) enabled = state.canContinueFromLastPeriod, modifier = Modifier.fillMaxWidth(), ) { Text("Continue") } + } } @Composable private fun PeriodEnd(state: OnboardingUiState, viewModel: OnboardingViewModel) { - Spacer(Modifier.height(16.dp)) - // Smaller than the other steps: this one carries three buttons under the - // date field, and the primary must stay reachable without scrolling. - PeriodEndIllustration(size = 104.dp) - Spacer(Modifier.height(16.dp)) - Heading("When did it end?") - + // A shorter hero than the other steps: this one carries three buttons under + // the date field, and the primary must stay reachable without scrolling. + // Verified at font scale 2.0, which is how the 104dp version was chosen. + StepBody( + artHeight = 200.dp, + art = { PeriodEndIllustration(size = it) }, + title = "When did it end?", + ) { var picked by remember { mutableStateOf(null) } DateField( label = picked?.pretty() ?: "Choose a date", @@ -185,18 +240,17 @@ private fun PeriodEnd(state: OnboardingUiState, viewModel: OnboardingViewModel) OutlinedButton(onClick = { viewModel.setPeriodEnd(null) }, Modifier.fillMaxWidth()) { Text("Still going") } Spacer(Modifier.height(8.dp)) OutlinedButton(onClick = { viewModel.setPeriodEnd(null) }, Modifier.fillMaxWidth()) { Text("I'm not sure") } + } } @Composable private fun PreviousHistory(state: OnboardingUiState, viewModel: OnboardingViewModel) { - Spacer(Modifier.height(24.dp)) - LearningIllustration(size = 120.dp) - Spacer(Modifier.height(24.dp)) - Heading( - "Remember any earlier periods?", - "Adding previous dates helps us learn your cycle faster.", - ) - + StepBody( + artHeight = 240.dp, + art = { LearningIllustration(size = it) }, + title = "Remember any earlier periods?", + body = "Adding previous dates helps us learn your cycle faster.", + ) { state.previousStarts.forEach { date -> Row( Modifier.fillMaxWidth().padding(vertical = 2.dp), @@ -220,35 +274,36 @@ private fun PreviousHistory(state: OnboardingUiState, viewModel: OnboardingViewM Button(onClick = viewModel::donePreviousHistory, modifier = Modifier.fillMaxWidth()) { Text(if (state.previousStarts.isEmpty()) "Skip" else "Continue") } + } } @Composable private fun PrivacyPromise(onNext: () -> Unit) { - Spacer(Modifier.height(24.dp)) - PrivacyIllustration(size = 140.dp) - Spacer(Modifier.height(24.dp)) // §4 requires this promise here, in Settings, and on the public privacy // page. The wording is deliberate: we never SELL your data. It does not // claim no third party ever processes anything, because Play Billing and an // ad SDK will, and a promise the implementation cannot keep is worse than a // narrower one it can. - Heading( - "Your cycle belongs to you.", - "We will never sell your personal or health data.\n\n" + + StepBody( + artHeight = 280.dp, + art = { PrivacyIllustration(size = it) }, + title = "Your cycle belongs to you.", + body = "We will never sell your personal or health data.\n\n" + "Your period history and fertility information are private. We don't sell them " + "to advertisers, data brokers, or third parties.", - ) - Button(onClick = onNext, modifier = Modifier.fillMaxWidth()) { Text("Continue") } + ) { + Button(onClick = onNext, modifier = Modifier.fillMaxWidth()) { Text("Continue") } + } } @Composable private fun NotificationPrivacyStep(state: OnboardingUiState, viewModel: OnboardingViewModel) { - Spacer(Modifier.height(16.dp)) - // Also small: three option cards and a button follow it. - NotificationPrivacyIllustration(size = 104.dp) - Spacer(Modifier.height(16.dp)) - Heading("How should reminders appear?") - + // Also shorter: three option cards and a button follow it. + StepBody( + artHeight = 200.dp, + art = { NotificationPrivacyIllustration(size = it) }, + title = "How should reminders appear?", + ) { Column(Modifier.selectableGroup().fillMaxWidth()) { PrivacyOption( title = "Discreet", @@ -277,6 +332,7 @@ private fun NotificationPrivacyStep(state: OnboardingUiState, viewModel: Onboard enabled = !state.saving, modifier = Modifier.fillMaxWidth(), ) { Text(if (state.saving) "Saving…" else "Continue") } + } } @Composable @@ -318,11 +374,11 @@ private fun PrivacyOption(title: String, example: String, selected: Boolean, onS @Composable private fun FirstForecast(state: OnboardingUiState, onDone: () -> Unit) { - Spacer(Modifier.height(24.dp)) - ForecastIllustration(size = 120.dp) - Spacer(Modifier.height(24.dp)) - Heading("Your first forecast") - + StepBody( + artHeight = 240.dp, + art = { ForecastIllustration(size = it) }, + title = "Your first forecast", + ) { val forecast = state.forecast if (forecast == null) { Text( @@ -354,6 +410,7 @@ private fun FirstForecast(state: OnboardingUiState, onDone: () -> Unit) { Spacer(Modifier.height(28.dp)) Button(onClick = onDone, modifier = Modifier.fillMaxWidth()) { Text("Go to Today") } + } } @Composable diff --git a/core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt b/core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt index c00e956..82155b0 100644 --- a/core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt +++ b/core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt @@ -2,119 +2,124 @@ package dev.privacyllc.period.designsystem.art import androidx.annotation.DrawableRes import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import dev.privacyllc.period.designsystem.R /** - * The onboarding and empty-state illustrations. + * The onboarding and empty-state illustrations, drawn as full-bleed heroes. * * ## Why these are raster, when PRODUCT_PLAN.md §42 asks for vectors * * §42's preference list ends *"avoid **unnecessary** raster imagery"*, and it * offers polished placeholder vectors as the fallback for artwork that does not - * exist yet — which is what used to be here. The artwork now exists: eight + * exist yet — which is what used to be here. The artwork exists: eight * illustrations supplied by the project owner, in a matched light and dark pair. * * They are gradient landscapes with soft glow and depth. There is no honest * `VectorDrawable` of one; converting them would either lose the drawing or - * produce a path soup larger and slower than the image. So the raster is - * necessary rather than unnecessary, and the whole set costs 216 KB. + * produce a path soup larger and slower than the image. * - * [CycleProgressMark] stays a vector, because it is the one mark that is - * *drawn from data* — its arc is the user's actual position in their cycle. + * [CycleProgressMark] and the calendar markers stay vectors, because they are + * drawn *from data* — a marker's shape is what a day is, and the progress arc is + * where the user actually stands in their cycle. + * + * ## Why there are density buckets and no plain `drawable/` + * + * A file in a density-less `drawable/` is treated as mdpi and pre-scaled at + * decode by the device's bucket. The previous 552 × 480 set cost roughly 17 MB + * of heap for one bitmap on an xxxhdpi device, and this artwork is far larger. + * So each illustration ships at four widths — 617, 822, 1233 and 1644 px, which + * is a 411 dp screen at hdpi through xxxhdpi — and Android decodes the one it + * needs. **Never put one of these back in a plain `drawable/`.** + * + * The whole set is 2.3 MB across every bucket, but a release is an AAB and Play + * splits by density, so a device downloads only its own: 130–500 KB. * * ## How the theme swap works * * There is no `isSystemInDarkTheme()` branch here on purpose. Each drawable has - * a `drawable/` and a `drawable-night/` copy, and Android resolves it. That - * means the dark art also follows a device-level theme change with no - * recomposition of ours, and a screenshot test of either theme picks the right - * one for free. + * a `-night` twin at every density, and Android resolves it. Nothing of ours + * runs on a theme change. The cost is that a missing night asset is invisible in + * light mode, which is why `OnboardingPreviews.kt` renders one entry per + * illustration — and why #42 exists. * * ## Sources * * `docs/design/dist/` holds the delivered set at full resolution, with a README - * recording the decisions behind them — no baked text, no step numbers, and a - * forecast illustration that deliberately names no fields. Re-read it before - * regenerating any of these. + * recording the decisions behind them — no text in the pixels, no step numbers, + * and a forecast illustration that deliberately names no fields. **Re-read it + * before regenerating any of these.** */ /** Step 1. Overlapping rings over a dawn landscape — the cycle, before it means anything specific. */ @Composable -fun WelcomeIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_welcome, "Overlapping circles above a sunrise", modifier, size) +fun WelcomeIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_welcome, "Overlapping circles above a sunrise", modifier, size) /** Step 2. A cycle ring with one marked day — the first day being recorded. */ @Composable -fun LastPeriodIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_last_period, "A cycle ring with one marked day", modifier, size) +fun LastPeriodIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_last_period, "A cycle ring with one marked day", modifier, size) /** Step 3. The same ring with a span between two marked days — a period with a start and an end. */ @Composable -fun PeriodEndIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_period_end, "A cycle ring with a marked span between two days", modifier, size) +fun PeriodEndIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_period_end, "A cycle ring with a marked span between two days", modifier, size) /** * Step 4, and the Insights empty state. * - * Earlier cycles fading behind the current one — §42's "points gradually - * converging into a pattern", which is the honest picture of what the engine - * does with history. Used in two places, which is why it carries no step number. + * Earlier cycles fading behind the current one. Used in two places, which is why + * it carries no step number. */ @Composable -fun LearningIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_learning, "Earlier cycle rings fading behind the current one", modifier, size) +fun LearningIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_learning, "Earlier cycle rings fading behind the current one", modifier, size) /** * Step 5, the privacy promise. * - * A shield with the cycle ring inside it. Putting the cycle *inside* the shield - * is the whole sentence the screen is making, and enclosure reads as protection - * without reaching for a padlock — which would make a calm product look like a - * security one. + * A shield with the cycle ring inside it. Enclosure reads as protection without + * reaching for a padlock, which would make a calm product look like a security + * one. */ @Composable -fun PrivacyIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_privacy, "A shield containing a cycle ring", modifier, size) +fun PrivacyIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_privacy, "A shield containing a cycle ring", modifier, size) /** Step 6. A notification with a shield on it — what a reminder shows, and what it withholds. */ @Composable -fun NotificationPrivacyIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_notification_privacy, "A notification card marked with a shield", modifier, size) +fun NotificationPrivacyIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_notification_privacy, "A notification card marked with a shield", modifier, size) /** * Step 7, the first forecast. * * A calendar and a cycle arc running from logged days into dashed predicted - * ones. It deliberately names no fields: a drawing that said "fertile window" - * or "confidence: low" would contradict the live forecast beside it, which on a + * ones. It deliberately names no fields: a drawing that said "fertile window" or + * "confidence: low" would contradict the live forecast beside it, which on a * first run declines to estimate fertility at all. */ @Composable -fun ForecastIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_forecast, "A calendar with a cycle arc continuing into predicted days", modifier, size) +fun ForecastIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_forecast, "A calendar with a cycle arc continuing into predicted days", modifier, size) /** * The empty state — no periods logged yet. @@ -124,27 +129,32 @@ fun ForecastIllustration( * which is what makes this read as *waiting* rather than *broken*. */ @Composable -fun EmptyStateIllustration( - modifier: Modifier = Modifier, - size: Dp = IllustrationDefaults.Size, -) = Illustration(R.drawable.art_empty_state, "An empty cycle ring above a sunrise", modifier, size) +fun EmptyStateIllustration(modifier: Modifier = Modifier, size: Dp = IllustrationDefaults.Size) = + Illustration(R.drawable.art_empty_state, "An empty cycle ring above a sunrise", modifier, size) /** - * One drawing, sized by height and clipped to a rounded corner. + * One drawing, full width, fading into the page. * - * **Height rather than a square box**: the set does not share one aspect ratio, - * and forcing a square would letterbox some and crop others. Each illustration - * keeps its own proportions and every screen agrees on how tall the artwork is, - * which is the dimension that decides whether the button below it stays on - * screen. + * **Full-bleed, and no clip.** These are 3:2 scenes composed to reach every + * edge. An earlier set was composed as cards with their own rounded corners, and + * the corners had to be clipped off; that is gone, and putting a `clip` back + * would cut the artwork rather than tidy it. * - * **Clipped because the corners are painted in.** Each drawing is composed as a - * card with its own rounded corner, so the pixels outside that curve are the - * card's own backdrop — near-black in the dark set, near-white in the light one. - * Drawn unclipped they appear as four notches against the app's background, - * which is obvious the moment you look at a device and invisible in code review. - * Clipping slightly inside the painted curve removes them, and what is left - * reads as a deliberate rounded card rather than a pasted rectangle. + * **[size] is a ceiling, not a height.** It is capped at a third of the screen + * so the primary button below stays reachable. Landscape is the case that makes + * this necessary: 914 × 411 dp, where a fixed 280 dp hero would be 68% of the + * screen and every step would break on rotation. + * + * The cap reads `LocalConfiguration.screenHeightDp` rather than + * `BoxWithConstraints`. Inside a `verticalScroll` column a child is measured + * with an **infinite** height constraint, so `maxHeight` there is `Dp.Infinity` + * and the cap would silently never apply — a guard that looks right and does + * nothing. + * + * **The fade is drawn, not baked.** A gradient to + * `MaterialTheme.colorScheme.background` over the bottom third, so the scene + * settles into the page in whichever theme is showing and the artwork never + * hard-codes a colour it cannot know. */ @Composable private fun Illustration( @@ -153,22 +163,41 @@ private fun Illustration( modifier: Modifier, size: Dp, ) { - Image( - painter = painterResource(id), - contentDescription = description, - modifier = modifier - .height(size) - .clip(RoundedCornerShape(IllustrationDefaults.CornerRadius)), - contentScale = ContentScale.FillHeight, + val cap = (LocalConfiguration.current.screenHeightDp * IllustrationDefaults.MaxScreenFraction).dp + Box(modifier.fillMaxWidth().height(minOf(size, cap))) { + Image( + painter = painterResource(id), + contentDescription = description, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop, + ) + BottomFade() + } +} + +/** The gradient that joins the artwork to the page. */ +@Composable +private fun BoxScope.BottomFade() { + Box( + Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .fillMaxHeight(IllustrationDefaults.FadeFraction) + .background( + Brush.verticalGradient( + listOf(Color.Transparent, MaterialTheme.colorScheme.background), + ), + ), ) } object IllustrationDefaults { - val Size: Dp = 160.dp + /** The hero height a step asks for, before the screen-fraction cap. */ + val Size: Dp = 280.dp - /** - * Slightly wider than the curve painted into the artwork, so the clip lands - * inside it and no corner of the source card's backdrop survives. - */ - val CornerRadius: Dp = 18.dp + /** Never taller than this share of the screen — see [Illustration]. */ + const val MaxScreenFraction = 0.32f + + /** How much of the image the fade covers. The artwork keeps this band quiet. */ + const val FadeFraction = 0.34f } diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-hdpi/art_empty_state.webp new file mode 100644 index 0000000..6d4f2d8 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-hdpi/art_forecast.webp new file mode 100644 index 0000000..932022e Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-hdpi/art_last_period.webp new file mode 100644 index 0000000..359fb18 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-hdpi/art_learning.webp new file mode 100644 index 0000000..3e1aae7 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-hdpi/art_notification_privacy.webp new file mode 100644 index 0000000..746cec5 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-hdpi/art_period_end.webp new file mode 100644 index 0000000..62b7d88 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-hdpi/art_privacy.webp new file mode 100644 index 0000000..a06a925 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-hdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-hdpi/art_welcome.webp new file mode 100644 index 0000000..0eac4ab Binary files /dev/null and b/core/designsystem/src/main/res/drawable-hdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_empty_state.webp new file mode 100644 index 0000000..e817181 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_forecast.webp new file mode 100644 index 0000000..adfb87c Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_last_period.webp new file mode 100644 index 0000000..70d9d84 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_learning.webp new file mode 100644 index 0000000..d122640 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_notification_privacy.webp new file mode 100644 index 0000000..bdd402d Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_period_end.webp new file mode 100644 index 0000000..3bb1277 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_privacy.webp new file mode 100644 index 0000000..8b05772 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-hdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-night-hdpi/art_welcome.webp new file mode 100644 index 0000000..afd6ca1 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-hdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_empty_state.webp new file mode 100644 index 0000000..05b8538 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_forecast.webp new file mode 100644 index 0000000..bbd021a Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_last_period.webp new file mode 100644 index 0000000..60a49ab Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_learning.webp new file mode 100644 index 0000000..32bb9f5 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..6ce5fdb Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_period_end.webp new file mode 100644 index 0000000..19affe9 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_privacy.webp new file mode 100644 index 0000000..116dbe5 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-night-xhdpi/art_welcome.webp new file mode 100644 index 0000000..821aa01 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_empty_state.webp new file mode 100644 index 0000000..2f6dda0 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_forecast.webp new file mode 100644 index 0000000..121a849 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_last_period.webp new file mode 100644 index 0000000..8c693b3 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_learning.webp new file mode 100644 index 0000000..7a8b5c3 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..3cb0a3e Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_period_end.webp new file mode 100644 index 0000000..fbd6dc9 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_privacy.webp new file mode 100644 index 0000000..f279b13 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_welcome.webp new file mode 100644 index 0000000..9ee4579 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_empty_state.webp new file mode 100644 index 0000000..f8098cb Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_forecast.webp new file mode 100644 index 0000000..cfd9a03 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_last_period.webp new file mode 100644 index 0000000..b86b4b0 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_learning.webp new file mode 100644 index 0000000..04b0928 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..40cbdf3 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_period_end.webp new file mode 100644 index 0000000..c1b4000 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_privacy.webp new file mode 100644 index 0000000..1f5b4f2 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_welcome.webp new file mode 100644 index 0000000..da90aa4 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-night-xxxhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-night/art_empty_state.webp b/core/designsystem/src/main/res/drawable-night/art_empty_state.webp deleted file mode 100644 index d280f36..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_empty_state.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_forecast.webp b/core/designsystem/src/main/res/drawable-night/art_forecast.webp deleted file mode 100644 index 8a738b0..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_forecast.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_last_period.webp b/core/designsystem/src/main/res/drawable-night/art_last_period.webp deleted file mode 100644 index 579e876..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_last_period.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_learning.webp b/core/designsystem/src/main/res/drawable-night/art_learning.webp deleted file mode 100644 index 71b697f..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_learning.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-night/art_notification_privacy.webp deleted file mode 100644 index 188e663..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_notification_privacy.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_period_end.webp b/core/designsystem/src/main/res/drawable-night/art_period_end.webp deleted file mode 100644 index 956afdb..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_period_end.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_privacy.webp b/core/designsystem/src/main/res/drawable-night/art_privacy.webp deleted file mode 100644 index b6d9617..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_privacy.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-night/art_welcome.webp b/core/designsystem/src/main/res/drawable-night/art_welcome.webp deleted file mode 100644 index de65140..0000000 Binary files a/core/designsystem/src/main/res/drawable-night/art_welcome.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_empty_state.webp new file mode 100644 index 0000000..f02dab5 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_forecast.webp new file mode 100644 index 0000000..de0b349 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_last_period.webp new file mode 100644 index 0000000..7f51c2d Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_learning.webp new file mode 100644 index 0000000..7d6d4df Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..47da250 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_period_end.webp new file mode 100644 index 0000000..485216f Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_privacy.webp new file mode 100644 index 0000000..78cce69 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-xhdpi/art_welcome.webp new file mode 100644 index 0000000..97d33e7 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_empty_state.webp new file mode 100644 index 0000000..8c3660a Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_forecast.webp new file mode 100644 index 0000000..dddc627 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_last_period.webp new file mode 100644 index 0000000..7a95685 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_learning.webp new file mode 100644 index 0000000..6a8ebf5 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..8c312c9 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_period_end.webp new file mode 100644 index 0000000..1c8e75d Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_privacy.webp new file mode 100644 index 0000000..a35636a Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-xxhdpi/art_welcome.webp new file mode 100644 index 0000000..0bcebfd Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_empty_state.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_empty_state.webp new file mode 100644 index 0000000..cea0867 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_empty_state.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_forecast.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_forecast.webp new file mode 100644 index 0000000..3b86c9e Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_forecast.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_last_period.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_last_period.webp new file mode 100644 index 0000000..7744ddb Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_last_period.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_learning.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_learning.webp new file mode 100644 index 0000000..ae96539 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_learning.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_notification_privacy.webp new file mode 100644 index 0000000..50cc0c2 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_notification_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_period_end.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_period_end.webp new file mode 100644 index 0000000..0b9ccde Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_period_end.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_privacy.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_privacy.webp new file mode 100644 index 0000000..42aa935 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_privacy.webp differ diff --git a/core/designsystem/src/main/res/drawable-xxxhdpi/art_welcome.webp b/core/designsystem/src/main/res/drawable-xxxhdpi/art_welcome.webp new file mode 100644 index 0000000..6e757a0 Binary files /dev/null and b/core/designsystem/src/main/res/drawable-xxxhdpi/art_welcome.webp differ diff --git a/core/designsystem/src/main/res/drawable/art_empty_state.webp b/core/designsystem/src/main/res/drawable/art_empty_state.webp deleted file mode 100644 index b697fc6..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_empty_state.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_forecast.webp b/core/designsystem/src/main/res/drawable/art_forecast.webp deleted file mode 100644 index 0d8cb5c..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_forecast.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_last_period.webp b/core/designsystem/src/main/res/drawable/art_last_period.webp deleted file mode 100644 index 45994b5..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_last_period.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_learning.webp b/core/designsystem/src/main/res/drawable/art_learning.webp deleted file mode 100644 index 56cb916..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_learning.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_notification_privacy.webp b/core/designsystem/src/main/res/drawable/art_notification_privacy.webp deleted file mode 100644 index d6129d4..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_notification_privacy.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_period_end.webp b/core/designsystem/src/main/res/drawable/art_period_end.webp deleted file mode 100644 index b0e347e..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_period_end.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_privacy.webp b/core/designsystem/src/main/res/drawable/art_privacy.webp deleted file mode 100644 index 3c26a64..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_privacy.webp and /dev/null differ diff --git a/core/designsystem/src/main/res/drawable/art_welcome.webp b/core/designsystem/src/main/res/drawable/art_welcome.webp deleted file mode 100644 index fb99fa0..0000000 Binary files a/core/designsystem/src/main/res/drawable/art_welcome.webp and /dev/null differ diff --git a/docs/design/README.md b/docs/design/README.md index 234ada2..8039595 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -169,8 +169,19 @@ and it is worth keeping for the artwork still to come. **These are raster, and §42 still holds.** Its preference list ends "avoid *unnecessary* raster imagery", and it offers placeholder vectors as the fallback for artwork that does not exist. The artwork exists, and it is gradient -landscapes with glow and depth — there is no honest `VectorDrawable` of one. The -whole set costs 130 KB of the release APK. +landscapes with glow and depth — there is no honest `VectorDrawable` of one. + +**They ship as full-bleed heroes in density buckets.** Each illustration spans +the screen and fades into the page, at a height capped to a third of the screen +so the primary button stays reachable — the cap is what makes landscape survive, +where the screen is 411 dp tall and an uncapped 280 dp hero would fill 68% of it. +Four buckets per theme, 617 to 1644 px; a plain `drawable/` would be treated as +mdpi and decoded at 4× on a dense screen. 2.3 MB across every bucket, but a +release is an AAB and Play splits by density, so a device downloads 130–500 KB. + +**The fade is drawn, not baked**, as a gradient to +`MaterialTheme.colorScheme.background`. Artwork cannot know which theme it is +sitting on, and a baked fade would be a hard-coded colour by another name. **The calendar markers stay vectors**, and so does `CycleProgressMark`. They are drawn *from data* — a marker's shape is what a day is, and the progress arc is