feat: implement user limits and onboarding features

- Added user limits management with daily can and spend limits.
- Integrated onboarding flow to guide users through setting limits.
- Enhanced greeting messages to reflect user limits and violations.
- Updated CSS for new limit-related components and improved UI consistency.
- Refactored coach session to utilize user limits in interactions.
This commit is contained in:
Ned Halksworth
2026-05-23 21:17:36 +01:00
parent 2be7609172
commit 0db81bddd5
9 changed files with 1683 additions and 86 deletions
+120 -31
View File
@@ -36,6 +36,7 @@ body {
background: #f8fbff;
color: #1f252a;
font-family: "Google Sans", "Google Sans Text", "Product Sans", Roboto, -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
@@ -96,7 +97,7 @@ textarea:focus-visible {
}
.state-chip {
@apply inline-flex min-h-10 items-center gap-2 px-3 text-sm font-semibold;
@apply inline-flex min-h-10 items-center gap-2 px-3 text-sm font-medium;
background: var(--primary-container);
border-radius: 999px;
color: var(--on-primary-container);
@@ -110,7 +111,7 @@ textarea:focus-visible {
}
.segmented-control button {
@apply min-h-10 px-3 text-sm font-semibold transition;
@apply min-h-10 px-3 text-sm font-normal transition;
border-radius: 999px;
color: var(--muted);
}
@@ -146,7 +147,7 @@ textarea:focus-visible {
}
.drawer-primary-action {
@apply mb-5 inline-flex min-h-14 items-center justify-center gap-3 px-5 text-sm font-semibold shadow-can transition active:scale-[0.99];
@apply mb-5 inline-flex min-h-14 items-center justify-center gap-3 px-5 text-sm font-medium shadow-can transition active:scale-[0.99];
background: var(--primary-container);
border-radius: 18px;
color: var(--on-primary-container);
@@ -191,12 +192,12 @@ textarea:focus-visible {
}
.top-kicker {
@apply text-sm font-medium;
@apply text-sm font-normal;
color: var(--primary);
}
.top-title {
@apply mt-1 break-words text-4xl font-semibold sm:text-5xl;
@apply mt-1 break-words text-4xl font-medium sm:text-5xl;
color: var(--text);
}
@@ -205,7 +206,7 @@ textarea:focus-visible {
}
.account-chip {
@apply inline-flex min-h-10 max-w-full items-center rounded-md px-3 text-xs font-semibold;
@apply inline-flex min-h-10 max-w-full items-center rounded-md px-3 text-xs font-normal;
background: var(--surface-container-high);
color: var(--muted);
}
@@ -224,10 +225,11 @@ textarea:focus-visible {
background: color-mix(in srgb, var(--surface-container-high) 92%, white);
border-color: var(--outline-variant);
border-radius: 28px;
padding-bottom: calc(0.25rem + env(safe-area-inset-bottom, 0px));
}
.mobile-nav-item {
@apply flex min-h-16 flex-col items-center justify-center gap-1 text-[11px] font-semibold transition;
@apply flex min-h-16 flex-col items-center justify-center gap-1 text-[11px] font-medium transition;
border-radius: 22px;
color: var(--muted);
}
@@ -290,15 +292,86 @@ textarea:focus-visible {
}
.oura-ring span {
@apply text-4xl font-semibold leading-none;
@apply text-4xl font-medium leading-none;
color: var(--text);
}
.oura-ring small {
@apply mt-1 text-xs font-semibold uppercase;
@apply mt-1 text-xs font-normal uppercase;
color: var(--muted);
}
.oura-ring--warn {
background: conic-gradient(var(--chart-tertiary) var(--progress), var(--surface-container-high) 0);
}
.oura-ring--over {
background: conic-gradient(var(--chart-error) var(--progress), var(--surface-container-high) 0);
}
.limits-card {
@apply border border-white/10;
}
.limit-row {
@apply rounded-xl border p-4;
border-color: var(--outline-variant);
background: color-mix(in srgb, var(--surface-container-high) 72%, white);
}
.limit-row--warn {
border-color: color-mix(in srgb, var(--chart-tertiary) 55%, transparent);
background: color-mix(in srgb, var(--chart-tertiary) 12%, var(--surface-container-high));
}
.limit-row--over {
border-color: color-mix(in srgb, var(--chart-error) 55%, transparent);
background: color-mix(in srgb, var(--chart-error) 12%, var(--surface-container-high));
}
.limit-row-head {
@apply flex items-center justify-between gap-2 text-sm;
}
.limit-row-head span {
color: var(--muted);
}
.limit-row-head strong {
color: var(--text);
font-weight: 500;
}
.limit-row-value {
@apply mt-2 text-sm;
color: var(--muted);
}
.limit-progress {
@apply mt-3 h-2 overflow-hidden rounded-full;
background: color-mix(in srgb, var(--outline-variant) 40%, transparent);
}
.limit-progress-fill {
@apply h-full rounded-full transition-all duration-300;
background: var(--primary);
}
.limit-row--warn .limit-progress-fill {
background: var(--chart-tertiary);
}
.limit-row--over .limit-progress-fill {
background: var(--chart-error);
}
.limit-banner {
@apply rounded-lg border px-3 py-2 text-sm leading-6;
border-color: color-mix(in srgb, var(--chart-tertiary) 45%, transparent);
background: color-mix(in srgb, var(--chart-tertiary) 14%, var(--surface-container-high));
color: var(--text);
}
.wellness-pill {
@apply flex items-center justify-between gap-3 rounded-full border px-4 py-3 text-sm;
background: color-mix(in srgb, var(--surface-container-high) 78%, white);
@@ -314,7 +387,7 @@ textarea:focus-visible {
}
.suggestion-chip {
@apply min-h-11 rounded-full border px-4 py-2 text-sm font-semibold transition disabled:cursor-not-allowed;
@apply min-h-11 rounded-full border px-4 py-2 text-sm font-medium transition disabled:cursor-not-allowed;
background: color-mix(in srgb, var(--surface-container-high) 72%, white);
border-color: var(--outline-variant);
color: var(--text);
@@ -353,12 +426,12 @@ textarea:focus-visible {
}
.coach-panel-kicker {
@apply text-xs font-semibold uppercase tracking-wide;
@apply text-xs font-medium uppercase tracking-wide;
color: var(--primary);
}
.coach-panel-heading {
@apply text-lg font-semibold leading-snug;
@apply text-lg font-medium leading-snug;
color: var(--text);
}
@@ -367,7 +440,7 @@ textarea:focus-visible {
}
.coach-status-pill {
@apply inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-semibold;
@apply inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium;
background: var(--surface-container-high);
border-color: var(--outline-variant);
color: var(--muted);
@@ -389,7 +462,7 @@ textarea:focus-visible {
}
.coach-expand-button {
@apply inline-flex items-center gap-1 rounded-full border px-3 py-1 text-xs font-semibold;
@apply inline-flex items-center gap-1 rounded-full border px-3 py-1 text-xs font-medium;
border-color: var(--outline-variant);
color: var(--text);
}
@@ -399,7 +472,7 @@ textarea:focus-visible {
}
.coach-thread-chip {
@apply inline-flex items-center overflow-hidden rounded-full border text-xs font-semibold;
@apply inline-flex items-center overflow-hidden rounded-full border text-xs font-medium;
border-color: var(--outline-variant);
background: var(--surface-container-high);
}
@@ -426,7 +499,7 @@ textarea:focus-visible {
}
.coach-panel-context {
@apply flex flex-wrap gap-3 text-xs font-semibold;
@apply flex flex-wrap gap-3 text-xs font-medium;
color: var(--muted);
}
@@ -461,7 +534,7 @@ textarea:focus-visible {
}
.coach-line-avatar {
@apply flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold;
@apply flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[10px] font-medium;
background: var(--surface-container-high);
color: var(--text);
}
@@ -522,12 +595,12 @@ textarea:focus-visible {
}
.thinking-pill-label {
@apply relative z-[1] text-xs font-semibold tracking-wide;
@apply relative z-[1] text-xs font-medium tracking-wide;
color: var(--muted);
}
.thinking-pill-chevron {
@apply absolute right-3 z-[1] text-xs font-bold opacity-70;
@apply absolute right-3 z-[1] text-xs font-medium opacity-70;
color: var(--primary);
animation: thinking-unlock-nudge 1.8s ease-in-out infinite;
}
@@ -554,7 +627,7 @@ textarea:focus-visible {
}
.thinking-details summary {
@apply cursor-pointer text-xs font-medium;
@apply cursor-pointer text-xs font-normal;
color: var(--muted);
}
@@ -681,7 +754,7 @@ textarea:focus-visible {
.command-button,
.secondary-button {
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
background: var(--secondary-container);
border-color: transparent;
color: var(--on-secondary-container);
@@ -689,7 +762,7 @@ textarea:focus-visible {
}
.primary-button {
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold shadow-can transition active:scale-[0.99] disabled:cursor-not-allowed;
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium shadow-can transition active:scale-[0.99] disabled:cursor-not-allowed;
background: var(--primary);
border-color: transparent;
color: var(--on-primary);
@@ -697,7 +770,7 @@ textarea:focus-visible {
}
.excel-button {
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
background: var(--tertiary-container);
border-color: transparent;
color: var(--on-tertiary-container);
@@ -725,7 +798,7 @@ textarea:focus-visible {
}
.danger-button {
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
@apply inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-medium shadow-sm transition active:scale-[0.99] disabled:cursor-not-allowed;
background: var(--error);
border-color: transparent;
color: var(--on-error);
@@ -733,7 +806,7 @@ textarea:focus-visible {
}
.nav-item {
@apply flex min-h-12 items-center gap-3 border border-transparent px-4 text-sm font-medium transition;
@apply flex min-h-12 items-center gap-3 border border-transparent px-4 text-sm font-normal transition;
border-radius: 999px;
color: var(--muted);
}
@@ -757,14 +830,14 @@ textarea:focus-visible {
}
.quick-add {
@apply inline-flex min-h-12 items-center gap-2 rounded-md border px-3 text-sm font-semibold shadow-sm transition;
@apply inline-flex min-h-12 items-center gap-2 rounded-md border px-3 text-sm font-medium shadow-sm transition;
background: var(--surface-container-high);
border-color: var(--outline-variant);
color: var(--text);
}
.field-label {
@apply grid gap-2 text-sm font-medium;
@apply grid gap-2 text-sm font-normal;
color: var(--muted);
}
@@ -801,7 +874,7 @@ textarea:focus-visible {
}
.list-button {
@apply flex min-h-11 items-center justify-between rounded-lg border px-3 text-sm font-semibold transition;
@apply flex min-h-11 items-center justify-between rounded-lg border px-3 text-sm font-medium transition;
background: var(--secondary-container);
border-color: transparent;
color: var(--on-secondary-container);
@@ -812,7 +885,7 @@ textarea:focus-visible {
}
.theme-indicator {
@apply inline-flex min-h-11 items-center gap-2 rounded-full border px-3 text-sm font-semibold transition;
@apply inline-flex min-h-11 items-center gap-2 rounded-full border px-3 text-sm font-medium transition;
background: var(--surface-container-high);
border-color: var(--outline-variant);
color: var(--text);
@@ -842,7 +915,7 @@ textarea:focus-visible {
}
.settings-tabs button {
@apply rounded-full px-4 py-2 text-sm font-semibold transition;
@apply rounded-full px-4 py-2 text-sm font-medium transition;
color: var(--muted);
}
@@ -888,7 +961,7 @@ textarea:focus-visible {
}
.theme-tile-label {
@apply text-sm font-semibold leading-5;
@apply text-sm font-medium leading-5;
}
}
@@ -964,6 +1037,22 @@ textarea:focus-visible {
letter-spacing: 0 !important;
}
.app-shell .font-black,
.app-shell .font-extrabold {
font-weight: 600 !important;
}
.app-shell .font-bold,
.app-shell .font-semibold,
.app-shell strong,
.app-shell b {
font-weight: 500 !important;
}
.app-shell .font-medium {
font-weight: 400 !important;
}
.app-shell .text-white,
.app-shell .text-slate-50,
.app-shell .text-slate-100,