diff --git a/docs/frontend-design-cookbook.md b/docs/frontend-design-cookbook.md index ef98dba..c33af2d 100644 --- a/docs/frontend-design-cookbook.md +++ b/docs/frontend-design-cookbook.md @@ -80,10 +80,12 @@ Use these wrappers before introducing route-specific structure: - `editorial-page`: standard constrained content width for route pages. - `editorial-hero`: top summary strip for title, subtitle, and primary actions. +- `PageHeader.svelte`: preferred reusable wrapper for page-level hero sections; use it to keep title hierarchy, backlinks, meta rows, and action placement consistent. - `editorial-panel`: primary surface for forms, tables, and ledgers. - `editorial-toolbar`: compact action row under hero copy. - `editorial-backlink`: standard top-left back navigation style. - `editorial-alert`, `editorial-alert--error`, `editorial-alert--success`, `editorial-alert--warning`, `editorial-alert--info`: feedback banners. +- `page-header-meta`, `page-header-foot`, `hero-strip`: shared secondary rows inside page headers for compact metadata and summary stats. ### Collapsible panels @@ -117,10 +119,14 @@ This matches the warm editorial aesthetic and maintains visual consistency with These classes are already in use and should be reused: -- Lists and ledgers: `routine-ledger-row`, `products-mobile-card`, `health-entry-row` -- Group headers: `products-section-title` -- Table shell: `products-table-shell` +- Lists and ledgers: `routine-ledger-row`, `editorial-mobile-card`, `health-entry-row` +- Group headers: `editorial-section-title` +- Table shell: `editorial-table-shell` +- Compact metadata rows: `editorial-meta-strip` - Tabs shell: `products-tabs`, `editorial-tabs` +- App shell/navigation: `app-mobile-header`, `app-drawer`, `app-nav-list`, `app-nav-link`, `app-sidebar-footer` +- Reusable locale control: `LanguageSwitcher.svelte` with `language-switcher*` classes +- Dashboard summary patterns: `dashboard-stat-strip`, `dashboard-stat-card`, `dashboard-attention-list`, `dashboard-attention-item` - Health semantic pills: `health-kind-pill*`, `health-flag-pill*` - Lab results utilities: - metadata chips: `lab-results-meta-strip`, `lab-results-meta-pill` @@ -145,6 +151,7 @@ These classes are already in use and should be reused: - In dense row-based lists, prefer `ghost` action controls; use icon-only buttons on desktop tables and short text+icon `ghost` actions on mobile cards to keep row actions subordinate to data. - For editable data tables, open a dedicated inline edit panel above the list (instead of per-row expanded forms) and prefill it from row actions; keep users on the same filtered/paginated context after save. - When a list is narrowed to a single entity key (for example `test_code`), display an explicit "filtered by" banner with a one-click clear action and avoid extra grouping wrappers that add no context. +- For dashboard-style summaries, prefer compact stat strips and attention rows over large decorative cards; each item should pair one strong value with one short explanatory line. ### DRY form primitives @@ -211,6 +218,7 @@ These classes are already in use and should be reused: - Core tokens and global look: `frontend/src/app.css` - App shell and route domain mapping: `frontend/src/routes/+layout.svelte` +- Shared page header: `frontend/src/lib/components/PageHeader.svelte` - Route examples using the pattern: - `frontend/src/routes/+page.svelte` - `frontend/src/routes/products/+page.svelte` diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 5efb7b6..9dcc24a 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -32,8 +32,74 @@ "dashboard_title": "Dashboard", "dashboard_subtitle": "Your recent health & skincare overview", + "dashboard_dailyBriefing": "A quick read on what changed, what is missing, and where to look next.", "dashboard_latestSnapshot": "Latest Skin Snapshot", "dashboard_recentRoutines": "Recent Routines", + "dashboard_requiresAttention": "Requires attention", + "dashboard_healthPulse": "Health Pulse", + "dashboard_viewSkinHistory": "Open skin history", + "dashboard_viewLabResults": "Open lab results", + "dashboard_heroFreshness": "Freshness", + "dashboard_sinceLastSnapshot": "since last snapshot", + "dashboard_skinFreshness": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "Last snapshot was {count} day ago.", + "countPlural=*": "Last snapshot was {count} days ago." + } + } + ], + "dashboard_daysAgo": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} day ago", + "countPlural=*": "{count} days ago" + } + } + ], + "dashboard_daysAgoShort": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count}d", + "countPlural=*": "{count}d" + } + } + ], + "dashboard_flaggedResults": "flagged results", + "dashboard_flaggedLabsCount": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} flagged result", + "countPlural=*": "{count} flagged results" + } + } + ], + "dashboard_attentionSnapshot": "Skin log", + "dashboard_attentionRoutineAM": "AM routine", + "dashboard_attentionRoutinePM": "PM routine", + "dashboard_attentionLabs": "Lab review", + "dashboard_attentionMissing": "Missing", + "dashboard_attentionToday": "Today", + "dashboard_attentionStable": "No flagged items", + "dashboard_statusLogged": "Logged", + "dashboard_statusOpen": "Open", + "dashboard_metricHydration": "Hydration", + "dashboard_metricSensitivity": "Sensitivity", + "dashboard_metricSebumTzone": "Sebum T-zone", + "dashboard_metricSebumCheeks": "Sebum cheeks", + "dashboard_metricDelta": "Delta {delta}", + "dashboard_averageSteps": "Avg. {count} steps", + "dashboard_lastRoutine": "Last routine", + "dashboard_lastLabDate": "Last collection", + "dashboard_noLabResults": "No lab results yet.", "dashboard_noSnapshots": "No skin snapshots yet.", "dashboard_noRoutines": "No routines in the past 2 weeks.", @@ -152,6 +218,9 @@ "grooming_title": "Grooming Schedule", "grooming_backToRoutines": "Routines", "grooming_addEntry": "+ Add entry", + "grooming_newTitle": "New grooming entry", + "grooming_newSubtitle": "Add a recurring entry to your weekly grooming schedule.", + "grooming_newSectionIntro": "Choose the day, action, and an optional note.", "grooming_entryAdded": "Entry added.", "grooming_entryUpdated": "Entry updated.", "grooming_entryDeleted": "Entry deleted.", @@ -254,6 +323,8 @@ ], "medications_addNew": "+ Add medication", "medications_newTitle": "New medication", + "medications_newSubtitle": "Add a basic medication or supplement record for later tracking.", + "medications_newSectionIntro": "Start with the type, product name, and active substance.", "medications_kind": "Kind", "medications_productName": "Product name *", "medications_productNamePlaceholder": "e.g. Vitamin D3", @@ -291,6 +362,8 @@ ], "labResults_addNew": "+ Add result", "labResults_newTitle": "New lab result", + "labResults_newSubtitle": "Save a single lab result and add it to your health history.", + "labResults_newSectionIntro": "Start with the date and LOINC code, then add the remaining details.", "labResults_flagFilter": "Flag:", "labResults_flagAll": "All", "labResults_flagNone": "None", @@ -374,6 +447,8 @@ "skin_analyzePhotos": "Analyze photos", "skin_analyzing": "Analyzing…", "skin_newSnapshotTitle": "New skin snapshot", + "skin_newSubtitle": "Capture today’s skin state manually or prefill the form with AI photo analysis.", + "skin_newSectionIntro": "Start with the date and overall condition, then refine the details.", "skin_date": "Date *", "skin_overallState": "Overall state", "skin_texture": "Texture", diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index cfd8308..b0fca21 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -32,8 +32,82 @@ "dashboard_title": "Dashboard", "dashboard_subtitle": "Przegląd zdrowia i pielęgnacji", + "dashboard_dailyBriefing": "Szybki rzut oka na zmiany, braki i miejsca, które warto teraz sprawdzić.", "dashboard_latestSnapshot": "Ostatni stan skóry", "dashboard_recentRoutines": "Ostatnie rutyny", + "dashboard_requiresAttention": "Wymaga uwagi", + "dashboard_healthPulse": "Puls zdrowia", + "dashboard_viewSkinHistory": "Otwórz historię skóry", + "dashboard_viewLabResults": "Otwórz wyniki badań", + "dashboard_heroFreshness": "Świeżość", + "dashboard_sinceLastSnapshot": "od ostatniego wpisu", + "dashboard_skinFreshness": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "Ostatni wpis był {count} dzień temu.", + "countPlural=few": "Ostatni wpis był {count} dni temu.", + "countPlural=many": "Ostatni wpis był {count} dni temu.", + "countPlural=*": "Ostatni wpis był {count} dni temu." + } + } + ], + "dashboard_daysAgo": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} dzień temu", + "countPlural=few": "{count} dni temu", + "countPlural=many": "{count} dni temu", + "countPlural=*": "{count} dni temu" + } + } + ], + "dashboard_daysAgoShort": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} d", + "countPlural=few": "{count} d", + "countPlural=many": "{count} d", + "countPlural=*": "{count} d" + } + } + ], + "dashboard_flaggedResults": "wyników oznaczonych flagą", + "dashboard_flaggedLabsCount": [ + { + "declarations": ["input count", "local countPlural = count: plural"], + "selectors": ["countPlural"], + "match": { + "countPlural=one": "{count} wynik z flagą", + "countPlural=few": "{count} wyniki z flagą", + "countPlural=many": "{count} wyników z flagą", + "countPlural=*": "{count} wyników z flagą" + } + } + ], + "dashboard_attentionSnapshot": "Dziennik skóry", + "dashboard_attentionRoutineAM": "Rutyna AM", + "dashboard_attentionRoutinePM": "Rutyna PM", + "dashboard_attentionLabs": "Przegląd badań", + "dashboard_attentionMissing": "Brak", + "dashboard_attentionToday": "Dzisiaj", + "dashboard_attentionStable": "Brak flagowanych pozycji", + "dashboard_statusLogged": "Zapisane", + "dashboard_statusOpen": "Do uzupełnienia", + "dashboard_metricHydration": "Nawodnienie", + "dashboard_metricSensitivity": "Wrażliwość", + "dashboard_metricSebumTzone": "Sebum T-zone", + "dashboard_metricSebumCheeks": "Sebum policzki", + "dashboard_metricDelta": "Zmiana {delta}", + "dashboard_averageSteps": "Śr. {count} kroków", + "dashboard_lastRoutine": "Ostatnia rutyna", + "dashboard_lastLabDate": "Ostatnie badanie", + "dashboard_noLabResults": "Brak wyników badań.", "dashboard_noSnapshots": "Brak wpisów o stanie skóry.", "dashboard_noRoutines": "Brak rutyn w ciągu ostatnich 2 tygodni.", @@ -156,6 +230,9 @@ "grooming_title": "Harmonogram pielęgnacji", "grooming_backToRoutines": "Rutyny", "grooming_addEntry": "+ Dodaj wpis", + "grooming_newTitle": "Nowy wpis pielęgnacyjny", + "grooming_newSubtitle": "Dodaj stały wpis do tygodniowego harmonogramu pielęgnacji.", + "grooming_newSectionIntro": "Ustal dzień, czynność i krótką notatkę, jeśli chcesz.", "grooming_entryAdded": "Wpis dodany.", "grooming_entryUpdated": "Wpis zaktualizowany.", "grooming_entryDeleted": "Wpis usunięty.", @@ -262,6 +339,8 @@ ], "medications_addNew": "+ Dodaj lek", "medications_newTitle": "Nowy lek", + "medications_newSubtitle": "Dodaj podstawowy rekord leku lub suplementu do dalszego śledzenia.", + "medications_newSectionIntro": "Zacznij od rodzaju, nazwy i substancji czynnej.", "medications_kind": "Rodzaj", "medications_productName": "Nazwa produktu *", "medications_productNamePlaceholder": "np. Witamina D3", @@ -303,6 +382,8 @@ ], "labResults_addNew": "+ Dodaj wynik", "labResults_newTitle": "Nowy wynik badania", + "labResults_newSubtitle": "Zapisz pojedynczy wynik badania, aby dołączyć go do historii zdrowia.", + "labResults_newSectionIntro": "Najpierw podaj datę i kod LOINC, resztę możesz uzupełnić skrótowo.", "labResults_flagFilter": "Flaga:", "labResults_flagAll": "Wszystkie", "labResults_flagNone": "Brak", @@ -388,6 +469,8 @@ "skin_analyzePhotos": "Analizuj zdjęcia", "skin_analyzing": "Analizuję…", "skin_newSnapshotTitle": "Nowy wpis", + "skin_newSubtitle": "Zapisz bieżący stan skóry ręcznie lub uzupełnij pola analizą AI ze zdjęć.", + "skin_newSectionIntro": "Zacznij od daty i ogólnej oceny, a potem doprecyzuj szczegóły.", "skin_date": "Data *", "skin_overallState": "Ogólny stan", "skin_texture": "Tekstura", diff --git a/frontend/src/app.css b/frontend/src/app.css index 93a802d..4aa2272 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -153,40 +153,182 @@ body { } .app-mobile-header { + position: sticky; + top: 0; + z-index: 40; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; border-bottom: 1px solid hsl(35 22% 76% / 0.7); - background: linear-gradient(180deg, hsl(44 35% 97%), hsl(44 25% 94%)); + background: linear-gradient(180deg, hsl(44 35% 97% / 0.92), hsl(44 25% 94% / 0.96)); + backdrop-filter: blur(16px); + padding: 0.9rem 1rem; +} + +.app-mobile-titleblock { + min-width: 0; +} + +.app-mobile-overline, +.app-sidebar-subtitle { + margin: 0; + color: var(--muted-foreground); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.16em; + line-height: 1.35; + overflow-wrap: anywhere; + text-transform: uppercase; } .app-mobile-title, .app-brand { + display: block; font-family: 'Cormorant Infant', 'Times New Roman', serif; font-size: 1.2rem; font-weight: 600; letter-spacing: 0.02em; } +.app-mobile-toggle, .app-icon-button { display: flex; - height: 2rem; - width: 2rem; + height: 2.6rem; + width: 2.6rem; align-items: center; justify-content: center; - border: 1px solid hsl(34 21% 75%); - border-radius: 0.45rem; + border: 1px solid hsl(34 21% 75% / 0.95); + border-radius: 999px; + background: hsl(42 32% 95% / 0.92); color: var(--muted-foreground); + box-shadow: 0 10px 24px -20px hsl(220 32% 14% / 0.55); } +.app-mobile-toggle:hover, .app-icon-button:hover { color: var(--foreground); border-color: var(--page-accent); background: var(--page-accent-soft); } +.app-drawer-backdrop { + position: fixed; + inset: 0; + z-index: 50; + background: hsl(220 40% 8% / 0.42); + backdrop-filter: blur(4px); +} + .app-sidebar { border-right: 1px solid hsl(36 20% 73% / 0.75); background: linear-gradient(180deg, hsl(44 34% 97%), hsl(42 28% 94%)); } +.app-drawer { + position: fixed; + inset: 0 auto 0 0; + z-index: 60; + display: flex; + width: min(20rem, calc(100vw - 1.5rem)); + flex-direction: column; + gap: 1rem; + overflow-y: auto; + border-right: 1px solid hsl(36 20% 73% / 0.75); + background: linear-gradient(180deg, hsl(44 35% 97%), hsl(42 29% 94%)); + padding: 1.1rem 0.85rem 1rem; + box-shadow: 0 28px 56px -28px hsl(220 34% 14% / 0.42); +} + +.app-sidebar-brandblock { + margin-bottom: 0.8rem; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.75rem; + padding: 0 0.8rem; +} + +.app-sidebar-brandcopy { + min-width: 0; + max-width: 100%; +} + +.app-mobile-header--menu-open { + border-bottom-color: transparent; + background: transparent; + backdrop-filter: none; +} + +.app-nav-list { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.app-nav-link { + display: flex; + align-items: center; + gap: 0.75rem; + border: 1px solid transparent; + border-radius: 0.95rem; + padding: 0.8rem 0.9rem; + color: var(--muted-foreground); + font-size: 0.93rem; + text-decoration: none; + transition: border-color 140ms ease, background-color 140ms ease, color 140ms ease, transform 140ms ease; +} + +.app-nav-link:hover { + transform: translateX(2px); + border-color: hsl(35 23% 76% / 0.75); + background: hsl(42 28% 92% / 0.78); + color: var(--foreground); +} + +.app-nav-link--active { + border-color: color-mix(in srgb, var(--page-accent) 45%, white); + background: color-mix(in srgb, var(--page-accent) 13%, white); + color: var(--foreground); + box-shadow: inset 0 1px 0 hsl(0 0% 100% / 0.7); +} + +.app-sidebar-footer { + margin-top: auto; + padding: 0.8rem; +} + +.language-switcher { + display: inline-flex; + width: 100%; + border: 1px solid hsl(35 22% 75% / 0.82); + border-radius: 999px; + background: hsl(42 30% 93% / 0.9); + padding: 0.2rem; +} + +.language-switcher__button { + flex: 1; + border-radius: 999px; + padding: 0.45rem 0.7rem; + color: var(--muted-foreground); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.16em; + text-transform: uppercase; + transition: background-color 140ms ease, color 140ms ease, box-shadow 140ms ease; +} + +.language-switcher__button:hover { + color: var(--foreground); +} + +.language-switcher__button--active { + background: color-mix(in srgb, var(--page-accent) 14%, white); + color: var(--foreground); + box-shadow: inset 0 1px 0 hsl(0 0% 100% / 0.74); +} + .app-sidebar a { border: 1px solid transparent; } @@ -212,6 +354,7 @@ body { width: min(1160px, 100%); } +.app-main h1, .app-main h2 { font-family: 'Cormorant Infant', 'Times New Roman', serif; font-size: clamp(1.9rem, 3.3vw, 2.7rem); @@ -241,6 +384,19 @@ body { color: var(--foreground); } +.page-header-meta, +.page-header-foot { + grid-column: 1 / -1; +} + +.page-header-meta { + margin-top: 0.65rem; +} + +.page-header-foot { + margin-top: 1rem; +} + .editorial-toolbar { margin-top: 0.9rem; display: flex; @@ -289,7 +445,7 @@ body { color: hsl(207 78% 28%); } -.products-table-shell { +.editorial-table-shell { border: 1px solid hsl(35 24% 74% / 0.85); border-radius: 0.9rem; overflow: hidden; @@ -299,14 +455,14 @@ body { background: color-mix(in srgb, var(--page-accent) 10%, white); } -.products-mobile-card { +.editorial-mobile-card { display: block; border: 1px solid hsl(35 21% 76% / 0.85); border-radius: 0.8rem; padding: 0.95rem; } -.products-section-title { +.editorial-section-title { border-bottom: 1px dashed color-mix(in srgb, var(--page-accent) 35%, var(--border)); padding-bottom: 0.3rem; padding-top: 0.5rem; @@ -317,11 +473,7 @@ body { text-transform: uppercase; } -.products-sticky-actions { - border-color: color-mix(in srgb, var(--page-accent) 25%, var(--border)); -} - -.products-meta-strip { +.editorial-meta-strip { display: flex; flex-wrap: wrap; align-items: center; @@ -488,7 +640,7 @@ body { font-feature-settings: 'tnum'; } -.lab-results-mobile-grid .products-section-title { +.lab-results-mobile-grid .editorial-section-title { margin-top: 0.15rem; } @@ -525,6 +677,20 @@ body { } @media (min-width: 768px) { + .app-mobile-header, + .app-drawer, + .app-drawer-backdrop { + display: none !important; + } + + .app-sidebar { + position: sticky; + top: 0; + align-self: flex-start; + height: 100vh; + overflow-y: auto; + } + .app-shell { flex-direction: row; } @@ -556,6 +722,10 @@ body { grid-area: subtitle; } + .dashboard-stat-strip { + grid-column: 1 / -1; + } + .editorial-toolbar { grid-area: actions; margin-top: 0; @@ -661,6 +831,122 @@ body { font-weight: 600; } +.dashboard-stat-strip { + margin-top: 1.8rem; + display: grid; + gap: 0.65rem; + grid-template-columns: repeat(4, minmax(0, 1fr)); + border-top: 1px dashed color-mix(in srgb, var(--page-accent) 24%, var(--editorial-line)); + padding-top: 1rem; +} + +.dashboard-stat-card { + border: 1px solid hsl(36 18% 77% / 0.62); + border-radius: 0.85rem; + background: linear-gradient(180deg, hsl(44 32% 96% / 0.74), hsl(45 24% 93% / 0.66)); + padding: 0.72rem 0.78rem; +} + +.dashboard-stat-label, +.dashboard-metric-label, +.dashboard-featured-label, +.dashboard-health-subline, +.dashboard-attention-label { + margin: 0; + color: var(--editorial-muted); + font-size: 0.73rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.dashboard-stat-value, +.dashboard-metric-value, +.dashboard-featured-value { + margin: 0.28rem 0 0; + font-family: 'Cormorant Infant', 'Times New Roman', serif; + font-size: 1.28rem; + font-weight: 600; + line-height: 1; +} + +.dashboard-stat-detail, +.dashboard-featured-meta, +.dashboard-featured-notes, +.dashboard-health-value, +.dashboard-attention-value, +.dashboard-panel-note, +.dashboard-metric-trend { + margin: 0.28rem 0 0; + color: var(--editorial-muted); + font-size: 0.8rem; +} + +.dashboard-attention-panel { + position: relative; + z-index: 1; + margin-bottom: 1rem; + border: 1px solid hsl(36 25% 74% / 0.8); + border-radius: 1.2rem; + background: linear-gradient(180deg, hsl(44 40% 95% / 0.92), hsl(42 32% 93% / 0.94)); + box-shadow: + 0 20px 40px -34px hsl(219 32% 14% / 0.38), + inset 0 1px 0 hsl(0 0% 100% / 0.7); + padding: 0.9rem; +} + +.dashboard-attention-header { + display: flex; + align-items: baseline; + gap: 0.75rem; + margin-bottom: 0.85rem; +} + +.dashboard-attention-header h3 { + margin: 0; + font-family: 'Cormorant Infant', 'Times New Roman', serif; + font-size: clamp(1.3rem, 2.3vw, 1.6rem); + font-weight: 600; +} + +.dashboard-attention-list { + display: grid; + gap: 0.6rem; + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.dashboard-attention-item { + display: flex; + min-height: 4.35rem; + flex-direction: column; + justify-content: space-between; + border: 1px solid hsl(36 22% 74% / 0.75); + border-radius: 0.9rem; + padding: 0.72rem 0.8rem; + text-decoration: none; + color: inherit; + transition: transform 140ms ease, border-color 140ms ease, background-color 140ms ease; +} + +.dashboard-attention-item:hover { + transform: translateY(-1px); + border-color: color-mix(in srgb, var(--page-accent) 42%, var(--border)); +} + +.dashboard-attention-item--alert { + background: linear-gradient(180deg, hsl(27 76% 95%), hsl(18 60% 91%)); +} + +.dashboard-attention-item--calm { + background: linear-gradient(180deg, hsl(130 24% 95%), hsl(136 26% 91%)); +} + +.dashboard-attention-value { + color: var(--foreground); + font-size: 0.88rem; + font-weight: 600; +} + .editorial-grid { position: relative; z-index: 1; @@ -671,11 +957,16 @@ body { .editorial-panel { border-radius: 1.2rem; - padding: 1rem; + padding: 0.9rem; +} + +.dashboard-grid { + grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr) minmax(0, 0.92fr); + align-items: start; } .panel-header { - margin-bottom: 0.9rem; + margin-bottom: 0.75rem; display: flex; align-items: baseline; justify-content: space-between; @@ -687,7 +978,7 @@ body { .panel-header h3 { margin: 0; font-family: 'Cormorant Infant', 'Times New Roman', serif; - font-size: clamp(1.35rem, 2.4vw, 1.7rem); + font-size: clamp(1.22rem, 2.1vw, 1.56rem); font-weight: 600; } @@ -714,7 +1005,7 @@ body { .snapshot-date { color: var(--editorial-muted); - font-size: 0.9rem; + font-size: 0.84rem; font-weight: 600; } @@ -786,33 +1077,51 @@ body { list-style: none; } +.dashboard-metric-row { + margin-top: 0.85rem; + display: grid; + gap: 0.55rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.dashboard-metric-card { + border: 1px solid hsl(36 23% 74% / 0.76); + border-radius: 0.8rem; + background: hsl(42 36% 93% / 0.7); + padding: 0.65rem 0.72rem; +} + +.dashboard-metric-value { + font-size: 1.08rem; +} + .routine-summary-strip { - margin-bottom: 0.7rem; + margin-bottom: 0.6rem; display: flex; flex-wrap: wrap; align-items: center; - gap: 0.4rem; + gap: 0.35rem; } .routine-summary-chip { border: 1px solid hsl(35 24% 71% / 0.85); border-radius: 999px; - padding: 0.22rem 0.62rem; + padding: 0.16rem 0.5rem; color: var(--editorial-muted); - font-size: 0.74rem; + font-size: 0.68rem; font-weight: 700; - letter-spacing: 0.08em; + letter-spacing: 0.06em; } .panel-action-link, .routine-summary-link { border: 1px solid color-mix(in srgb, var(--page-accent) 38%, var(--editorial-line)); border-radius: 999px; - padding: 0.24rem 0.64rem; + padding: 0.2rem 0.56rem; color: var(--page-accent); - font-size: 0.76rem; + font-size: 0.68rem; font-weight: 700; - letter-spacing: 0.08em; + letter-spacing: 0.06em; text-decoration: none; text-transform: uppercase; } @@ -826,6 +1135,32 @@ body { background: var(--page-accent-soft); } +.dashboard-featured-routine { + display: block; + margin-bottom: 0.7rem; + border: 1px solid hsl(36 24% 73% / 0.82); + border-radius: 0.88rem; + background: linear-gradient(155deg, color-mix(in srgb, var(--page-accent) 5%, white), hsl(44 30% 94%)); + padding: 0.78rem 0.84rem; + text-decoration: none; + color: inherit; +} + +.dashboard-featured-routine:hover { + border-color: color-mix(in srgb, var(--page-accent) 44%, var(--border)); +} + +.dashboard-featured-routine-topline { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; +} + +.dashboard-featured-notes { + line-height: 1.4; +} + .routine-item + .routine-item { border-top: 1px dashed hsl(36 26% 72% / 0.7); } @@ -834,7 +1169,7 @@ body { display: flex; align-items: center; gap: 0.6rem; - padding: 0.78rem 0; + padding: 0.68rem 0; text-decoration: none; color: inherit; transition: transform 140ms ease, color 160ms ease; @@ -858,9 +1193,9 @@ body { .routine-meta { display: flex; flex-wrap: wrap; - gap: 0.75rem; + gap: 0.6rem; color: var(--editorial-muted); - font-size: 0.8rem; + font-size: 0.76rem; } .routine-note-inline { @@ -882,7 +1217,7 @@ body { } .routine-date { - font-size: 0.93rem; + font-size: 0.88rem; font-weight: 600; } @@ -909,6 +1244,56 @@ body { display: flex; } +.dashboard-health-meta-strip { + margin-bottom: 0.65rem; +} + +.dashboard-health-list { + display: flex; + flex-direction: column; + gap: 0.55rem; +} + +.dashboard-health-item { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.65rem; + border: 1px solid hsl(36 22% 75% / 0.78); + border-radius: 0.85rem; + background: hsl(44 34% 95% / 0.75); + padding: 0.68rem 0.78rem; + text-decoration: none; + color: inherit; +} + +.dashboard-health-item:hover { + border-color: color-mix(in srgb, var(--page-accent) 36%, var(--border)); + background: var(--page-accent-soft); +} + +.dashboard-health-test { + margin: 0; + font-size: 0.88rem; + font-weight: 600; +} + +.dashboard-health-value-wrap { + display: flex; + min-width: 0; + flex-direction: column; + align-items: flex-end; + gap: 0.35rem; +} + +.dashboard-health-value { + margin-top: 0; + color: var(--foreground); + font-size: 0.8rem; + font-weight: 600; + text-align: right; +} + .reveal-1, .reveal-2, .reveal-3 { @@ -933,12 +1318,23 @@ body { } @media (max-width: 1024px) { + .dashboard-stat-strip, + .dashboard-attention-list, .editorial-grid { grid-template-columns: minmax(0, 1fr); } + + .dashboard-grid { + grid-template-columns: minmax(0, 1fr); + } } @media (max-width: 640px) { + .dashboard-stat-strip { + margin-top: 1.35rem; + padding-top: 0.8rem; + } + .editorial-title { font-size: 2.05rem; } @@ -951,6 +1347,21 @@ body { font-size: 1.4rem; } + .dashboard-metric-row { + grid-template-columns: minmax(0, 1fr); + } + + .dashboard-health-item, + .dashboard-featured-routine-topline, + .dashboard-attention-item { + align-items: flex-start; + flex-direction: column; + } + + .dashboard-health-value-wrap { + align-items: flex-start; + } + .state-pill, .routine-pill { letter-spacing: 0.08em; diff --git a/frontend/src/lib/components/AutoFixBadge.svelte b/frontend/src/lib/components/AutoFixBadge.svelte index 5a5797d..3b481f5 100644 --- a/frontend/src/lib/components/AutoFixBadge.svelte +++ b/frontend/src/lib/components/AutoFixBadge.svelte @@ -1,6 +1,6 @@ + +{#if messages.length} +
{kicker}
+ {/if} + +{subtitle}
+ {/if} + + {#if meta} + + {/if} + + {#if actions} + + {/if} + + {#if children} +{m["nav_appSubtitle"]()}
{m["nav_appName"]()}