@hbslovely/baby-feeding 2026.7.2 → 2026.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ import { CommonModule, DecimalPipe } from '@angular/common';
|
|
|
6
6
|
import { ActivatedRoute } from '@angular/router';
|
|
7
7
|
import { map, catchError, tap, switchMap, mergeMap, scan, takeLast, startWith, finalize, concatMap } from 'rxjs/operators';
|
|
8
8
|
import { throwError, of, from, Subject, interval, firstValueFrom, forkJoin } from 'rxjs';
|
|
9
|
-
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
9
|
+
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
|
|
10
10
|
import * as i1 from '@angular/forms';
|
|
11
11
|
import { FormsModule } from '@angular/forms';
|
|
12
12
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -19,6 +19,9 @@ const DEFAULT_FEEDING_CONFIG = {
|
|
|
19
19
|
googleClientId: '',
|
|
20
20
|
enableExplorer: false,
|
|
21
21
|
appsScriptDirect: false,
|
|
22
|
+
hfInferenceUrl: '/api/hf-chat',
|
|
23
|
+
hfApiToken: '',
|
|
24
|
+
hfModel: 'mistralai/Mistral-7B-Instruct-v0.3',
|
|
22
25
|
};
|
|
23
26
|
const FEEDING_CONFIG_TOKEN = new InjectionToken('FEEDING_CONFIG', {
|
|
24
27
|
providedIn: 'root',
|
|
@@ -14529,9 +14532,13 @@ class HeroHeaderComponent {
|
|
|
14529
14532
|
this.weightLabel = input(null, ...(ngDevMode ? [{ debugName: "weightLabel" }] : /* istanbul ignore next */ []));
|
|
14530
14533
|
this.isAnyLoading = input(false, ...(ngDevMode ? [{ debugName: "isAnyLoading" }] : /* istanbul ignore next */ []));
|
|
14531
14534
|
this.showRefresh = input(true, ...(ngDevMode ? [{ debugName: "showRefresh" }] : /* istanbul ignore next */ []));
|
|
14535
|
+
/** Hiện/ẩn nút mở trợ lý chat (mặc định hiện). */
|
|
14536
|
+
this.showChat = input(true, ...(ngDevMode ? [{ debugName: "showChat" }] : /* istanbul ignore next */ []));
|
|
14532
14537
|
this.refresh = output();
|
|
14533
14538
|
/** Hamburger trên hero → mở float menu (xử lý ở parent). */
|
|
14534
14539
|
this.openMenu = output();
|
|
14540
|
+
/** Nút chat trên hero → mở subroute trợ lý AI (xử lý ở parent). */
|
|
14541
|
+
this.openChat = output();
|
|
14535
14542
|
}
|
|
14536
14543
|
get genderEmoji() {
|
|
14537
14544
|
const g = this.profile().gender;
|
|
@@ -14554,12 +14561,12 @@ class HeroHeaderComponent {
|
|
|
14554
14561
|
return parts.join(' ');
|
|
14555
14562
|
}
|
|
14556
14563
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HeroHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14557
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: HeroHeaderComponent, isStandalone: true, selector: "app-hero-header", inputs: { profile: { classPropertyName: "profile", publicName: "profile", isSignal: true, isRequired: true, transformFunction: null }, age: { classPropertyName: "age", publicName: "age", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, weightLabel: { classPropertyName: "weightLabel", publicName: "weightLabel", isSignal: true, isRequired: false, transformFunction: null }, isAnyLoading: { classPropertyName: "isAnyLoading", publicName: "isAnyLoading", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", openMenu: "openMenu" }, ngImport: i0, template: "<div class=\"hero-header\">\n <!-- \u2500\u2500 ROW A: Identity \u2500\u2500 -->\n <div class=\"hero-header__identity\">\n <!-- Avatar -->\n <div class=\"hero-header__avatar\" aria-hidden=\"true\">\n {{ genderEmoji }}\n </div>\n\n <!-- Name + meta -->\n <div class=\"hero-header__info\">\n <div class=\"hero-header__name-row\">\n <h1 class=\"hero-header__name\">{{ profile().babyName }}</h1>\n <span class=\"hero-header__age-chip\">{{ ageChip }}</span>\n </div>\n <p class=\"hero-header__meta\">\n <span>{{ age().weeks }} tu\u1EA7n</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ profile().birthDate | date: 'dd/MM/yyyy' }}</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ user() }}</span>\n @if (weightLabel()) {\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"hero-header__weight\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n {{ weightLabel() }}\n </span>\n }\n </p>\n </div>\n\n <!-- Actions -->\n <div class=\"hero-header__actions\">\n <!-- Activity log is projected via ng-content -->\n <ng-content select=\"[slot=activity-log]\"></ng-content>\n\n @if (showRefresh()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"refresh.emit()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i'\"\n aria-label=\"T\u1EA3i l\u1EA1i d\u1EEF li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\" aria-hidden=\"true\"></i>\n </button>\n }\n\n <!-- Float menu trigger (hamburger) -->\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"openMenu.emit()\"\n title=\"Menu\"\n aria-label=\"Menu\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".hero-header{padding:12px 16px 10px}.hero-header__identity{display:flex;align-items:center;gap:10px}.hero-header__avatar{width:44px;height:44px;border-radius:14px;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;border:2px solid rgba(91,108,255,.25)}.hero-header__info{flex:1;min-width:0}.hero-header__name-row{display:flex;align-items:center;gap:7px;margin-bottom:3px}.hero-header__name{font-size:18px;font-weight:700;color:var(--text);letter-spacing:-.3px;white-space:nowrap;margin:0;line-height:1.2}.hero-header__age-chip{padding:2px 8px;border-radius:20px;background:var(--primary);color:#fff;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0}.hero-header__meta{display:flex;gap:4px;align-items:center;overflow:hidden;white-space:nowrap;margin:0;font-size:10px;color:var(--muted)}.hero-header__dot{color:var(--muted-2);font-size:9px}.hero-header__weight{display:inline-flex;align-items:center;gap:3px}.hero-header__weight .pi{font-size:9px;color:var(--accent)}.hero-header__actions{display:flex;gap:5px;align-items:center;flex-shrink:0}.hero-header__icon-btn{width:34px;height:34px;border-radius:10px;background:#5b6cff0f;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;color:var(--muted);cursor:pointer;transition:background .15s,color .15s;list-style:none}.hero-header__icon-btn:hover:not([disabled]){background:#5b6cff1f;color:var(--primary)}.hero-header__icon-btn[disabled]{opacity:.45;cursor:not-allowed}.hero-header__icon-btn .spin{animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.hero-header__menu{position:relative}.hero-header__menu-trigger::-webkit-details-marker{display:none}.hero-header__menu-panel{position:absolute;top:calc(100% + 6px);right:0;z-index:100;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:4px;min-width:190px;box-shadow:0 4px 16px #0f172a1f}.hero-header__menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border-radius:8px;font-size:13px;font-weight:500;color:var(--text);background:transparent;border:none;cursor:pointer;text-align:left;transition:background .12s}.hero-header__menu-item .pi{font-size:14px;color:var(--muted);width:16px;text-align:center}.hero-header__menu-item:hover{background:var(--primary-soft);color:var(--primary)}.hero-header__menu-item:hover .pi{color:var(--primary)}.hero-header__menu-item--danger,.hero-header__menu-item--danger .pi{color:var(--rose)}.hero-header__menu-item--danger:hover{background:var(--rose-soft);color:var(--rose)}.hero-header__menu-divider{border:none;border-top:1px solid var(--border);margin:3px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
14564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: HeroHeaderComponent, isStandalone: true, selector: "app-hero-header", inputs: { profile: { classPropertyName: "profile", publicName: "profile", isSignal: true, isRequired: true, transformFunction: null }, age: { classPropertyName: "age", publicName: "age", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, weightLabel: { classPropertyName: "weightLabel", publicName: "weightLabel", isSignal: true, isRequired: false, transformFunction: null }, isAnyLoading: { classPropertyName: "isAnyLoading", publicName: "isAnyLoading", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showChat: { classPropertyName: "showChat", publicName: "showChat", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", openMenu: "openMenu", openChat: "openChat" }, ngImport: i0, template: "<div class=\"hero-header\">\n <!-- \u2500\u2500 ROW A: Identity \u2500\u2500 -->\n <div class=\"hero-header__identity\">\n <!-- Avatar -->\n <div class=\"hero-header__avatar\" aria-hidden=\"true\">\n {{ genderEmoji }}\n </div>\n\n <!-- Name + meta -->\n <div class=\"hero-header__info\">\n <div class=\"hero-header__name-row\">\n <h1 class=\"hero-header__name\">{{ profile().babyName }}</h1>\n <span class=\"hero-header__age-chip\">{{ ageChip }}</span>\n </div>\n <p class=\"hero-header__meta\">\n <span>{{ age().weeks }} tu\u1EA7n</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ profile().birthDate | date: 'dd/MM/yyyy' }}</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ user() }}</span>\n @if (weightLabel()) {\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"hero-header__weight\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n {{ weightLabel() }}\n </span>\n }\n </p>\n </div>\n\n <!-- Actions -->\n <div class=\"hero-header__actions\">\n <!-- Activity log is projected via ng-content -->\n <ng-content select=\"[slot=activity-log]\"></ng-content>\n\n @if (showRefresh()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"refresh.emit()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i'\"\n aria-label=\"T\u1EA3i l\u1EA1i d\u1EEF li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (showChat()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn hero-header__icon-btn--chat\"\n (click)=\"openChat.emit()\"\n title=\"Tr\u1EE3 l\u00FD AI\"\n aria-label=\"M\u1EDF tr\u1EE3 l\u00FD AI\"\n >\n <i class=\"pi pi-comments\" aria-hidden=\"true\"></i>\n </button>\n }\n\n <!-- Float menu trigger (hamburger) -->\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"openMenu.emit()\"\n title=\"Menu\"\n aria-label=\"Menu\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".hero-header{padding:12px 16px 10px}.hero-header__identity{display:flex;align-items:center;gap:10px}.hero-header__avatar{width:44px;height:44px;border-radius:14px;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;border:2px solid rgba(91,108,255,.25)}.hero-header__info{flex:1;min-width:0}.hero-header__name-row{display:flex;align-items:center;gap:7px;margin-bottom:3px}.hero-header__name{font-size:18px;font-weight:700;color:var(--text);letter-spacing:-.3px;white-space:nowrap;margin:0;line-height:1.2}.hero-header__age-chip{padding:2px 8px;border-radius:20px;background:var(--primary);color:#fff;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0}.hero-header__meta{display:flex;gap:4px;align-items:center;overflow:hidden;white-space:nowrap;margin:0;font-size:10px;color:var(--muted)}.hero-header__dot{color:var(--muted-2);font-size:9px}.hero-header__weight{display:inline-flex;align-items:center;gap:3px}.hero-header__weight .pi{font-size:9px;color:var(--accent)}.hero-header__actions{display:flex;gap:5px;align-items:center;flex-shrink:0}.hero-header__icon-btn{width:34px;height:34px;border-radius:10px;background:#5b6cff0f;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;color:var(--muted);cursor:pointer;transition:background .15s,color .15s;list-style:none}.hero-header__icon-btn:hover:not([disabled]){background:#5b6cff1f;color:var(--primary)}.hero-header__icon-btn[disabled]{opacity:.45;cursor:not-allowed}.hero-header__icon-btn .spin{animation:spin .8s linear infinite}.hero-header__icon-btn--chat{background:var(--accent-soft);border-color:transparent;color:var(--accent)}.hero-header__icon-btn--chat:hover:not([disabled]){background:color-mix(in srgb,var(--accent-soft) 70%,var(--accent) 10%);color:var(--accent)}@keyframes spin{to{transform:rotate(360deg)}}.hero-header__menu{position:relative}.hero-header__menu-trigger::-webkit-details-marker{display:none}.hero-header__menu-panel{position:absolute;top:calc(100% + 6px);right:0;z-index:100;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:4px;min-width:190px;box-shadow:0 4px 16px #0f172a1f}.hero-header__menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border-radius:8px;font-size:13px;font-weight:500;color:var(--text);background:transparent;border:none;cursor:pointer;text-align:left;transition:background .12s}.hero-header__menu-item .pi{font-size:14px;color:var(--muted);width:16px;text-align:center}.hero-header__menu-item:hover{background:var(--primary-soft);color:var(--primary)}.hero-header__menu-item:hover .pi{color:var(--primary)}.hero-header__menu-item--danger,.hero-header__menu-item--danger .pi{color:var(--rose)}.hero-header__menu-item--danger:hover{background:var(--rose-soft);color:var(--rose)}.hero-header__menu-divider{border:none;border-top:1px solid var(--border);margin:3px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
14558
14565
|
}
|
|
14559
14566
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HeroHeaderComponent, decorators: [{
|
|
14560
14567
|
type: Component,
|
|
14561
|
-
args: [{ selector: 'app-hero-header', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"hero-header\">\n <!-- \u2500\u2500 ROW A: Identity \u2500\u2500 -->\n <div class=\"hero-header__identity\">\n <!-- Avatar -->\n <div class=\"hero-header__avatar\" aria-hidden=\"true\">\n {{ genderEmoji }}\n </div>\n\n <!-- Name + meta -->\n <div class=\"hero-header__info\">\n <div class=\"hero-header__name-row\">\n <h1 class=\"hero-header__name\">{{ profile().babyName }}</h1>\n <span class=\"hero-header__age-chip\">{{ ageChip }}</span>\n </div>\n <p class=\"hero-header__meta\">\n <span>{{ age().weeks }} tu\u1EA7n</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ profile().birthDate | date: 'dd/MM/yyyy' }}</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ user() }}</span>\n @if (weightLabel()) {\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"hero-header__weight\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n {{ weightLabel() }}\n </span>\n }\n </p>\n </div>\n\n <!-- Actions -->\n <div class=\"hero-header__actions\">\n <!-- Activity log is projected via ng-content -->\n <ng-content select=\"[slot=activity-log]\"></ng-content>\n\n @if (showRefresh()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"refresh.emit()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i'\"\n aria-label=\"T\u1EA3i l\u1EA1i d\u1EEF li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\" aria-hidden=\"true\"></i>\n </button>\n }\n\n <!-- Float menu trigger (hamburger) -->\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"openMenu.emit()\"\n title=\"Menu\"\n aria-label=\"Menu\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".hero-header{padding:12px 16px 10px}.hero-header__identity{display:flex;align-items:center;gap:10px}.hero-header__avatar{width:44px;height:44px;border-radius:14px;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;border:2px solid rgba(91,108,255,.25)}.hero-header__info{flex:1;min-width:0}.hero-header__name-row{display:flex;align-items:center;gap:7px;margin-bottom:3px}.hero-header__name{font-size:18px;font-weight:700;color:var(--text);letter-spacing:-.3px;white-space:nowrap;margin:0;line-height:1.2}.hero-header__age-chip{padding:2px 8px;border-radius:20px;background:var(--primary);color:#fff;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0}.hero-header__meta{display:flex;gap:4px;align-items:center;overflow:hidden;white-space:nowrap;margin:0;font-size:10px;color:var(--muted)}.hero-header__dot{color:var(--muted-2);font-size:9px}.hero-header__weight{display:inline-flex;align-items:center;gap:3px}.hero-header__weight .pi{font-size:9px;color:var(--accent)}.hero-header__actions{display:flex;gap:5px;align-items:center;flex-shrink:0}.hero-header__icon-btn{width:34px;height:34px;border-radius:10px;background:#5b6cff0f;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;color:var(--muted);cursor:pointer;transition:background .15s,color .15s;list-style:none}.hero-header__icon-btn:hover:not([disabled]){background:#5b6cff1f;color:var(--primary)}.hero-header__icon-btn[disabled]{opacity:.45;cursor:not-allowed}.hero-header__icon-btn .spin{animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.hero-header__menu{position:relative}.hero-header__menu-trigger::-webkit-details-marker{display:none}.hero-header__menu-panel{position:absolute;top:calc(100% + 6px);right:0;z-index:100;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:4px;min-width:190px;box-shadow:0 4px 16px #0f172a1f}.hero-header__menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border-radius:8px;font-size:13px;font-weight:500;color:var(--text);background:transparent;border:none;cursor:pointer;text-align:left;transition:background .12s}.hero-header__menu-item .pi{font-size:14px;color:var(--muted);width:16px;text-align:center}.hero-header__menu-item:hover{background:var(--primary-soft);color:var(--primary)}.hero-header__menu-item:hover .pi{color:var(--primary)}.hero-header__menu-item--danger,.hero-header__menu-item--danger .pi{color:var(--rose)}.hero-header__menu-item--danger:hover{background:var(--rose-soft);color:var(--rose)}.hero-header__menu-divider{border:none;border-top:1px solid var(--border);margin:3px 8px}\n"] }]
|
|
14562
|
-
}], propDecorators: { profile: [{ type: i0.Input, args: [{ isSignal: true, alias: "profile", required: true }] }], age: [{ type: i0.Input, args: [{ isSignal: true, alias: "age", required: true }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }], weightLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "weightLabel", required: false }] }], isAnyLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAnyLoading", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refresh: [{ type: i0.Output, args: ["refresh"] }], openMenu: [{ type: i0.Output, args: ["openMenu"] }] } });
|
|
14568
|
+
args: [{ selector: 'app-hero-header', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"hero-header\">\n <!-- \u2500\u2500 ROW A: Identity \u2500\u2500 -->\n <div class=\"hero-header__identity\">\n <!-- Avatar -->\n <div class=\"hero-header__avatar\" aria-hidden=\"true\">\n {{ genderEmoji }}\n </div>\n\n <!-- Name + meta -->\n <div class=\"hero-header__info\">\n <div class=\"hero-header__name-row\">\n <h1 class=\"hero-header__name\">{{ profile().babyName }}</h1>\n <span class=\"hero-header__age-chip\">{{ ageChip }}</span>\n </div>\n <p class=\"hero-header__meta\">\n <span>{{ age().weeks }} tu\u1EA7n</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ profile().birthDate | date: 'dd/MM/yyyy' }}</span>\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span>{{ user() }}</span>\n @if (weightLabel()) {\n <span class=\"hero-header__dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"hero-header__weight\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n {{ weightLabel() }}\n </span>\n }\n </p>\n </div>\n\n <!-- Actions -->\n <div class=\"hero-header__actions\">\n <!-- Activity log is projected via ng-content -->\n <ng-content select=\"[slot=activity-log]\"></ng-content>\n\n @if (showRefresh()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"refresh.emit()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i'\"\n aria-label=\"T\u1EA3i l\u1EA1i d\u1EEF li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (showChat()) {\n <button\n type=\"button\"\n class=\"hero-header__icon-btn hero-header__icon-btn--chat\"\n (click)=\"openChat.emit()\"\n title=\"Tr\u1EE3 l\u00FD AI\"\n aria-label=\"M\u1EDF tr\u1EE3 l\u00FD AI\"\n >\n <i class=\"pi pi-comments\" aria-hidden=\"true\"></i>\n </button>\n }\n\n <!-- Float menu trigger (hamburger) -->\n <button\n type=\"button\"\n class=\"hero-header__icon-btn\"\n (click)=\"openMenu.emit()\"\n title=\"Menu\"\n aria-label=\"Menu\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".hero-header{padding:12px 16px 10px}.hero-header__identity{display:flex;align-items:center;gap:10px}.hero-header__avatar{width:44px;height:44px;border-radius:14px;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;border:2px solid rgba(91,108,255,.25)}.hero-header__info{flex:1;min-width:0}.hero-header__name-row{display:flex;align-items:center;gap:7px;margin-bottom:3px}.hero-header__name{font-size:18px;font-weight:700;color:var(--text);letter-spacing:-.3px;white-space:nowrap;margin:0;line-height:1.2}.hero-header__age-chip{padding:2px 8px;border-radius:20px;background:var(--primary);color:#fff;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0}.hero-header__meta{display:flex;gap:4px;align-items:center;overflow:hidden;white-space:nowrap;margin:0;font-size:10px;color:var(--muted)}.hero-header__dot{color:var(--muted-2);font-size:9px}.hero-header__weight{display:inline-flex;align-items:center;gap:3px}.hero-header__weight .pi{font-size:9px;color:var(--accent)}.hero-header__actions{display:flex;gap:5px;align-items:center;flex-shrink:0}.hero-header__icon-btn{width:34px;height:34px;border-radius:10px;background:#5b6cff0f;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;color:var(--muted);cursor:pointer;transition:background .15s,color .15s;list-style:none}.hero-header__icon-btn:hover:not([disabled]){background:#5b6cff1f;color:var(--primary)}.hero-header__icon-btn[disabled]{opacity:.45;cursor:not-allowed}.hero-header__icon-btn .spin{animation:spin .8s linear infinite}.hero-header__icon-btn--chat{background:var(--accent-soft);border-color:transparent;color:var(--accent)}.hero-header__icon-btn--chat:hover:not([disabled]){background:color-mix(in srgb,var(--accent-soft) 70%,var(--accent) 10%);color:var(--accent)}@keyframes spin{to{transform:rotate(360deg)}}.hero-header__menu{position:relative}.hero-header__menu-trigger::-webkit-details-marker{display:none}.hero-header__menu-panel{position:absolute;top:calc(100% + 6px);right:0;z-index:100;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:4px;min-width:190px;box-shadow:0 4px 16px #0f172a1f}.hero-header__menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border-radius:8px;font-size:13px;font-weight:500;color:var(--text);background:transparent;border:none;cursor:pointer;text-align:left;transition:background .12s}.hero-header__menu-item .pi{font-size:14px;color:var(--muted);width:16px;text-align:center}.hero-header__menu-item:hover{background:var(--primary-soft);color:var(--primary)}.hero-header__menu-item:hover .pi{color:var(--primary)}.hero-header__menu-item--danger,.hero-header__menu-item--danger .pi{color:var(--rose)}.hero-header__menu-item--danger:hover{background:var(--rose-soft);color:var(--rose)}.hero-header__menu-divider{border:none;border-top:1px solid var(--border);margin:3px 8px}\n"] }]
|
|
14569
|
+
}], propDecorators: { profile: [{ type: i0.Input, args: [{ isSignal: true, alias: "profile", required: true }] }], age: [{ type: i0.Input, args: [{ isSignal: true, alias: "age", required: true }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }], weightLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "weightLabel", required: false }] }], isAnyLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAnyLoading", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showChat: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChat", required: false }] }], refresh: [{ type: i0.Output, args: ["refresh"] }], openMenu: [{ type: i0.Output, args: ["openMenu"] }], openChat: [{ type: i0.Output, args: ["openChat"] }] } });
|
|
14563
14570
|
|
|
14564
14571
|
class HeroStatsStripComponent {
|
|
14565
14572
|
constructor() {
|
|
@@ -14832,6 +14839,1210 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
14832
14839
|
args: [{ selector: 'app-settings-menu', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"sm-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Menu\">\n <header class=\"sm-header\">\n <h1 class=\"sm-title\">C\u00E0i \u0111\u1EB7t</h1>\n <span class=\"sm-spacer\"></span>\n <button\n type=\"button\"\n class=\"sm-close\"\n (click)=\"close.emit()\"\n aria-label=\"\u0110\u00F3ng\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"sm-body\">\n <!-- \u2500\u2500 Profile actions \u2500\u2500 -->\n <nav class=\"sm-list\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('edit')\"\n >\n <i class=\"pi pi-pencil sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">S\u1EEDa profile b\u00E9</span>\n <span class=\"sm-item__hint\">\u0110\u1ED5i t\u00EAn, ng\u00E0y sinh, gi\u1EDBi t\u00EDnh</span>\n </span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('settings')\"\n >\n <i class=\"pi pi-sliders-h sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">C\u00E0i \u0111\u1EB7t chung</span>\n <span class=\"sm-item__hint\">C\u1EA3nh b\u00E1o, ng\u01B0\u1EE1ng c\u1EEF, th\u00F4ng b\u00E1o\u2026</span>\n </span>\n </button>\n </nav>\n\n <hr class=\"sm-divider\" />\n\n <!-- \u2500\u2500 Subroute entries \u2500\u2500 -->\n <nav class=\"sm-list\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('mom')\"\n >\n <i class=\"pi pi-sun sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">M\u1EB9</span>\n <span class=\"sm-item__hint\">S\u1EE9c kho\u1EBB & dinh d\u01B0\u1EE1ng m\u1EB9</span>\n </span>\n <i class=\"pi pi-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n @if (showDocuments()) {\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('documents')\"\n >\n <i class=\"pi pi-folder sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Explorer</span>\n <span class=\"sm-item__hint\">Kho t\u00E0i li\u1EC7u c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"pi pi-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('notifications')\"\n >\n <i class=\"pi pi-bell sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Th\u00F4ng b\u00E1o</span>\n <span class=\"sm-item__hint\">M\u1EDBi, \u0111\u00E3 \u0111\u1ECDc & c\u0169 h\u01A1n</span>\n </span>\n @if (unreadCount() > 0) {\n <span class=\"sm-item__badge\">{{ unreadCount() }}</span>\n } @else {\n <i class=\"pi pi-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n }\n </button>\n </nav>\n\n <!-- \u2500\u2500 About inline \u2500\u2500 -->\n <section class=\"sm-about\" aria-label=\"About\">\n <div class=\"sm-about__icon\" aria-hidden=\"true\">\n <i class=\"pi pi-info-circle\"></i>\n </div>\n <div class=\"sm-about__body\">\n <span class=\"sm-about__name\">{{ appName() }}</span>\n <span class=\"sm-about__version\">Phi\u00EAn b\u1EA3n {{ appVersion() }}</span>\n </div>\n <span class=\"sm-about__tag\">App</span>\n </section>\n\n <!-- \u2500\u2500 Delete (bottom, danger) \u2500\u2500 -->\n <hr class=\"sm-divider sm-divider--strong\" />\n <nav class=\"sm-list sm-bottom\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item sm-item--danger\"\n role=\"menuitem\"\n (click)=\"onSelect('delete')\"\n >\n <i class=\"pi pi-trash sm-item__icon\" aria-hidden=\"true\"></i>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Xo\u00E1 profile</span>\n <span class=\"sm-item__hint\">G\u1EE1 h\u1ED3 s\u01A1 b\u00E9 kh\u1ECFi thi\u1EBFt b\u1ECB n\u00E0y</span>\n </span>\n </button>\n </nav>\n </div>\n</section>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--accent: #ff5c8a;--accent-soft: #ffe7ef;--rose: #ef4444;--rose-soft: #fee2e2;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:sm-slide-in .22s cubic-bezier(.22,1,.36,1)}@keyframes sm-slide-in{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.sm-root{display:flex;flex-direction:column;flex:1;min-height:0}.sm-header{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:8px 12px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.sm-title{margin:0;font-size:17px;font-weight:700;color:var(--text)}.sm-spacer{flex:1}.sm-close{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:9px;border:none;background:transparent;color:var(--muted);font-size:15px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.sm-close:hover{background:var(--primary-soft);color:var(--primary)}.sm-body{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:8px 0 calc(24px + env(safe-area-inset-bottom,0px))}.sm-list{display:flex;flex-direction:column;background:var(--surface)}.sm-divider{border:none;height:1px;background:var(--border);margin:0}.sm-divider--strong{height:8px;background:var(--bg)}.sm-item{display:flex;align-items:center;gap:14px;width:100%;padding:12px 16px;border:none;background:transparent;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-item:hover{background:var(--primary-soft)}.sm-item:active{background:color-mix(in srgb,var(--primary-soft) 55%,var(--surface))}.sm-item__icon{flex-shrink:0;width:22px;text-align:center;font-size:16px;color:var(--muted)}.sm-item__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-item__label{font-size:15px;font-weight:600;color:var(--text)}.sm-item__hint{font-size:12px;color:var(--muted-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-item__chev{flex-shrink:0;font-size:13px;color:var(--muted-2)}.sm-item__badge{flex-shrink:0;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--accent);color:#fff;font-size:11px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}.sm-about{display:flex;align-items:center;gap:12px;margin:12px 12px 4px;padding:12px 14px;border-radius:14px;background:var(--primary-soft);border:1px solid color-mix(in srgb,var(--primary) 14%,transparent)}.sm-about__icon{flex-shrink:0;width:38px;height:38px;border-radius:999px;background:var(--surface);color:var(--primary);font-size:18px;display:flex;align-items:center;justify-content:center}.sm-about__body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.sm-about__name{font-size:15px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-about__version{font-size:12px;color:var(--muted)}.sm-about__tag{flex-shrink:0;font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--primary);background:var(--surface);padding:3px 8px;border-radius:999px}.sm-bottom{background:var(--surface)}.sm-item--danger:hover{background:var(--rose-soft)}.sm-item--danger:active{background:color-mix(in srgb,var(--rose-soft) 55%,var(--surface))}.sm-item--danger .sm-item__icon{color:var(--rose)}.sm-item--danger .sm-item__label{color:var(--rose);font-weight:600}\n"] }]
|
|
14833
14840
|
}], propDecorators: { appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], appVersion: [{ type: i0.Input, args: [{ isSignal: true, alias: "appVersion", required: false }] }], showDocuments: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDocuments", required: false }] }], unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], select: [{ type: i0.Output, args: ["select"] }] } });
|
|
14834
14841
|
|
|
14842
|
+
/**
|
|
14843
|
+
* Khung nhập + suggestion pills ở đáy chat.
|
|
14844
|
+
* - Enter gửi, Shift+Enter xuống dòng.
|
|
14845
|
+
* - Click pill → emit send với text gợi ý.
|
|
14846
|
+
*/
|
|
14847
|
+
class ChatComposerComponent {
|
|
14848
|
+
constructor() {
|
|
14849
|
+
this.send = new EventEmitter();
|
|
14850
|
+
this.draft = '';
|
|
14851
|
+
this.suggestions = [
|
|
14852
|
+
'Thêm cữ bú 80ml lúc 14h',
|
|
14853
|
+
'Pha sữa 90ml',
|
|
14854
|
+
'Bé bú hôm nay thế nào?',
|
|
14855
|
+
'Ghi cân nặng 6.2kg',
|
|
14856
|
+
'Lịch tiêm kế tiếp',
|
|
14857
|
+
'Tư vấn tăng cữ sữa',
|
|
14858
|
+
];
|
|
14859
|
+
}
|
|
14860
|
+
onEnter(ev) {
|
|
14861
|
+
if (ev.shiftKey)
|
|
14862
|
+
return; // xuống dòng
|
|
14863
|
+
ev.preventDefault();
|
|
14864
|
+
this.submit();
|
|
14865
|
+
}
|
|
14866
|
+
submit() {
|
|
14867
|
+
const text = this.draft.trim();
|
|
14868
|
+
if (!text)
|
|
14869
|
+
return;
|
|
14870
|
+
this.send.emit(text);
|
|
14871
|
+
this.draft = '';
|
|
14872
|
+
}
|
|
14873
|
+
emitSend(text) {
|
|
14874
|
+
this.send.emit(text);
|
|
14875
|
+
}
|
|
14876
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14877
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ChatComposerComponent, isStandalone: true, selector: "app-chat-composer", outputs: { send: "send" }, ngImport: i0, template: `
|
|
14878
|
+
<div class="cc-suggestions">
|
|
14879
|
+
@for (s of suggestions; track s) {
|
|
14880
|
+
<button type="button" class="cc-pill" (click)="emitSend(s)">{{ s }}</button>
|
|
14881
|
+
}
|
|
14882
|
+
</div>
|
|
14883
|
+
<form class="cc-row" (submit)="$event.preventDefault(); submit()">
|
|
14884
|
+
<textarea
|
|
14885
|
+
class="cc-input"
|
|
14886
|
+
rows="1"
|
|
14887
|
+
[(ngModel)]="draft"
|
|
14888
|
+
name="draft"
|
|
14889
|
+
placeholder="Nhập yêu cầu… vd: thêm cữ bú 80ml lúc 14h"
|
|
14890
|
+
(keydown.enter)="onEnter($event)"
|
|
14891
|
+
aria-label="Nhập yêu cầu cho trợ lý"
|
|
14892
|
+
></textarea>
|
|
14893
|
+
<button type="submit" class="cc-send" [disabled]="!draft.trim()" aria-label="Gửi">
|
|
14894
|
+
<i class="pi pi-send" aria-hidden="true"></i>
|
|
14895
|
+
</button>
|
|
14896
|
+
</form>
|
|
14897
|
+
`, isInline: true, styles: [":host{display:block;border-top:1px solid var(--border);background:var(--bg);padding:8px 12px calc(10px + env(safe-area-inset-bottom,0px))}.cc-suggestions{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:8px}.cc-pill{border:1px solid var(--border);background:var(--surface);color:var(--primary);font-size:11.5px;font-weight:600;padding:5px 9px;border-radius:999px;cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent}.cc-pill:hover{background:var(--primary-soft);border-color:transparent}.cc-row{display:flex;align-items:flex-end;gap:7px;background:var(--surface);border:1px solid var(--border);border-radius:18px;padding:5px 5px 5px 12px}.cc-input{flex:1;border:none;outline:none;resize:none;background:transparent;font-size:13px;line-height:1.4;color:var(--text);max-height:96px;font-family:inherit;padding:4px 0}.cc-input::placeholder{color:var(--muted-2)}.cc-send{flex-shrink:0;width:32px;height:32px;border-radius:999px;border:none;background:var(--primary);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center}.cc-send:disabled{opacity:.4;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
14898
|
+
}
|
|
14899
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatComposerComponent, decorators: [{
|
|
14900
|
+
type: Component,
|
|
14901
|
+
args: [{ selector: 'app-chat-composer', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
14902
|
+
<div class="cc-suggestions">
|
|
14903
|
+
@for (s of suggestions; track s) {
|
|
14904
|
+
<button type="button" class="cc-pill" (click)="emitSend(s)">{{ s }}</button>
|
|
14905
|
+
}
|
|
14906
|
+
</div>
|
|
14907
|
+
<form class="cc-row" (submit)="$event.preventDefault(); submit()">
|
|
14908
|
+
<textarea
|
|
14909
|
+
class="cc-input"
|
|
14910
|
+
rows="1"
|
|
14911
|
+
[(ngModel)]="draft"
|
|
14912
|
+
name="draft"
|
|
14913
|
+
placeholder="Nhập yêu cầu… vd: thêm cữ bú 80ml lúc 14h"
|
|
14914
|
+
(keydown.enter)="onEnter($event)"
|
|
14915
|
+
aria-label="Nhập yêu cầu cho trợ lý"
|
|
14916
|
+
></textarea>
|
|
14917
|
+
<button type="submit" class="cc-send" [disabled]="!draft.trim()" aria-label="Gửi">
|
|
14918
|
+
<i class="pi pi-send" aria-hidden="true"></i>
|
|
14919
|
+
</button>
|
|
14920
|
+
</form>
|
|
14921
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;border-top:1px solid var(--border);background:var(--bg);padding:8px 12px calc(10px + env(safe-area-inset-bottom,0px))}.cc-suggestions{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:8px}.cc-pill{border:1px solid var(--border);background:var(--surface);color:var(--primary);font-size:11.5px;font-weight:600;padding:5px 9px;border-radius:999px;cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent}.cc-pill:hover{background:var(--primary-soft);border-color:transparent}.cc-row{display:flex;align-items:flex-end;gap:7px;background:var(--surface);border:1px solid var(--border);border-radius:18px;padding:5px 5px 5px 12px}.cc-input{flex:1;border:none;outline:none;resize:none;background:transparent;font-size:13px;line-height:1.4;color:var(--text);max-height:96px;font-family:inherit;padding:4px 0}.cc-input::placeholder{color:var(--muted-2)}.cc-send{flex-shrink:0;width:32px;height:32px;border-radius:999px;border:none;background:var(--primary);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center}.cc-send:disabled{opacity:.4;cursor:not-allowed}\n"] }]
|
|
14922
|
+
}], propDecorators: { send: [{
|
|
14923
|
+
type: Output
|
|
14924
|
+
}] } });
|
|
14925
|
+
|
|
14926
|
+
/**
|
|
14927
|
+
* Render một tin nhắn chat theo `kind`:
|
|
14928
|
+
* - user: bubble phải, nền accent
|
|
14929
|
+
* - text: bubble trái, prose tư vấn
|
|
14930
|
+
* - context: card tóm tắt dữ liệu (greeting)
|
|
14931
|
+
* - pending: thẻ chờ Confirm trước khi thực thi tool (có thể có ô nhập
|
|
14932
|
+
* ngày/giờ nếu model chưa biết — user phải điền trước khi bấm Xác nhận)
|
|
14933
|
+
* - action: thẻ xác nhận đã ghi + nút "Hoàn tác" (cũng cần xác nhận)
|
|
14934
|
+
* - error: callout đỏ
|
|
14935
|
+
*/
|
|
14936
|
+
class ChatMessageComponent {
|
|
14937
|
+
constructor() {
|
|
14938
|
+
this.msg = input.required(...(ngDevMode ? [{ debugName: "msg" }] : /* istanbul ignore next */ []));
|
|
14939
|
+
/** Phát khi user bấm "Hoàn tác" — shell sẽ vào chế độ chờ xác nhận hoàn tác. */
|
|
14940
|
+
this.undo = new EventEmitter();
|
|
14941
|
+
/** Phát khi user bấm "Xác nhận" trên thẻ pending — shell sẽ dispatch tool. */
|
|
14942
|
+
this.confirm = new EventEmitter();
|
|
14943
|
+
/** Phát khi user bấm "Huỷ" trên thẻ pending — shell sẽ đánh dấu đã huỷ. */
|
|
14944
|
+
this.cancel = new EventEmitter();
|
|
14945
|
+
/** Phát khi user xác nhận hoàn tác — shell sẽ gọi undo callback thật. */
|
|
14946
|
+
this.confirmUndo = new EventEmitter();
|
|
14947
|
+
/** Phát khi user huỷ ý định hoàn tác — shell sẽ thoát chế độ chờ xác nhận. */
|
|
14948
|
+
this.cancelUndo = new EventEmitter();
|
|
14949
|
+
/** Phát khi user gõ vào ô nhập — shell cập nhật inputDraft. */
|
|
14950
|
+
this.inputChange = new EventEmitter();
|
|
14951
|
+
/** True khi mọi ô nhập required đã có giá trị — dùng để enable nút Xác nhận. */
|
|
14952
|
+
this.pendingReady = computed(() => {
|
|
14953
|
+
const action = this.msg().action;
|
|
14954
|
+
if (!action?.inputs || action.inputs.length === 0)
|
|
14955
|
+
return true;
|
|
14956
|
+
const draft = action.inputDraft ?? {};
|
|
14957
|
+
return action.inputs.every((inp) => !inp.required || (draft[inp.key]?.trim() ?? '').length > 0);
|
|
14958
|
+
}, ...(ngDevMode ? [{ debugName: "pendingReady" }] : /* istanbul ignore next */ []));
|
|
14959
|
+
}
|
|
14960
|
+
inputType(kind) {
|
|
14961
|
+
return kind;
|
|
14962
|
+
}
|
|
14963
|
+
draftValue(key) {
|
|
14964
|
+
return this.msg().action?.inputDraft?.[key] ?? '';
|
|
14965
|
+
}
|
|
14966
|
+
onInputChange(key, event) {
|
|
14967
|
+
const value = event.target.value;
|
|
14968
|
+
this.inputChange.emit({ id: this.msg().id, key, value });
|
|
14969
|
+
}
|
|
14970
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14971
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ChatMessageComponent, isStandalone: true, selector: "app-chat-message", inputs: { msg: { classPropertyName: "msg", publicName: "msg", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { undo: "undo", confirm: "confirm", cancel: "cancel", confirmUndo: "confirmUndo", cancelUndo: "cancelUndo", inputChange: "inputChange" }, ngImport: i0, template: `
|
|
14972
|
+
@switch (msg().kind) {
|
|
14973
|
+
@case ('user') {
|
|
14974
|
+
<div class="cm-row cm-row--user">
|
|
14975
|
+
<div class="cm-bubble cm-bubble--user">{{ msg().text }}</div>
|
|
14976
|
+
</div>
|
|
14977
|
+
}
|
|
14978
|
+
@case ('text') {
|
|
14979
|
+
<div class="cm-row">
|
|
14980
|
+
<div class="cm-bubble cm-bubble--assistant">{{ msg().text }}</div>
|
|
14981
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
14982
|
+
</div>
|
|
14983
|
+
}
|
|
14984
|
+
@case ('context') {
|
|
14985
|
+
<div class="cm-row">
|
|
14986
|
+
<div class="cm-bubble cm-bubble--assistant cm-bubble--context">{{ msg().text }}</div>
|
|
14987
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
14988
|
+
</div>
|
|
14989
|
+
}
|
|
14990
|
+
@case ('pending') {
|
|
14991
|
+
<div class="cm-row">
|
|
14992
|
+
<div class="cm-action cm-action--pending">
|
|
14993
|
+
<div class="cm-action__head">
|
|
14994
|
+
<span class="cm-chip cm-chip--pending">
|
|
14995
|
+
{{ msg().action?.cancelled ? 'đã huỷ' : 'chờ xác nhận' }}
|
|
14996
|
+
</span>
|
|
14997
|
+
<span class="cm-action__detail">{{ msg().action?.detail }}</span>
|
|
14998
|
+
</div>
|
|
14999
|
+
@if (msg().action?.inputs?.length) {
|
|
15000
|
+
<div class="cm-inputs">
|
|
15001
|
+
@for (inp of msg().action!.inputs!; track inp.key) {
|
|
15002
|
+
<label class="cm-input">
|
|
15003
|
+
<span class="cm-input__label">
|
|
15004
|
+
{{ inp.label }}{{ inp.required ? ' *' : '' }}
|
|
15005
|
+
</span>
|
|
15006
|
+
<input
|
|
15007
|
+
[type]="inputType(inp.kind)"
|
|
15008
|
+
[value]="draftValue(inp.key)"
|
|
15009
|
+
(input)="onInputChange(inp.key, $event)"
|
|
15010
|
+
/>
|
|
15011
|
+
</label>
|
|
15012
|
+
}
|
|
15013
|
+
</div>
|
|
15014
|
+
}
|
|
15015
|
+
<div class="cm-action__foot">
|
|
15016
|
+
<code class="cm-action__tool">{{ msg().action?.tool }}</code>
|
|
15017
|
+
@if (msg().action; as action) {
|
|
15018
|
+
@if (!action.cancelled) {
|
|
15019
|
+
<button
|
|
15020
|
+
type="button"
|
|
15021
|
+
class="cm-confirm"
|
|
15022
|
+
[disabled]="!pendingReady()"
|
|
15023
|
+
(click)="confirm.emit(msg().id)"
|
|
15024
|
+
>Xác nhận</button>
|
|
15025
|
+
<button
|
|
15026
|
+
type="button"
|
|
15027
|
+
class="cm-cancel"
|
|
15028
|
+
(click)="cancel.emit(msg().id)"
|
|
15029
|
+
>Huỷ</button>
|
|
15030
|
+
}
|
|
15031
|
+
}
|
|
15032
|
+
</div>
|
|
15033
|
+
</div>
|
|
15034
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15035
|
+
</div>
|
|
15036
|
+
}
|
|
15037
|
+
@case ('action') {
|
|
15038
|
+
<div class="cm-row">
|
|
15039
|
+
<div class="cm-action">
|
|
15040
|
+
<div class="cm-action__head">
|
|
15041
|
+
<span class="cm-chip" [class.cm-chip--undone]="msg().action?.undone">
|
|
15042
|
+
{{ msg().action?.undone ? 'đã hoàn tác' : 'đã ghi' }}
|
|
15043
|
+
</span>
|
|
15044
|
+
<span class="cm-action__detail">{{ msg().action?.detail }}</span>
|
|
15045
|
+
</div>
|
|
15046
|
+
<div class="cm-action__foot">
|
|
15047
|
+
<code class="cm-action__tool">{{ msg().action?.tool }}</code>
|
|
15048
|
+
@if (msg().action; as action) {
|
|
15049
|
+
@if (!action.undone && !action.undoPending) {
|
|
15050
|
+
<button type="button" class="cm-undo" (click)="undo.emit(msg().id)">Hoàn tác</button>
|
|
15051
|
+
}
|
|
15052
|
+
@if (!action.undone && action.undoPending) {
|
|
15053
|
+
<span class="cm-undo-hint">Xác nhận hoàn tác?</span>
|
|
15054
|
+
<button
|
|
15055
|
+
type="button"
|
|
15056
|
+
class="cm-confirm"
|
|
15057
|
+
(click)="confirmUndo.emit(msg().id)"
|
|
15058
|
+
>Xác nhận</button>
|
|
15059
|
+
<button
|
|
15060
|
+
type="button"
|
|
15061
|
+
class="cm-cancel"
|
|
15062
|
+
(click)="cancelUndo.emit(msg().id)"
|
|
15063
|
+
>Huỷ</button>
|
|
15064
|
+
}
|
|
15065
|
+
}
|
|
15066
|
+
</div>
|
|
15067
|
+
</div>
|
|
15068
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15069
|
+
</div>
|
|
15070
|
+
}
|
|
15071
|
+
@case ('error') {
|
|
15072
|
+
<div class="cm-row">
|
|
15073
|
+
<div class="cm-error">{{ msg().text }}</div>
|
|
15074
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15075
|
+
</div>
|
|
15076
|
+
}
|
|
15077
|
+
}
|
|
15078
|
+
`, isInline: true, styles: [":host{display:block}.cm-row{display:flex;flex-direction:column;align-items:flex-start;gap:3px}.cm-row--user{align-items:flex-end}.cm-bubble{max-width:82%;padding:9px 12px;border-radius:14px;font-size:13px;line-height:1.42;white-space:pre-wrap;word-break:break-word}.cm-bubble--user{background:var(--primary);color:#fff;border-bottom-right-radius:4px}.cm-bubble--assistant{background:var(--surface);border:1px solid var(--border);color:var(--text);border-bottom-left-radius:4}.cm-bubble--context{background:var(--primary-soft);border-color:transparent;color:var(--text);font-size:12.5px}.cm-ts{font-size:10px;color:var(--muted-2);padding:0 4px}.cm-action{max-width:88%;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--mint, #10b981);border-radius:12px;padding:9px 11px;display:flex;flex-direction:column;gap:7px}.cm-action__head{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.cm-action__detail{font-size:12.5px;font-weight:600;color:var(--text)}.cm-action__foot{display:flex;align-items:center;justify-content:space-between;gap:8px}.cm-action__tool{font-size:10.5px;color:var(--muted-2);background:var(--bg);padding:2px 6px;border-radius:5px;font-family:SF Mono,ui-monospace,Menlo,monospace}.cm-chip{font-size:10.5px;font-weight:700;color:var(--mint, #10b981);background:var(--mint-soft, #d1fae5);padding:2px 7px;border-radius:999px;text-transform:uppercase;letter-spacing:.02em}.cm-chip--undone{color:var(--muted);background:var(--bg)}.cm-chip--pending{color:var(--amber, #d97706);background:var(--amber-soft, #fef3c7)}.cm-undo{border:none;background:transparent;color:var(--primary);font-size:11.5px;font-weight:600;cursor:pointer;padding:2px 4px;border-radius:6px}.cm-undo:hover{background:var(--primary-soft)}.cm-confirm{border:none;background:var(--primary);color:#fff;font-size:11.5px;font-weight:600;cursor:pointer;padding:4px 12px;border-radius:6px}.cm-confirm:hover{filter:brightness(1.05)}.cm-cancel{border:1px solid var(--border);background:transparent;color:var(--muted);font-size:11.5px;font-weight:600;cursor:pointer;padding:3px 10px;border-radius:6px}.cm-cancel:hover{background:var(--bg)}.cm-action--pending{border-left-color:var(--amber, #d97706)}.cm-undo-hint{font-size:11.5px;color:var(--amber, #d97706);font-weight:600}.cm-inputs{display:flex;flex-wrap:wrap;gap:8px}.cm-input{display:flex;flex-direction:column;gap:3px;flex:1 1 120px}.cm-input__label{font-size:10.5px;color:var(--muted-2);font-weight:600}.cm-input input{border:1px solid var(--border);border-radius:6px;padding:5px 8px;font-size:12.5px;color:var(--text);background:var(--bg);outline:none}.cm-input input:focus{border-color:var(--primary)}.cm-error{max-width:88%;background:var(--rose-soft, #fee2e2);color:var(--rose, #ef4444);border-left:3px solid var(--rose, #ef4444);border-radius:10px;padding:8px 11px;font-size:12.5px;line-height:1.4}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15079
|
+
}
|
|
15080
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
15081
|
+
type: Component,
|
|
15082
|
+
args: [{ selector: 'app-chat-message', standalone: true, imports: [CommonModule], template: `
|
|
15083
|
+
@switch (msg().kind) {
|
|
15084
|
+
@case ('user') {
|
|
15085
|
+
<div class="cm-row cm-row--user">
|
|
15086
|
+
<div class="cm-bubble cm-bubble--user">{{ msg().text }}</div>
|
|
15087
|
+
</div>
|
|
15088
|
+
}
|
|
15089
|
+
@case ('text') {
|
|
15090
|
+
<div class="cm-row">
|
|
15091
|
+
<div class="cm-bubble cm-bubble--assistant">{{ msg().text }}</div>
|
|
15092
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15093
|
+
</div>
|
|
15094
|
+
}
|
|
15095
|
+
@case ('context') {
|
|
15096
|
+
<div class="cm-row">
|
|
15097
|
+
<div class="cm-bubble cm-bubble--assistant cm-bubble--context">{{ msg().text }}</div>
|
|
15098
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15099
|
+
</div>
|
|
15100
|
+
}
|
|
15101
|
+
@case ('pending') {
|
|
15102
|
+
<div class="cm-row">
|
|
15103
|
+
<div class="cm-action cm-action--pending">
|
|
15104
|
+
<div class="cm-action__head">
|
|
15105
|
+
<span class="cm-chip cm-chip--pending">
|
|
15106
|
+
{{ msg().action?.cancelled ? 'đã huỷ' : 'chờ xác nhận' }}
|
|
15107
|
+
</span>
|
|
15108
|
+
<span class="cm-action__detail">{{ msg().action?.detail }}</span>
|
|
15109
|
+
</div>
|
|
15110
|
+
@if (msg().action?.inputs?.length) {
|
|
15111
|
+
<div class="cm-inputs">
|
|
15112
|
+
@for (inp of msg().action!.inputs!; track inp.key) {
|
|
15113
|
+
<label class="cm-input">
|
|
15114
|
+
<span class="cm-input__label">
|
|
15115
|
+
{{ inp.label }}{{ inp.required ? ' *' : '' }}
|
|
15116
|
+
</span>
|
|
15117
|
+
<input
|
|
15118
|
+
[type]="inputType(inp.kind)"
|
|
15119
|
+
[value]="draftValue(inp.key)"
|
|
15120
|
+
(input)="onInputChange(inp.key, $event)"
|
|
15121
|
+
/>
|
|
15122
|
+
</label>
|
|
15123
|
+
}
|
|
15124
|
+
</div>
|
|
15125
|
+
}
|
|
15126
|
+
<div class="cm-action__foot">
|
|
15127
|
+
<code class="cm-action__tool">{{ msg().action?.tool }}</code>
|
|
15128
|
+
@if (msg().action; as action) {
|
|
15129
|
+
@if (!action.cancelled) {
|
|
15130
|
+
<button
|
|
15131
|
+
type="button"
|
|
15132
|
+
class="cm-confirm"
|
|
15133
|
+
[disabled]="!pendingReady()"
|
|
15134
|
+
(click)="confirm.emit(msg().id)"
|
|
15135
|
+
>Xác nhận</button>
|
|
15136
|
+
<button
|
|
15137
|
+
type="button"
|
|
15138
|
+
class="cm-cancel"
|
|
15139
|
+
(click)="cancel.emit(msg().id)"
|
|
15140
|
+
>Huỷ</button>
|
|
15141
|
+
}
|
|
15142
|
+
}
|
|
15143
|
+
</div>
|
|
15144
|
+
</div>
|
|
15145
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15146
|
+
</div>
|
|
15147
|
+
}
|
|
15148
|
+
@case ('action') {
|
|
15149
|
+
<div class="cm-row">
|
|
15150
|
+
<div class="cm-action">
|
|
15151
|
+
<div class="cm-action__head">
|
|
15152
|
+
<span class="cm-chip" [class.cm-chip--undone]="msg().action?.undone">
|
|
15153
|
+
{{ msg().action?.undone ? 'đã hoàn tác' : 'đã ghi' }}
|
|
15154
|
+
</span>
|
|
15155
|
+
<span class="cm-action__detail">{{ msg().action?.detail }}</span>
|
|
15156
|
+
</div>
|
|
15157
|
+
<div class="cm-action__foot">
|
|
15158
|
+
<code class="cm-action__tool">{{ msg().action?.tool }}</code>
|
|
15159
|
+
@if (msg().action; as action) {
|
|
15160
|
+
@if (!action.undone && !action.undoPending) {
|
|
15161
|
+
<button type="button" class="cm-undo" (click)="undo.emit(msg().id)">Hoàn tác</button>
|
|
15162
|
+
}
|
|
15163
|
+
@if (!action.undone && action.undoPending) {
|
|
15164
|
+
<span class="cm-undo-hint">Xác nhận hoàn tác?</span>
|
|
15165
|
+
<button
|
|
15166
|
+
type="button"
|
|
15167
|
+
class="cm-confirm"
|
|
15168
|
+
(click)="confirmUndo.emit(msg().id)"
|
|
15169
|
+
>Xác nhận</button>
|
|
15170
|
+
<button
|
|
15171
|
+
type="button"
|
|
15172
|
+
class="cm-cancel"
|
|
15173
|
+
(click)="cancelUndo.emit(msg().id)"
|
|
15174
|
+
>Huỷ</button>
|
|
15175
|
+
}
|
|
15176
|
+
}
|
|
15177
|
+
</div>
|
|
15178
|
+
</div>
|
|
15179
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15180
|
+
</div>
|
|
15181
|
+
}
|
|
15182
|
+
@case ('error') {
|
|
15183
|
+
<div class="cm-row">
|
|
15184
|
+
<div class="cm-error">{{ msg().text }}</div>
|
|
15185
|
+
<span class="cm-ts">{{ msg().ts }}</span>
|
|
15186
|
+
</div>
|
|
15187
|
+
}
|
|
15188
|
+
}
|
|
15189
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.cm-row{display:flex;flex-direction:column;align-items:flex-start;gap:3px}.cm-row--user{align-items:flex-end}.cm-bubble{max-width:82%;padding:9px 12px;border-radius:14px;font-size:13px;line-height:1.42;white-space:pre-wrap;word-break:break-word}.cm-bubble--user{background:var(--primary);color:#fff;border-bottom-right-radius:4px}.cm-bubble--assistant{background:var(--surface);border:1px solid var(--border);color:var(--text);border-bottom-left-radius:4}.cm-bubble--context{background:var(--primary-soft);border-color:transparent;color:var(--text);font-size:12.5px}.cm-ts{font-size:10px;color:var(--muted-2);padding:0 4px}.cm-action{max-width:88%;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--mint, #10b981);border-radius:12px;padding:9px 11px;display:flex;flex-direction:column;gap:7px}.cm-action__head{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.cm-action__detail{font-size:12.5px;font-weight:600;color:var(--text)}.cm-action__foot{display:flex;align-items:center;justify-content:space-between;gap:8px}.cm-action__tool{font-size:10.5px;color:var(--muted-2);background:var(--bg);padding:2px 6px;border-radius:5px;font-family:SF Mono,ui-monospace,Menlo,monospace}.cm-chip{font-size:10.5px;font-weight:700;color:var(--mint, #10b981);background:var(--mint-soft, #d1fae5);padding:2px 7px;border-radius:999px;text-transform:uppercase;letter-spacing:.02em}.cm-chip--undone{color:var(--muted);background:var(--bg)}.cm-chip--pending{color:var(--amber, #d97706);background:var(--amber-soft, #fef3c7)}.cm-undo{border:none;background:transparent;color:var(--primary);font-size:11.5px;font-weight:600;cursor:pointer;padding:2px 4px;border-radius:6px}.cm-undo:hover{background:var(--primary-soft)}.cm-confirm{border:none;background:var(--primary);color:#fff;font-size:11.5px;font-weight:600;cursor:pointer;padding:4px 12px;border-radius:6px}.cm-confirm:hover{filter:brightness(1.05)}.cm-cancel{border:1px solid var(--border);background:transparent;color:var(--muted);font-size:11.5px;font-weight:600;cursor:pointer;padding:3px 10px;border-radius:6px}.cm-cancel:hover{background:var(--bg)}.cm-action--pending{border-left-color:var(--amber, #d97706)}.cm-undo-hint{font-size:11.5px;color:var(--amber, #d97706);font-weight:600}.cm-inputs{display:flex;flex-wrap:wrap;gap:8px}.cm-input{display:flex;flex-direction:column;gap:3px;flex:1 1 120px}.cm-input__label{font-size:10.5px;color:var(--muted-2);font-weight:600}.cm-input input{border:1px solid var(--border);border-radius:6px;padding:5px 8px;font-size:12.5px;color:var(--text);background:var(--bg);outline:none}.cm-input input:focus{border-color:var(--primary)}.cm-error{max-width:88%;background:var(--rose-soft, #fee2e2);color:var(--rose, #ef4444);border-left:3px solid var(--rose, #ef4444);border-radius:10px;padding:8px 11px;font-size:12.5px;line-height:1.4}\n"] }]
|
|
15190
|
+
}], propDecorators: { msg: [{ type: i0.Input, args: [{ isSignal: true, alias: "msg", required: true }] }], undo: [{
|
|
15191
|
+
type: Output
|
|
15192
|
+
}], confirm: [{
|
|
15193
|
+
type: Output
|
|
15194
|
+
}], cancel: [{
|
|
15195
|
+
type: Output
|
|
15196
|
+
}], confirmUndo: [{
|
|
15197
|
+
type: Output
|
|
15198
|
+
}], cancelUndo: [{
|
|
15199
|
+
type: Output
|
|
15200
|
+
}], inputChange: [{
|
|
15201
|
+
type: Output
|
|
15202
|
+
}] } });
|
|
15203
|
+
|
|
15204
|
+
/**
|
|
15205
|
+
* Wrap HuggingFace chat completion qua proxy `/api/hf-chat` (mặc định).
|
|
15206
|
+
*
|
|
15207
|
+
* - Browser không gọi HF trực tiếp vì CORS + Authorization header bị block
|
|
15208
|
+
* (status 0). Proxy (Vercel function `api/hf-chat.js` hoặc dev server local
|
|
15209
|
+
* `node api/hf-chat.js` qua proxy.conf.js) giữ token ở server, inject
|
|
15210
|
+
* `Authorization`, và XÂY SYSTEM PROMPT từ `context` (xem api/hf-prompt.js).
|
|
15211
|
+
* - Client chỉ gửi `{ context, messages, model, temperature, max_tokens }`.
|
|
15212
|
+
* Body messages theo OpenAI shape — khớp HF Router chat-completions.
|
|
15213
|
+
* - Khi self-host TGI / vLLM có CORS mở, set `NG_APP_HF_INFERENCE_URL` tới
|
|
15214
|
+
* endpoint đó và `NG_APP_HF_API_TOKEN` (nếu cần) — service sẽ gọi thẳng.
|
|
15215
|
+
*/
|
|
15216
|
+
class HfAssistantService {
|
|
15217
|
+
constructor() {
|
|
15218
|
+
this.http = inject(HttpClient);
|
|
15219
|
+
this.config = inject(FEEDING_CONFIG_TOKEN);
|
|
15220
|
+
/** Endpoint client gọi — mặc định proxy /api/hf-chat. */
|
|
15221
|
+
this.endpoint = this.config.hfInferenceUrl || '/api/hf-chat';
|
|
15222
|
+
}
|
|
15223
|
+
/** True khi có endpoint — UI dùng để ẩn/hiện chat. */
|
|
15224
|
+
get configured() {
|
|
15225
|
+
return !!this.endpoint;
|
|
15226
|
+
}
|
|
15227
|
+
get model() {
|
|
15228
|
+
return this.config.hfModel;
|
|
15229
|
+
}
|
|
15230
|
+
/**
|
|
15231
|
+
* Gửi context (snapshot bé) + lịch sử chat, nhận về nội dung assistant raw.
|
|
15232
|
+
* Server tự build system prompt từ context — client không cầm prompt/tool-spec.
|
|
15233
|
+
* Trả về raw text (chưa parse tool-call) — parser xử lý sau.
|
|
15234
|
+
*/
|
|
15235
|
+
async send(context, history) {
|
|
15236
|
+
if (!this.configured) {
|
|
15237
|
+
throw new Error('Chưa cấu hình NG_APP_HF_INFERENCE_URL — chat assistant đang tắt.');
|
|
15238
|
+
}
|
|
15239
|
+
const headers = {
|
|
15240
|
+
'Content-Type': 'application/json',
|
|
15241
|
+
};
|
|
15242
|
+
// Chỉ gửi token client-side khi user tự cấu hình (self-host / direct mode).
|
|
15243
|
+
// Qua proxy /api/hf-chat mặc định thì KHÔNG gửi — proxy inject server-side.
|
|
15244
|
+
if (this.config.hfApiToken && !this.endpoint.startsWith('/api/')) {
|
|
15245
|
+
headers['Authorization'] = `Bearer ${this.config.hfApiToken}`;
|
|
15246
|
+
}
|
|
15247
|
+
const body = {
|
|
15248
|
+
context,
|
|
15249
|
+
model: this.config.hfModel,
|
|
15250
|
+
messages: history,
|
|
15251
|
+
temperature: 0.3,
|
|
15252
|
+
max_tokens: 512,
|
|
15253
|
+
};
|
|
15254
|
+
let resp;
|
|
15255
|
+
try {
|
|
15256
|
+
resp = await firstValueFrom(this.http.post(this.endpoint, body, {
|
|
15257
|
+
headers: new HttpHeaders(headers),
|
|
15258
|
+
}));
|
|
15259
|
+
}
|
|
15260
|
+
catch (err) {
|
|
15261
|
+
throw new Error(this.formatHttpError(err));
|
|
15262
|
+
}
|
|
15263
|
+
if (resp.error) {
|
|
15264
|
+
const msg = typeof resp.error === 'string' ? resp.error : resp.error.message || JSON.stringify(resp.error);
|
|
15265
|
+
throw new Error(`HF: ${msg}`);
|
|
15266
|
+
}
|
|
15267
|
+
const text = resp.choices?.[0]?.message?.content ??
|
|
15268
|
+
resp.generated_text ??
|
|
15269
|
+
'';
|
|
15270
|
+
if (!text)
|
|
15271
|
+
throw new Error('HF trả về phản hồi rỗng.');
|
|
15272
|
+
return text;
|
|
15273
|
+
}
|
|
15274
|
+
/** Biến lỗi HttpClient thành message tiếng Việt rõ ràng. */
|
|
15275
|
+
formatHttpError(err) {
|
|
15276
|
+
if (err instanceof HttpErrorResponse) {
|
|
15277
|
+
if (err.status === 0) {
|
|
15278
|
+
return 'Không kết nối được tới trợ lý AI (status 0 — kiểm tra proxy /api/hf-chat và token HF_TOKEN).';
|
|
15279
|
+
}
|
|
15280
|
+
const body = err.error;
|
|
15281
|
+
const apiMsg = typeof body === 'string' ? body : body?.error?.message || body?.error || body?.message;
|
|
15282
|
+
return `HF ${err.status}: ${apiMsg || err.statusText || 'lỗi không xác định'}`;
|
|
15283
|
+
}
|
|
15284
|
+
return err?.message || 'lỗi mạng không xác định';
|
|
15285
|
+
}
|
|
15286
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HfAssistantService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
15287
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HfAssistantService, providedIn: 'root' }); }
|
|
15288
|
+
}
|
|
15289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HfAssistantService, decorators: [{
|
|
15290
|
+
type: Injectable,
|
|
15291
|
+
args: [{ providedIn: 'root' }]
|
|
15292
|
+
}] });
|
|
15293
|
+
|
|
15294
|
+
/**
|
|
15295
|
+
* Parse tool-call từ output của HF model.
|
|
15296
|
+
*
|
|
15297
|
+
* Quy ước: model trả về một khối ```json {...}``` chứa
|
|
15298
|
+
* `{ tool: string; args: Record<string, unknown> }`.
|
|
15299
|
+
* Nếu không có khối JSON nào → đây là trả lời prose thuần (advisory).
|
|
15300
|
+
*/
|
|
15301
|
+
const FENCE_RE = /```(?:json)?\s*([\s\S]*?)```/i;
|
|
15302
|
+
function tryParseJson(s) {
|
|
15303
|
+
try {
|
|
15304
|
+
return JSON.parse(s);
|
|
15305
|
+
}
|
|
15306
|
+
catch {
|
|
15307
|
+
return null;
|
|
15308
|
+
}
|
|
15309
|
+
}
|
|
15310
|
+
/** Coerce một giá trị bất kỳ về ParsedToolCall hoặc null. */
|
|
15311
|
+
function asToolCall(v) {
|
|
15312
|
+
if (!v || typeof v !== 'object')
|
|
15313
|
+
return null;
|
|
15314
|
+
const obj = v;
|
|
15315
|
+
const toolVal = obj['tool'];
|
|
15316
|
+
const tool = typeof toolVal === 'string' ? toolVal.trim() : '';
|
|
15317
|
+
if (!tool)
|
|
15318
|
+
return null;
|
|
15319
|
+
const argsVal = obj['args'];
|
|
15320
|
+
const args = argsVal && typeof argsVal === 'object' ? argsVal : {};
|
|
15321
|
+
return { tool, args };
|
|
15322
|
+
}
|
|
15323
|
+
/**
|
|
15324
|
+
* Tách reply thành text hoặc tool-call. Ưu tiên tìm khối JSON fence;
|
|
15325
|
+
* nếu không có, thử parse toàn bộ output (model đôi khi quên fence).
|
|
15326
|
+
*/
|
|
15327
|
+
function parseHfReply(raw) {
|
|
15328
|
+
const trimmed = raw.trim();
|
|
15329
|
+
if (!trimmed)
|
|
15330
|
+
return { kind: 'text', text: '' };
|
|
15331
|
+
const fenceMatch = trimmed.match(FENCE_RE);
|
|
15332
|
+
if (fenceMatch) {
|
|
15333
|
+
const parsed = asToolCall(tryParseJson(fenceMatch[1].trim()));
|
|
15334
|
+
if (parsed)
|
|
15335
|
+
return { kind: 'tool', ...parsed, raw: trimmed };
|
|
15336
|
+
}
|
|
15337
|
+
const direct = asToolCall(tryParseJson(trimmed));
|
|
15338
|
+
if (direct)
|
|
15339
|
+
return { kind: 'tool', ...direct, raw: trimmed };
|
|
15340
|
+
return { kind: 'text', text: trimmed };
|
|
15341
|
+
}
|
|
15342
|
+
// ── Arg validators (throw nếu sai, trả về typed arg) ────────────────
|
|
15343
|
+
function reqString(args, key) {
|
|
15344
|
+
const v = args[key];
|
|
15345
|
+
if (typeof v !== 'string' || !v.trim())
|
|
15346
|
+
throw new Error(`Thiếu tham số "${key}"`);
|
|
15347
|
+
return v.trim();
|
|
15348
|
+
}
|
|
15349
|
+
function reqNumber(args, key) {
|
|
15350
|
+
const v = args[key];
|
|
15351
|
+
const n = typeof v === 'number' ? v : typeof v === 'string' ? parseFloat(v) : NaN;
|
|
15352
|
+
if (!isFinite(n))
|
|
15353
|
+
throw new Error(`Tham số "${key}" không hợp lệ`);
|
|
15354
|
+
return n;
|
|
15355
|
+
}
|
|
15356
|
+
function optString(args, key) {
|
|
15357
|
+
const v = args[key];
|
|
15358
|
+
return typeof v === 'string' && v.trim() ? v.trim() : undefined;
|
|
15359
|
+
}
|
|
15360
|
+
/** Validate HH:mm (24h). */
|
|
15361
|
+
function validateTime(v) {
|
|
15362
|
+
if (!/^\d{1,2}:\d{2}$/.test(v))
|
|
15363
|
+
throw new Error(`Giờ "${v}" không đúng định dạng HH:mm`);
|
|
15364
|
+
const [h, m] = v.split(':').map(Number);
|
|
15365
|
+
if (h < 0 || h > 23 || m < 0 || m > 59)
|
|
15366
|
+
throw new Error(`Giờ "${v}" ngoài phạm vi`);
|
|
15367
|
+
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
|
|
15368
|
+
}
|
|
15369
|
+
/** Validate YYYY-MM-DD. */
|
|
15370
|
+
function validateDate(v) {
|
|
15371
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(v))
|
|
15372
|
+
throw new Error(`Ngày "${v}" không đúng định dạng YYYY-MM-DD`);
|
|
15373
|
+
return v;
|
|
15374
|
+
}
|
|
15375
|
+
const ToolArgs = {
|
|
15376
|
+
add_feeding_session(args) {
|
|
15377
|
+
return { time: validateTime(reqString(args, 'time')), volumeMl: reqNumber(args, 'volume_ml'), note: optString(args, 'note') };
|
|
15378
|
+
},
|
|
15379
|
+
prepare_formula(args) {
|
|
15380
|
+
return { volumeMl: reqNumber(args, 'volume_ml') };
|
|
15381
|
+
},
|
|
15382
|
+
log_weight(args) {
|
|
15383
|
+
return { weightKg: reqNumber(args, 'weight_kg') };
|
|
15384
|
+
},
|
|
15385
|
+
query_recent(_args) {
|
|
15386
|
+
return {};
|
|
15387
|
+
},
|
|
15388
|
+
next_event(_args) {
|
|
15389
|
+
return {};
|
|
15390
|
+
},
|
|
15391
|
+
set_reminder(args) {
|
|
15392
|
+
const title = reqString(args, 'title');
|
|
15393
|
+
const dateRaw = optString(args, 'date');
|
|
15394
|
+
const timeRaw = optString(args, 'time');
|
|
15395
|
+
return {
|
|
15396
|
+
title,
|
|
15397
|
+
date: dateRaw ? validateDate(dateRaw) : undefined,
|
|
15398
|
+
time: timeRaw ? validateTime(timeRaw) : undefined,
|
|
15399
|
+
};
|
|
15400
|
+
},
|
|
15401
|
+
send_notification(args) {
|
|
15402
|
+
return { content: reqString(args, 'content') };
|
|
15403
|
+
},
|
|
15404
|
+
};
|
|
15405
|
+
const TOOL_NAMES = Object.keys(ToolArgs);
|
|
15406
|
+
function isToolName(s) {
|
|
15407
|
+
return s in ToolArgs;
|
|
15408
|
+
}
|
|
15409
|
+
|
|
15410
|
+
/**
|
|
15411
|
+
* Registry map tool-name (từ HF model) → handler gọi method có sẵn trên
|
|
15412
|
+
* các service hiện có. Không chứa logic nghiệp vụ mới — chỉ orchestrate.
|
|
15413
|
+
*
|
|
15414
|
+
* Mọi handler trả về ToolResult. UI tự render chip "đã ghi" + nút "Hoàn tác"
|
|
15415
|
+
* dựa trên `undo` có tồn tại hay không.
|
|
15416
|
+
*/
|
|
15417
|
+
class ToolRegistry {
|
|
15418
|
+
constructor() {
|
|
15419
|
+
this.feeding = inject(FeedingLogService);
|
|
15420
|
+
this.weight = inject(WeightLogService);
|
|
15421
|
+
this.activity = inject(ActivityLogService);
|
|
15422
|
+
this.events = inject(EventLogService);
|
|
15423
|
+
this.notifications = inject(NotificationLogService);
|
|
15424
|
+
/** Danh sách tool công khai — dùng để validate / debug. */
|
|
15425
|
+
this.toolNames = Object.keys(ToolArgs);
|
|
15426
|
+
/** Tool có side-effect (ghi sheet) — cần user Confirm trước khi thực thi. */
|
|
15427
|
+
this.writeTools = new Set([
|
|
15428
|
+
'add_feeding_session',
|
|
15429
|
+
'prepare_formula',
|
|
15430
|
+
'log_weight',
|
|
15431
|
+
'set_reminder',
|
|
15432
|
+
'send_notification',
|
|
15433
|
+
]);
|
|
15434
|
+
}
|
|
15435
|
+
/** True nếu tool ghi thay đổi lên sheet — UI sẽ hiển thị thẻ Confirm trước. */
|
|
15436
|
+
isWriteTool(tool) {
|
|
15437
|
+
return this.writeTools.has(tool);
|
|
15438
|
+
}
|
|
15439
|
+
/**
|
|
15440
|
+
* Mô tả ngắn gọn (tiếng Việt) thao tác sắp thực thi — KHÔNG side-effect.
|
|
15441
|
+
* Dùng cho thẻ Confirm trước khi gọi `dispatch`. Throw nếu args không hợp lệ.
|
|
15442
|
+
* Trả về cả danh sách ô nhập user cần điền (vd ngày/giờ thiếu cho set_reminder).
|
|
15443
|
+
*/
|
|
15444
|
+
describe(tool, rawArgs) {
|
|
15445
|
+
if (!isToolName(tool)) {
|
|
15446
|
+
return { detail: `Tool "${tool}" không được hỗ trợ` };
|
|
15447
|
+
}
|
|
15448
|
+
switch (tool) {
|
|
15449
|
+
case 'add_feeding_session': {
|
|
15450
|
+
const a = ToolArgs.add_feeding_session(rawArgs);
|
|
15451
|
+
return {
|
|
15452
|
+
detail: `Ghi cữ bú ${a.time} — ${a.volumeMl}ml${a.note ? ` (${a.note})` : ''}`,
|
|
15453
|
+
};
|
|
15454
|
+
}
|
|
15455
|
+
case 'prepare_formula': {
|
|
15456
|
+
const a = ToolArgs.prepare_formula(rawArgs);
|
|
15457
|
+
return {
|
|
15458
|
+
detail: a.volumeMl <= 0 ? 'Xoá sữa đã pha (clear bottle prep)' : `Pha sữa ${a.volumeMl}ml`,
|
|
15459
|
+
};
|
|
15460
|
+
}
|
|
15461
|
+
case 'log_weight': {
|
|
15462
|
+
const a = ToolArgs.log_weight(rawArgs);
|
|
15463
|
+
return { detail: `Ghi cân nặng ${a.weightKg}kg` };
|
|
15464
|
+
}
|
|
15465
|
+
case 'set_reminder': {
|
|
15466
|
+
const a = ToolArgs.set_reminder(rawArgs);
|
|
15467
|
+
const parts = [`Lên lịch "${a.title}"`];
|
|
15468
|
+
if (a.date && a.time)
|
|
15469
|
+
parts.push(`${a.date} ${a.time}`);
|
|
15470
|
+
else if (a.date)
|
|
15471
|
+
parts.push(a.date);
|
|
15472
|
+
else if (a.time)
|
|
15473
|
+
parts.push(a.time);
|
|
15474
|
+
else
|
|
15475
|
+
parts.push('(cần nhập ngày & giờ)');
|
|
15476
|
+
const inputs = [];
|
|
15477
|
+
if (!a.date) {
|
|
15478
|
+
inputs.push({ key: 'date', label: 'Ngày', kind: 'date', required: true });
|
|
15479
|
+
}
|
|
15480
|
+
if (!a.time) {
|
|
15481
|
+
inputs.push({ key: 'time', label: 'Giờ', kind: 'time', required: true });
|
|
15482
|
+
}
|
|
15483
|
+
return {
|
|
15484
|
+
detail: parts.join(' — '),
|
|
15485
|
+
inputs: inputs.length > 0 ? inputs : undefined,
|
|
15486
|
+
};
|
|
15487
|
+
}
|
|
15488
|
+
case 'send_notification': {
|
|
15489
|
+
const a = ToolArgs.send_notification(rawArgs);
|
|
15490
|
+
return { detail: `Gửi thông báo: ${a.content}` };
|
|
15491
|
+
}
|
|
15492
|
+
case 'query_recent':
|
|
15493
|
+
return { detail: 'Tóm tắt cữ bú hôm nay' };
|
|
15494
|
+
case 'next_event':
|
|
15495
|
+
return { detail: 'Xem sự kiện kế tiếp' };
|
|
15496
|
+
}
|
|
15497
|
+
}
|
|
15498
|
+
async dispatch(tool, rawArgs, ctx) {
|
|
15499
|
+
if (!isToolName(tool)) {
|
|
15500
|
+
return { ok: false, detail: `Tool "${tool}" không được hỗ trợ` };
|
|
15501
|
+
}
|
|
15502
|
+
try {
|
|
15503
|
+
switch (tool) {
|
|
15504
|
+
case 'add_feeding_session':
|
|
15505
|
+
return await this.addFeeding(ToolArgs.add_feeding_session(rawArgs), ctx);
|
|
15506
|
+
case 'prepare_formula':
|
|
15507
|
+
return await this.prepareFormula(ToolArgs.prepare_formula(rawArgs), ctx);
|
|
15508
|
+
case 'log_weight':
|
|
15509
|
+
return await this.logWeight(ToolArgs.log_weight(rawArgs), ctx);
|
|
15510
|
+
case 'set_reminder':
|
|
15511
|
+
return await this.setReminder(ToolArgs.set_reminder(rawArgs), ctx);
|
|
15512
|
+
case 'send_notification':
|
|
15513
|
+
return await this.sendNotification(ToolArgs.send_notification(rawArgs), ctx);
|
|
15514
|
+
case 'query_recent':
|
|
15515
|
+
return this.queryRecent(ctx);
|
|
15516
|
+
case 'next_event':
|
|
15517
|
+
return this.nextEvent(ctx);
|
|
15518
|
+
}
|
|
15519
|
+
}
|
|
15520
|
+
catch (err) {
|
|
15521
|
+
const msg = err instanceof Error ? err.message : 'thất bại';
|
|
15522
|
+
return { ok: false, detail: `Không thực thi: ${msg}` };
|
|
15523
|
+
}
|
|
15524
|
+
}
|
|
15525
|
+
// ── Write tools ───────────────────────────────────────────────────
|
|
15526
|
+
async addFeeding(a, ctx) {
|
|
15527
|
+
const log = {
|
|
15528
|
+
user: ctx.user,
|
|
15529
|
+
date: todayStr(),
|
|
15530
|
+
time: a.time,
|
|
15531
|
+
volume: a.volumeMl,
|
|
15532
|
+
note: a.note,
|
|
15533
|
+
};
|
|
15534
|
+
const resp = await firstValueFrom(this.feeding.addLog(log));
|
|
15535
|
+
const rowIndex = resp.rowIndex;
|
|
15536
|
+
// Audit trail — giống flow UI (feeding-dialogs submitLog).
|
|
15537
|
+
if (resp.success) {
|
|
15538
|
+
await firstValueFrom(this.activity.logFeeding(ctx.user, 'FEEDING_ADDED', {
|
|
15539
|
+
time: a.time,
|
|
15540
|
+
volume: a.volumeMl,
|
|
15541
|
+
}));
|
|
15542
|
+
}
|
|
15543
|
+
return {
|
|
15544
|
+
ok: resp.success,
|
|
15545
|
+
detail: `Đã ghi cữ bú ${a.time} — ${a.volumeMl}ml${a.note ? ` (${a.note})` : ''}`,
|
|
15546
|
+
undo: rowIndex
|
|
15547
|
+
? async () => {
|
|
15548
|
+
await firstValueFrom(this.feeding.deleteLog(rowIndex));
|
|
15549
|
+
await firstValueFrom(this.activity.logFeeding(ctx.user, 'FEEDING_DELETED', {
|
|
15550
|
+
time: a.time,
|
|
15551
|
+
volume: a.volumeMl,
|
|
15552
|
+
reason: 'hoàn tác từ trợ lý AI',
|
|
15553
|
+
}));
|
|
15554
|
+
}
|
|
15555
|
+
: undefined,
|
|
15556
|
+
};
|
|
15557
|
+
}
|
|
15558
|
+
async prepareFormula(a, ctx) {
|
|
15559
|
+
// volumeMl <= 0 → user muốn xoá sữa đã pha, gọi clearBottlePrep thay vì set 0ml.
|
|
15560
|
+
if (a.volumeMl <= 0) {
|
|
15561
|
+
let prevVolume;
|
|
15562
|
+
try {
|
|
15563
|
+
const prev = await firstValueFrom(this.feeding.getBottlePrep());
|
|
15564
|
+
prevVolume = prev?.volumeMl;
|
|
15565
|
+
}
|
|
15566
|
+
catch {
|
|
15567
|
+
/* không chặn flow clear */
|
|
15568
|
+
}
|
|
15569
|
+
await firstValueFrom(this.feeding.clearBottlePrepOnSheet());
|
|
15570
|
+
await firstValueFrom(this.activity.logBottlePrepManualClear(ctx.user, prevVolume ?? 0, 'qua trợ lý AI'));
|
|
15571
|
+
return {
|
|
15572
|
+
ok: true,
|
|
15573
|
+
detail: prevVolume
|
|
15574
|
+
? `Đã xoá ${prevVolume}ml sữa đã pha`
|
|
15575
|
+
: `Đã xoá sữa đã pha`,
|
|
15576
|
+
};
|
|
15577
|
+
}
|
|
15578
|
+
// Pha sữa mới: ghi dung tích + giờ pha lên sheet (bottle-prep tracker) rồi
|
|
15579
|
+
// viết activity log BOTTLE_PREP_ADDED — giống flow feeding-daily.saveBottlePrep.
|
|
15580
|
+
const atIso = new Date().toISOString();
|
|
15581
|
+
const time = toTimeStr(new Date(atIso));
|
|
15582
|
+
await firstValueFrom(this.feeding.setBottlePrepOnSheet({
|
|
15583
|
+
user: ctx.user,
|
|
15584
|
+
volumeMl: a.volumeMl,
|
|
15585
|
+
time,
|
|
15586
|
+
atIso,
|
|
15587
|
+
}));
|
|
15588
|
+
await firstValueFrom(this.activity.addLog({
|
|
15589
|
+
user: ctx.user,
|
|
15590
|
+
eventType: ACTIVITY_EVENT.BOTTLE_PREP_ADDED,
|
|
15591
|
+
content: formatLogContent(ACTIVITY_EVENT.BOTTLE_PREP_ADDED, {
|
|
15592
|
+
volume: a.volumeMl,
|
|
15593
|
+
time,
|
|
15594
|
+
}),
|
|
15595
|
+
}));
|
|
15596
|
+
return {
|
|
15597
|
+
ok: true,
|
|
15598
|
+
detail: `Đã pha sữa ${a.volumeMl}ml lúc ${time}`,
|
|
15599
|
+
// setBottlePrepOnSheet là ghi đè (không phải append) — không có undo rõ ràng qua chat.
|
|
15600
|
+
};
|
|
15601
|
+
}
|
|
15602
|
+
async logWeight(a, ctx) {
|
|
15603
|
+
const date = todayStr();
|
|
15604
|
+
const log = { user: ctx.user, date, weightKg: a.weightKg };
|
|
15605
|
+
const resp = await firstValueFrom(this.weight.addLog(log));
|
|
15606
|
+
if (resp.success) {
|
|
15607
|
+
await firstValueFrom(this.activity.logWeight(ctx.user, 'WEIGHT_ADDED', { date, weight: a.weightKg }));
|
|
15608
|
+
}
|
|
15609
|
+
return {
|
|
15610
|
+
ok: resp.success,
|
|
15611
|
+
detail: `Đã ghi cân nặng ${a.weightKg}kg`,
|
|
15612
|
+
undo: resp.rowIndex
|
|
15613
|
+
? async () => {
|
|
15614
|
+
await firstValueFrom(this.weight.deleteLog(resp.rowIndex));
|
|
15615
|
+
await firstValueFrom(this.activity.logWeight(ctx.user, 'WEIGHT_DELETED', {
|
|
15616
|
+
date,
|
|
15617
|
+
weight: a.weightKg,
|
|
15618
|
+
}));
|
|
15619
|
+
}
|
|
15620
|
+
: undefined,
|
|
15621
|
+
};
|
|
15622
|
+
}
|
|
15623
|
+
async setReminder(a, ctx) {
|
|
15624
|
+
if (!a.date || !a.time) {
|
|
15625
|
+
return {
|
|
15626
|
+
ok: false,
|
|
15627
|
+
detail: 'Thiếu ngày hoặc giờ để lên lịch — hãy điền vào ô nhập rồi xác nhận lại.',
|
|
15628
|
+
};
|
|
15629
|
+
}
|
|
15630
|
+
const date = a.date;
|
|
15631
|
+
const time = a.time;
|
|
15632
|
+
const title = a.title;
|
|
15633
|
+
const ev = {
|
|
15634
|
+
user: ctx.user,
|
|
15635
|
+
date,
|
|
15636
|
+
time,
|
|
15637
|
+
title,
|
|
15638
|
+
acknowledged: false,
|
|
15639
|
+
};
|
|
15640
|
+
const resp = await firstValueFrom(this.events.addEvent(ev));
|
|
15641
|
+
if (resp.success) {
|
|
15642
|
+
await firstValueFrom(this.activity.logSchedule(ctx.user, 'SCHEDULE_ADDED', { title, date }));
|
|
15643
|
+
}
|
|
15644
|
+
return {
|
|
15645
|
+
ok: resp.success,
|
|
15646
|
+
detail: `Đã lên lịch "${title}" — ${date} ${time}`,
|
|
15647
|
+
undo: resp.rowIndex
|
|
15648
|
+
? async () => {
|
|
15649
|
+
await firstValueFrom(this.events.deleteEvent(resp.rowIndex));
|
|
15650
|
+
await firstValueFrom(this.activity.logSchedule(ctx.user, 'SCHEDULE_DELETED', {
|
|
15651
|
+
title,
|
|
15652
|
+
date,
|
|
15653
|
+
}));
|
|
15654
|
+
}
|
|
15655
|
+
: undefined,
|
|
15656
|
+
};
|
|
15657
|
+
}
|
|
15658
|
+
async sendNotification(a, ctx) {
|
|
15659
|
+
const resp = await firstValueFrom(this.notifications.addNotification({ user: ctx.user, content: a.content }));
|
|
15660
|
+
if (resp.success) {
|
|
15661
|
+
await firstValueFrom(this.activity.addLog({
|
|
15662
|
+
user: ctx.user,
|
|
15663
|
+
eventType: ACTIVITY_EVENT.NOTIFICATION_CREATED,
|
|
15664
|
+
content: formatLogContent(ACTIVITY_EVENT.NOTIFICATION_CREATED, { content: a.content }),
|
|
15665
|
+
}));
|
|
15666
|
+
}
|
|
15667
|
+
return {
|
|
15668
|
+
ok: resp.success,
|
|
15669
|
+
detail: `Đã gửi thông báo tới nhóm`,
|
|
15670
|
+
undo: resp.rowIndex
|
|
15671
|
+
? async () => {
|
|
15672
|
+
await firstValueFrom(this.notifications.deleteNotification(resp.rowIndex, ctx.user));
|
|
15673
|
+
await firstValueFrom(this.activity.addLog({
|
|
15674
|
+
user: ctx.user,
|
|
15675
|
+
eventType: ACTIVITY_EVENT.NOTIFICATION_DELETED,
|
|
15676
|
+
content: formatLogContent(ACTIVITY_EVENT.NOTIFICATION_DELETED, {
|
|
15677
|
+
content: a.content,
|
|
15678
|
+
}),
|
|
15679
|
+
}));
|
|
15680
|
+
}
|
|
15681
|
+
: undefined,
|
|
15682
|
+
};
|
|
15683
|
+
}
|
|
15684
|
+
// ── Read-only tools (không side-effect, trả về detail prose) ──────
|
|
15685
|
+
queryRecent(ctx) {
|
|
15686
|
+
const detail = `Hôm nay bé bú ${ctx.todayCount} cữ, tổng ${ctx.todayTotalMl}ml. Cữ gần nhất ${ctx.todayLogs[0] ? `${ctx.todayLogs[0].time} — ${ctx.todayLogs[0].volume}ml` : 'chưa có'}. Cữ tiếp theo dự kiến ${ctx.nextFeedTime ?? 'không xác định'}.`;
|
|
15687
|
+
return { ok: true, detail };
|
|
15688
|
+
}
|
|
15689
|
+
nextEvent(ctx) {
|
|
15690
|
+
const next = ctx.upcomingEvents[0];
|
|
15691
|
+
return {
|
|
15692
|
+
ok: true,
|
|
15693
|
+
detail: next ? `Sự kiện kế tiếp: ${next.title} — ${next.dateLabel}` : 'Không có sự kiện sắp tới.',
|
|
15694
|
+
};
|
|
15695
|
+
}
|
|
15696
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToolRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
15697
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToolRegistry, providedIn: 'root' }); }
|
|
15698
|
+
}
|
|
15699
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToolRegistry, decorators: [{
|
|
15700
|
+
type: Injectable,
|
|
15701
|
+
args: [{ providedIn: 'root' }]
|
|
15702
|
+
}] });
|
|
15703
|
+
/** YYYY-MM-DD theo giờ địa phương. */
|
|
15704
|
+
function todayStr() {
|
|
15705
|
+
const d = new Date();
|
|
15706
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
15707
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
15708
|
+
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
15709
|
+
}
|
|
15710
|
+
/** HH:mm (24h, giờ địa phương) từ Date. */
|
|
15711
|
+
function toTimeStr(d) {
|
|
15712
|
+
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
15713
|
+
}
|
|
15714
|
+
|
|
15715
|
+
let idCounter = 0;
|
|
15716
|
+
const nextId = () => `c${++idCounter}`;
|
|
15717
|
+
const nowTs = () => new Date().toLocaleTimeString('vi-VN', { hour: '2-digit', minute: '2-digit' });
|
|
15718
|
+
/**
|
|
15719
|
+
* Lớp vỏ khung chat — giữ messages signal, orchestrate flow:
|
|
15720
|
+
* user send → build context → HF inference → parse → (text | tool) → render.
|
|
15721
|
+
*
|
|
15722
|
+
* Tool có side-effect (ghi sheet) → render thẻ «chờ xác nhận»; user bấm
|
|
15723
|
+
* «Xác nhận» mới dispatch. Tool read-only → dispatch ngay.
|
|
15724
|
+
*
|
|
15725
|
+
* Input `context` là snapshot dữ liệu bé từ FeedingComponent (signals).
|
|
15726
|
+
* `disabled` true khi chưa cấu hình HF — render callout thay vì khung nhập.
|
|
15727
|
+
*/
|
|
15728
|
+
class ChatShellComponent {
|
|
15729
|
+
constructor() {
|
|
15730
|
+
this.hf = inject(HfAssistantService);
|
|
15731
|
+
this.tools = inject(ToolRegistry);
|
|
15732
|
+
/** Snapshot dữ liệu bé — parent truyền vào mỗi khi mở chat. */
|
|
15733
|
+
this.context = input.required(...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
15734
|
+
/** True khi chưa cấu hình HF — chỉ render callout. */
|
|
15735
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
15736
|
+
this.messages = signal([], ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
15737
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
15738
|
+
/** Map id → undo callback (lưu từ ToolResult khi dispatch). */
|
|
15739
|
+
this.undoMap = new Map();
|
|
15740
|
+
/** Map id → {tool, args} cho thẻ pending chờ user Confirm. */
|
|
15741
|
+
this.pendingMap = new Map();
|
|
15742
|
+
/** Lịch sử dạng OpenAI messages: user + assistant text + action result (model thấy kết quả tool). */
|
|
15743
|
+
this.history = computed(() => this.messages()
|
|
15744
|
+
.filter((m) => m.kind === 'user' || m.kind === 'text' || m.kind === 'action')
|
|
15745
|
+
.map((m) => ({
|
|
15746
|
+
role: m.kind === 'user' ? 'user' : 'assistant',
|
|
15747
|
+
content: m.kind === 'action' ? m.action?.detail ?? '' : m.text ?? '',
|
|
15748
|
+
})), ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
|
|
15749
|
+
this.greeted = false;
|
|
15750
|
+
// Auto-scroll tới tin mới nhất mỗi khi messages/busy thay đổi.
|
|
15751
|
+
// effect chạy sau khi signal update; dùng rAF để chờ DOM paint xong.
|
|
15752
|
+
effect(() => {
|
|
15753
|
+
this.messages();
|
|
15754
|
+
this.busy();
|
|
15755
|
+
this.scrollToBottom();
|
|
15756
|
+
});
|
|
15757
|
+
// Greet khi có context lần đầu.
|
|
15758
|
+
queueMicrotask(() => this.maybeGreet());
|
|
15759
|
+
}
|
|
15760
|
+
ngOnChanges() {
|
|
15761
|
+
this.maybeGreet();
|
|
15762
|
+
}
|
|
15763
|
+
maybeGreet() {
|
|
15764
|
+
if (this.greeted)
|
|
15765
|
+
return;
|
|
15766
|
+
const ctx = this.context();
|
|
15767
|
+
if (!ctx)
|
|
15768
|
+
return;
|
|
15769
|
+
this.greeted = true;
|
|
15770
|
+
this.messages.update((prev) => [
|
|
15771
|
+
...prev,
|
|
15772
|
+
{
|
|
15773
|
+
id: nextId(),
|
|
15774
|
+
kind: 'context',
|
|
15775
|
+
text: `Xin chào! Mình là trợ lý bú sữa. Bé ${ctx.babyName || ''} ${ctx.ageLabel}, ` +
|
|
15776
|
+
`${ctx.weightKg !== undefined ? ctx.weightKg + 'kg' : 'chưa rõ cân nặng'}. ` +
|
|
15777
|
+
`Hôm nay bé bú ${ctx.todayCount} cữ, tổng ${ctx.todayTotalMl}ml. ` +
|
|
15778
|
+
`Bạn muốn hỏi hay ghi nhận gì?`,
|
|
15779
|
+
ts: nowTs(),
|
|
15780
|
+
},
|
|
15781
|
+
]);
|
|
15782
|
+
}
|
|
15783
|
+
async onSend(text) {
|
|
15784
|
+
if (this.busy() || this.disabled())
|
|
15785
|
+
return;
|
|
15786
|
+
const ctx = this.context();
|
|
15787
|
+
// 1. append user msg
|
|
15788
|
+
this.messages.update((prev) => [
|
|
15789
|
+
...prev,
|
|
15790
|
+
{ id: nextId(), kind: 'user', text, ts: nowTs() },
|
|
15791
|
+
]);
|
|
15792
|
+
this.scrollToBottom();
|
|
15793
|
+
this.busy.set(true);
|
|
15794
|
+
try {
|
|
15795
|
+
const raw = await this.hf.send(ctx, this.history());
|
|
15796
|
+
const reply = parseHfReply(raw);
|
|
15797
|
+
if (reply.kind === 'text') {
|
|
15798
|
+
this.messages.update((prev) => [
|
|
15799
|
+
...prev,
|
|
15800
|
+
{ id: nextId(), kind: 'text', text: reply.text, ts: nowTs() },
|
|
15801
|
+
]);
|
|
15802
|
+
}
|
|
15803
|
+
else {
|
|
15804
|
+
this.routeToolCall(reply.tool, reply.args, ctx);
|
|
15805
|
+
}
|
|
15806
|
+
}
|
|
15807
|
+
catch (err) {
|
|
15808
|
+
const msg = err instanceof Error ? err.message : 'lỗi không xác định';
|
|
15809
|
+
this.messages.update((prev) => [
|
|
15810
|
+
...prev,
|
|
15811
|
+
{ id: nextId(), kind: 'error', text: msg, ts: nowTs() },
|
|
15812
|
+
]);
|
|
15813
|
+
}
|
|
15814
|
+
finally {
|
|
15815
|
+
this.busy.set(false);
|
|
15816
|
+
this.scrollToBottom();
|
|
15817
|
+
}
|
|
15818
|
+
}
|
|
15819
|
+
/**
|
|
15820
|
+
* Phân luồng tool call:
|
|
15821
|
+
* - Write tool (ghi sheet) → hiển thị thẻ «chờ xác nhận», đợi user bấm Confirm.
|
|
15822
|
+
* - Read-only tool → dispatch ngay.
|
|
15823
|
+
*/
|
|
15824
|
+
routeToolCall(tool, args, ctx) {
|
|
15825
|
+
let preview;
|
|
15826
|
+
try {
|
|
15827
|
+
preview = this.tools.describe(tool, args);
|
|
15828
|
+
}
|
|
15829
|
+
catch (err) {
|
|
15830
|
+
const msg = err instanceof Error ? err.message : 'thất bại';
|
|
15831
|
+
this.messages.update((prev) => [
|
|
15832
|
+
...prev,
|
|
15833
|
+
{ id: nextId(), kind: 'error', text: `Không thực thi: ${msg}`, ts: nowTs() },
|
|
15834
|
+
]);
|
|
15835
|
+
return;
|
|
15836
|
+
}
|
|
15837
|
+
if (this.tools.isWriteTool(tool)) {
|
|
15838
|
+
const id = nextId();
|
|
15839
|
+
this.pendingMap.set(id, { tool, args });
|
|
15840
|
+
this.messages.update((prev) => [
|
|
15841
|
+
...prev,
|
|
15842
|
+
{
|
|
15843
|
+
id,
|
|
15844
|
+
kind: 'pending',
|
|
15845
|
+
action: {
|
|
15846
|
+
tool,
|
|
15847
|
+
detail: preview.detail,
|
|
15848
|
+
args,
|
|
15849
|
+
inputs: preview.inputs,
|
|
15850
|
+
inputDraft: {},
|
|
15851
|
+
},
|
|
15852
|
+
ts: nowTs(),
|
|
15853
|
+
},
|
|
15854
|
+
]);
|
|
15855
|
+
return;
|
|
15856
|
+
}
|
|
15857
|
+
// Read-only: dispatch ngay.
|
|
15858
|
+
void this.executeTool(tool, args, ctx);
|
|
15859
|
+
}
|
|
15860
|
+
/** User gõ vào ô nhập trên thẻ pending — cập nhật inputDraft. */
|
|
15861
|
+
onInputChange(e) {
|
|
15862
|
+
this.messages.update((prev) => prev.map((m) => m.id === e.id && m.action
|
|
15863
|
+
? {
|
|
15864
|
+
...m,
|
|
15865
|
+
action: {
|
|
15866
|
+
...m.action,
|
|
15867
|
+
inputDraft: { ...(m.action.inputDraft ?? {}), [e.key]: e.value },
|
|
15868
|
+
},
|
|
15869
|
+
}
|
|
15870
|
+
: m));
|
|
15871
|
+
}
|
|
15872
|
+
/** User bấm «Xác nhận» trên thẻ pending — dispatch tool thật với input đã gộp. */
|
|
15873
|
+
async onConfirm(msgId) {
|
|
15874
|
+
const pending = this.pendingMap.get(msgId);
|
|
15875
|
+
if (!pending)
|
|
15876
|
+
return;
|
|
15877
|
+
const ctx = this.context();
|
|
15878
|
+
const { tool, args } = pending;
|
|
15879
|
+
this.pendingMap.delete(msgId);
|
|
15880
|
+
// Gộp giá trị user đã nhập vào ô nhập (date/time…) vào args gốc.
|
|
15881
|
+
const msg = this.messages().find((m) => m.id === msgId);
|
|
15882
|
+
const draft = msg?.action?.inputDraft ?? {};
|
|
15883
|
+
const mergedArgs = { ...args };
|
|
15884
|
+
for (const [key, value] of Object.entries(draft)) {
|
|
15885
|
+
if (value && value.trim())
|
|
15886
|
+
mergedArgs[key] = value.trim();
|
|
15887
|
+
}
|
|
15888
|
+
// Xoá thẻ pending — executeTool sẽ append thẻ action (hoặc error) mới.
|
|
15889
|
+
this.messages.update((prev) => prev.filter((m) => m.id !== msgId));
|
|
15890
|
+
await this.executeTool(tool, mergedArgs, ctx);
|
|
15891
|
+
}
|
|
15892
|
+
/** User bấm «Huỷ» trên thẻ pending — đánh dấu đã huỷ, drop args. */
|
|
15893
|
+
onCancel(msgId) {
|
|
15894
|
+
this.pendingMap.delete(msgId);
|
|
15895
|
+
this.messages.update((prev) => prev.map((m) => m.id === msgId && m.kind === 'pending' && m.action
|
|
15896
|
+
? { ...m, action: { ...m.action, cancelled: true, args: undefined } }
|
|
15897
|
+
: m));
|
|
15898
|
+
}
|
|
15899
|
+
async executeTool(tool, args, ctx) {
|
|
15900
|
+
const result = await this.tools.dispatch(tool, args, ctx);
|
|
15901
|
+
const id = nextId();
|
|
15902
|
+
if (result.undo)
|
|
15903
|
+
this.undoMap.set(id, result.undo);
|
|
15904
|
+
if (!result.ok) {
|
|
15905
|
+
this.messages.update((prev) => [
|
|
15906
|
+
...prev,
|
|
15907
|
+
{ id, kind: 'error', text: result.detail, ts: nowTs() },
|
|
15908
|
+
]);
|
|
15909
|
+
return;
|
|
15910
|
+
}
|
|
15911
|
+
this.messages.update((prev) => [
|
|
15912
|
+
...prev,
|
|
15913
|
+
{
|
|
15914
|
+
id,
|
|
15915
|
+
kind: 'action',
|
|
15916
|
+
action: { tool, detail: result.detail },
|
|
15917
|
+
ts: nowTs(),
|
|
15918
|
+
},
|
|
15919
|
+
]);
|
|
15920
|
+
// Kết quả tool đã nằm trong action card; `history` computed bao gồm action
|
|
15921
|
+
// (map sang assistant message) nên model thấy được — không cần text bubble thừa.
|
|
15922
|
+
}
|
|
15923
|
+
async onUndo(msgId) {
|
|
15924
|
+
// Bước 1: vào chế độ chờ xác nhận — KHÔNG gọi undo ngay.
|
|
15925
|
+
this.messages.update((prev) => prev.map((m) => m.id === msgId && m.action && !m.action.undone
|
|
15926
|
+
? { ...m, action: { ...m.action, undoPending: true } }
|
|
15927
|
+
: m));
|
|
15928
|
+
}
|
|
15929
|
+
/** User xác nhận hoàn tác — gọi undo callback thật + ghi activity log. */
|
|
15930
|
+
async onConfirmUndo(msgId) {
|
|
15931
|
+
const undo = this.undoMap.get(msgId);
|
|
15932
|
+
if (!undo) {
|
|
15933
|
+
this.messages.update((prev) => prev.map((m) => m.id === msgId && m.action
|
|
15934
|
+
? { ...m, action: { ...m.action, undoPending: false } }
|
|
15935
|
+
: m));
|
|
15936
|
+
return;
|
|
15937
|
+
}
|
|
15938
|
+
try {
|
|
15939
|
+
await undo();
|
|
15940
|
+
this.undoMap.delete(msgId);
|
|
15941
|
+
this.messages.update((prev) => prev.map((m) => m.id === msgId && m.action
|
|
15942
|
+
? { ...m, action: { ...m.action, undone: true, undoPending: false } }
|
|
15943
|
+
: m));
|
|
15944
|
+
}
|
|
15945
|
+
catch (err) {
|
|
15946
|
+
const msg = err instanceof Error ? err.message : 'hoàn tác thất bại';
|
|
15947
|
+
this.messages.update((prev) => [
|
|
15948
|
+
...prev.map((m) => m.id === msgId && m.action
|
|
15949
|
+
? { ...m, action: { ...m.action, undoPending: false } }
|
|
15950
|
+
: m),
|
|
15951
|
+
{ id: nextId(), kind: 'error', text: `Hoàn tác: ${msg}`, ts: nowTs() },
|
|
15952
|
+
]);
|
|
15953
|
+
}
|
|
15954
|
+
}
|
|
15955
|
+
/** User huỷ ý định hoàn tác — thoát chế độ chờ xác nhận. */
|
|
15956
|
+
onCancelUndo(msgId) {
|
|
15957
|
+
this.messages.update((prev) => prev.map((m) => m.id === msgId && m.action
|
|
15958
|
+
? { ...m, action: { ...m.action, undoPending: false } }
|
|
15959
|
+
: m));
|
|
15960
|
+
}
|
|
15961
|
+
scrollToBottom() {
|
|
15962
|
+
// requestAnimationFrame chờ Angular vẽ DOM xong rồi mới cuộn — tránh scrollHeight stale.
|
|
15963
|
+
requestAnimationFrame(() => {
|
|
15964
|
+
const el = this.scrollRef?.nativeElement;
|
|
15965
|
+
if (el)
|
|
15966
|
+
el.scrollTop = el.scrollHeight;
|
|
15967
|
+
});
|
|
15968
|
+
}
|
|
15969
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15970
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ChatShellComponent, isStandalone: true, selector: "app-chat-shell", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "scrollRef", first: true, predicate: ["scroll"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
15971
|
+
<div class="cs-scroll" #scroll>
|
|
15972
|
+
@for (m of messages(); track m.id) {
|
|
15973
|
+
<app-chat-message
|
|
15974
|
+
[msg]="m"
|
|
15975
|
+
(undo)="onUndo($event)"
|
|
15976
|
+
(confirm)="onConfirm($event)"
|
|
15977
|
+
(cancel)="onCancel($event)"
|
|
15978
|
+
(confirmUndo)="onConfirmUndo($event)"
|
|
15979
|
+
(cancelUndo)="onCancelUndo($event)"
|
|
15980
|
+
(inputChange)="onInputChange($event)"
|
|
15981
|
+
></app-chat-message>
|
|
15982
|
+
}
|
|
15983
|
+
@if (busy()) {
|
|
15984
|
+
<div class="cs-typing" aria-live="polite" aria-label="Trợ lý đang trả lời">
|
|
15985
|
+
<span class="cs-typing__bubble">
|
|
15986
|
+
<span class="cs-typing__dot"></span>
|
|
15987
|
+
<span class="cs-typing__dot"></span>
|
|
15988
|
+
<span class="cs-typing__dot"></span>
|
|
15989
|
+
</span>
|
|
15990
|
+
<span class="cs-typing__label">Trợ lý đang trả lời…</span>
|
|
15991
|
+
</div>
|
|
15992
|
+
}
|
|
15993
|
+
</div>
|
|
15994
|
+
|
|
15995
|
+
@if (disabled()) {
|
|
15996
|
+
<div class="cs-disabled">
|
|
15997
|
+
<i class="pi pi-exclamation-triangle" aria-hidden="true"></i>
|
|
15998
|
+
<span>Trợ lý AI chưa cấu hình. Set <code>NG_APP_HF_INFERENCE_URL</code> để bật.</span>
|
|
15999
|
+
</div>
|
|
16000
|
+
} @else {
|
|
16001
|
+
<app-chat-composer (send)="onSend($event)"></app-chat-composer>
|
|
16002
|
+
}
|
|
16003
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg)}.cs-scroll{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:14px 12px;display:flex;flex-direction:column;gap:12px}.cs-typing{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;padding:8px 12px;background:var(--surface);border:1px solid var(--border);border-radius:14px 14px 14px 4px}.cs-typing__bubble{display:inline-flex;gap:4px}.cs-typing__dot{width:6px;height:6px;border-radius:999px;background:var(--muted-2);animation:cs-bounce 1.2s infinite ease-in-out}.cs-typing__dot:nth-child(2){animation-delay:.15s}.cs-typing__dot:nth-child(3){animation-delay:.3s}.cs-typing__label{font-size:12px;color:var(--muted-2);line-height:1}@keyframes cs-bounce{0%,80%,to{transform:translateY(0);opacity:.5}40%{transform:translateY(-3px);opacity:1}}.cs-disabled{display:flex;align-items:center;gap:8px;margin:10px 12px;padding:12px;border-radius:10px;background:var(--amber-soft, #fef3c7);color:var(--amber, #f59e0b);font-size:12.5px;line-height:1.4}.cs-disabled i{font-size:16px;flex-shrink:0}.cs-disabled code{background:#f59e0b26;padding:1px 5px;border-radius:4px;font-size:11px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatComposerComponent, selector: "app-chat-composer", outputs: ["send"] }, { kind: "component", type: ChatMessageComponent, selector: "app-chat-message", inputs: ["msg"], outputs: ["undo", "confirm", "cancel", "confirmUndo", "cancelUndo", "inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
16004
|
+
}
|
|
16005
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ChatShellComponent, decorators: [{
|
|
16006
|
+
type: Component,
|
|
16007
|
+
args: [{ selector: 'app-chat-shell', standalone: true, imports: [CommonModule, ChatComposerComponent, ChatMessageComponent], template: `
|
|
16008
|
+
<div class="cs-scroll" #scroll>
|
|
16009
|
+
@for (m of messages(); track m.id) {
|
|
16010
|
+
<app-chat-message
|
|
16011
|
+
[msg]="m"
|
|
16012
|
+
(undo)="onUndo($event)"
|
|
16013
|
+
(confirm)="onConfirm($event)"
|
|
16014
|
+
(cancel)="onCancel($event)"
|
|
16015
|
+
(confirmUndo)="onConfirmUndo($event)"
|
|
16016
|
+
(cancelUndo)="onCancelUndo($event)"
|
|
16017
|
+
(inputChange)="onInputChange($event)"
|
|
16018
|
+
></app-chat-message>
|
|
16019
|
+
}
|
|
16020
|
+
@if (busy()) {
|
|
16021
|
+
<div class="cs-typing" aria-live="polite" aria-label="Trợ lý đang trả lời">
|
|
16022
|
+
<span class="cs-typing__bubble">
|
|
16023
|
+
<span class="cs-typing__dot"></span>
|
|
16024
|
+
<span class="cs-typing__dot"></span>
|
|
16025
|
+
<span class="cs-typing__dot"></span>
|
|
16026
|
+
</span>
|
|
16027
|
+
<span class="cs-typing__label">Trợ lý đang trả lời…</span>
|
|
16028
|
+
</div>
|
|
16029
|
+
}
|
|
16030
|
+
</div>
|
|
16031
|
+
|
|
16032
|
+
@if (disabled()) {
|
|
16033
|
+
<div class="cs-disabled">
|
|
16034
|
+
<i class="pi pi-exclamation-triangle" aria-hidden="true"></i>
|
|
16035
|
+
<span>Trợ lý AI chưa cấu hình. Set <code>NG_APP_HF_INFERENCE_URL</code> để bật.</span>
|
|
16036
|
+
</div>
|
|
16037
|
+
} @else {
|
|
16038
|
+
<app-chat-composer (send)="onSend($event)"></app-chat-composer>
|
|
16039
|
+
}
|
|
16040
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg)}.cs-scroll{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:14px 12px;display:flex;flex-direction:column;gap:12px}.cs-typing{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;padding:8px 12px;background:var(--surface);border:1px solid var(--border);border-radius:14px 14px 14px 4px}.cs-typing__bubble{display:inline-flex;gap:4px}.cs-typing__dot{width:6px;height:6px;border-radius:999px;background:var(--muted-2);animation:cs-bounce 1.2s infinite ease-in-out}.cs-typing__dot:nth-child(2){animation-delay:.15s}.cs-typing__dot:nth-child(3){animation-delay:.3s}.cs-typing__label{font-size:12px;color:var(--muted-2);line-height:1}@keyframes cs-bounce{0%,80%,to{transform:translateY(0);opacity:.5}40%{transform:translateY(-3px);opacity:1}}.cs-disabled{display:flex;align-items:center;gap:8px;margin:10px 12px;padding:12px;border-radius:10px;background:var(--amber-soft, #fef3c7);color:var(--amber, #f59e0b);font-size:12.5px;line-height:1.4}.cs-disabled i{font-size:16px;flex-shrink:0}.cs-disabled code{background:#f59e0b26;padding:1px 5px;border-radius:4px;font-size:11px}\n"] }]
|
|
16041
|
+
}], ctorParameters: () => [], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], scrollRef: [{
|
|
16042
|
+
type: ViewChild,
|
|
16043
|
+
args: ['scroll', { static: true }]
|
|
16044
|
+
}] } });
|
|
16045
|
+
|
|
14835
16046
|
const STORAGE_PREFIX = 'feeding-profile::';
|
|
14836
16047
|
const INITIAL_NOTIFICATION_VISIBLE = 2;
|
|
14837
16048
|
class FeedingComponent {
|
|
@@ -14855,6 +16066,22 @@ class FeedingComponent {
|
|
|
14855
16066
|
document.body.scrollTop = 0;
|
|
14856
16067
|
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
|
14857
16068
|
}
|
|
16069
|
+
/**
|
|
16070
|
+
* Lịch sử cữ bú của `daysBack` ngày gần đây KHÔNG tính hôm nay
|
|
16071
|
+
* (mới → cũ, gộp tất cả cữ của các ngày đó). Dùng cho chat context
|
|
16072
|
+
* để trợ lý có bối cảnh xu hướng bú mấy ngày qua.
|
|
16073
|
+
*/
|
|
16074
|
+
recentFeedingLogs(daysBack) {
|
|
16075
|
+
const today = this.todayDateStr();
|
|
16076
|
+
const cutoff = this.dateStrDaysAgo(daysBack);
|
|
16077
|
+
return this.logs()
|
|
16078
|
+
.filter((l) => l.date < today && l.date >= cutoff)
|
|
16079
|
+
.sort((a, b) => {
|
|
16080
|
+
if (a.date !== b.date)
|
|
16081
|
+
return b.date.localeCompare(a.date);
|
|
16082
|
+
return b.time.localeCompare(a.time);
|
|
16083
|
+
});
|
|
16084
|
+
}
|
|
14858
16085
|
constructor() {
|
|
14859
16086
|
this.route = inject(ActivatedRoute);
|
|
14860
16087
|
this.destroyRef = inject(DestroyRef);
|
|
@@ -14899,6 +16126,11 @@ class FeedingComponent {
|
|
|
14899
16126
|
* Cột user chỉ để biết ai log — không lọc theo user khi lấy giá trị này.
|
|
14900
16127
|
*/
|
|
14901
16128
|
this.latestWeightKgFromSheet = signal(undefined, ...(ngDevMode ? [{ debugName: "latestWeightKgFromSheet" }] : /* istanbul ignore next */ []));
|
|
16129
|
+
/**
|
|
16130
|
+
* Toàn bộ bản ghi cân nặng + chiều cao từ sheet Weight (mới → cũ).
|
|
16131
|
+
* Dùng cho chat context — trợ lý có thể thấy lịch sử cân/cao gần đây.
|
|
16132
|
+
*/
|
|
16133
|
+
this.weightLogs = signal([], ...(ngDevMode ? [{ debugName: "weightLogs" }] : /* istanbul ignore next */ []));
|
|
14902
16134
|
this.editing = signal(true, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
|
|
14903
16135
|
this.now = signal(new Date(), ...(ngDevMode ? [{ debugName: "now" }] : /* istanbul ignore next */ []));
|
|
14904
16136
|
// ===== Feeding log state =====
|
|
@@ -14938,6 +16170,54 @@ class FeedingComponent {
|
|
|
14938
16170
|
this.momRouteOpen = signal(false, ...(ngDevMode ? [{ debugName: "momRouteOpen" }] : /* istanbul ignore next */ []));
|
|
14939
16171
|
/** Subroute Tài liệu (full-screen, mở từ Settings menu). */
|
|
14940
16172
|
this.documentsRouteOpen = signal(false, ...(ngDevMode ? [{ debugName: "documentsRouteOpen" }] : /* istanbul ignore next */ []));
|
|
16173
|
+
/** Subroute Trợ lý AI (full-screen, mở từ nút chat trên hero). */
|
|
16174
|
+
this.chatRouteOpen = signal(false, ...(ngDevMode ? [{ debugName: "chatRouteOpen" }] : /* istanbul ignore next */ []));
|
|
16175
|
+
/** True khi chưa cấu hình NG_APP_HF_INFERENCE_URL — chat shell render callout thay vì composer. */
|
|
16176
|
+
this.chatDisabled = computed(() => !this.feedingConfig.hfInferenceUrl, ...(ngDevMode ? [{ debugName: "chatDisabled" }] : /* istanbul ignore next */ []));
|
|
16177
|
+
/** Snapshot dữ liệu bé truyền vào ChatShell — đọc từ các signals hiện có. */
|
|
16178
|
+
this.chatContext = computed(() => {
|
|
16179
|
+
const age = this.ageBreakdown();
|
|
16180
|
+
const ageLabel = age
|
|
16181
|
+
? `${age.years > 0 ? age.years + ' năm ' : ''}${age.months} tháng ${age.weeksInMonth} tuần`.trim()
|
|
16182
|
+
: 'chưa rõ';
|
|
16183
|
+
const today = this.todayLogs();
|
|
16184
|
+
const stats = this.todayStats();
|
|
16185
|
+
const target = this.nutritionTarget();
|
|
16186
|
+
return {
|
|
16187
|
+
babyName: this.profile()?.babyName ?? '',
|
|
16188
|
+
ageLabel,
|
|
16189
|
+
ageInDays: this.ageInDays(),
|
|
16190
|
+
weightKg: this.latestWeightKgFromSheet(),
|
|
16191
|
+
todayLogs: today,
|
|
16192
|
+
todayTotalMl: stats.total,
|
|
16193
|
+
todayCount: stats.count,
|
|
16194
|
+
nextFeedTime: this.prediction()?.nextTime ?? null,
|
|
16195
|
+
nutritionTargetLabel: target
|
|
16196
|
+
? `${target.perKgFactor} ml/kg/ngày ≈ ${target.dailyMlMin}–${target.dailyMlMax} ml`
|
|
16197
|
+
: null,
|
|
16198
|
+
upcomingEvents: this.eventLogService
|
|
16199
|
+
.events()
|
|
16200
|
+
.filter((e) => {
|
|
16201
|
+
// Chỉ lấy sự kiện từ ngày hiện tại trở đi (so theo ngày, bỏ qua giờ).
|
|
16202
|
+
const dt = EventLogService.eventDateTime(e);
|
|
16203
|
+
if (!dt)
|
|
16204
|
+
return false;
|
|
16205
|
+
const eventDay = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()).getTime();
|
|
16206
|
+
const todayDay = new Date(this.now().getFullYear(), this.now().getMonth(), this.now().getDate()).getTime();
|
|
16207
|
+
return eventDay >= todayDay;
|
|
16208
|
+
})
|
|
16209
|
+
.map((e) => ({
|
|
16210
|
+
title: e.title,
|
|
16211
|
+
dateLabel: e.date,
|
|
16212
|
+
time: e.time,
|
|
16213
|
+
place: e.place,
|
|
16214
|
+
})),
|
|
16215
|
+
recentWeightLogs: this.weightLogs(),
|
|
16216
|
+
recentFeedingLogs: this.recentFeedingLogs(3),
|
|
16217
|
+
unreadNotifications: this.visibleNotifications().length,
|
|
16218
|
+
user: this.user(),
|
|
16219
|
+
};
|
|
16220
|
+
}, ...(ngDevMode ? [{ debugName: "chatContext" }] : /* istanbul ignore next */ []));
|
|
14941
16221
|
this.acknowledgeAllConfirmDialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "acknowledgeAllConfirmDialogOpen" }] : /* istanbul ignore next */ []));
|
|
14942
16222
|
/** Ngày đang xem trên card lịch sử (mặc định: hôm qua). */
|
|
14943
16223
|
this.ageInDays = computed(() => {
|
|
@@ -15233,15 +16513,16 @@ class FeedingComponent {
|
|
|
15233
16513
|
this.activityLogService.setRefreshInterval(s.activityLogRefreshMinutes);
|
|
15234
16514
|
setTimeout(() => this.loadFeedingSettings(), 600);
|
|
15235
16515
|
}
|
|
15236
|
-
/** Tải cân
|
|
16516
|
+
/** Tải toàn bộ bản ghi cân nặng từ sheet Weight (cùng nguồn với tab Cân nặng). */
|
|
15237
16517
|
loadWeightLogs() {
|
|
15238
16518
|
this.weightLogService.getLogs().subscribe({
|
|
15239
16519
|
next: (rows) => {
|
|
15240
|
-
|
|
15241
|
-
this.latestWeightKgFromSheet.set(
|
|
16520
|
+
this.weightLogs.set(rows);
|
|
16521
|
+
this.latestWeightKgFromSheet.set(rows[0]?.weightKg);
|
|
15242
16522
|
},
|
|
15243
16523
|
error: (err) => {
|
|
15244
16524
|
console.error(err);
|
|
16525
|
+
this.weightLogs.set([]);
|
|
15245
16526
|
this.latestWeightKgFromSheet.set(undefined);
|
|
15246
16527
|
},
|
|
15247
16528
|
});
|
|
@@ -15292,6 +16573,13 @@ class FeedingComponent {
|
|
|
15292
16573
|
return;
|
|
15293
16574
|
this.notifRouteOpen.set(false);
|
|
15294
16575
|
}
|
|
16576
|
+
// ── Trợ lý AI subroute ──
|
|
16577
|
+
openChatRoute() {
|
|
16578
|
+
this.chatRouteOpen.set(true);
|
|
16579
|
+
}
|
|
16580
|
+
closeChatRoute() {
|
|
16581
|
+
this.chatRouteOpen.set(false);
|
|
16582
|
+
}
|
|
15295
16583
|
// ── Settings float menu ──
|
|
15296
16584
|
openSettingsMenu() {
|
|
15297
16585
|
this.settingsMenuOpen.set(true);
|
|
@@ -15821,7 +17109,7 @@ class FeedingComponent {
|
|
|
15821
17109
|
}).format(kg);
|
|
15822
17110
|
}
|
|
15823
17111
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FeedingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15824
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FeedingComponent, isStandalone: true, selector: "app-feeding", viewQueries: [{ propertyName: "feedingPageRef", first: true, predicate: ["feedingPage"], descendants: true, static: true }, { propertyName: "documentsCmp", first: true, predicate: DocumentsComponent, descendants: true }, { propertyName: "medicalCmp", first: true, predicate: MedicalHistoryComponent, descendants: true }, { propertyName: "weightCmp", first: true, predicate: WeightComponent, descendants: true }, { propertyName: "feedingDialogs", first: true, predicate: ["feedingDialogs"], descendants: true }], ngImport: i0, template: "<div class=\"feeding-root\">\n <div class=\"feeding-page\" #feedingPage>\n <div class=\"backdrop\"></div>\n\n <header class=\"hero\" [class.hero--profile-merged]=\"profile() && !editing() && ageBreakdown()\">\n @if (profile() && !editing() && ageBreakdown(); as age) {\n <!-- \u2550\u2550 REDESIGNED HERO \u2550\u2550 -->\n <app-hero-header\n [profile]=\"profile()!\"\n [age]=\"age\"\n [user]=\"user()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [isAnyLoading]=\"isAnyLoading()\"\n [showRefresh]=\"bottomTab() !== 'schedule'\"\n (openMenu)=\"openSettingsMenu()\"\n (refresh)=\"refreshAll()\"\n >\n <app-activity-log slot=\"activity-log\" (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n </app-hero-header>\n\n <app-hero-stats-strip\n [currentTime]=\"currentTimeStr\"\n [dateLabel]=\"heroDateLabel\"\n [nextFeedTime]=\"prediction()?.nextTime ?? null\"\n [nextFeedCountdown]=\"heroNextFeedCountdown()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [whoStatus]=\"nutritionTarget()?.ageLabel ?? null\"\n ></app-hero-stats-strip>\n\n <div class=\"hero-notif-wrap\">\n <app-notification-banner\n [notifications]=\"visibleNotifications()\"\n [saving]=\"notificationSaving()\"\n [currentUser]=\"user()\"\n [dialogOpen]=\"notifDialogOpen()\"\n (openDialog)=\"openNotificationRoute()\"\n (closeDialog)=\"notifDialogOpen.set(false)\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"notifDialogOpen.set(false); openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"notifDialogOpen.set(false); openNotificationComposerDialog()\"\n ></app-notification-banner>\n </div>\n\n } @else {\n <!-- \u2500\u2500 Fallback: no profile / editing \u2500\u2500 -->\n <div class=\"hero-inner\">\n <div class=\"hero-brand\">\n <div class=\"hero-title-row\">\n <div class=\"hero-title\">\n <div class=\"emoji\" aria-hidden=\"true\">\uD83D\uDC76</div>\n <div class=\"hero-text\">\n <h1>B\u00E9 y\u00EAu c\u1EE7a b\u1EA1n</h1>\n <p class=\"hero-userline\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <strong>{{ user() }}</strong>\n </p>\n </div>\n </div>\n @if (profile() && !editing()) {\n <div class=\"hero-actions\">\n <app-activity-log (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"feedingDialogs.openFeedingSettingsDialog()\"\n title=\"C\u00E0i \u0111\u1EB7t\"\n aria-label=\"C\u00E0i \u0111\u1EB7t c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-sliders-h\"></i>\n </button>\n @if (bottomTab() !== 'schedule') {\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"refreshAll()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"\n isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, C\u00E2n, L\u1ECBch, Y t\u1EBF, T\u00E0i li\u1EC7u'\n \"\n aria-label=\"T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, Th\u1EC3 tr\u1EA1ng, L\u1ECBch, Ti\u1EC1n s\u1EED y t\u1EBF v\u00E0 T\u00E0i li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"hero-datetime\" aria-live=\"polite\">\n <div class=\"hero-datetime-strip\">\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--date\">\n <i class=\"pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__wd\">{{ heroWeekday }}</span>\n <time class=\"hero-datetime-strip__date\" [attr.datetime]=\"todayDateStr()\">{{\n heroDateNumeric\n }}</time>\n </div>\n <span class=\"hero-datetime-strip__divider\" aria-hidden=\"true\"></span>\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--time\">\n <i class=\"pi pi-clock\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__clock\">{{ currentTimeStr }}</span>\n </div>\n </div>\n </div>\n\n <section class=\"hero-notification\" aria-label=\"Th\u00F4ng b\u00E1o n\u1ED9i b\u1ED9\">\n <!-- watermark background icons -->\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bell\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-megaphone\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-comments\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--send\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-send\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--star\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-star\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bolt\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bolt\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--heart\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-heart\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--info\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-info-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--check\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-check-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--eye\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-eye\"></i>\n </span>\n\n <div class=\"hero-notification__head\">\n <label class=\"hero-notification__composer-label\" for=\"hero-notification-input\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n <span>Th\u00F4ng b\u00E1o</span>\n @if (visibleNotifications().length > 0) {\n <span class=\"hero-notification__unread-badge\">{{\n visibleNotifications().length\n }}</span>\n }\n </label>\n <div class=\"hero-notification__head-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button\n type=\"button\"\n class=\"hero-notification__add\"\n (click)=\"openNotificationComposerDialog()\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n title=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (displayedNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of displayedNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">\n {{ notice.user | slice: 0 : 2 | uppercase }}\n </div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{\n formatNotificationCreatedAt(notice.createdAt)\n }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\"\n title=\"\u0110\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button\n type=\"button\"\n class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\"\n title=\"X\u00F3a th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\"\n >M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span\n >\n </div>\n }\n\n @if (hiddenNotificationCount() > 0) {\n <button\n type=\"button\"\n class=\"hero-notification__more\"\n (click)=\"openNotificationListDialog()\"\n >\n Xem th\u00EAm {{ hiddenNotificationCount() }} th\u00F4ng b\u00E1o \u2192\n </button>\n }\n </section>\n\n </div>\n } <!-- end @else no-profile -->\n </header>\n\n <!-- \u2550\u2550 NOTIFICATION DIALOGS \u2014 rendered outside <header> to avoid overflow/stacking-context clipping \u2550\u2550 -->\n @if (notificationComposerDialogOpen()) {\n <div\n class=\"dialog-overlay dialog-overlay--front\"\n (click)=\"closeNotificationComposerDialog()\"\n >\n <div\n class=\"dialog hero-notification-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n (click)=\"$event.stopPropagation()\"\n >\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--send\" aria-hidden=\"true\"><i class=\"pi pi-send\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n\n <div class=\"dialog-head hero-notification-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n </div>\n <div>\n <h2 class=\"hero-notification-dialog__title\">T\u1EA1o th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">Th\u00F4ng b\u00E1o s\u1EBD hi\u1EC3n th\u1ECB cho to\u00E0n b\u1ED9 th\u00E0nh vi\u00EAn</p>\n </div>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationComposerDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <div class=\"hero-notification__composer-panel\">\n <div class=\"hero-notification__composer-row\">\n <div class=\"hero-notification__input-wrap\">\n <textarea\n id=\"hero-notification-input\"\n class=\"hero-notification__input\"\n [value]=\"notificationDraft()\"\n (input)=\"updateNotificationDraft($any($event.target).value)\"\n [disabled]=\"notificationSaving()\"\n maxlength=\"300\"\n placeholder=\"Nh\u1EADp n\u1ED9i dung th\u00F4ng b\u00E1o cho m\u1ECDi ng\u01B0\u1EDDi...\"\n aria-label=\"N\u1ED9i dung th\u00F4ng b\u00E1o\"\n ></textarea>\n <div class=\"hero-notification__char-ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 36 36\">\n <circle class=\"hero-notification__char-ring-track\" cx=\"18\" cy=\"18\" r=\"15\" />\n <circle class=\"hero-notification__char-ring-fill\" cx=\"18\" cy=\"18\" r=\"15\"\n [style.stroke-dashoffset]=\"94 - (notificationDraft().trim().length / 300) * 94\" />\n </svg>\n <span class=\"hero-notification__char-count\">{{ notificationDraft().trim().length }}</span>\n </div>\n </div>\n </div>\n <div class=\"hero-notification__composer-actions\">\n <button type=\"button\" class=\"btn ghost hero-notification__cancel\"\n (click)=\"closeNotificationComposerDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn hero-notification__send\"\n (click)=\"createNotification()\"\n [disabled]=\"notificationSaving() || !notificationDraft().trim()\">\n <i class=\"pi pi-send\"></i>\n <span>G\u1EEDi th\u00F4ng b\u00E1o</span>\n </button>\n </div>\n </div>\n @if (notificationError()) {\n <p class=\"hero-notification__error\">{{ notificationError() }}</p>\n }\n </div>\n </div>\n }\n\n @if (notificationListDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeNotificationListDialog()\">\n <div class=\"dialog hero-notification-list-dialog\" role=\"dialog\" aria-modal=\"true\"\n aria-label=\"Danh s\u00E1ch th\u00F4ng b\u00E1o\" (click)=\"$event.stopPropagation()\">\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--check\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></span>\n\n <div class=\"dialog-head hero-notification-list-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\"><i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i></div>\n <div>\n <h2 class=\"hero-notification-list-dialog__title\">To\u00E0n b\u1ED9 th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">{{ visibleNotifications().length }} th\u00F4ng b\u00E1o</p>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__head-actions\">\n <button type=\"button\" class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationListDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__body\">\n @if (visibleNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of visibleNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">{{ notice.user | slice: 0 : 2 | uppercase }}</div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{ formatNotificationCreatedAt(notice.createdAt) }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button type=\"button\" class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\" title=\"\u0110\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button type=\"button\" class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\" title=\"X\u00F3a th\u00F4ng b\u00E1o\">\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\">M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (acknowledgeAllConfirmDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAcknowledgeAllConfirmDialog()\">\n <div class=\"dialog ack-all-confirm-dialog\" role=\"alertdialog\" aria-modal=\"true\"\n aria-labelledby=\"ack-all-confirm-title\" (click)=\"$event.stopPropagation()\">\n <div class=\"ack-all-confirm-dialog__icon-wrap\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></div>\n <h2 id=\"ack-all-confirm-title\" class=\"ack-all-confirm-dialog__title\">\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc?</h2>\n <p class=\"ack-all-confirm-dialog__desc\">\n T\u1EA5t c\u1EA3 {{ visibleNotifications().length }} th\u00F4ng b\u00E1o s\u1EBD \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u \u0111\u00E3 \u0111\u1ECDc v\u00E0 \u1EA9n kh\u1ECFi danh s\u00E1ch.\n </p>\n <div class=\"ack-all-confirm-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeAcknowledgeAllConfirmDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn ack-all-confirm-dialog__confirm-btn\"\n (click)=\"confirmAcknowledgeAll()\" [disabled]=\"notificationSaving()\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> X\u00E1c nh\u1EADn\n </button>\n </div>\n </div>\n </div>\n }\n\n <main\n class=\"content\"\n [class.is-documents]=\"bottomTab() === 'documents'\"\n [class.is-weight]=\"bottomTab() === 'weight'\"\n [class.is-schedule]=\"bottomTab() === 'schedule'\"\n [class.is-mom]=\"bottomTab() === 'mom'\"\n [class.is-medical]=\"bottomTab() === 'medical'\"\n >\n <!--\n ============ TAB PANES ============\n D\u00F9ng `[hidden]` thay v\u00EC `*ngIf` \u0111\u1EC3 c\u00E1c tab c\u00F9ng gi\u1EEF tr\u1EA1ng th\u00E1i khi\n user qua l\u1EA1i: scroll position, search query \u0111ang g\u00F5, viewMode, folder\n \u0111ang \u0111\u1EE9ng, file \u0111ang ch\u1ECDn, draft \u0111ang nh\u1EADp\u2026 \u0111\u1EC1u kh\u00F4ng b\u1ECB reset. H\u1EC7\n qu\u1EA3: `<app-documents>` \u0111\u01B0\u1EE3c mount 1 l\u1EA7n \u1EDF app load \u2014 `loadEntries()`\n ch\u1EA1y 1 l\u1EA7n, c\u00E1c l\u1EA7n switch tab k\u1EBF ti\u1EBFp kh\u00F4ng g\u1ECDi API th\u00EAm.\n -->\n <!-- ============ TAB: DOCUMENTS \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: WEIGHT ============ -->\n <div class=\"tab-pane tab-pane--weight\" [hidden]=\"bottomTab() !== 'weight'\">\n <app-weight\n [user]=\"user()\"\n [birthDate]=\"profile()?.birthDate\"\n [gender]=\"profile()?.gender\"\n [babyName]=\"profile()?.babyName\"\n ></app-weight>\n </div>\n\n <!-- ============ TAB: L\u1ECACH \u2014 Google Sheet tab \"Event\" ============ -->\n <div class=\"tab-pane tab-pane--schedule\" [hidden]=\"bottomTab() !== 'schedule'\">\n <app-feeding-schedule [user]=\"user()\"></app-feeding-schedule>\n </div>\n\n <!-- ============ TAB: TI\u1EC0N S\u1EEC Y T\u1EBE \u2014 Medical history V2 ============ -->\n <div class=\"tab-pane tab-pane--medical\" [hidden]=\"bottomTab() !== 'medical'\">\n <app-medical-history\n [user]=\"user()\"\n (openExplorerFile)=\"openMedicalAttachmentInDocuments($event)\"\n ></app-medical-history>\n </div>\n\n <!-- ============ TAB: M\u1EB8 \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: FEEDING ============ -->\n <div class=\"tab-pane tab-pane--feeding\" [hidden]=\"bottomTab() !== 'feeding'\">\n <app-feeding-profile\n [user]=\"user()\"\n [profile]=\"profile()\"\n [draft]=\"draft()\"\n [editing]=\"editing()\"\n [maxBirthDate]=\"maxBirthDate\"\n (save)=\"save()\"\n (cancelEdit)=\"cancelEdit()\"\n (startEdit)=\"startEdit()\"\n (clearProfile)=\"clearProfile()\"\n (draftNameChange)=\"updateDraftName($event)\"\n (draftBirthChange)=\"updateDraftBirth($event)\"\n (draftGenderChange)=\"setDraftGender($event)\"\n />\n\n @if (profile() && !editing() && ageBreakdown()) {\n <app-feeding-daily\n [logs]=\"logs()\"\n [feedingSettings]=\"feedingSettings()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n [user]=\"user()\"\n [syncMessage]=\"syncMessage()\"\n [syncError]=\"syncError()\"\n [logDialogOpen]=\"logDialogOpen()\"\n [loadingLogs]=\"loadingLogs()\"\n [refreshToken]=\"dailyRefreshToken()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (bottlePrepChange)=\"bottlePrep.set($event)\"\n (editLog)=\"feedingDialogs.openEditDialog($event)\"\n (historyOpen)=\"feedingDialogs.openHistoryDialog($event.filter, $event.dateStr)\"\n (feedRowClick)=\"feedingDialogs.onFeedRowClick($event)\"\n >\n <app-feeding-charts\n data-feeding-charts\n [logs]=\"logs()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n />\n </app-feeding-daily>\n <app-feeding-nutrition\n [nutritionTarget]=\"nutritionTarget()\"\n [todayStats]=\"todayStats()\"\n [now]=\"now()\"\n [latestWeightKg]=\"latestWeightKgFromSheet()\"\n [editing]=\"editing()\"\n (goToWeight)=\"setBottomTab('weight')\"\n />\n }\n\n <!-- ============ FAB \u00AB Ghi c\u1EEF b\u00FA \u00BB c\u0169 \u0111\u00E3 \u0111\u01B0\u1EE3c thay b\u1EB1ng FAB gi\u1EEFa bottom-nav ============ -->\n </div>\n <!-- /.tab-pane--feeding -->\n </main>\n </div>\n <!-- /.feeding-page -->\n\n <!-- ============ BOTTOM NAVIGATION (C\u1EEF b\u00FA \u00B7 Th\u1EC3 tr\u1EA1ng \u00B7 + \u00B7 L\u1ECBch \u00B7 Y t\u1EBF) ============ -->\n <nav\n class=\"bottom-nav\"\n role=\"tablist\"\n aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\"\n >\n <div class=\"bn-side bn-side--left\">\n @for (tab of navLeftTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"bn-fab\"\n (click)=\"feedingDialogs.openLogDialog()\"\n [disabled]=\"!profile() || editing()\"\n [title]=\"(!profile() || editing()) ? 'Ch\u1ECDn b\u00E9 tr\u01B0\u1EDBc khi ghi c\u1EEF b\u00FA' : 'Ghi nh\u1EADn c\u1EEF b\u00FA m\u1EDBi'\"\n aria-label=\"Ghi nh\u1EADn c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"bn-side bn-side--right\">\n @for (tab of navRightTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n </nav>\n\n <!-- ============ NOTIFICATION SUBROUTE (full-screen) ============ -->\n @if (notifRouteOpen()) {\n <app-notification-list\n [notifications]=\"allNotifications()\"\n [currentUser]=\"user()\"\n [saving]=\"notificationSaving()\"\n (back)=\"closeNotificationRoute()\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"openNotificationComposerDialog()\"\n />\n }\n\n <!-- ============ SETTINGS FLOAT MENU (full-screen) ============ -->\n @if (settingsMenuOpen()) {\n <app-settings-menu\n [appName]=\"appInfo.name\"\n [appVersion]=\"appInfo.version\"\n [showDocuments]=\"explorerEnabled\"\n [unreadCount]=\"visibleNotifications().length\"\n (close)=\"closeSettingsMenu()\"\n (select)=\"onSettingsMenuSelect($event)\"\n />\n }\n\n <!-- ============ M\u1EB8 SUBROUTE (full-screen) ============ -->\n @if (momRouteOpen()) {\n <app-subroute-shell\n title=\"M\u1EB9\"\n (back)=\"closeMomRoute()\"\n >\n <app-feeding-mom\n [profile]=\"profile()\"\n [editing]=\"editing()\"\n (goToFeeding)=\"closeMomRoute(); setBottomTab('feeding')\"\n ></app-feeding-mom>\n </app-subroute-shell>\n }\n\n <!-- ============ T\u00C0I LI\u1EC6U SUBROUTE (full-screen) ============ -->\n @if (documentsRouteOpen()) {\n <app-subroute-shell\n title=\"T\u00E0i li\u1EC7u\"\n (back)=\"closeDocumentsRoute()\"\n >\n <app-documents></app-documents>\n </app-subroute-shell>\n }\n\n <!-- About -->\n @if (aboutDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAboutDialog()\">\n <div\n class=\"dialog about-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"feeding-about-title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head about-dialog__head\">\n <h2 id=\"feeding-about-title\" class=\"about-dialog__title\">\n <i class=\"pi pi-info-circle\" aria-hidden=\"true\"></i>\n About\n </h2>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeAboutDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <dl class=\"about-dialog__meta\">\n <div class=\"about-dialog__row\">\n <dt>\u1EE8ng d\u1EE5ng</dt>\n <dd>{{ appInfo.name }}</dd>\n </div>\n <div class=\"about-dialog__row\">\n <dt>Phi\u00EAn b\u1EA3n</dt>\n <dd>\n <code>{{ appInfo.version }}</code>\n </dd>\n </div>\n </dl>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn primary\" (click)=\"closeAboutDialog()\">\u0110\u00F3ng</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Nh\u1EAFc s\u1EF1 ki\u1EC7n l\u1ECBch (sheet Event + Settings) -->\n @if (eventReminderDialogOpen() && scheduleReminderEvents().length > 0) {\n <div\n class=\"dialog-overlay dialog-overlay--front dialog-overlay--event-reminder\"\n (click)=\"closeEventReminderDialog()\"\n >\n <div\n class=\"dialog event-reminder-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head event-reminder-dialog__head\">\n <h2 class=\"event-reminder-dialog__title\">\n <i class=\"pi pi-bell\" aria-hidden=\"true\"></i>\n B\u1EA1n c\u00F3 l\u1ECBch s\u1EAFp t\u1EDBi\n </h2>\n <button\n type=\"button\"\n class=\"dialog-close\"\n (click)=\"closeEventReminderDialog()\"\n aria-label=\"\u0110\u00F3ng\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <ul class=\"event-reminder-list\">\n @for (ev of scheduleReminderEvents(); track ev) {\n <li>\n <article class=\"event-reminder-card\">\n <h3 class=\"event-reminder-card__title\">{{ ev.title }}</h3>\n <p class=\"event-reminder-card__datetime\">\n <span class=\"event-reminder-card__date\">{{ formatReminderEventWhen(ev) }}</span>\n <span class=\"event-reminder-card__sep\" aria-hidden=\"true\"> \u2013 </span>\n <span class=\"event-reminder-card__hour\">{{\n formatReminderEventTime(ev.time)\n }}</span>\n </p>\n @if (ev.place) {\n <p class=\"event-reminder-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n <span>{{ ev.place }}</span>\n </p>\n }\n @if (ev.note) {\n <div class=\"event-reminder-card__content-wrap\">\n <span class=\"event-reminder-card__content-label\">N\u1ED9i dung</span>\n <p class=\"event-reminder-card__content\">{{ ev.note }}</p>\n </div>\n }\n </article>\n </li>\n }\n </ul>\n <div class=\"dialog-actions event-reminder-actions\">\n <button\n type=\"button\"\n class=\"event-reminder-actions__link\"\n (click)=\"acknowledgeAllEventReminders()\"\n >\n Kh\u00F4ng nh\u1EAFc l\u1EA1i\n </button>\n <button\n type=\"button\"\n class=\"btn primary event-reminder-actions__primary\"\n (click)=\"skipEventReminderSession()\"\n >\n \u0110\u00E3 hi\u1EC3u\n </button>\n </div>\n </div>\n </div>\n }\n\n <app-feeding-dialogs\n #feedingDialogs\n [logs]=\"logs()\"\n [user]=\"user()\"\n [now]=\"now()\"\n [bottlePrep]=\"bottlePrep()\"\n [feedingSettings]=\"feedingSettings()\"\n [prediction]=\"prediction()\"\n [todayStats]=\"todayStats()\"\n [yesterdayStats]=\"yesterdayStats()\"\n [todayLogs]=\"todayLogs()\"\n [yesterdayLogs]=\"yesterdayLogs()\"\n [maxBirthDate]=\"maxBirthDate\"\n [syncError]=\"syncError()\"\n [syncMessage]=\"syncMessage()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (settingsChanged)=\"onFeedingSettingsChanged($event)\"\n (syncErrorChange)=\"syncError.set($event)\"\n (syncMessageChange)=\"syncMessage.set($event)\"\n (logDialogOpenChange)=\"logDialogOpen.set($event)\"\n (bottlePrepClearRequested)=\"clearBottlePrep()\"\n />\n</div>\n<!-- /.feeding-root -->\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;position:absolute;inset:0;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--border-strong: #d1d5db;--primary: #5b6cff;--primary-2: #4c5cf0;--primary-soft: #eef0ff;--accent: #ff5c8a;--accent-soft: #ffe7ef;--mint: #10b981;--mint-soft: #d1fae5;--amber: #f59e0b;--amber-soft: #fef3c7;--rose: #ef4444;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8}.feeding-root{flex:1;min-height:0;width:100%;display:flex;flex-direction:column;position:relative}.feeding-root .bp-prep-user{font-weight:800;color:#0369a1;background:#0ea5e933;padding:0 5px;margin-right:2px;border-radius:5px;white-space:nowrap;transition:color .2s,background .2s}.feeding-root .bp-prep-time{font-weight:800;font-variant-numeric:tabular-nums;color:#0284c7;background:linear-gradient(180deg,#f0f9ff,#e0f2fe);border:1px solid rgba(14,165,233,.35);padding:1px 7px;margin-left:2px;border-radius:6px;white-space:nowrap;box-shadow:0 1px #ffffffa6 inset;transition:color .2s,background .2s,border-color .2s}.feeding-page{position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;color:var(--text);padding-bottom:calc(170px + env(safe-area-inset-bottom,0px))}.backdrop{position:fixed;inset:0;background:var(--bg);z-index:-1}.hero-notif-wrap{padding:0 16px 8px}.hero{padding:0;background:#fffffff5;border-bottom:1px solid rgba(226,232,240,.9)}@media(max-width:640px){.hero{padding-top:calc(26px + env(safe-area-inset-top,0px));padding-bottom:10px}}.hero-inner{max-width:1100px;margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:14px 18px}.hero-brand{min-width:0}.hero-title-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;width:100%}.hero-title{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.hero-title .emoji{font-size:36px;line-height:1;flex-shrink:0;filter:drop-shadow(0 2px 4px rgba(15,23,42,.08))}@media(max-width:640px){.hero-title .emoji{font-size:32px}}.hero-title .hero-text{min-width:0}.hero-title h1{margin:0;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em;line-height:1.2}@media(max-width:640px){.hero-title h1{font-size:17px}}.hero-title .hero-userline{margin:5px 0 0;display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:12.5px;color:var(--muted)}.hero-title .hero-userline i{font-size:12px;color:var(--primary);opacity:.9}.hero-title .hero-userline strong{color:var(--primary);font-weight:700;letter-spacing:.01em}.hero-title .hero-userline .hero-last-update{font-size:11px;color:#94a3b8;font-weight:400}.hero--profile-merged{background:var(--primary-soft);padding-bottom:10px;margin-bottom:8px}.hero-title--merged{align-items:flex-start}.hero-title--merged .emoji{font-size:30px}.hero-title--merged h1{font-size:18px;line-height:1.15}.hero-merged-head{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;min-width:0;row-gap:6px}.hero-age-chip{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:lowercase;color:var(--primary);background:var(--primary-soft);padding:3px 9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--primary) 18%,transparent);white-space:nowrap}.hero-merged-stat{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:700;color:var(--muted);background:#94a3b824;border:1px solid rgba(148,163,184,.28);white-space:nowrap;line-height:1.25}.hero-merged-stat>i{font-size:10px;opacity:.88;color:var(--muted-2)}.hero-merged-stat--weight>i{color:var(--accent);opacity:1}.hero-merged-meta{margin:5px 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:11.5px;line-height:1.45;color:var(--muted)}.hero-merged-meta .dot{color:var(--muted-2);font-weight:700;-webkit-user-select:none;user-select:none}.hero-merged-meta i{font-size:10px;margin-right:3px;color:var(--muted-2);opacity:.95}.hero-merged-meta strong{color:var(--primary);font-weight:700}.hero-merged-sync{color:#94a3b8;font-weight:400;font-size:11px}.hero-actions--merged{flex-shrink:0}.hero-profile-menu{position:relative;flex:0 0 auto;list-style:none}.hero-profile-menu__trigger{list-style:none}.hero-profile-menu__trigger::-webkit-details-marker{display:none}.hero-profile-menu__trigger::marker{content:\"\"}.hero-profile-menu__panel{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:180px;padding:6px;border-radius:12px;border:1px solid var(--border);background:#fff;box-shadow:0 8px 24px -8px #0f172a2e}.hero-profile-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;border:0;border-radius:8px;background:transparent;font:inherit;font-size:13px;font-weight:600;color:var(--text);text-align:left;cursor:pointer;transition:background .12s ease,color .12s ease}.hero-profile-menu__item i{font-size:14px;color:var(--muted-2);width:1.1em;text-align:center}.hero-profile-menu__item:hover:not(:disabled){background:var(--primary-soft, #eef0ff);color:var(--primary)}.hero-profile-menu__item:hover:not(:disabled) i{color:var(--primary)}.hero-profile-menu__item--danger:hover:not(:disabled){color:var(--rose);background:var(--rose-soft)}.hero-profile-menu__item--danger:hover:not(:disabled) i{color:var(--rose)}.about-dialog{max-width:360px}.about-dialog__head{margin-bottom:8px}.about-dialog__title{display:flex;align-items:center;gap:8px;margin:0;font-size:18px;font-weight:700}.about-dialog__title i{color:var(--primary)}.about-dialog__meta{margin:0 0 16px;padding:0}.about-dialog__row{display:grid;grid-template-columns:96px 1fr;gap:8px 12px;padding:10px 0;border-bottom:1px solid var(--border)}.about-dialog__row:last-child{border-bottom:0}.about-dialog__row dt{margin:0;font-size:13px;font-weight:600;color:var(--muted-2)}.about-dialog__row dd{margin:0;font-size:14px;font-weight:600;color:var(--text)}.about-dialog__row dd code{font-size:13px;font-weight:600;padding:2px 8px;border-radius:6px;background:var(--primary-soft, #eef0ff);color:var(--primary)}@media(max-width:640px){.hero-title--merged .emoji{font-size:26px}.hero-merged-meta{font-size:11px}.hero-merged-stat{font-size:10.5px;padding:2px 7px}}.hero-datetime{display:flex;flex-wrap:wrap;align-items:center;flex-shrink:0}.hero-datetime-strip{display:inline-flex;align-items:stretch;flex-wrap:nowrap;max-width:100%;min-height:34px;border:1px solid var(--border);border-radius:8px;background:linear-gradient(180deg,#fff,#f1f5f9);box-shadow:0 1px 1px #0f172a0d}.hero-datetime-strip__block{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;min-width:0}.hero-datetime-strip__block>.pi{font-size:12px;color:var(--primary);opacity:.82;flex-shrink:0}.hero-datetime-strip__block--date{flex-wrap:wrap;row-gap:0}.hero-datetime-strip__wd{font-size:10px;font-weight:700;color:var(--muted);text-transform:capitalize;letter-spacing:.02em;line-height:1.15;max-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hero-datetime-strip__date{font-size:12px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--text);letter-spacing:.02em;white-space:nowrap}.hero-datetime-strip__divider{align-self:stretch;width:1px;margin:5px 0;background:#94a3b866;flex-shrink:0}.hero-datetime-strip__clock{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:.04em;color:var(--primary);line-height:1;white-space:nowrap}.hero-datetime-strip__block--time{padding-right:10px}.hero-datetime-strip__block--time>.pi{font-size:11px}@media(max-width:520px){.hero-inner{flex-direction:column;align-items:stretch}.hero-datetime{width:100%;justify-content:flex-start}.hero-datetime-strip{flex:1 1 auto;width:100%;justify-content:space-between}.hero-notification__composer-row{flex-direction:column;align-items:stretch}.hero-notification__send,.hero-notification__cancel{width:100%;justify-content:center}}.hero-notification__bg-icon{position:absolute;pointer-events:none;color:var(--primary);opacity:.13;line-height:1}.hero-notification__bg-icon i{display:block}.hero-notification__bg-icon--bell{top:8%;left:6%;font-size:28px;transform:rotate(-18deg)}.hero-notification__bg-icon--megaphone{top:10%;right:8%;font-size:24px;transform:rotate(12deg)}.hero-notification__bg-icon--comments{bottom:14%;right:10%;font-size:22px;transform:rotate(-8deg)}.hero-notification__bg-icon--send{bottom:10%;left:50%;font-size:20px;transform:rotate(-22deg)}.hero-notification__bg-icon--star{bottom:16%;left:4%;font-size:18px;transform:rotate(20deg)}.hero-notification__bg-icon--bolt{top:46%;right:2%;font-size:20px;transform:rotate(-5deg)}.hero-notification__bg-icon--heart{top:38%;left:14%;font-size:17px;transform:rotate(15deg)}.hero-notification__bg-icon--info{top:4%;left:50%;font-size:18px;transform:rotate(0)}.hero-notification__bg-icon--check{bottom:24%;left:35%;font-size:17px;transform:rotate(8deg)}.hero-notification__bg-icon--eye{top:14%;left:28%;font-size:18px;transform:rotate(-10deg)}.hero-notification{flex:1 1 100%;display:flex;flex-direction:column;gap:10px;margin-top:2px;padding:10px 12px;border:1px solid rgba(148,163,184,.28);border-radius:14px;background:#fff;position:relative;overflow:hidden}.hero-notification__head{display:flex;align-items:center;justify-content:space-between;gap:10px;position:relative}.hero-notification__head-actions{display:inline-flex;align-items:center;gap:6px}.hero-notification__composer-label{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--muted)}.hero-notification__composer-label i{color:var(--primary);font-size:12px}.hero-notification__unread-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--primary);color:#fff;font-size:10px;font-weight:700;line-height:1}.hero-notification__add{width:28px;height:28px;border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.25);border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.hero-notification__add i{font-size:11px}.hero-notification__add:hover:not(:disabled){background:var(--primary);border-color:var(--primary);color:#fff}.hero-notification__mark-all{height:26px;border:none;border-radius:0;background:transparent;color:var(--primary);font-size:11px;font-weight:600;padding:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:4px}.hero-notification__mark-all i{font-size:10px}.hero-notification__mark-all:hover:not(:disabled){opacity:.75}.hero-notification__mark-all:disabled{color:var(--muted-2);cursor:default}.hero-notification__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;position:relative}.hero-notification__item{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;border-radius:10px;background:#f8fafc;border:1px solid #e2e8f0;position:relative;transition:border-color .18s ease,transform .18s ease}@media(hover:hover){.hero-notification__item:hover{border-color:#c7d2fe;transform:translateY(-1px)}}.hero-notification__avatar{width:34px;height:34px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);font-size:11px;font-weight:700;letter-spacing:.5px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification__body{flex:1;min-width:0;padding-right:52px}.hero-notification__body-head{display:flex;align-items:center;gap:6px;margin-bottom:3px}.hero-notification__author{font-size:12px;font-weight:700;color:var(--text)}.hero-notification__content{margin:0;font-size:13.5px;line-height:1.5;color:var(--text);white-space:pre-wrap;word-break:break-word}.hero-notification__created{display:block;margin-top:4px;font-size:11px;color:#94a3b8;font-variant-numeric:tabular-nums}.hero-notification__item-actions{position:absolute;top:7px;right:8px;display:inline-flex;align-items:center;gap:4px}.hero-notification__ack-top{width:24px;height:24px;border:none;border-radius:50%;background:#eff6ff;color:#1d4ed8;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s ease,color .15s ease,transform .15s ease}.hero-notification__ack-top i{font-size:10px}.hero-notification__ack-top:active:not(:disabled){transform:scale(.96)}.hero-notification__ack-top:hover:not(:disabled){background:#dbeafe;color:#1e40af}.hero-notification__delete{width:24px;height:24px;border:none;border-radius:50%;background:transparent;color:#94a3b8;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.hero-notification__delete i{font-size:10px}.hero-notification__delete:hover:not(:disabled){background:#fee2e2;color:#b91c1c}.hero-notification__empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 8px;position:relative}.hero-notification__empty-icon{font-size:28px;color:var(--primary);opacity:.45;margin-bottom:4px}.hero-notification__empty-title{font-size:13px;font-weight:600;color:var(--muted)}.hero-notification__empty-sub{font-size:11px;color:var(--muted-2);text-align:center}.hero-notification__more{align-self:flex-start;border:none;background:transparent;color:var(--primary);font-size:12px;font-weight:700;cursor:pointer;padding:0;font-family:inherit;position:relative}.hero-notification__more:hover{opacity:.75}.hero-notification__composer-row{display:flex;align-items:stretch;gap:8px;min-width:0}.hero-notification__composer-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid #dbe4f3;border-radius:12px;background:#f8fbff}.hero-notification__input-wrap{flex:1;min-width:0;border-radius:11px;border:1.5px solid #cfd8e6;background:#fff;transition:border-color .15s ease;position:relative}.hero-notification__composer-row:focus-within .hero-notification__input-wrap{border-color:var(--primary)}.hero-notification__input{width:100%;min-width:0;min-height:100px;border:0;border-radius:11px;padding:10px 12px 32px;font-size:13px;line-height:1.45;color:var(--text);background:transparent;font-family:inherit;resize:vertical}.hero-notification__input:focus{outline:none}.hero-notification__char-ring{position:absolute;bottom:8px;right:10px;display:flex;align-items:center;gap:4px;pointer-events:none}.hero-notification__char-ring svg{width:22px;height:22px;transform:rotate(-90deg)}.hero-notification__char-ring-track{fill:none;stroke:#e2e8f0;stroke-width:3}.hero-notification__char-ring-fill{fill:none;stroke:var(--primary);stroke-width:3;stroke-dasharray:94 94;stroke-linecap:round;transition:stroke-dashoffset .2s ease}.hero-notification__char-count{font-size:10px;color:var(--muted-2);font-variant-numeric:tabular-nums}.hero-notification__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.hero-notification__send,.hero-notification__cancel{height:36px;border-radius:10px;min-width:86px;padding:0 14px;font-size:12px;font-weight:700}.hero-notification__send{background:var(--primary);color:#fff}.hero-notification__send:not(:disabled):hover{background:var(--primary-2)}.hero-notification__cancel{min-width:72px}.hero-notification__error{margin:0;font-size:12px;color:#b91c1c;position:relative}.hero-notification-dialog{max-width:min(520px,100vw - 24px);overflow:hidden}.hero-notification-dialog .hero-notification__composer-row{align-items:stretch;gap:10px}.hero-notification-dialog .hero-notification__input-wrap{min-height:110px}.hero-notification-dialog__head{margin-bottom:14px}.hero-notification-dialog__title-group{display:flex;align-items:flex-start;gap:10px}.hero-notification-dialog__icon-wrap{width:34px;height:34px;border-radius:10px;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification-dialog__icon-wrap i{font-size:14px}.hero-notification-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-dialog__subtitle{margin:2px 0 0;font-size:11px;color:var(--muted-2)}.hero-notification-list-dialog{max-width:min(620px,100vw - 24px)}.hero-notification-list-dialog__head{margin-bottom:10px;align-items:flex-start}.hero-notification-list-dialog__head-actions{display:inline-flex;align-items:center;gap:8px;flex-shrink:0}.hero-notification-list-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-list-dialog__body{max-height:min(60vh,480px);overflow-y:auto;padding-right:2px}.ack-all-confirm-dialog{max-width:min(380px,100vw - 24px);text-align:center;padding:28px 24px 24px;display:flex;flex-direction:column;align-items:center;gap:10px;overflow:hidden}.ack-all-confirm-dialog__icon-wrap{width:52px;height:52px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.2);color:var(--primary);display:flex;align-items:center;justify-content:center;margin-bottom:2px}.ack-all-confirm-dialog__icon-wrap i{font-size:22px}.ack-all-confirm-dialog__title{margin:0;font-size:16px;font-weight:800;color:var(--text)}.ack-all-confirm-dialog__desc{margin:0;font-size:13px;color:var(--muted);line-height:1.55;max-width:280px}.ack-all-confirm-dialog__actions{display:flex;gap:10px;margin-top:6px;width:100%;justify-content:center}.ack-all-confirm-dialog__actions .btn{min-width:96px;height:38px;border-radius:10px;font-size:13px;font-weight:700}.ack-all-confirm-dialog__confirm-btn{background:var(--primary);color:#fff;display:inline-flex;align-items:center;gap:6px}.ack-all-confirm-dialog__confirm-btn i{font-size:12px}.ack-all-confirm-dialog__confirm-btn:not(:disabled):hover{background:var(--primary-2)}.hero-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;align-self:center}.content{width:100%}.content .tab-pane[hidden]{display:none!important}.content .tab-pane--feeding{max-width:1100px;margin:0 auto;padding:0 20px}.content .tab-pane--documents{max-width:none;padding:0}.content .tab-pane--weight{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 5%,var(--surface, #fff)) 0%,var(--surface, #fff) min(36vh,280px),var(--surface, #fff) 100%)}.content .tab-pane--mom{max-width:1100px;margin:0 auto;padding:0 20px calc(24px + env(safe-area-inset-bottom,0px))}.content .tab-pane--medical{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 7%,var(--surface, #fff)) 0%,var(--surface, #fff) min(42vh,320px),var(--surface, #fff) 100%)}.bottom-nav{--bn-primary: #5b6cff;--bn-primary-rgb: 91, 108, 255;--bn-inactive: #b0b7c3;--bn-height: 62px;position:fixed;left:0;right:0;bottom:0;width:100%;box-sizing:border-box;z-index:90;display:flex;align-items:center;justify-content:space-between;gap:4px;min-height:var(--bn-height);padding:0 4px env(safe-area-inset-bottom,0px);background:#fffffff5;backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);border-top:1px solid rgba(0,0,0,.055);box-shadow:0 -4px 24px -4px #0f172a14}.bottom-nav .bn-side{flex:1;display:flex;align-items:center;justify-content:space-around;min-width:0}.bottom-nav .bn-fab{flex-shrink:0;width:56px;height:56px;margin-top:-20px;border-radius:999px;border:3px solid rgba(255,255,255,.96);background:var(--bn-primary);color:#fff;font-size:22px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:transform .15s ease,background .15s ease,opacity .15s ease}.bottom-nav .bn-fab:hover:not(:disabled){background:var(--primary-2, #4c5cf0);transform:translateY(-2px)}.bottom-nav .bn-fab:active:not(:disabled){transform:translateY(0) scale(.94)}.bottom-nav .bn-fab:disabled{background:var(--bn-inactive);cursor:not-allowed;opacity:.7}.bottom-nav .bn-item{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;height:var(--bn-height);padding:0 2px;background:transparent;border:none;color:var(--bn-inactive);cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:color .22s ease}.bottom-nav .bn-item .bn-icon-wrap{display:flex;align-items:center;justify-content:center;width:32px;height:30px;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.bottom-nav .bn-item .bn-icon{font-size:21px;line-height:1;transition:filter .22s ease;display:block}.bottom-nav .bn-item .bn-label{font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1;transition:font-weight .22s ease}.bottom-nav .bn-item:not(.active):hover{color:rgba(var(--bn-primary-rgb),.7)}.bottom-nav .bn-item:active .bn-icon-wrap{transform:scale(.88)}.bottom-nav .bn-item.active{color:var(--bn-primary)}.bottom-nav .bn-item.active .bn-icon-wrap{transform:translateY(-3px) scale(1.15)}.bottom-nav .bn-item.active .bn-icon{filter:drop-shadow(0 2px 8px rgba(var(--bn-primary-rgb),.4))}.bottom-nav .bn-item.active .bn-label{font-weight:700}.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px;margin-bottom:16px}.card-header{margin-bottom:16px}.card-header h2{margin:0;font-size:18px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:8px}.card-header h2 i{color:var(--primary)}.card-header p{margin:6px 0 0;color:var(--muted);font-size:13px}.eyebrow{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em}.eyebrow i{margin-right:4px}.form{display:flex;flex-direction:column;gap:14px}.field{display:flex;flex-direction:column;gap:6px}.field .label{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.02em;display:flex;align-items:center;gap:6px;flex-wrap:wrap}.field .label .label-hint{font-size:11px;font-weight:500;color:var(--muted-2);font-style:italic;text-transform:none;letter-spacing:0}.field input[type=text],.field input[type=date],.field input[type=time],.field input[type=number]{padding:12px 14px;border:1.5px solid var(--border);border-radius:10px;font-size:14px;font-family:inherit;color:var(--text);background:#fafbfc;width:100%;min-width:0;box-sizing:border-box;appearance:none;-webkit-appearance:none;transition:border-color .15s,background .15s;min-height:46px;font-variant-numeric:tabular-nums}.field input[type=text]:focus,.field input[type=date]:focus,.field input[type=time]:focus,.field input[type=number]:focus{outline:none;border-color:var(--primary);background:#fff}.field input[type=text]::placeholder,.field input[type=date]::placeholder,.field input[type=time]::placeholder,.field input[type=number]::placeholder{color:var(--muted-2)}.field input[type=number]{-moz-appearance:textfield}.field input[type=number]::-webkit-outer-spin-button,.field input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.radio-group{display:flex;gap:8px;flex-wrap:wrap}.chip{padding:9px 16px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;cursor:pointer;color:var(--text);transition:all .15s;font-family:inherit}.chip:hover{border-color:var(--primary);color:var(--primary)}.chip.active{background:var(--primary);border-color:var(--primary);color:#fff}.form-actions{display:flex;gap:10px;margin-top:4px;flex-wrap:wrap}.btn{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;border:none;border-radius:10px;font-size:14px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,color .15s,border-color .15s}.btn:disabled{opacity:.5;cursor:not-allowed}.btn.small{padding:6px 12px;font-size:12px}.btn.primary{background:var(--primary);color:#fff}.btn.primary:not(:disabled):hover{background:var(--primary-2)}.btn.ghost{background:transparent;color:var(--muted);border:1.5px solid var(--border)}.btn.ghost:hover{color:var(--text);border-color:var(--border-strong)}.btn.danger-ghost{background:transparent;color:var(--rose);border:1.5px solid var(--rose-soft)}.btn.danger-ghost:hover{background:var(--rose-soft)}.btn.icon-btn{padding:0;width:36px;height:36px;justify-content:center;background:transparent;color:var(--muted);border:1.5px solid var(--border);border-radius:10px}.btn.icon-btn i{font-size:14px}.btn.icon-btn:not(:disabled):hover{color:var(--primary);border-color:var(--primary);background:var(--primary-soft)}.btn.icon-btn .spin{animation:spin-refresh .9s linear infinite}@keyframes spin-refresh{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.age-stats{display:flex;align-items:baseline;justify-content:flex-start;gap:16px;padding:14px 16px;background:var(--primary-soft);border-radius:12px}.age-stats .stat{text-align:center}.age-stats .stat .num{font-size:26px;font-weight:700;color:var(--primary);line-height:1;font-variant-numeric:tabular-nums}.age-stats .stat .unit{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-top:3px}.age-stats .dot{color:var(--muted-2);font-size:20px}.period-card{margin-bottom:0}.period-card h2{margin:6px 0 8px;font-size:20px;font-weight:700}.period-card .period-summary{color:var(--muted);font-size:14px;margin:0 0 14px;line-height:1.55}.schedule-grid{display:grid;grid-template-columns:1fr;gap:10px}.schedule{display:flex;gap:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.schedule i{color:var(--primary);font-size:18px;margin-top:2px}.schedule .schedule-title{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px}.schedule .schedule-text{font-size:14px;color:var(--text);line-height:1.5}.compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}@media(max-width:768px){.compare-grid{grid-template-columns:1fr}}.day-card{margin-bottom:0;min-width:0;overflow:hidden}.day-card.today{border-color:#3b82f6;background:linear-gradient(180deg,#dbeafe,#fff 50%)}.day-card.yesterday{background:#fafbfc}.day-date-nav-wrap{position:relative;margin-bottom:12px}.day-date-nav-reset{position:absolute;top:-8px;right:-8px;z-index:1;width:24px;height:24px;border:1px solid var(--border);border-radius:50%;background:#fff;color:var(--muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;box-shadow:0 1px 3px #0f172a14}.day-date-nav-reset i{font-size:10px}.day-date-nav-reset:hover{background:#fee2e2;border-color:#fecaca;color:#dc2626}.day-date-nav-reset:active{background:#fee2e2;color:#dc2626}.day-date-strip{display:grid;grid-template-columns:32px 1fr 32px;gap:6px;align-items:stretch;padding:6px 8px;border-radius:10px;background:#fff;border:1px solid var(--border);touch-action:manipulation}.day-date-strip-scroller{display:flex;gap:4px;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:thin;padding:1px 2px}.day-date-strip-scroller::-webkit-scrollbar{height:4px}.day-date-strip-scroller::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px}.day-date-strip-day{flex:0 0 64px;scroll-snap-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;min-width:0;padding:6px 4px;border:1px solid transparent;border-radius:8px;background:#f8fafc;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-day:hover{background:#eef2ff;border-color:#c7d2fe}.day-date-strip-day.active{background:#dbeafe;border-color:#3b82f6;box-shadow:inset 0 0 0 1px #3b82f626}.day-date-strip-date{font-size:12px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml{font-size:11px;font-weight:600;color:#1d4ed8;font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml.empty{color:var(--muted);font-weight:500}.day-date-strip-arrow{width:32px;min-height:52px;flex-shrink:0;border:none;border-radius:8px;background:#f1f5f9;color:var(--text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-arrow i{font-size:12px}.day-date-strip-arrow:hover:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:active:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:disabled{opacity:.35;cursor:not-allowed}.day-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px}.day-head h3{margin:4px 0 0;font-size:16px;color:var(--text);font-variant-numeric:tabular-nums}.big-total{display:flex;align-items:baseline;gap:3px}.big-total .num{font-size:32px;font-weight:800;color:var(--text);line-height:1;font-variant-numeric:tabular-nums}.big-total .unit{font-size:14px;color:var(--muted);font-weight:500}.day-stats{display:flex;flex-wrap:wrap;gap:10px 16px;padding:10px 0 0;border-top:1px solid var(--border)}.day-stats>div{font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:5px}.day-stats>div i{color:var(--primary);font-size:12px}.compare-banner{margin-top:12px;padding:12px;background:var(--primary-soft);border-radius:12px;font-size:12px;color:var(--text);display:flex;flex-direction:column;gap:8px}.compare-banner strong{color:var(--primary);font-variant-numeric:tabular-nums;font-weight:700}.compare-banner .compare-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.compare-banner .compare-row:not(:last-child){padding-bottom:8px;border-bottom:1px dashed rgba(15,23,42,.1)}.compare-banner .compare-stats{display:flex;align-items:center;gap:6px;flex:1}.compare-banner .compare-stats .label{color:var(--muted);font-weight:500;font-size:11px}.compare-banner .compare-stats .count{color:var(--muted-2);font-size:11px}.compare-banner .compare-change{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;flex-shrink:0}.compare-banner .compare-change i{font-size:10px}.compare-banner .compare-change i.up{color:var(--mint)}.compare-banner .compare-change i.down{color:var(--amber)}.compare-banner .compare-change strong{font-size:12px}.compare-banner .compare-change span{color:var(--muted-2);font-weight:500}.today-logs{margin-top:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.logs-header{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}.logs-header span{display:flex;align-items:center;gap:6px}.logs-header span:before{content:\"\\1f37c\";font-size:14px}.logs-header .logs-more{text-transform:none;letter-spacing:0;background:transparent;border:none;color:var(--primary);font-size:12px;font-weight:600;padding:4px 8px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:3px;transition:background .2s ease}.logs-header .logs-more i{font-size:10px}.logs-header .logs-more:hover{background:var(--primary-soft)}.log-list{display:flex;flex-direction:column;gap:6px;min-width:0}.log-row{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#fafbfc;border-radius:8px;font-size:13px;min-width:0;max-width:100%;transition:all .2s ease;position:relative}.log-row.low-volume .log-volume{color:#f59e0b;font-weight:900}.log-row.high-volume .log-volume{color:#10b981;font-weight:900}.log-row:hover{background:#f1f5f9;transform:translateY(-1px);box-shadow:0 2px 8px #0000000d}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.log-time{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;min-width:42px}.log-volume{font-weight:800;color:var(--primary);font-size:16px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}.log-gap{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;background:#f3f4f6;color:#6b7280;border-radius:999px;font-size:10.5px;font-weight:600;white-space:nowrap;flex-shrink:0;font-variant-numeric:tabular-nums;transition:all .2s ease}.log-gap i{font-size:9px;opacity:.7}.log-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.log-gap.feed-gap-warn i{opacity:.95;color:inherit}.log-note{color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.log-actions{margin-left:auto;display:inline-flex;align-items:center;gap:2px}.log-edit,.log-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:4px 6px;border-radius:6px;font-size:12px;transition:all .2s ease}.log-edit:hover{color:var(--primary);background:var(--primary-soft)}.log-del:hover{color:var(--rose);background:var(--rose-soft)}.empty-mini{padding:12px;text-align:center;color:var(--muted-2);font-size:13px;font-style:italic}.countdown-hero{display:flex;flex-direction:column;gap:16px;padding:18px 20px;background:#fff;border-radius:16px;border:1px solid #e6e9ff;transition:all .3s ease;margin-bottom:20px}.countdown-hero .hero-main{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px}.countdown-hero .feeding-time-card{flex:1;min-width:0;text-align:center}.countdown-hero .feeding-time-card .time-header{margin-bottom:12px;display:flex;justify-content:center}.countdown-hero .feeding-time-card .time-header .status-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;padding:6px 12px;border-radius:14px;white-space:nowrap}.countdown-hero .feeding-time-card .time-header .status-badge i{font-size:11px}.countdown-hero .feeding-time-card .time-header .status-badge.status-waiting{background:var(--primary-soft);color:var(--primary)}.countdown-hero .feeding-time-card .time-header .status-badge.status-now{background:#fee2e2;color:#dc2626;animation:pulse-gentle 2s ease-in-out infinite;font-size:11px;text-transform:none;font-weight:700}.countdown-hero .feeding-time-card .time-header .status-badge.status-late{background:var(--rose-soft);color:var(--rose)}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:42px;font-weight:800;line-height:1;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.02em;margin-bottom:4px}.countdown-hero .feeding-time-card .time-display .time-remaining{font-size:15px;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}.countdown-hero .countdown-next{display:flex;flex-direction:column;gap:8px;padding-left:16px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:0}.countdown-hero .next-item{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.countdown-hero .next-item i{color:var(--primary);font-size:13px}.countdown-hero .next-item strong{font-weight:700}.countdown-hero .next-item .predict-label,.countdown-hero .next-item .recommend-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);margin-left:4px}.countdown-hero .next-item .recommend-label{color:var(--mint)}.countdown-hero .volume-info{display:flex;flex-direction:column;gap:8px;padding-left:20px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:160px;transition:all .3s ease}.countdown-hero .volume-info .volume-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.countdown-hero .volume-info .volume-row .volume-prefix{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);flex-shrink:0}.countdown-hero .volume-info .volume-row .volume-amount{font-size:22px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1;text-align:right}.countdown-hero .volume-info .volume-row.predicted-row .volume-prefix,.countdown-hero .volume-info .volume-row.predicted-row .volume-amount{color:var(--primary)}.countdown-hero .volume-info .volume-row.recommended-row .volume-prefix,.countdown-hero .volume-info .volume-row.recommended-row .volume-amount{color:var(--mint)}.countdown-hero[data-status=waiting]{border-color:#bbf7d0;background:linear-gradient(135deg,#f7fef8,#f0fdf4)}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .predicted-time{color:#16a34a}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .time-remaining{color:#15803d}.countdown-hero[data-status=waiting] .feeding-time-card .status-badge.status-waiting{background:#dcfce7!important;color:#15803d!important;border:1px solid #86efac}.countdown-hero[data-status=waiting] .volume-info .volume-amount{color:#16a34a}.countdown-hero[data-status=waiting] .volume-info .volume-prefix{color:#15803d}.countdown-hero[data-status=late]{border-color:#fca5a5;background:linear-gradient(135deg,#fef2f2,#fee2e2)}.countdown-hero[data-status=late] .feeding-time-card .time-display .predicted-time{color:#dc2626}.countdown-hero[data-status=late] .feeding-time-card .time-display .time-remaining{color:#b91c1c}.countdown-hero[data-status=late] .feeding-time-card .status-badge.status-late{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5}.countdown-hero[data-status=late] .volume-info .volume-amount{color:#dc2626}.countdown-hero[data-status=late] .volume-info .volume-prefix{color:#b91c1c}.countdown-hero[data-status=now]{border-color:#fbbf24;background:linear-gradient(135deg,#fefce8,#fef3c7);animation:pulse-card 2s ease-in-out infinite}.countdown-hero[data-status=now] .feeding-time-card .time-display .predicted-time{color:#92400e}.countdown-hero[data-status=now] .feeding-time-card .time-display .time-remaining{color:#a16207}.countdown-hero[data-status=now] .feeding-time-card .status-badge.status-now{background:#fef3c7!important;color:#b45309!important;border:1px solid #f59e0b!important}.countdown-hero[data-status=now] .volume-info .volume-amount{color:#92400e}.countdown-hero[data-status=now] .volume-info .volume-prefix{color:#a16207}@media(max-width:540px){.countdown-hero .hero-main{flex-direction:column;align-items:stretch;gap:12px}.countdown-hero .hero-main .volume-info{border-left:none;border-top:1px dashed #d8dcff;padding-left:0;padding-top:12px;min-width:auto}.countdown-hero .hero-main .volume-info .volume-row{justify-content:space-between;padding:0 4px}.countdown-hero .hero-main .feeding-time-card .time-display .predicted-time{font-size:36px}.countdown-hero .hero-main .feeding-time-card .volume-info .volume-amount{font-size:20px}}@media(max-width:400px){.countdown-hero .feeding-time-card .status-badge{font-size:10px;padding:4px 10px}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:32px}.countdown-hero .volume-info .volume-amount{font-size:18px}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.65}}@keyframes pulse-gentle{0%,to{opacity:1}50%{opacity:.75}}@keyframes pulse-urgent{0%,to{opacity:1;text-shadow:0 0 0 rgba(239,68,68,0)}50%{opacity:.8;text-shadow:0 0 8px rgba(239,68,68,.25)}}@keyframes pulse-card{0%,to{transform:scale(1);box-shadow:0 1px 3px #0000001a,0 0 #f8717100}50%{transform:scale(1.02);box-shadow:0 4px 12px #00000026,0 0 0 2px #f871714d}}.burp-warning{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin:0 0 16px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1px solid #f59e0b;border-radius:14px;box-shadow:0 2px 8px #f59e0b33;animation:burp-pulse 2s ease-in-out infinite}.burp-warning__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:#f59e0b;border-radius:50%;color:#fff;font-size:18px;animation:burp-icon-bounce 1.5s ease-in-out infinite}.burp-warning__content{flex:1;min-width:0}.burp-warning__title{font-size:15px;font-weight:700;color:#92400e;margin-bottom:4px}.burp-warning__text{font-size:13px;color:#78350f;line-height:1.5;margin-bottom:8px}.burp-warning__text strong{font-weight:600;color:#92400e}.burp-warning__countdown{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:#ffffffb3;border-radius:8px;font-size:13px;color:#92400e;font-weight:500}.burp-warning__countdown i{font-size:14px;color:#f59e0b}.burp-warning__countdown strong{font-size:16px;font-weight:700;color:#d97706}@keyframes burp-pulse{0%,to{box-shadow:0 2px 8px #f59e0b33}50%{box-shadow:0 4px 16px #f59e0b59}}@keyframes burp-icon-bounce{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.countdown-progress{margin-top:10px}.countdown-progress .cp-track{position:relative;height:6px;background:#ffffffbf;border-radius:999px;overflow:hidden}.countdown-progress .cp-fill{height:100%;background:linear-gradient(90deg,var(--primary),var(--accent));border-radius:999px;transition:width .4s ease}.countdown-progress .cp-fill[data-status=waiting]{background:linear-gradient(90deg,#22c55e,#16a34a)}.countdown-progress .cp-fill[data-status=late]{background:linear-gradient(90deg,#f87171,#dc2626)}.countdown-progress .cp-fill[data-status=now]{background:linear-gradient(90deg,#fbbf24,#d97706)}.countdown-progress .cp-labels{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px;font-size:11.5px;color:var(--muted)}.countdown-progress .cp-labels i{font-size:10px;margin-right:2px;color:var(--muted-2)}.countdown-progress .cp-labels .muted{color:var(--muted-2)}.countdown-progress .cp-labels .cp-right{font-weight:600;color:var(--muted);white-space:nowrap}.predict-head{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;margin-bottom:16px}.predict-head .eyebrow{color:var(--primary);display:flex;align-items:center;gap:4px}.predict-head .eyebrow.sparkle i{animation:sparkle 2s ease-in-out infinite}.predict-head h2{margin:4px 0 0;font-size:20px;font-weight:700}.last-feed{display:flex;align-items:center;gap:12px;padding:12px 14px;background:#fff;border-radius:12px;margin-bottom:14px;border:1px solid #e8e9ff}.last-feed .last-feed-icon{width:38px;height:38px;border-radius:50%;background:var(--primary-soft);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.last-feed .last-feed-icon i{color:var(--primary);font-size:16px}.last-feed .last-feed-body{flex:1;min-width:0}.last-feed .last-feed-label{font-size:10px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em;margin-bottom:2px}.last-feed .last-feed-value{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.last-feed .last-feed-value strong{color:var(--primary);font-size:18px;font-weight:800}.last-feed .last-feed-value .sep{color:var(--muted-2)}.last-feed .last-feed-value .ago{color:var(--accent);font-weight:600;font-size:13px}.last-feed .last-feed-note{margin-top:4px;font-size:12px;color:var(--muted);font-style:italic;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.predict-empty{padding:14px 16px;background:#fff;border-radius:12px;color:var(--muted);font-size:13px;display:flex;align-items:center;gap:8px}.predict-empty i{color:var(--primary)}.predict-body{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:14px}@media(max-width:640px){.predict-body{grid-template-columns:1fr}}.predict-metric{background:#fff;border-radius:12px;padding:14px;text-align:center}.predict-metric .metric-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;display:flex;align-items:center;justify-content:center;gap:5px}.predict-metric .metric-label i{color:var(--primary);font-size:12px}.predict-metric .metric-value{font-size:28px;font-weight:800;line-height:1;color:var(--primary);font-variant-numeric:tabular-nums}.predict-metric .metric-value .ml-unit{font-size:14px;color:var(--muted);font-weight:500;margin-left:2px}.predict-metric .metric-sub{margin-top:6px;font-size:12px;color:var(--muted)}.reasoning{background:#fff;border-radius:12px;padding:12px 14px;margin-bottom:12px}.reasoning .reasoning-head{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;margin-bottom:6px}.reasoning .reasoning-head i{color:var(--primary);margin-right:4px}.reasoning ul{margin:0;padding-left:18px;list-style:disc}.reasoning li{font-size:13px;color:var(--text);line-height:1.55;margin:2px 0}.predict-actions{display:flex;justify-content:flex-end}.feed-list-card{margin-bottom:16px}.feed-list-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.feed-list-head h2{margin:4px 0 0;font-size:18px;font-weight:700}.feed-list-compact{display:flex;flex-direction:column;gap:6px}.feed-row{display:grid;grid-template-columns:68px 80px 1fr auto;gap:10px;align-items:center;padding:10px 12px;transition:all .2s ease;position:relative}.feed-row.low-volume .feed-row-volume strong{color:#f59e0b;font-weight:900}.feed-row.high-volume .feed-row-volume strong{color:#10b981;font-weight:900}.feed-row{background:#fafbfc;border-radius:10px;border:1px solid var(--border)}.feed-row .feed-row-time{display:flex;flex-direction:column;line-height:1.2;font-variant-numeric:tabular-nums;gap:3px}.feed-row .feed-row-time .time{font-weight:700;color:var(--text);font-size:14px}.feed-row .feed-row-time .date{font-size:11px;color:var(--muted-2)}.feed-row .feed-row-time .feed-row-gap{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;font-weight:600;color:#6b7280;background:#f3f4f6;padding:1px 6px;border-radius:999px;width:fit-content;white-space:nowrap;font-variant-numeric:tabular-nums}.feed-row .feed-row-time .feed-row-gap i{font-size:8px;opacity:.7}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn i{opacity:.95;color:inherit}.feed-row .feed-row-volume{font-variant-numeric:tabular-nums}.feed-row .feed-row-volume strong{color:var(--primary);font-size:19px;font-weight:800;letter-spacing:-.01em}.feed-row .feed-row-volume .u{font-size:11px;color:var(--muted);margin-left:2px}.feed-row .feed-row-note{font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.feed-row .feed-row-note.muted{color:var(--muted-2);font-style:italic}.feed-row .feed-row-actions{display:inline-flex;align-items:center;gap:2px;margin-left:auto}.feed-row .feed-row-edit,.feed-row .feed-row-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:6px 8px;border-radius:6px;font-size:13px}.feed-row .feed-row-edit:hover{color:var(--primary, #5b6cff);background:#5b6cff1a}.feed-row .feed-row-del:hover{color:var(--rose);background:var(--rose-soft)}@media(max-width:480px){.feed-row{grid-template-columns:58px 70px 1fr auto;gap:8px;padding:8px 10px}.feed-row .feed-row-time .time{font-size:13px}}.dialog-wide{max-width:720px;display:flex;flex-direction:column;padding:0;overflow:hidden}@media(max-width:640px){.dialog-wide{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 72px);border-radius:14px}}.dialog-wide .dialog-head{position:sticky;top:0;z-index:5;background:var(--surface);padding:16px 20px;margin:0;border-bottom:1px solid var(--border);-webkit-backdrop-filter:saturate(1.2) blur(6px);backdrop-filter:saturate(1.2) blur(6px)}@media(max-width:640px){.dialog-wide .dialog-head{padding:12px 14px}}.dialog-wide .dialog-close{width:40px;height:40px;font-size:16px;background:#f1f3f7;color:var(--text)}.dialog-wide .dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:18px 20px 24px;overflow-y:auto;flex:1;min-height:0}@media(max-width:640px){.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:14px 14px 20px}}.history-body{display:flex;flex-direction:column;gap:18px}.history-paging-hint{margin:0;font-size:12px;line-height:1.45}.history-load-more{display:grid;justify-items:center;width:100%;box-sizing:border-box;padding:8px 0 12px}.history-load-more .btn{width:auto;max-width:min(320px,100%);min-width:0;margin-inline:auto;justify-content:center}.tips-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;background:#f1f3f7;border-radius:12px;margin-bottom:16px}.nav-arrow{width:40px;height:40px;border-radius:50%;border:none;background:#fff;color:var(--primary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px;transition:all .15s;flex-shrink:0}.nav-arrow:hover:not(:disabled){background:var(--primary);color:#fff;transform:scale(1.05)}.nav-arrow:active:not(:disabled){transform:scale(.95)}.nav-arrow:disabled{opacity:.35;cursor:not-allowed}.tips-now-btn{flex:1;padding:9px 16px;border:none;background:transparent;border-radius:999px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s}.tips-now-btn i{font-size:12px}.tips-now-btn:hover{color:var(--primary);background:#fff}.tips-now-btn.active{background:var(--primary);color:#fff;pointer-events:none}.tips-dialog-body .period-summary{font-size:14px;color:var(--muted);line-height:1.6;margin:0 0 14px}.tips-dialog-body .schedule-grid{grid-template-columns:repeat(2,1fr);margin-bottom:16px}@media(max-width:560px){.tips-dialog-body .schedule-grid{grid-template-columns:1fr}}.dialog-tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.history-group .history-group-head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:var(--primary-soft);border-radius:10px;margin-bottom:8px}.history-group .history-group-head .h-date{font-size:14px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-day{font-size:11px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.05em}.history-group .history-group-head .h-total{font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-total strong{color:var(--primary);font-size:20px;font-weight:800}.history-group .history-group-head .h-total .h-count{color:var(--muted);font-size:12px;font-weight:500;margin-left:4px}.history-group .history-group-head .h-total .h-avg{color:var(--primary);font-size:12px;font-weight:600;margin-left:4px;font-variant-numeric:tabular-nums}.history-group .history-group-rows{display:flex;flex-direction:column;gap:4px}.tips-section-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;padding:4px 4px 0}.tips-section-head .eyebrow i{margin-right:4px}.tips-section-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text)}.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}.filter-chip{padding:7px 14px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;font-weight:500;cursor:pointer;color:var(--muted);display:inline-flex;align-items:center;gap:5px;transition:all .15s;font-family:inherit}.filter-chip i{font-size:12px}.filter-chip:hover{border-color:var(--cat-color, var(--primary));color:var(--cat-color, var(--primary))}.filter-chip.active{background:var(--cat-color, var(--primary));border-color:var(--cat-color, var(--primary));color:#fff}.tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-bottom:20px}.tip-card{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--cat-color, var(--primary));border-radius:12px;padding:16px;transition:transform .15s}.tip-card:hover{transform:translateY(-2px)}.tip-card .tip-head{margin-bottom:8px}.tip-card .tip-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;background:var(--cat-color, var(--primary));color:#fff;font-size:11px;font-weight:600;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.tip-card .tip-pill i{font-size:10px}.tip-card .tip-title{margin:6px 0 8px;font-size:15px;font-weight:700;line-height:1.35;color:var(--text)}.tip-card .tip-body{margin:0;font-size:13px;line-height:1.55;color:var(--muted)}.empty{grid-column:1/-1;padding:40px 20px;text-align:center;color:var(--muted-2)}.empty i{font-size:32px;margin-bottom:8px;display:block}.upcoming{margin-bottom:24px}.upcoming h3{margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}.upcoming h3 i{color:var(--primary)}.upcoming-grid{display:flex;gap:8px;flex-wrap:wrap}.upcoming-item{padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:10px;font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:6px}.upcoming-item i{color:var(--primary);font-size:12px}.disclaimer{padding:16px;background:var(--amber-soft);border-radius:12px;color:#78350f;font-size:13px;line-height:1.5;margin-bottom:24px;display:flex;gap:8px;align-items:flex-start}.disclaimer i{color:var(--amber);margin-top:2px}.toast{position:fixed;bottom:100px;left:50%;transform:translate(-50%);z-index:90;padding:12px 18px;border-radius:12px;font-size:14px;font-weight:500;box-shadow:0 6px 24px #0f172a1f;display:flex;align-items:center;gap:8px;animation:toastIn .3s cubic-bezier(.4,0,.2,1)}.toast.success{background:var(--mint);color:#fff}.toast.error{background:var(--rose);color:#fff}@keyframes toastIn{0%{transform:translate(-50%,20px);opacity:0}to{transform:translate(-50%);opacity:1}}.fab{position:fixed;bottom:calc(80px + env(safe-area-inset-bottom,0px));right:20px;z-index:96;display:inline-flex;align-items:center;gap:10px;padding:14px 22px 14px 18px;background:var(--primary);color:#fff;border:none;border-radius:999px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;box-shadow:0 8px 24px #5b6cff73;transition:transform .15s,box-shadow .15s,background .15s}.fab i{font-size:18px;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:#ffffff2e}.fab .fab-label{white-space:nowrap}.fab:hover{background:var(--primary-2);transform:translateY(-2px);box-shadow:0 12px 28px #5b6cff8c}.fab:active{transform:translateY(0)}@media(max-width:480px){.fab{padding:14px}.fab .fab-label{display:none}.fab i{font-size:22px;width:28px;height:28px}}.dialog-overlay{position:fixed;inset:0;background:#0f172a8c;z-index:110;display:flex;align-items:center;justify-content:center;padding:20px;animation:overlayIn .2s ease-out}.dialog-overlay.dialog-overlay--front{z-index:400}.dialog-overlay.dialog-overlay--event-reminder{z-index:410}@media(max-width:640px){.dialog-overlay{padding:calc(env(safe-area-inset-top,0px) + 16px) 12px calc(env(safe-area-inset-bottom,0px) + 16px);align-items:flex-start}}@keyframes overlayIn{0%{opacity:0}to{opacity:1}}.dialog{background:var(--surface);border-radius:20px;width:100%;max-width:560px;max-height:min(100vh - 40px,100dvh - 40px);overflow-y:auto;padding:24px;animation:dialogIn .25s cubic-bezier(.4,0,.2,1);box-shadow:0 20px 60px #0f172a40}@media(max-width:640px){.dialog{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 32px);border-radius:16px}}@keyframes dialogIn{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:18px}.dialog-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}.dialog-head h2 i{color:var(--primary)}.dialog-close{background:#f1f3f7;border:none;width:36px;height:36px;border-radius:50%;cursor:pointer;color:var(--muted);font-size:14px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s,color .15s}.dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.log-form{display:flex;flex-direction:column;gap:14px}.log-volume-field{margin:0}.log-volume-field .log-volume-input-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 10px;min-width:0}.log-volume-field .log-volume-input-row>input[type=number]{flex:0 0 118px;width:118px;max-width:min(118px,100%);min-width:0}.log-volume-field .predict-hint--inline{flex:1 1 auto;min-width:0;margin:0;padding:6px 10px;gap:6px;font-size:12px;border-radius:8px}.log-volume-field .predict-hint--inline>i{font-size:13px;flex-shrink:0}.log-volume-field .predict-hint--inline .hint-text{flex:1 1 auto;min-width:0;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-volume-field .predict-hint--inline .btn.small{flex-shrink:0;padding:4px 8px;font-size:11px}@media(max-width:400px){.log-volume-field .log-volume-input-row>input[type=number]{flex:1 1 100px;width:100%;max-width:none}.log-volume-field .predict-hint--inline{flex:1 1 100%;white-space:normal}.log-volume-field .predict-hint--inline .hint-text{white-space:normal;overflow:visible;text-overflow:unset}}.edit-date-banner{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 14px;background:linear-gradient(135deg,#eef2ff,#f5f3ff);border:1px solid #dbe1ff;border-radius:12px;font-size:13px;color:var(--text)}.edit-date-banner>i.pi-calendar{color:var(--primary, #5b6cff);font-size:15px}.edit-date-banner .edb-label{color:var(--muted, #6b7280);font-weight:500}.edit-date-banner .edb-value{font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.edit-date-banner .edb-lock{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:50%;background:#ffffffe0;border:1px solid #e2e8f0;color:var(--muted, #64748b)}.edit-date-banner .edb-lock i{font-size:14px;line-height:1}.log-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.log-grid .field.full{grid-column:1/-1}.log-grid.log-grid--date-time{grid-template-columns:repeat(2,minmax(0,1fr))}@media(max-width:560px){.log-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:380px){.log-grid{grid-template-columns:1fr}}.quick-volumes{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.quick-volumes .quick-label{font-size:12px;color:var(--muted);font-weight:600;margin-right:4px}.quick-chip{padding:7px 13px;background:#f1f3f7;border:none;border-radius:999px;font-size:12px;font-weight:600;cursor:pointer;color:var(--muted);transition:all .15s;font-family:inherit}.quick-chip:hover{background:var(--primary-soft);color:var(--primary)}.quick-chip.now-chip{background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;gap:4px}.quick-chip.now-chip:hover{background:var(--primary);color:#fff}.quick-chip.formula-chip{display:inline-flex;align-items:center;gap:5px;background:#fff7ed;color:#c2410c;border:1px solid #fed7aa}.quick-chip.formula-chip i{font-size:11px;opacity:.9}.quick-chip.formula-chip:hover{background:#ffedd5;color:#9a3412;border-color:#fdba74}.quick-chip.formula-chip.active{background:#c2410c;color:#fff;border-color:#c2410c;box-shadow:0 2px 8px #c2410c40}.quick-chip.formula-chip.active:hover{background:#9a3412;border-color:#9a3412;color:#fff}.bottle-prep{background:#f0f9ff;border:1px solid #bae6fd;border-radius:12px;padding:8px 12px;margin-bottom:16px;display:flex;flex-direction:column;gap:8px;transition:background .2s,border-color .2s}.bottle-prep:has(.bp-warning){background:#fffbeb;border-color:#fde68a}.bottle-prep:has(.bp-warning) .bp-info>i{color:#f59e0b}.bottle-prep:has(.bp-warning) .bp-info strong{color:#d97706}.bottle-prep:has(.bp-warning) .bp-prep-user{color:#92400e;background:#fbbf246b}.bottle-prep:has(.bp-warning) .bp-prep-time{color:#c2410c;background:linear-gradient(180deg,#fff7ed,#ffedd5);border-color:#fb923c73}.bottle-prep:has(.bp-form){align-items:stretch;padding:10px 12px 12px}.bottle-prep .bp-warning{display:flex;align-items:center;justify-content:space-between;gap:10px;animation:slideDown .2s ease;flex-wrap:wrap}.bottle-prep .bp-warning__content{display:flex;align-items:center;gap:8px;font-size:13px;flex:1;min-width:0}.bottle-prep .bp-warning__content i{font-size:16px;flex-shrink:0}.bottle-prep .bp-warning__content strong{font-weight:700}.bottle-prep .bp-warning--expired{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:8px 10px;color:#991b1b}.bottle-prep .bp-warning--expired i{color:#dc2626}.bottle-prep .bp-warning--soon{background:#fefce8;border:1px solid #fde68a;border-radius:8px;padding:8px 10px;color:#92400e}.bottle-prep .bp-warning--soon i{color:#f59e0b}.bottle-prep .bp-warning__btn{flex-shrink:0;display:inline-flex;align-items:center;gap:5px;padding:7px 12px;font-size:12px;font-weight:700;font-family:inherit;color:#fff;background:linear-gradient(180deg,#f59e0b,#d97706);border:none;border-radius:8px;cursor:pointer;box-shadow:0 2px 4px #f59e0b40;transition:all .15s ease}.bottle-prep .bp-warning__btn i{font-size:12px}.bottle-prep .bp-warning__btn:hover{background:linear-gradient(180deg,#fbbf24,#f59e0b);transform:translateY(-1px);box-shadow:0 4px 8px #f59e0b59}.bottle-prep .bp-warning__btn:active{transform:translateY(0)}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.bottle-prep .bp-info-row{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%}.bottle-prep:not(:has(.bp-warning)):not(:has(.bp-form)){flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.bottle-prep .bp-info{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--text);flex:1;min-width:0}.bottle-prep .bp-info>i{color:#0ea5e9;font-size:16px;margin-top:2px;flex-shrink:0}.bottle-prep .bp-info .bp-text{line-height:1.45;min-width:0}.bottle-prep .bp-info .bp-useby{display:inline-block;margin-top:2px;font-size:13px}.bottle-prep .bp-info strong{color:#0284c7;font-weight:800;font-variant-numeric:tabular-nums}.bottle-prep .bp-info .muted{color:var(--muted);font-size:13px}.bottle-prep .bp-actions{display:flex;gap:4px}.bottle-prep .bp-add{flex:1;background:transparent;border:none;padding:6px 4px;font-size:13px;font-weight:600;color:#0284c7;cursor:pointer;display:flex;align-items:center;gap:8px;font-family:inherit;text-align:left}.bottle-prep .bp-add>i{font-size:15px}.bottle-prep .bp-add:hover{color:#0369a1}.bottle-prep .bp-form{display:flex;flex-direction:column;gap:8px;width:100%}.bottle-prep .bp-form-head{display:flex;align-items:center;gap:6px}.bottle-prep .bp-form-head>i{color:#0ea5e9;font-size:15px}.bottle-prep .bp-form-head .bp-label{font-size:13px;color:var(--muted);font-weight:600;white-space:nowrap}.bottle-prep .bp-input-wrap{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid #7dd3fc;border-radius:10px;padding:3px;transition:border-color .15s,box-shadow .15s}.bottle-prep .bp-input-wrap:focus-within{border-color:#0ea5e9;box-shadow:0 0 0 3px #0ea5e926}.bottle-prep .bp-input-wrap .bp-step{width:30px;height:30px;flex-shrink:0;border-radius:8px;border:none;background:#e0f2fe;color:#0369a1;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;transition:all .12s}.bottle-prep .bp-input-wrap .bp-step:hover{background:#bae6fd;color:#0284c7}.bottle-prep .bp-input-wrap .bp-step:active{transform:scale(.94)}.bottle-prep .bp-input-wrap .bp-step:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.bottle-prep .bp-input-wrap .bp-input{flex:1;min-width:60px;padding:4px 6px;font-size:18px;font-weight:800;border:none;background:transparent;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706;text-align:center;letter-spacing:-.01em}.bottle-prep .bp-input-wrap .bp-input:focus{outline:none}.bottle-prep .bp-input-wrap .bp-input:disabled{opacity:.55;cursor:not-allowed}.bottle-prep .bp-input-wrap .bp-unit{font-size:12px;color:var(--muted);font-weight:600;padding-right:4px}.bottle-prep .bp-quick{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.bottle-prep .bp-quick .bp-quick-label{font-size:11px;color:var(--muted);font-weight:600;margin-right:2px}.bottle-prep .bp-quick .bp-quick-chip{padding:4px 9px;background:#f0f9ff;border:1px solid #7dd3fc;border-radius:999px;font-size:11.5px;font-weight:700;color:#0369a1;cursor:pointer;font-family:inherit;font-variant-numeric:tabular-nums;transition:all .12s}.bottle-prep .bp-quick .bp-quick-chip:hover{background:#e0f2fe;border-color:#38bdf8;color:#0284c7}.bottle-prep .bp-quick .bp-quick-chip:active{transform:scale(.95)}.bottle-prep .bp-quick .bp-quick-chip:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-form-actions{display:flex;gap:6px;justify-content:flex-end}.bottle-prep .bp-btn{width:32px;height:32px;border-radius:8px;border:none;background:#fff;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:all .15s;flex-shrink:0}.bottle-prep .bp-btn:hover:not(:disabled){background:#e0f2fe;color:#0284c7}.bottle-prep .bp-btn:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-btn.primary{background:#0ea5e9;color:#fff}.bottle-prep .bp-btn.primary:hover:not(:disabled){background:#0284c7}.bottle-prep .bp-btn.danger:hover{background:var(--rose-soft);color:var(--rose)}.calc-block{border:1px solid #fde7c2;background:#fffaf0;border-radius:12px;overflow:hidden}.calc-block .calc-head{padding:10px 14px 0}.calc-block .prep-info{display:flex;align-items:flex-start;gap:6px;font-size:13px;color:var(--text)}.calc-block .prep-info>i{color:#f59e0b;font-size:14px;margin-top:2px;flex-shrink:0}.calc-block .prep-info .prep-info-text{line-height:1.45;min-width:0}.calc-block .prep-info strong{color:#d97706;font-weight:800;font-variant-numeric:tabular-nums}.calc-block .prep-info .muted{color:var(--muted-2);font-size:12px;display:inline-block;margin-top:2px}.calc-block .calc-body{padding:8px 14px 14px;display:flex;flex-direction:column;gap:10px}.calc-block .field.mini.remaining-input{position:relative;margin:0;display:flex;flex-direction:column}.calc-block .field.mini.remaining-input .label{font-size:11px;font-weight:600;color:var(--muted);margin-bottom:4px}.calc-block .field.mini.remaining-input input{width:100%;padding:10px 36px 10px 12px;font-size:16px;font-weight:700;border:1px solid #fadf9e;border-radius:8px;background:#fff;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706}.calc-block .field.mini.remaining-input input:focus{outline:none;border-color:#f59e0b;box-shadow:0 0 0 3px #f59e0b26}.calc-block .field.mini.remaining-input .suffix{position:absolute;right:12px;top:50%;transform:translateY(4px);font-size:12px;color:var(--muted-2);pointer-events:none}.calc-block .calc-result-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.calc-block .calc-result{display:flex;align-items:baseline;gap:4px;font-variant-numeric:tabular-nums}.calc-block .calc-result .calc-label{font-size:13px;color:var(--muted);font-weight:600}.calc-block .calc-result strong{font-size:22px;font-weight:800;color:var(--primary)}.calc-block .calc-result .u{font-size:12px;color:var(--primary);margin-left:2px}.calc-block .calc-result .muted{color:var(--muted-2);font-size:18px}.calc-block .calc-apply{flex-shrink:0}.calc-block .calc-apply:disabled{background:#e5e7eb;color:var(--muted-2);cursor:not-allowed;box-shadow:none}.predict-hint{display:flex;align-items:center;gap:10px;padding:10px 14px;background:linear-gradient(135deg,var(--primary-soft) 0%,var(--accent-soft) 100%);border-radius:10px;font-size:13px}.predict-hint>i{color:var(--primary);font-size:16px}.predict-hint .hint-text{flex:1;color:var(--text)}.predict-hint .hint-text strong{color:var(--primary);font-weight:700}.predict-hint.predict-hint--inline .hint-text strong{font-weight:800;font-variant-numeric:tabular-nums}.sync-error{padding:10px 14px;background:var(--rose-soft);color:#991b1b;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px}.sync-success{padding:10px 14px;background:var(--mint-soft);color:#166534;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px;animation:fadeIn .2s ease}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}.feeding-settings-dialog .feeding-settings-hint{margin:0 0 14px;font-size:13px;line-height:1.45}.feeding-settings-dialog .feeding-settings-hint code{font-size:11px;padding:1px 5px;border-radius:4px;background:#f1f5f9}.feeding-settings-dialog .feeding-settings-grid{grid-template-columns:1fr;gap:16px}.feeding-settings-dialog .field-hint{display:block;margin-top:8px;font-size:12px;line-height:1.4;padding:8px 12px;background:#f8fafc;border-radius:8px;border-left:3px solid #e2e8f0}.feeding-settings-dialog .label{position:relative}.feeding-settings-dialog .label code{font-size:11px;font-weight:600;color:var(--primary);background:var(--primary-soft);padding:2px 6px;border-radius:4px}.feeding-settings-dialog .label .notification-badge{display:inline-flex;align-items:center;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#fff;background:linear-gradient(135deg,#10b981,#059669);padding:2px 6px;border-radius:10px;margin-left:6px;box-shadow:0 1px 2px #10b98133;animation:gentle-pulse 2s ease-in-out infinite}.feeding-settings-dialog .notification-setting-field{position:relative}.feeding-settings-dialog .notification-setting-field input{border:1.5px solid #10b981;background:linear-gradient(to right,#fff,#f0fdf4)}.feeding-settings-dialog .notification-setting-field input:focus{border-color:#059669;box-shadow:0 0 0 3px #10b9811a;background:#fff}.feeding-settings-dialog .notification-setting-field input::placeholder{color:#6b7280;opacity:.7}.feeding-settings-dialog .notification-setting-field .field-hint{border-left-color:#10b981;background:linear-gradient(to right,#f0fdf4,#f8fafc)}@keyframes gentle-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.85;transform:scale(1.02)}}.log-time--with-badge,.feed-row-time .time--with-badge{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.log-merge-badge,.feed-merge-badge{font:inherit;font-size:10px;font-weight:700;padding:2px 7px;border-radius:999px;border:1px solid rgba(99,102,241,.35);background:linear-gradient(135deg,#e0e7ff,#c7d2fe);color:#312e81;cursor:pointer;line-height:1.2;white-space:nowrap}.log-merge-badge:hover,.feed-merge-badge:hover{filter:brightness(1.03)}.log-row--clickable,.feed-row--clickable{cursor:pointer}.log-row--clickable:hover,.feed-row--clickable:hover{background:#6366f10f}.log-row--clickable:focus-visible,.feed-row--clickable:focus-visible{outline:2px solid rgba(99,102,241,.45);outline-offset:2px}.dialog-overlay.dialog-overlay--front.feed-group-detail-overlay{z-index:125}.dialog-overlay.dialog-overlay--front.feed-detail-overlay{z-index:140;animation:overlayIn .12s ease-out}.dialog-overlay.dialog-overlay--front.feed-detail-overlay .feed-detail-dialog{animation:feedDetailIn .14s ease-out}@keyframes feedDetailIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.feed-detail-dialog{--fd-accent: 99, 102, 241;--fd-soft: #eef2ff;--fd-strong: #4338ca;max-width:min(400px,96vw);padding:0;overflow:hidden;display:flex;flex-direction:column}.feed-detail-dialog[data-status=low]{--fd-accent: 245, 158, 11;--fd-soft: #fffbeb;--fd-strong: #b45309}.feed-detail-dialog[data-status=high]{--fd-accent: 244, 63, 94;--fd-soft: #fff1f2;--fd-strong: #e11d48}.feed-detail-dialog[data-status=ok]{--fd-accent: 16, 185, 129;--fd-soft: #ecfdf5;--fd-strong: #059669}.feed-detail-top{position:relative;padding:20px 20px 16px;text-align:center;background:radial-gradient(120% 80% at 50% -20%,rgba(var(--fd-accent),.22),transparent 55%),linear-gradient(180deg,var(--fd-soft) 0%,#ffffff 72%);border-bottom:1px solid rgba(var(--fd-accent),.12)}.feed-detail-close{position:absolute;top:12px;right:12px;width:36px;height:36px;border:none;border-radius:50%;background:#ffffffd9;color:#64748b;cursor:pointer;display:grid;place-items:center;box-shadow:0 2px 8px #0f172a14;transition:background .15s ease,color .15s ease}.feed-detail-close:hover{background:#fff;color:#0f172a}.feed-detail-eyebrow{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.feed-detail-metric{display:flex;align-items:flex-end;justify-content:center;gap:4px;line-height:1;margin-bottom:10px}.feed-detail-metric__value{font-size:clamp(48px,14vw,56px);font-weight:800;letter-spacing:-.04em;color:var(--fd-strong);font-variant-numeric:tabular-nums}.feed-detail-metric__unit{font-size:18px;font-weight:700;color:rgba(var(--fd-accent),.85);padding-bottom:8px}.feed-detail-status{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:999px;font-size:12px;font-weight:700;background:#fff;border:1px solid rgba(var(--fd-accent),.28);color:var(--fd-strong);box-shadow:0 2px 6px rgba(var(--fd-accent),.1)}.feed-detail-status .pi{font-size:13px}.feed-detail-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:14px}.feed-detail-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:12px;background:#ffffffeb;border:1px solid #e2e8f0;font-size:12px;font-weight:600;color:#334155;line-height:1.3;box-shadow:0 1px 4px #0f172a0a}.feed-detail-chip .pi{font-size:13px;color:#6366f1;flex-shrink:0}.feed-detail-chip em{font-style:normal;font-weight:500;color:#94a3b8;margin-left:4px}.feed-detail-chip--time .pi{color:#dc2626}.feed-detail-chip--time span{font-variant-numeric:tabular-nums;font-weight:700;color:#dc2626}.feed-detail-chip--muted{background:#f8fafc;border-color:#e2e8f0;color:#64748b;font-weight:500}.feed-detail-chip--muted .pi{color:#94a3b8}.feed-detail-body{padding:16px 18px 8px;overflow-y:auto;flex:1;min-height:0}.feed-detail-section{margin-bottom:14px}.feed-detail-section:last-child{margin-bottom:0}.feed-detail-section__title{margin:0 0 10px;font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;display:flex;align-items:center;gap:7px}.feed-detail-section__title .pi{font-size:13px;color:#6366f1}.feed-detail-cards{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.feed-detail-card{display:flex;align-items:flex-start;gap:12px;padding:12px 13px;border-radius:14px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;box-shadow:0 2px 8px #0f172a0a}.feed-detail-card--warn,.feed-detail-card--gap.feed-detail-card--warn{border-color:#f59e0b66;background:linear-gradient(180deg,#fffbeb,#fff)}.feed-detail-card--warn .feed-detail-card__icon,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card--warn .feed-detail-card__text,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__text{color:#92400e}.feed-detail-card--position{border-color:#8b5cf659;background:linear-gradient(180deg,#f5f3ff,#fff)}.feed-detail-card--average{border-color:#0ea5e959;background:linear-gradient(180deg,#f0f9ff,#fff)}.feed-detail-card--next{border-color:#10b98159;background:linear-gradient(180deg,#ecfdf5,#fff)}.feed-detail-card--gap:not(.feed-detail-card--warn){border-color:#64748b59;background:linear-gradient(180deg,#f8fafc,#fff)}.feed-detail-card__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;background:#eef2ff;color:#4f46e5;font-size:15px}.feed-detail-card__icon[data-kind=position]{background:#ede9fe;color:#7c3aed}.feed-detail-card__icon[data-kind=average]{background:#e0f2fe;color:#0284c7}.feed-detail-card__icon[data-kind=next]{background:#d1fae5;color:#059669}.feed-detail-card__icon[data-kind=gap]{background:#e2e8f0;color:#475569}.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card__body{min-width:0;flex:1}.feed-detail-card__label{display:block;font-size:11px;font-weight:800;color:#64748b;margin-bottom:3px;letter-spacing:.02em}.feed-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:#0f172a}.fdc-position__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin-bottom:8px}.fdc-position__idx{font-size:18px;font-weight:800;color:#6d28d9;letter-spacing:-.02em}.fdc-position__of{font-size:14px;font-weight:700;color:#a78bfa}.fdc-position__tag{font-size:11px;font-weight:600;color:#64748b;padding:2px 8px;border-radius:999px;background:#8b5cf61a}.fdc-progress{height:8px;border-radius:999px;background:#ede9fe;overflow:hidden;margin-bottom:10px}.fdc-progress__fill{height:100%;border-radius:inherit;background:linear-gradient(90deg,#8b5cf6,#6366f1);transition:width .2s ease}.fdc-position__stats{display:flex;flex-wrap:wrap;gap:8px}.fdc-stat{display:inline-flex;align-items:baseline;gap:4px;padding:6px 10px;border-radius:10px;font-size:12px}.fdc-stat strong{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-stat small{font-size:11px;font-weight:600;opacity:.85}.fdc-stat--violet{background:#f5f3ff;color:#5b21b6}.fdc-stat--violet strong{color:#6d28d9}.fdc-stat--indigo{background:#eef2ff;color:#4338ca}.fdc-stat--indigo strong{color:#4f46e5}.fdc-avg,.fdc-next{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}.fdc-pill{display:inline-flex;padding:4px 10px;border-radius:999px;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-pill--up{background:#dcfce7;color:#15803d}.fdc-pill--down{background:#fee2e2;color:#b91c1c}.fdc-pill--neutral{background:#f1f5f9;color:#475569}.fdc-avg__meta{font-size:12px;color:#64748b;line-height:1.4}.fdc-avg__meta strong{color:#0f172a;font-weight:700}.fdc-next__time{font-size:16px;font-weight:800;color:#dc2626;font-variant-numeric:tabular-nums}.fdc-next__vol{font-size:14px;font-weight:800;color:#059669;padding:4px 10px;border-radius:8px;background:#d1fae5}.fdc-next__gap{font-size:12px;font-weight:600;color:#64748b;display:inline-flex;align-items:center;gap:4px}.fdc-gap{display:flex;flex-direction:column;gap:10px}.fdc-gap__prev{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:10px 12px;border-radius:10px;background:#ffffffe6;border:1px solid #e2e8f0}.fdc-gap__prev-label{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;width:100%}.fdc-gap__prev-time{font-size:17px;font-weight:800;color:#0f172a;font-variant-numeric:tabular-nums}.fdc-gap__prev-vol{font-size:14px;font-weight:800;color:#475569;padding:3px 8px;border-radius:8px;background:#f1f5f9}.fdc-gap__prev-date{font-size:11px;font-weight:600;color:#94a3b8}.fdc-gap__row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;min-width:0}.fdc-gap__interval{display:inline-flex;align-items:center;gap:6px;flex:1 1 auto;min-width:0;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700;color:#475569;background:#e2e8f0;white-space:nowrap}.fdc-gap__interval .pi{font-size:13px;flex-shrink:0}.fdc-gap__interval span{overflow:hidden;text-overflow:ellipsis}.fdc-gap__interval--warn{color:#b45309;background:#fde68a;border:1px solid rgba(245,158,11,.35)}.fdc-gap__vol-diff{flex:0 0 auto;white-space:nowrap;font-size:12px;padding:5px 10px}.feed-detail-quote{margin:0;padding:12px 14px 12px 16px;border-radius:0;border-left:3px solid #c7d2fe;background:#f8fafc;font-size:14px;line-height:1.55;color:#334155;font-style:normal}.feed-detail-footer{padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fafbff}.feed-detail-cta{display:flex;gap:8px;justify-content:stretch}.feed-detail-cta .btn{flex:1;justify-content:center}.feed-detail-cta .btn.primary{flex:1.2}.feed-group-detail-dialog .feed-group-detail-summary{margin:0 0 8px;padding:0 2px;font-size:13px;line-height:1.45}.feed-group-detail-dialog .feed-group-detail-hint{margin:0 0 12px;font-size:12px;line-height:1.4;display:flex;align-items:center;gap:6px}.feed-group-detail-dialog .feed-group-detail-history{margin-bottom:4px}.feed-group-detail-dialog .feed-group-detail-rows{max-height:min(58vh,420px);overflow-y:auto;padding-right:2px}.mom-food-card{--mf-tone: 244, 114, 182;--mf-soft: #fdf2f8;--mf-ring: #fbcfe8;--mf-strong: #db2777;position:relative;overflow:hidden;background:radial-gradient(900px 260px at 0% 0%,rgba(var(--mf-tone),.14),transparent 60%),#fff;border-color:rgba(var(--mf-tone),.22);padding:18px;margin-top:16px}.mom-food-card[data-accent=rose]{--mf-tone: 244, 63, 94;--mf-soft: #fff1f2;--mf-ring: #fecdd3;--mf-strong: #e11d48}.mom-food-card[data-accent=peach]{--mf-tone: 251, 146, 60;--mf-soft: #fff7ed;--mf-ring: #fed7aa;--mf-strong: #ea580c}.mom-food-card[data-accent=mint]{--mf-tone: 16, 185, 129;--mf-soft: #ecfdf5;--mf-ring: #a7f3d0;--mf-strong: #059669}.mom-food-card[data-accent=lavender]{--mf-tone: 139, 92, 246;--mf-soft: #f5f3ff;--mf-ring: #ddd6fe;--mf-strong: #7c3aed}.mom-food-card[data-accent=amber]{--mf-tone: 245, 158, 11;--mf-soft: #fffbeb;--mf-ring: #fde68a;--mf-strong: #d97706}.mf-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.mf-head__lead{min-width:0;flex:1}.mf-head .eyebrow{display:inline-flex;align-items:center;gap:6px;color:var(--mf-strong);font-weight:700}.mf-head .eyebrow i{font-size:13px}.mf-head h2{margin:4px 0 0;font-size:18px;font-weight:800;color:var(--text);line-height:1.25}.mf-headline{margin:4px 0 0;font-size:13px;color:var(--mf-strong);font-weight:600}.mf-nav{display:inline-flex;align-items:center;gap:6px;background:var(--mf-soft);border-radius:999px;padding:4px;border:1px solid rgba(var(--mf-tone),.25);flex-shrink:0}.mf-arrow{width:30px;height:30px;border-radius:50%;border:none;background:#fff;color:var(--mf-strong);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .15s ease,background .15s ease}.mf-arrow i{font-size:12px}.mf-arrow:hover:not(:disabled){transform:scale(1.08);background:var(--mf-strong);color:#fff}.mf-arrow:disabled{opacity:.35;cursor:not-allowed}.mf-step{font-variant-numeric:tabular-nums;font-size:12px;color:var(--muted);padding:0 4px}.mf-step strong{color:var(--mf-strong);font-weight:800;font-size:13px}.mf-reset{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--mf-strong);background:transparent;border:1px dashed rgba(var(--mf-tone),.5);border-radius:999px;padding:4px 12px;cursor:pointer;margin-bottom:10px;transition:background .15s ease}.mf-reset:hover{background:var(--mf-soft)}.mf-reset i{font-size:11px}.mf-desc{margin:0 0 14px;font-size:13px;color:var(--muted);line-height:1.55}.mf-columns{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}@media(max-width:640px){.mf-columns{grid-template-columns:1fr}}.mf-col{padding:12px 14px;border-radius:14px;background:#fff;border:1px solid #eef0f3}.mf-col--good{background:linear-gradient(180deg,#f0fdf4,#fff 80%);border-color:#bbf7d0}.mf-col--bad{background:linear-gradient(180deg,#fef2f2,#fff 80%);border-color:#fecaca}.mf-col-title{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:14px;font-weight:800;color:var(--text);letter-spacing:.01em}.mf-dot{width:22px;height:22px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;flex-shrink:0;font-size:11px}.mf-dot i{font-size:10px}.mf-dot.good{background:#10b981;box-shadow:0 2px 6px #10b9814d}.mf-dot.bad{background:#ef4444;box-shadow:0 2px 6px #ef44444d}.mf-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mf-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffffb3;transition:background .15s ease,transform .15s ease}.mf-item:hover{background:#fff;transform:translate(2px)}.mf-item--bad .mf-name{color:#7f1d1d}.mf-item--good .mf-name{color:#064e3b}.mf-emoji{width:28px;height:28px;border-radius:8px;background:#fff;display:inline-grid;place-items:center;font-size:16px;flex-shrink:0;box-shadow:0 1px 2px #0f172a0f}.mf-emoji--x{background:#fef2f2;color:#ef4444}.mf-emoji--x i{font-size:12px}.mf-item-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.mf-name{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.mf-note{font-size:11.5px;color:var(--muted);line-height:1.35}.mf-tip{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;border-radius:12px;background:var(--mf-soft);border:1px solid rgba(var(--mf-tone),.22);font-size:12.5px;color:var(--text);line-height:1.5}.mf-tip i{color:var(--mf-strong);font-size:15px;margin-top:1px;flex-shrink:0}.mom-tab .card{padding:12px 14px;margin-bottom:10px;border-radius:14px}.mom-tab .mom-wellness-section,.mom-tab .mom-tips-week{margin-bottom:10px}.mom-tab .mom-food-card{padding:12px 14px;margin-top:10px}.mom-tab .mf-head{margin-bottom:8px}.mom-tab .mf-head h2{font-size:16px}.mom-tab .mf-desc{margin-bottom:10px;font-size:12px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.mom-tab .mf-columns{gap:8px;margin-bottom:8px}.mom-tab .mf-col{padding:8px 10px}.mom-tab .mf-list{gap:6px}.mom-tab .mf-item{padding:6px 8px}.mom-tab .mf-tip{padding:8px 10px;font-size:12px}.mom-hero{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin-bottom:14px;background:linear-gradient(135deg,#fdf2f8,#faf5ff,#eef2ff);border:1px solid #f9a8d4;border-radius:16px}.mom-hero .mom-hero__icon{font-size:36px;line-height:1;flex-shrink:0}.mom-hero h2{margin:0 0 6px;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em}.mom-hero .mom-hero__sub{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}.mom-hero .mom-hero__sub strong{color:var(--text)}.mom-hero .mom-hero__sub .muted-inline{display:block;margin-top:4px;font-size:12.5px;opacity:.92}.mom-hero--compact{align-items:center;padding:10px 12px;margin-bottom:10px;border-radius:14px;gap:10px}.mom-hero--compact .mom-hero__icon{font-size:26px}.mom-hero--compact h2{margin:0;font-size:16px;letter-spacing:-.01em}.mom-hero--compact .mom-hero__title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px}.mom-hero--compact .mom-hero__baby{font-size:12px;font-weight:700;color:#be185d;padding:2px 8px;border-radius:999px;background:#ffffffbf;border:1px solid rgba(244,114,182,.35)}.mom-hero--compact .mom-hero__sub{margin:4px 0 0;font-size:11.5px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.mom-hero--compact .mom-hero__sub .muted-inline{display:inline;margin-top:0;font-size:11.5px}.section-head--tight{margin-bottom:8px}.section-head--tight h3{margin:0}.mom-disclaimer--compact{font-size:11px;line-height:1.4;padding:8px 10px;margin-top:4px;margin-bottom:6px}.section-head{margin-bottom:14px}.section-head h3{margin:0 0 6px;font-size:16px;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}.section-head h3 i{color:#db2777;font-size:15px}.section-head .section-sub{margin:0;font-size:12.5px;color:var(--muted);line-height:1.45}.mom-wellness-section{margin-bottom:16px}.mom-tab .mom-wellness-grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}.mom-wellness-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.mom-w-card{padding:14px 14px 12px;border-radius:14px;border:1px solid var(--border);background:#fafafa;transition:border-color .15s,box-shadow .15s}.mom-w-card:hover{border-color:#f472b6;box-shadow:0 6px 18px -12px #db277759}.mom-w-card .mom-w-card__icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#fce7f3,#ede9fe);display:flex;align-items:center;justify-content:center;margin-bottom:10px}.mom-w-card .mom-w-card__icon i{font-size:16px;color:#be185d}.mom-w-card h4{margin:0 0 8px;font-size:13.5px;font-weight:800;color:var(--text);line-height:1.3}.mom-w-card p{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-tips-week{margin-bottom:16px}.mom-tab .mom-tips-list{gap:6px}.mom-tab .mom-tips-list li{padding:8px 10px;border-radius:10px}.mom-tab .mom-tips-list li strong{margin-bottom:2px;font-size:12px}.mom-tab .mom-tips-list li span{font-size:11.5px;line-height:1.4}.mom-tips-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.mom-tips-list li{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:12px;background:#fefce8;border:1px solid #fde047}.mom-tips-list li>i{color:#ca8a04;margin-top:2px;flex-shrink:0}.mom-tips-list li strong{display:block;font-size:13px;font-weight:800;color:var(--text);margin-bottom:4px}.mom-tips-list li span{font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-disclaimer{margin-top:8px;margin-bottom:8px}.mom-empty-profile{max-width:520px;margin:24px auto 40px;text-align:center}.mom-empty-profile__inner{padding:28px 22px}.mom-empty-profile__inner .mom-empty-emoji{font-size:48px;display:block;margin-bottom:12px}.mom-empty-profile__inner h2{margin:0 0 10px;font-size:17px;font-weight:800;color:var(--text)}.mom-empty-profile__inner p{margin:0 0 18px;font-size:14px;line-height:1.55;color:var(--muted)}.event-reminder-dialog{max-width:min(520px,100vw - 28px);padding-bottom:8px}.event-reminder-dialog__head{align-items:flex-start;margin-bottom:4px}.event-reminder-dialog__title{margin:0;padding-right:40px;font-size:20px;font-weight:800;line-height:1.3;color:var(--text, #0f172a);display:flex;align-items:center;gap:10px;letter-spacing:-.02em}.event-reminder-dialog__title .pi-bell{color:#f59e0b;font-size:18px;flex-shrink:0}.event-reminder-list{list-style:none;margin:12px 0 0;padding:0;max-height:min(52vh,400px);overflow:auto;display:flex;flex-direction:column;gap:10px}.event-reminder-card{padding:14px 16px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;border-radius:14px;box-shadow:0 2px 8px #0f172a0d}.event-reminder-card__title{margin:0;font-size:16px;font-weight:800;color:var(--text, #0f172a);line-height:1.35}.event-reminder-card__datetime{margin:5px 0 0;font-size:12.5px;font-weight:500;color:#64748b;line-height:1.4}.event-reminder-card__date{color:#475569;font-weight:600}.event-reminder-card__sep{color:#94a3b8}.event-reminder-card__hour{color:#dc2626;font-weight:700;font-variant-numeric:tabular-nums}.event-reminder-card__place{margin:6px 0 0;display:flex;align-items:flex-start;gap:6px;font-size:13px;color:#475569;line-height:1.4}.event-reminder-card__place .pi-map-marker{margin-top:2px;flex-shrink:0;font-size:13px;color:var(--primary, #5b6cff)}.event-reminder-card__content-wrap{margin-top:10px;padding-top:10px;border-top:1px solid #eef1f6}.event-reminder-card__content-label{display:block;margin-bottom:4px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#94a3b8}.event-reminder-card__content{margin:0;font-size:13.5px;color:#334155;line-height:1.5;white-space:pre-wrap}.event-reminder-actions{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border, #e5e7eb)}.event-reminder-actions__link{flex:0 1 auto;min-width:0;padding:6px 4px;border:none;background:transparent;color:#64748b;font-size:13px;font-weight:600;font-family:inherit;text-decoration:underline;text-underline-offset:3px;cursor:pointer}.event-reminder-actions__link:hover:not(:disabled){color:#334155}.event-reminder-actions__link:disabled{opacity:.5;cursor:not-allowed}.event-reminder-actions__primary{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:10px 18px;font-size:15px;font-weight:700;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DocumentsComponent, selector: "app-documents" }, { kind: "component", type: MedicalHistoryComponent, selector: "app-medical-history", inputs: ["user"], outputs: ["openExplorerFile"] }, { kind: "component", type: WeightComponent, selector: "app-weight", inputs: ["user", "birthDate", "gender", "babyName"] }, { kind: "component", type: ActivityLogComponent, selector: "app-activity-log", outputs: ["activityChanged"] }, { kind: "component", type: FeedingScheduleComponent, selector: "app-feeding-schedule", inputs: ["user"] }, { kind: "component", type: FeedingMomComponent, selector: "app-feeding-mom", inputs: ["profile", "editing"], outputs: ["goToFeeding"] }, { kind: "component", type: FeedingChartsComponent, selector: "app-feeding-charts", inputs: ["logs", "now", "nutritionTarget"] }, { kind: "component", type: FeedingNutritionComponent, selector: "app-feeding-nutrition", inputs: ["nutritionTarget", "todayStats", "now", "latestWeightKg", "editing"], outputs: ["goToWeight"] }, { kind: "component", type: FeedingProfileComponent, selector: "app-feeding-profile", inputs: ["user", "profile", "draft", "editing", "maxBirthDate"], outputs: ["save", "cancelEdit", "startEdit", "clearProfile", "draftNameChange", "draftBirthChange", "draftGenderChange"] }, { kind: "component", type: FeedingDialogsComponent, selector: "app-feeding-dialogs", inputs: ["logs", "user", "now", "bottlePrep", "feedingSettings", "prediction", "todayStats", "yesterdayStats", "todayLogs", "yesterdayLogs", "maxBirthDate", "syncError", "syncMessage"], outputs: ["logsChanged", "settingsChanged", "syncErrorChange", "syncMessageChange", "logDialogOpenChange", "bottlePrepClearRequested"] }, { kind: "component", type: FeedingDailyComponent, selector: "app-feeding-daily", inputs: ["logs", "feedingSettings", "now", "nutritionTarget", "user", "syncMessage", "syncError", "logDialogOpen", "loadingLogs", "refreshToken"], outputs: ["logsChanged", "bottlePrepChange", "editLog", "historyOpen", "feedRowClick"] }, { kind: "component", type: HeroHeaderComponent, selector: "app-hero-header", inputs: ["profile", "age", "user", "weightLabel", "isAnyLoading", "showRefresh"], outputs: ["refresh", "openMenu"] }, { kind: "component", type: HeroStatsStripComponent, selector: "app-hero-stats-strip", inputs: ["currentTime", "dateLabel", "nextFeedTime", "nextFeedCountdown", "weightLabel", "whoStatus"] }, { kind: "component", type: NotificationBannerComponent, selector: "app-notification-banner", inputs: ["notifications", "saving", "currentUser", "dialogOpen"], outputs: ["openDialog", "closeDialog", "acknowledge", "acknowledgeAll", "delete", "openComposer"] }, { kind: "component", type: NotificationListComponent, selector: "app-notification-list", inputs: ["notifications", "currentUser", "saving"], outputs: ["back", "acknowledge", "acknowledgeAll", "delete", "openComposer"] }, { kind: "component", type: SubrouteShellComponent, selector: "app-subroute-shell", inputs: ["title"], outputs: ["back"] }, { kind: "component", type: SettingsMenuComponent, selector: "app-settings-menu", inputs: ["appName", "appVersion", "showDocuments", "unreadCount"], outputs: ["close", "select"] }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1$1.SlicePipe, name: "slice" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
17112
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FeedingComponent, isStandalone: true, selector: "app-feeding", viewQueries: [{ propertyName: "feedingPageRef", first: true, predicate: ["feedingPage"], descendants: true, static: true }, { propertyName: "documentsCmp", first: true, predicate: DocumentsComponent, descendants: true }, { propertyName: "medicalCmp", first: true, predicate: MedicalHistoryComponent, descendants: true }, { propertyName: "weightCmp", first: true, predicate: WeightComponent, descendants: true }, { propertyName: "feedingDialogs", first: true, predicate: ["feedingDialogs"], descendants: true }], ngImport: i0, template: "<div class=\"feeding-root\">\n <div class=\"feeding-page\" #feedingPage>\n <div class=\"backdrop\"></div>\n\n <header class=\"hero\" [class.hero--profile-merged]=\"profile() && !editing() && ageBreakdown()\">\n @if (profile() && !editing() && ageBreakdown(); as age) {\n <!-- \u2550\u2550 REDESIGNED HERO \u2550\u2550 -->\n <app-hero-header\n [profile]=\"profile()!\"\n [age]=\"age\"\n [user]=\"user()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [isAnyLoading]=\"isAnyLoading()\"\n [showRefresh]=\"bottomTab() !== 'schedule'\"\n (openMenu)=\"openSettingsMenu()\"\n (openChat)=\"openChatRoute()\"\n (refresh)=\"refreshAll()\"\n >\n <app-activity-log slot=\"activity-log\" (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n </app-hero-header>\n\n <app-hero-stats-strip\n [currentTime]=\"currentTimeStr\"\n [dateLabel]=\"heroDateLabel\"\n [nextFeedTime]=\"prediction()?.nextTime ?? null\"\n [nextFeedCountdown]=\"heroNextFeedCountdown()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [whoStatus]=\"nutritionTarget()?.ageLabel ?? null\"\n ></app-hero-stats-strip>\n\n <div class=\"hero-notif-wrap\">\n <app-notification-banner\n [notifications]=\"visibleNotifications()\"\n [saving]=\"notificationSaving()\"\n [currentUser]=\"user()\"\n [dialogOpen]=\"notifDialogOpen()\"\n (openDialog)=\"openNotificationRoute()\"\n (closeDialog)=\"notifDialogOpen.set(false)\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"notifDialogOpen.set(false); openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"notifDialogOpen.set(false); openNotificationComposerDialog()\"\n ></app-notification-banner>\n </div>\n\n } @else {\n <!-- \u2500\u2500 Fallback: no profile / editing \u2500\u2500 -->\n <div class=\"hero-inner\">\n <div class=\"hero-brand\">\n <div class=\"hero-title-row\">\n <div class=\"hero-title\">\n <div class=\"emoji\" aria-hidden=\"true\">\uD83D\uDC76</div>\n <div class=\"hero-text\">\n <h1>B\u00E9 y\u00EAu c\u1EE7a b\u1EA1n</h1>\n <p class=\"hero-userline\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <strong>{{ user() }}</strong>\n </p>\n </div>\n </div>\n @if (profile() && !editing()) {\n <div class=\"hero-actions\">\n <app-activity-log (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"feedingDialogs.openFeedingSettingsDialog()\"\n title=\"C\u00E0i \u0111\u1EB7t\"\n aria-label=\"C\u00E0i \u0111\u1EB7t c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-sliders-h\"></i>\n </button>\n @if (bottomTab() !== 'schedule') {\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"refreshAll()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"\n isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, C\u00E2n, L\u1ECBch, Y t\u1EBF, T\u00E0i li\u1EC7u'\n \"\n aria-label=\"T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, Th\u1EC3 tr\u1EA1ng, L\u1ECBch, Ti\u1EC1n s\u1EED y t\u1EBF v\u00E0 T\u00E0i li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"hero-datetime\" aria-live=\"polite\">\n <div class=\"hero-datetime-strip\">\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--date\">\n <i class=\"pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__wd\">{{ heroWeekday }}</span>\n <time class=\"hero-datetime-strip__date\" [attr.datetime]=\"todayDateStr()\">{{\n heroDateNumeric\n }}</time>\n </div>\n <span class=\"hero-datetime-strip__divider\" aria-hidden=\"true\"></span>\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--time\">\n <i class=\"pi pi-clock\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__clock\">{{ currentTimeStr }}</span>\n </div>\n </div>\n </div>\n\n <section class=\"hero-notification\" aria-label=\"Th\u00F4ng b\u00E1o n\u1ED9i b\u1ED9\">\n <!-- watermark background icons -->\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bell\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-megaphone\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-comments\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--send\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-send\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--star\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-star\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bolt\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bolt\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--heart\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-heart\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--info\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-info-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--check\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-check-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--eye\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-eye\"></i>\n </span>\n\n <div class=\"hero-notification__head\">\n <label class=\"hero-notification__composer-label\" for=\"hero-notification-input\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n <span>Th\u00F4ng b\u00E1o</span>\n @if (visibleNotifications().length > 0) {\n <span class=\"hero-notification__unread-badge\">{{\n visibleNotifications().length\n }}</span>\n }\n </label>\n <div class=\"hero-notification__head-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button\n type=\"button\"\n class=\"hero-notification__add\"\n (click)=\"openNotificationComposerDialog()\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n title=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (displayedNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of displayedNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">\n {{ notice.user | slice: 0 : 2 | uppercase }}\n </div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{\n formatNotificationCreatedAt(notice.createdAt)\n }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\"\n title=\"\u0110\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button\n type=\"button\"\n class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\"\n title=\"X\u00F3a th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\"\n >M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span\n >\n </div>\n }\n\n @if (hiddenNotificationCount() > 0) {\n <button\n type=\"button\"\n class=\"hero-notification__more\"\n (click)=\"openNotificationListDialog()\"\n >\n Xem th\u00EAm {{ hiddenNotificationCount() }} th\u00F4ng b\u00E1o \u2192\n </button>\n }\n </section>\n\n </div>\n } <!-- end @else no-profile -->\n </header>\n\n <main\n class=\"content\"\n [class.is-documents]=\"bottomTab() === 'documents'\"\n [class.is-weight]=\"bottomTab() === 'weight'\"\n [class.is-schedule]=\"bottomTab() === 'schedule'\"\n [class.is-mom]=\"bottomTab() === 'mom'\"\n [class.is-medical]=\"bottomTab() === 'medical'\"\n >\n <!--\n ============ TAB PANES ============\n D\u00F9ng `[hidden]` thay v\u00EC `*ngIf` \u0111\u1EC3 c\u00E1c tab c\u00F9ng gi\u1EEF tr\u1EA1ng th\u00E1i khi\n user qua l\u1EA1i: scroll position, search query \u0111ang g\u00F5, viewMode, folder\n \u0111ang \u0111\u1EE9ng, file \u0111ang ch\u1ECDn, draft \u0111ang nh\u1EADp\u2026 \u0111\u1EC1u kh\u00F4ng b\u1ECB reset. H\u1EC7\n qu\u1EA3: `<app-documents>` \u0111\u01B0\u1EE3c mount 1 l\u1EA7n \u1EDF app load \u2014 `loadEntries()`\n ch\u1EA1y 1 l\u1EA7n, c\u00E1c l\u1EA7n switch tab k\u1EBF ti\u1EBFp kh\u00F4ng g\u1ECDi API th\u00EAm.\n -->\n <!-- ============ TAB: DOCUMENTS \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: WEIGHT ============ -->\n <div class=\"tab-pane tab-pane--weight\" [hidden]=\"bottomTab() !== 'weight'\">\n <app-weight\n [user]=\"user()\"\n [birthDate]=\"profile()?.birthDate\"\n [gender]=\"profile()?.gender\"\n [babyName]=\"profile()?.babyName\"\n ></app-weight>\n </div>\n\n <!-- ============ TAB: L\u1ECACH \u2014 Google Sheet tab \"Event\" ============ -->\n <div class=\"tab-pane tab-pane--schedule\" [hidden]=\"bottomTab() !== 'schedule'\">\n <app-feeding-schedule [user]=\"user()\"></app-feeding-schedule>\n </div>\n\n <!-- ============ TAB: TI\u1EC0N S\u1EEC Y T\u1EBE \u2014 Medical history V2 ============ -->\n <div class=\"tab-pane tab-pane--medical\" [hidden]=\"bottomTab() !== 'medical'\">\n <app-medical-history\n [user]=\"user()\"\n (openExplorerFile)=\"openMedicalAttachmentInDocuments($event)\"\n ></app-medical-history>\n </div>\n\n <!-- ============ TAB: M\u1EB8 \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: FEEDING ============ -->\n <div class=\"tab-pane tab-pane--feeding\" [hidden]=\"bottomTab() !== 'feeding'\">\n <app-feeding-profile\n [user]=\"user()\"\n [profile]=\"profile()\"\n [draft]=\"draft()\"\n [editing]=\"editing()\"\n [maxBirthDate]=\"maxBirthDate\"\n (save)=\"save()\"\n (cancelEdit)=\"cancelEdit()\"\n (startEdit)=\"startEdit()\"\n (clearProfile)=\"clearProfile()\"\n (draftNameChange)=\"updateDraftName($event)\"\n (draftBirthChange)=\"updateDraftBirth($event)\"\n (draftGenderChange)=\"setDraftGender($event)\"\n />\n\n @if (profile() && !editing() && ageBreakdown()) {\n <app-feeding-daily\n [logs]=\"logs()\"\n [feedingSettings]=\"feedingSettings()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n [user]=\"user()\"\n [syncMessage]=\"syncMessage()\"\n [syncError]=\"syncError()\"\n [logDialogOpen]=\"logDialogOpen()\"\n [loadingLogs]=\"loadingLogs()\"\n [refreshToken]=\"dailyRefreshToken()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (bottlePrepChange)=\"bottlePrep.set($event)\"\n (editLog)=\"feedingDialogs.openEditDialog($event)\"\n (historyOpen)=\"feedingDialogs.openHistoryDialog($event.filter, $event.dateStr)\"\n (feedRowClick)=\"feedingDialogs.onFeedRowClick($event)\"\n >\n <app-feeding-charts\n data-feeding-charts\n [logs]=\"logs()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n />\n </app-feeding-daily>\n <app-feeding-nutrition\n [nutritionTarget]=\"nutritionTarget()\"\n [todayStats]=\"todayStats()\"\n [now]=\"now()\"\n [latestWeightKg]=\"latestWeightKgFromSheet()\"\n [editing]=\"editing()\"\n (goToWeight)=\"setBottomTab('weight')\"\n />\n }\n\n <!-- ============ FAB \u00AB Ghi c\u1EEF b\u00FA \u00BB c\u0169 \u0111\u00E3 \u0111\u01B0\u1EE3c thay b\u1EB1ng FAB gi\u1EEFa bottom-nav ============ -->\n </div>\n <!-- /.tab-pane--feeding -->\n </main>\n </div>\n <!-- /.feeding-page -->\n\n <!-- ============ BOTTOM NAVIGATION (C\u1EEF b\u00FA \u00B7 Th\u1EC3 tr\u1EA1ng \u00B7 + \u00B7 L\u1ECBch \u00B7 Y t\u1EBF) ============ -->\n <nav\n class=\"bottom-nav\"\n role=\"tablist\"\n aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\"\n >\n <div class=\"bn-side bn-side--left\">\n @for (tab of navLeftTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"bn-fab\"\n (click)=\"feedingDialogs.openLogDialog()\"\n [disabled]=\"!profile() || editing()\"\n [title]=\"(!profile() || editing()) ? 'Ch\u1ECDn b\u00E9 tr\u01B0\u1EDBc khi ghi c\u1EEF b\u00FA' : 'Ghi nh\u1EADn c\u1EEF b\u00FA m\u1EDBi'\"\n aria-label=\"Ghi nh\u1EADn c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"bn-side bn-side--right\">\n @for (tab of navRightTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n </nav>\n\n <!-- ============ NOTIFICATION SUBROUTE (full-screen) ============ -->\n @if (notifRouteOpen()) {\n <app-notification-list\n [notifications]=\"allNotifications()\"\n [currentUser]=\"user()\"\n [saving]=\"notificationSaving()\"\n (back)=\"closeNotificationRoute()\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"openNotificationComposerDialog()\"\n />\n }\n\n <!-- \u2550\u2550 NOTIFICATION DIALOGS \u2014 rendered as siblings of .feeding-page (outside the\n overflow/stacking-context scroll zone) so they always overlay the\n full-screen notification subroute. See .feeding-page overflow-y:auto +\n -webkit-overflow-scrolling:touch in feeding.component.scss. \u2550\u2550 -->\n @if (notificationComposerDialogOpen()) {\n <div\n class=\"dialog-overlay dialog-overlay--front\"\n (click)=\"closeNotificationComposerDialog()\"\n >\n <div\n class=\"dialog hero-notification-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n (click)=\"$event.stopPropagation()\"\n >\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--send\" aria-hidden=\"true\"><i class=\"pi pi-send\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n\n <div class=\"dialog-head hero-notification-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n </div>\n <div>\n <h2 class=\"hero-notification-dialog__title\">T\u1EA1o th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">Th\u00F4ng b\u00E1o s\u1EBD hi\u1EC3n th\u1ECB cho to\u00E0n b\u1ED9 th\u00E0nh vi\u00EAn</p>\n </div>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationComposerDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <div class=\"hero-notification__composer-panel\">\n <div class=\"hero-notification__composer-row\">\n <div class=\"hero-notification__input-wrap\">\n <textarea\n id=\"hero-notification-input\"\n class=\"hero-notification__input\"\n [value]=\"notificationDraft()\"\n (input)=\"updateNotificationDraft($any($event.target).value)\"\n [disabled]=\"notificationSaving()\"\n maxlength=\"300\"\n placeholder=\"Nh\u1EADp n\u1ED9i dung th\u00F4ng b\u00E1o cho m\u1ECDi ng\u01B0\u1EDDi...\"\n aria-label=\"N\u1ED9i dung th\u00F4ng b\u00E1o\"\n ></textarea>\n <div class=\"hero-notification__char-ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 36 36\">\n <circle class=\"hero-notification__char-ring-track\" cx=\"18\" cy=\"18\" r=\"15\" />\n <circle class=\"hero-notification__char-ring-fill\" cx=\"18\" cy=\"18\" r=\"15\"\n [style.stroke-dashoffset]=\"94 - (notificationDraft().trim().length / 300) * 94\" />\n </svg>\n <span class=\"hero-notification__char-count\">{{ notificationDraft().trim().length }}</span>\n </div>\n </div>\n </div>\n <div class=\"hero-notification__composer-actions\">\n <button type=\"button\" class=\"btn ghost hero-notification__cancel\"\n (click)=\"closeNotificationComposerDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn hero-notification__send\"\n (click)=\"createNotification()\"\n [disabled]=\"notificationSaving() || !notificationDraft().trim()\">\n <i class=\"pi pi-send\"></i>\n <span>G\u1EEDi th\u00F4ng b\u00E1o</span>\n </button>\n </div>\n </div>\n @if (notificationError()) {\n <p class=\"hero-notification__error\">{{ notificationError() }}</p>\n }\n </div>\n </div>\n }\n\n @if (notificationListDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeNotificationListDialog()\">\n <div class=\"dialog hero-notification-list-dialog\" role=\"dialog\" aria-modal=\"true\"\n aria-label=\"Danh s\u00E1ch th\u00F4ng b\u00E1o\" (click)=\"$event.stopPropagation()\">\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--check\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></span>\n\n <div class=\"dialog-head hero-notification-list-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\"><i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i></div>\n <div>\n <h2 class=\"hero-notification-list-dialog__title\">To\u00E0n b\u1ED9 th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">{{ visibleNotifications().length }} th\u00F4ng b\u00E1o</p>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__head-actions\">\n <button type=\"button\" class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationListDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__body\">\n @if (visibleNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of visibleNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">{{ notice.user | slice: 0 : 2 | uppercase }}</div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{ formatNotificationCreatedAt(notice.createdAt) }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button type=\"button\" class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\" title=\"\u0110\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button type=\"button\" class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\" title=\"X\u00F3a th\u00F4ng b\u00E1o\">\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\">M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (acknowledgeAllConfirmDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAcknowledgeAllConfirmDialog()\">\n <div class=\"dialog ack-all-confirm-dialog\" role=\"alertdialog\" aria-modal=\"true\"\n aria-labelledby=\"ack-all-confirm-title\" (click)=\"$event.stopPropagation()\">\n <div class=\"ack-all-confirm-dialog__icon-wrap\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></div>\n <h2 id=\"ack-all-confirm-title\" class=\"ack-all-confirm-dialog__title\">\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc?</h2>\n <p class=\"ack-all-confirm-dialog__desc\">\n T\u1EA5t c\u1EA3 {{ visibleNotifications().length }} th\u00F4ng b\u00E1o s\u1EBD \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u \u0111\u00E3 \u0111\u1ECDc v\u00E0 \u1EA9n kh\u1ECFi danh s\u00E1ch.\n </p>\n <div class=\"ack-all-confirm-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeAcknowledgeAllConfirmDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn ack-all-confirm-dialog__confirm-btn\"\n (click)=\"confirmAcknowledgeAll()\" [disabled]=\"notificationSaving()\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> X\u00E1c nh\u1EADn\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- ============ SETTINGS FLOAT MENU (full-screen) ============ -->\n @if (settingsMenuOpen()) {\n <app-settings-menu\n [appName]=\"appInfo.name\"\n [appVersion]=\"appInfo.version\"\n [showDocuments]=\"explorerEnabled\"\n [unreadCount]=\"visibleNotifications().length\"\n (close)=\"closeSettingsMenu()\"\n (select)=\"onSettingsMenuSelect($event)\"\n />\n }\n\n <!-- ============ M\u1EB8 SUBROUTE (full-screen) ============ -->\n @if (momRouteOpen()) {\n <app-subroute-shell\n title=\"M\u1EB9\"\n (back)=\"closeMomRoute()\"\n >\n <app-feeding-mom\n [profile]=\"profile()\"\n [editing]=\"editing()\"\n (goToFeeding)=\"closeMomRoute(); setBottomTab('feeding')\"\n ></app-feeding-mom>\n </app-subroute-shell>\n }\n\n <!-- ============ T\u00C0I LI\u1EC6U SUBROUTE (full-screen) ============ -->\n @if (documentsRouteOpen()) {\n <app-subroute-shell\n title=\"T\u00E0i li\u1EC7u\"\n (back)=\"closeDocumentsRoute()\"\n >\n <app-documents></app-documents>\n </app-subroute-shell>\n }\n\n <!-- ============ TR\u1EE2 L\u00DD AI SUBROUTE (full-screen, m\u1EDF t\u1EEB n\u00FAt chat tr\u00EAn hero) ============ -->\n @if (chatRouteOpen()) {\n <app-subroute-shell\n title=\"Tr\u1EE3 l\u00FD AI\"\n (back)=\"closeChatRoute()\"\n >\n <app-chat-shell\n [context]=\"chatContext()\"\n [disabled]=\"chatDisabled()\"\n ></app-chat-shell>\n </app-subroute-shell>\n }\n\n <!-- About -->\n @if (aboutDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAboutDialog()\">\n <div\n class=\"dialog about-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"feeding-about-title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head about-dialog__head\">\n <h2 id=\"feeding-about-title\" class=\"about-dialog__title\">\n <i class=\"pi pi-info-circle\" aria-hidden=\"true\"></i>\n About\n </h2>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeAboutDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <dl class=\"about-dialog__meta\">\n <div class=\"about-dialog__row\">\n <dt>\u1EE8ng d\u1EE5ng</dt>\n <dd>{{ appInfo.name }}</dd>\n </div>\n <div class=\"about-dialog__row\">\n <dt>Phi\u00EAn b\u1EA3n</dt>\n <dd>\n <code>{{ appInfo.version }}</code>\n </dd>\n </div>\n </dl>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn primary\" (click)=\"closeAboutDialog()\">\u0110\u00F3ng</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Nh\u1EAFc s\u1EF1 ki\u1EC7n l\u1ECBch (sheet Event + Settings) -->\n @if (eventReminderDialogOpen() && scheduleReminderEvents().length > 0) {\n <div\n class=\"dialog-overlay dialog-overlay--front dialog-overlay--event-reminder\"\n (click)=\"closeEventReminderDialog()\"\n >\n <div\n class=\"dialog event-reminder-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head event-reminder-dialog__head\">\n <h2 class=\"event-reminder-dialog__title\">\n <i class=\"pi pi-bell\" aria-hidden=\"true\"></i>\n B\u1EA1n c\u00F3 l\u1ECBch s\u1EAFp t\u1EDBi\n </h2>\n <button\n type=\"button\"\n class=\"dialog-close\"\n (click)=\"closeEventReminderDialog()\"\n aria-label=\"\u0110\u00F3ng\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <ul class=\"event-reminder-list\">\n @for (ev of scheduleReminderEvents(); track ev) {\n <li>\n <article class=\"event-reminder-card\">\n <h3 class=\"event-reminder-card__title\">{{ ev.title }}</h3>\n <p class=\"event-reminder-card__datetime\">\n <span class=\"event-reminder-card__date\">{{ formatReminderEventWhen(ev) }}</span>\n <span class=\"event-reminder-card__sep\" aria-hidden=\"true\"> \u2013 </span>\n <span class=\"event-reminder-card__hour\">{{\n formatReminderEventTime(ev.time)\n }}</span>\n </p>\n @if (ev.place) {\n <p class=\"event-reminder-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n <span>{{ ev.place }}</span>\n </p>\n }\n @if (ev.note) {\n <div class=\"event-reminder-card__content-wrap\">\n <span class=\"event-reminder-card__content-label\">N\u1ED9i dung</span>\n <p class=\"event-reminder-card__content\">{{ ev.note }}</p>\n </div>\n }\n </article>\n </li>\n }\n </ul>\n <div class=\"dialog-actions event-reminder-actions\">\n <button\n type=\"button\"\n class=\"event-reminder-actions__link\"\n (click)=\"acknowledgeAllEventReminders()\"\n >\n Kh\u00F4ng nh\u1EAFc l\u1EA1i\n </button>\n <button\n type=\"button\"\n class=\"btn primary event-reminder-actions__primary\"\n (click)=\"skipEventReminderSession()\"\n >\n \u0110\u00E3 hi\u1EC3u\n </button>\n </div>\n </div>\n </div>\n }\n\n <app-feeding-dialogs\n #feedingDialogs\n [logs]=\"logs()\"\n [user]=\"user()\"\n [now]=\"now()\"\n [bottlePrep]=\"bottlePrep()\"\n [feedingSettings]=\"feedingSettings()\"\n [prediction]=\"prediction()\"\n [todayStats]=\"todayStats()\"\n [yesterdayStats]=\"yesterdayStats()\"\n [todayLogs]=\"todayLogs()\"\n [yesterdayLogs]=\"yesterdayLogs()\"\n [maxBirthDate]=\"maxBirthDate\"\n [syncError]=\"syncError()\"\n [syncMessage]=\"syncMessage()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (settingsChanged)=\"onFeedingSettingsChanged($event)\"\n (syncErrorChange)=\"syncError.set($event)\"\n (syncMessageChange)=\"syncMessage.set($event)\"\n (logDialogOpenChange)=\"logDialogOpen.set($event)\"\n (bottlePrepClearRequested)=\"clearBottlePrep()\"\n />\n</div>\n<!-- /.feeding-root -->\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;position:absolute;inset:0;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--border-strong: #d1d5db;--primary: #5b6cff;--primary-2: #4c5cf0;--primary-soft: #eef0ff;--accent: #ff5c8a;--accent-soft: #ffe7ef;--mint: #10b981;--mint-soft: #d1fae5;--amber: #f59e0b;--amber-soft: #fef3c7;--rose: #ef4444;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8}.feeding-root{flex:1;min-height:0;width:100%;display:flex;flex-direction:column;position:relative}.feeding-root .bp-prep-user{font-weight:800;color:#0369a1;background:#0ea5e933;padding:0 5px;margin-right:2px;border-radius:5px;white-space:nowrap;transition:color .2s,background .2s}.feeding-root .bp-prep-time{font-weight:800;font-variant-numeric:tabular-nums;color:#0284c7;background:linear-gradient(180deg,#f0f9ff,#e0f2fe);border:1px solid rgba(14,165,233,.35);padding:1px 7px;margin-left:2px;border-radius:6px;white-space:nowrap;box-shadow:0 1px #ffffffa6 inset;transition:color .2s,background .2s,border-color .2s}.feeding-page{position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;color:var(--text);padding-bottom:calc(170px + env(safe-area-inset-bottom,0px))}.backdrop{position:fixed;inset:0;background:var(--bg);z-index:-1}.hero-notif-wrap{padding:0 16px 8px}.hero{padding:0;background:#fffffff5;border-bottom:1px solid rgba(226,232,240,.9)}@media(max-width:640px){.hero{padding-top:calc(26px + env(safe-area-inset-top,0px));padding-bottom:10px}}.hero-inner{max-width:1100px;margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:14px 18px}.hero-brand{min-width:0}.hero-title-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;width:100%}.hero-title{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.hero-title .emoji{font-size:36px;line-height:1;flex-shrink:0;filter:drop-shadow(0 2px 4px rgba(15,23,42,.08))}@media(max-width:640px){.hero-title .emoji{font-size:32px}}.hero-title .hero-text{min-width:0}.hero-title h1{margin:0;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em;line-height:1.2}@media(max-width:640px){.hero-title h1{font-size:17px}}.hero-title .hero-userline{margin:5px 0 0;display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:12.5px;color:var(--muted)}.hero-title .hero-userline i{font-size:12px;color:var(--primary);opacity:.9}.hero-title .hero-userline strong{color:var(--primary);font-weight:700;letter-spacing:.01em}.hero-title .hero-userline .hero-last-update{font-size:11px;color:#94a3b8;font-weight:400}.hero--profile-merged{background:var(--primary-soft);padding-bottom:10px;margin-bottom:8px}.hero-title--merged{align-items:flex-start}.hero-title--merged .emoji{font-size:30px}.hero-title--merged h1{font-size:18px;line-height:1.15}.hero-merged-head{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;min-width:0;row-gap:6px}.hero-age-chip{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:lowercase;color:var(--primary);background:var(--primary-soft);padding:3px 9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--primary) 18%,transparent);white-space:nowrap}.hero-merged-stat{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:700;color:var(--muted);background:#94a3b824;border:1px solid rgba(148,163,184,.28);white-space:nowrap;line-height:1.25}.hero-merged-stat>i{font-size:10px;opacity:.88;color:var(--muted-2)}.hero-merged-stat--weight>i{color:var(--accent);opacity:1}.hero-merged-meta{margin:5px 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:11.5px;line-height:1.45;color:var(--muted)}.hero-merged-meta .dot{color:var(--muted-2);font-weight:700;-webkit-user-select:none;user-select:none}.hero-merged-meta i{font-size:10px;margin-right:3px;color:var(--muted-2);opacity:.95}.hero-merged-meta strong{color:var(--primary);font-weight:700}.hero-merged-sync{color:#94a3b8;font-weight:400;font-size:11px}.hero-actions--merged{flex-shrink:0}.hero-profile-menu{position:relative;flex:0 0 auto;list-style:none}.hero-profile-menu__trigger{list-style:none}.hero-profile-menu__trigger::-webkit-details-marker{display:none}.hero-profile-menu__trigger::marker{content:\"\"}.hero-profile-menu__panel{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:180px;padding:6px;border-radius:12px;border:1px solid var(--border);background:#fff;box-shadow:0 8px 24px -8px #0f172a2e}.hero-profile-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;border:0;border-radius:8px;background:transparent;font:inherit;font-size:13px;font-weight:600;color:var(--text);text-align:left;cursor:pointer;transition:background .12s ease,color .12s ease}.hero-profile-menu__item i{font-size:14px;color:var(--muted-2);width:1.1em;text-align:center}.hero-profile-menu__item:hover:not(:disabled){background:var(--primary-soft, #eef0ff);color:var(--primary)}.hero-profile-menu__item:hover:not(:disabled) i{color:var(--primary)}.hero-profile-menu__item--danger:hover:not(:disabled){color:var(--rose);background:var(--rose-soft)}.hero-profile-menu__item--danger:hover:not(:disabled) i{color:var(--rose)}.about-dialog{max-width:360px}.about-dialog__head{margin-bottom:8px}.about-dialog__title{display:flex;align-items:center;gap:8px;margin:0;font-size:18px;font-weight:700}.about-dialog__title i{color:var(--primary)}.about-dialog__meta{margin:0 0 16px;padding:0}.about-dialog__row{display:grid;grid-template-columns:96px 1fr;gap:8px 12px;padding:10px 0;border-bottom:1px solid var(--border)}.about-dialog__row:last-child{border-bottom:0}.about-dialog__row dt{margin:0;font-size:13px;font-weight:600;color:var(--muted-2)}.about-dialog__row dd{margin:0;font-size:14px;font-weight:600;color:var(--text)}.about-dialog__row dd code{font-size:13px;font-weight:600;padding:2px 8px;border-radius:6px;background:var(--primary-soft, #eef0ff);color:var(--primary)}@media(max-width:640px){.hero-title--merged .emoji{font-size:26px}.hero-merged-meta{font-size:11px}.hero-merged-stat{font-size:10.5px;padding:2px 7px}}.hero-datetime{display:flex;flex-wrap:wrap;align-items:center;flex-shrink:0}.hero-datetime-strip{display:inline-flex;align-items:stretch;flex-wrap:nowrap;max-width:100%;min-height:34px;border:1px solid var(--border);border-radius:8px;background:linear-gradient(180deg,#fff,#f1f5f9);box-shadow:0 1px 1px #0f172a0d}.hero-datetime-strip__block{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;min-width:0}.hero-datetime-strip__block>.pi{font-size:12px;color:var(--primary);opacity:.82;flex-shrink:0}.hero-datetime-strip__block--date{flex-wrap:wrap;row-gap:0}.hero-datetime-strip__wd{font-size:10px;font-weight:700;color:var(--muted);text-transform:capitalize;letter-spacing:.02em;line-height:1.15;max-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hero-datetime-strip__date{font-size:12px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--text);letter-spacing:.02em;white-space:nowrap}.hero-datetime-strip__divider{align-self:stretch;width:1px;margin:5px 0;background:#94a3b866;flex-shrink:0}.hero-datetime-strip__clock{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:.04em;color:var(--primary);line-height:1;white-space:nowrap}.hero-datetime-strip__block--time{padding-right:10px}.hero-datetime-strip__block--time>.pi{font-size:11px}@media(max-width:520px){.hero-inner{flex-direction:column;align-items:stretch}.hero-datetime{width:100%;justify-content:flex-start}.hero-datetime-strip{flex:1 1 auto;width:100%;justify-content:space-between}.hero-notification__composer-row{flex-direction:column;align-items:stretch}.hero-notification__send,.hero-notification__cancel{width:100%;justify-content:center}}.hero-notification__bg-icon{position:absolute;pointer-events:none;color:var(--primary);opacity:.13;line-height:1}.hero-notification__bg-icon i{display:block}.hero-notification__bg-icon--bell{top:8%;left:6%;font-size:28px;transform:rotate(-18deg)}.hero-notification__bg-icon--megaphone{top:10%;right:8%;font-size:24px;transform:rotate(12deg)}.hero-notification__bg-icon--comments{bottom:14%;right:10%;font-size:22px;transform:rotate(-8deg)}.hero-notification__bg-icon--send{bottom:10%;left:50%;font-size:20px;transform:rotate(-22deg)}.hero-notification__bg-icon--star{bottom:16%;left:4%;font-size:18px;transform:rotate(20deg)}.hero-notification__bg-icon--bolt{top:46%;right:2%;font-size:20px;transform:rotate(-5deg)}.hero-notification__bg-icon--heart{top:38%;left:14%;font-size:17px;transform:rotate(15deg)}.hero-notification__bg-icon--info{top:4%;left:50%;font-size:18px;transform:rotate(0)}.hero-notification__bg-icon--check{bottom:24%;left:35%;font-size:17px;transform:rotate(8deg)}.hero-notification__bg-icon--eye{top:14%;left:28%;font-size:18px;transform:rotate(-10deg)}.hero-notification{flex:1 1 100%;display:flex;flex-direction:column;gap:10px;margin-top:2px;padding:10px 12px;border:1px solid rgba(148,163,184,.28);border-radius:14px;background:#fff;position:relative;overflow:hidden}.hero-notification__head{display:flex;align-items:center;justify-content:space-between;gap:10px;position:relative}.hero-notification__head-actions{display:inline-flex;align-items:center;gap:6px}.hero-notification__composer-label{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--muted)}.hero-notification__composer-label i{color:var(--primary);font-size:12px}.hero-notification__unread-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--primary);color:#fff;font-size:10px;font-weight:700;line-height:1}.hero-notification__add{width:28px;height:28px;border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.25);border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.hero-notification__add i{font-size:11px}.hero-notification__add:hover:not(:disabled){background:var(--primary);border-color:var(--primary);color:#fff}.hero-notification__mark-all{height:26px;border:none;border-radius:0;background:transparent;color:var(--primary);font-size:11px;font-weight:600;padding:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:4px}.hero-notification__mark-all i{font-size:10px}.hero-notification__mark-all:hover:not(:disabled){opacity:.75}.hero-notification__mark-all:disabled{color:var(--muted-2);cursor:default}.hero-notification__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;position:relative}.hero-notification__item{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;border-radius:10px;background:#f8fafc;border:1px solid #e2e8f0;position:relative;transition:border-color .18s ease,transform .18s ease}@media(hover:hover){.hero-notification__item:hover{border-color:#c7d2fe;transform:translateY(-1px)}}.hero-notification__avatar{width:34px;height:34px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);font-size:11px;font-weight:700;letter-spacing:.5px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification__body{flex:1;min-width:0;padding-right:52px}.hero-notification__body-head{display:flex;align-items:center;gap:6px;margin-bottom:3px}.hero-notification__author{font-size:12px;font-weight:700;color:var(--text)}.hero-notification__content{margin:0;font-size:13.5px;line-height:1.5;color:var(--text);white-space:pre-wrap;word-break:break-word}.hero-notification__created{display:block;margin-top:4px;font-size:11px;color:#94a3b8;font-variant-numeric:tabular-nums}.hero-notification__item-actions{position:absolute;top:7px;right:8px;display:inline-flex;align-items:center;gap:4px}.hero-notification__ack-top{width:24px;height:24px;border:none;border-radius:50%;background:#eff6ff;color:#1d4ed8;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s ease,color .15s ease,transform .15s ease}.hero-notification__ack-top i{font-size:10px}.hero-notification__ack-top:active:not(:disabled){transform:scale(.96)}.hero-notification__ack-top:hover:not(:disabled){background:#dbeafe;color:#1e40af}.hero-notification__delete{width:24px;height:24px;border:none;border-radius:50%;background:transparent;color:#94a3b8;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.hero-notification__delete i{font-size:10px}.hero-notification__delete:hover:not(:disabled){background:#fee2e2;color:#b91c1c}.hero-notification__empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 8px;position:relative}.hero-notification__empty-icon{font-size:28px;color:var(--primary);opacity:.45;margin-bottom:4px}.hero-notification__empty-title{font-size:13px;font-weight:600;color:var(--muted)}.hero-notification__empty-sub{font-size:11px;color:var(--muted-2);text-align:center}.hero-notification__more{align-self:flex-start;border:none;background:transparent;color:var(--primary);font-size:12px;font-weight:700;cursor:pointer;padding:0;font-family:inherit;position:relative}.hero-notification__more:hover{opacity:.75}.hero-notification__composer-row{display:flex;align-items:stretch;gap:8px;min-width:0}.hero-notification__composer-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid #dbe4f3;border-radius:12px;background:#f8fbff}.hero-notification__input-wrap{flex:1;min-width:0;border-radius:11px;border:1.5px solid #cfd8e6;background:#fff;transition:border-color .15s ease;position:relative}.hero-notification__composer-row:focus-within .hero-notification__input-wrap{border-color:var(--primary)}.hero-notification__input{width:100%;min-width:0;min-height:100px;border:0;border-radius:11px;padding:10px 12px 32px;font-size:13px;line-height:1.45;color:var(--text);background:transparent;font-family:inherit;resize:vertical}.hero-notification__input:focus{outline:none}.hero-notification__char-ring{position:absolute;bottom:8px;right:10px;display:flex;align-items:center;gap:4px;pointer-events:none}.hero-notification__char-ring svg{width:22px;height:22px;transform:rotate(-90deg)}.hero-notification__char-ring-track{fill:none;stroke:#e2e8f0;stroke-width:3}.hero-notification__char-ring-fill{fill:none;stroke:var(--primary);stroke-width:3;stroke-dasharray:94 94;stroke-linecap:round;transition:stroke-dashoffset .2s ease}.hero-notification__char-count{font-size:10px;color:var(--muted-2);font-variant-numeric:tabular-nums}.hero-notification__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.hero-notification__send,.hero-notification__cancel{height:36px;border-radius:10px;min-width:86px;padding:0 14px;font-size:12px;font-weight:700}.hero-notification__send{background:var(--primary);color:#fff}.hero-notification__send:not(:disabled):hover{background:var(--primary-2)}.hero-notification__cancel{min-width:72px}.hero-notification__error{margin:0;font-size:12px;color:#b91c1c;position:relative}.hero-notification-dialog{max-width:min(520px,100vw - 24px);overflow:hidden}.hero-notification-dialog .hero-notification__composer-row{align-items:stretch;gap:10px}.hero-notification-dialog .hero-notification__input-wrap{min-height:110px}.hero-notification-dialog__head{margin-bottom:14px}.hero-notification-dialog__title-group{display:flex;align-items:flex-start;gap:10px}.hero-notification-dialog__icon-wrap{width:34px;height:34px;border-radius:10px;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification-dialog__icon-wrap i{font-size:14px}.hero-notification-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-dialog__subtitle{margin:2px 0 0;font-size:11px;color:var(--muted-2)}.hero-notification-list-dialog{max-width:min(620px,100vw - 24px)}.hero-notification-list-dialog__head{margin-bottom:10px;align-items:flex-start}.hero-notification-list-dialog__head-actions{display:inline-flex;align-items:center;gap:8px;flex-shrink:0}.hero-notification-list-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-list-dialog__body{max-height:min(60vh,480px);overflow-y:auto;padding-right:2px}.ack-all-confirm-dialog{max-width:min(380px,100vw - 24px);text-align:center;padding:28px 24px 24px;display:flex;flex-direction:column;align-items:center;gap:10px;overflow:hidden}.ack-all-confirm-dialog__icon-wrap{width:52px;height:52px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.2);color:var(--primary);display:flex;align-items:center;justify-content:center;margin-bottom:2px}.ack-all-confirm-dialog__icon-wrap i{font-size:22px}.ack-all-confirm-dialog__title{margin:0;font-size:16px;font-weight:800;color:var(--text)}.ack-all-confirm-dialog__desc{margin:0;font-size:13px;color:var(--muted);line-height:1.55;max-width:280px}.ack-all-confirm-dialog__actions{display:flex;gap:10px;margin-top:6px;width:100%;justify-content:center}.ack-all-confirm-dialog__actions .btn{min-width:96px;height:38px;border-radius:10px;font-size:13px;font-weight:700}.ack-all-confirm-dialog__confirm-btn{background:var(--primary);color:#fff;display:inline-flex;align-items:center;gap:6px}.ack-all-confirm-dialog__confirm-btn i{font-size:12px}.ack-all-confirm-dialog__confirm-btn:not(:disabled):hover{background:var(--primary-2)}.hero-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;align-self:center}.content{width:100%}.content .tab-pane[hidden]{display:none!important}.content .tab-pane--feeding{max-width:1100px;margin:0 auto;padding:0 20px}.content .tab-pane--documents{max-width:none;padding:0}.content .tab-pane--weight{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 5%,var(--surface, #fff)) 0%,var(--surface, #fff) min(36vh,280px),var(--surface, #fff) 100%)}.content .tab-pane--mom{max-width:1100px;margin:0 auto;padding:0 20px calc(24px + env(safe-area-inset-bottom,0px))}.content .tab-pane--medical{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 7%,var(--surface, #fff)) 0%,var(--surface, #fff) min(42vh,320px),var(--surface, #fff) 100%)}.bottom-nav{--bn-primary: #5b6cff;--bn-primary-rgb: 91, 108, 255;--bn-inactive: #b0b7c3;--bn-height: 62px;position:fixed;left:0;right:0;bottom:0;width:100%;box-sizing:border-box;z-index:90;display:flex;align-items:center;justify-content:space-between;gap:4px;min-height:var(--bn-height);padding:0 4px env(safe-area-inset-bottom,0px);background:#fffffff5;backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);border-top:1px solid rgba(0,0,0,.055);box-shadow:0 -4px 24px -4px #0f172a14}.bottom-nav .bn-side{flex:1;display:flex;align-items:center;justify-content:space-around;min-width:0}.bottom-nav .bn-fab{flex-shrink:0;width:56px;height:56px;margin-top:-20px;border-radius:999px;border:3px solid rgba(255,255,255,.96);background:var(--bn-primary);color:#fff;font-size:22px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:transform .15s ease,background .15s ease,opacity .15s ease}.bottom-nav .bn-fab:hover:not(:disabled){background:var(--primary-2, #4c5cf0);transform:translateY(-2px)}.bottom-nav .bn-fab:active:not(:disabled){transform:translateY(0) scale(.94)}.bottom-nav .bn-fab:disabled{background:var(--bn-inactive);cursor:not-allowed;opacity:.7}.bottom-nav .bn-item{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;height:var(--bn-height);padding:0 2px;background:transparent;border:none;color:var(--bn-inactive);cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:color .22s ease}.bottom-nav .bn-item .bn-icon-wrap{display:flex;align-items:center;justify-content:center;width:32px;height:30px;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.bottom-nav .bn-item .bn-icon{font-size:21px;line-height:1;transition:filter .22s ease;display:block}.bottom-nav .bn-item .bn-label{font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1;transition:font-weight .22s ease}.bottom-nav .bn-item:not(.active):hover{color:rgba(var(--bn-primary-rgb),.7)}.bottom-nav .bn-item:active .bn-icon-wrap{transform:scale(.88)}.bottom-nav .bn-item.active{color:var(--bn-primary)}.bottom-nav .bn-item.active .bn-icon-wrap{transform:translateY(-3px) scale(1.15)}.bottom-nav .bn-item.active .bn-icon{filter:drop-shadow(0 2px 8px rgba(var(--bn-primary-rgb),.4))}.bottom-nav .bn-item.active .bn-label{font-weight:700}.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px;margin-bottom:16px}.card-header{margin-bottom:16px}.card-header h2{margin:0;font-size:18px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:8px}.card-header h2 i{color:var(--primary)}.card-header p{margin:6px 0 0;color:var(--muted);font-size:13px}.eyebrow{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em}.eyebrow i{margin-right:4px}.form{display:flex;flex-direction:column;gap:14px}.field{display:flex;flex-direction:column;gap:6px}.field .label{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.02em;display:flex;align-items:center;gap:6px;flex-wrap:wrap}.field .label .label-hint{font-size:11px;font-weight:500;color:var(--muted-2);font-style:italic;text-transform:none;letter-spacing:0}.field input[type=text],.field input[type=date],.field input[type=time],.field input[type=number]{padding:12px 14px;border:1.5px solid var(--border);border-radius:10px;font-size:14px;font-family:inherit;color:var(--text);background:#fafbfc;width:100%;min-width:0;box-sizing:border-box;appearance:none;-webkit-appearance:none;transition:border-color .15s,background .15s;min-height:46px;font-variant-numeric:tabular-nums}.field input[type=text]:focus,.field input[type=date]:focus,.field input[type=time]:focus,.field input[type=number]:focus{outline:none;border-color:var(--primary);background:#fff}.field input[type=text]::placeholder,.field input[type=date]::placeholder,.field input[type=time]::placeholder,.field input[type=number]::placeholder{color:var(--muted-2)}.field input[type=number]{-moz-appearance:textfield}.field input[type=number]::-webkit-outer-spin-button,.field input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.radio-group{display:flex;gap:8px;flex-wrap:wrap}.chip{padding:9px 16px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;cursor:pointer;color:var(--text);transition:all .15s;font-family:inherit}.chip:hover{border-color:var(--primary);color:var(--primary)}.chip.active{background:var(--primary);border-color:var(--primary);color:#fff}.form-actions{display:flex;gap:10px;margin-top:4px;flex-wrap:wrap}.btn{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;border:none;border-radius:10px;font-size:14px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,color .15s,border-color .15s}.btn:disabled{opacity:.5;cursor:not-allowed}.btn.small{padding:6px 12px;font-size:12px}.btn.primary{background:var(--primary);color:#fff}.btn.primary:not(:disabled):hover{background:var(--primary-2)}.btn.ghost{background:transparent;color:var(--muted);border:1.5px solid var(--border)}.btn.ghost:hover{color:var(--text);border-color:var(--border-strong)}.btn.danger-ghost{background:transparent;color:var(--rose);border:1.5px solid var(--rose-soft)}.btn.danger-ghost:hover{background:var(--rose-soft)}.btn.icon-btn{padding:0;width:36px;height:36px;justify-content:center;background:transparent;color:var(--muted);border:1.5px solid var(--border);border-radius:10px}.btn.icon-btn i{font-size:14px}.btn.icon-btn:not(:disabled):hover{color:var(--primary);border-color:var(--primary);background:var(--primary-soft)}.btn.icon-btn .spin{animation:spin-refresh .9s linear infinite}@keyframes spin-refresh{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.age-stats{display:flex;align-items:baseline;justify-content:flex-start;gap:16px;padding:14px 16px;background:var(--primary-soft);border-radius:12px}.age-stats .stat{text-align:center}.age-stats .stat .num{font-size:26px;font-weight:700;color:var(--primary);line-height:1;font-variant-numeric:tabular-nums}.age-stats .stat .unit{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-top:3px}.age-stats .dot{color:var(--muted-2);font-size:20px}.period-card{margin-bottom:0}.period-card h2{margin:6px 0 8px;font-size:20px;font-weight:700}.period-card .period-summary{color:var(--muted);font-size:14px;margin:0 0 14px;line-height:1.55}.schedule-grid{display:grid;grid-template-columns:1fr;gap:10px}.schedule{display:flex;gap:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.schedule i{color:var(--primary);font-size:18px;margin-top:2px}.schedule .schedule-title{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px}.schedule .schedule-text{font-size:14px;color:var(--text);line-height:1.5}.compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}@media(max-width:768px){.compare-grid{grid-template-columns:1fr}}.day-card{margin-bottom:0;min-width:0;overflow:hidden}.day-card.today{border-color:#3b82f6;background:linear-gradient(180deg,#dbeafe,#fff 50%)}.day-card.yesterday{background:#fafbfc}.day-date-nav-wrap{position:relative;margin-bottom:12px}.day-date-nav-reset{position:absolute;top:-8px;right:-8px;z-index:1;width:24px;height:24px;border:1px solid var(--border);border-radius:50%;background:#fff;color:var(--muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;box-shadow:0 1px 3px #0f172a14}.day-date-nav-reset i{font-size:10px}.day-date-nav-reset:hover{background:#fee2e2;border-color:#fecaca;color:#dc2626}.day-date-nav-reset:active{background:#fee2e2;color:#dc2626}.day-date-strip{display:grid;grid-template-columns:32px 1fr 32px;gap:6px;align-items:stretch;padding:6px 8px;border-radius:10px;background:#fff;border:1px solid var(--border);touch-action:manipulation}.day-date-strip-scroller{display:flex;gap:4px;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:thin;padding:1px 2px}.day-date-strip-scroller::-webkit-scrollbar{height:4px}.day-date-strip-scroller::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px}.day-date-strip-day{flex:0 0 64px;scroll-snap-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;min-width:0;padding:6px 4px;border:1px solid transparent;border-radius:8px;background:#f8fafc;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-day:hover{background:#eef2ff;border-color:#c7d2fe}.day-date-strip-day.active{background:#dbeafe;border-color:#3b82f6;box-shadow:inset 0 0 0 1px #3b82f626}.day-date-strip-date{font-size:12px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml{font-size:11px;font-weight:600;color:#1d4ed8;font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml.empty{color:var(--muted);font-weight:500}.day-date-strip-arrow{width:32px;min-height:52px;flex-shrink:0;border:none;border-radius:8px;background:#f1f5f9;color:var(--text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-arrow i{font-size:12px}.day-date-strip-arrow:hover:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:active:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:disabled{opacity:.35;cursor:not-allowed}.day-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px}.day-head h3{margin:4px 0 0;font-size:16px;color:var(--text);font-variant-numeric:tabular-nums}.big-total{display:flex;align-items:baseline;gap:3px}.big-total .num{font-size:32px;font-weight:800;color:var(--text);line-height:1;font-variant-numeric:tabular-nums}.big-total .unit{font-size:14px;color:var(--muted);font-weight:500}.day-stats{display:flex;flex-wrap:wrap;gap:10px 16px;padding:10px 0 0;border-top:1px solid var(--border)}.day-stats>div{font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:5px}.day-stats>div i{color:var(--primary);font-size:12px}.compare-banner{margin-top:12px;padding:12px;background:var(--primary-soft);border-radius:12px;font-size:12px;color:var(--text);display:flex;flex-direction:column;gap:8px}.compare-banner strong{color:var(--primary);font-variant-numeric:tabular-nums;font-weight:700}.compare-banner .compare-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.compare-banner .compare-row:not(:last-child){padding-bottom:8px;border-bottom:1px dashed rgba(15,23,42,.1)}.compare-banner .compare-stats{display:flex;align-items:center;gap:6px;flex:1}.compare-banner .compare-stats .label{color:var(--muted);font-weight:500;font-size:11px}.compare-banner .compare-stats .count{color:var(--muted-2);font-size:11px}.compare-banner .compare-change{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;flex-shrink:0}.compare-banner .compare-change i{font-size:10px}.compare-banner .compare-change i.up{color:var(--mint)}.compare-banner .compare-change i.down{color:var(--amber)}.compare-banner .compare-change strong{font-size:12px}.compare-banner .compare-change span{color:var(--muted-2);font-weight:500}.today-logs{margin-top:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.logs-header{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}.logs-header span{display:flex;align-items:center;gap:6px}.logs-header span:before{content:\"\\1f37c\";font-size:14px}.logs-header .logs-more{text-transform:none;letter-spacing:0;background:transparent;border:none;color:var(--primary);font-size:12px;font-weight:600;padding:4px 8px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:3px;transition:background .2s ease}.logs-header .logs-more i{font-size:10px}.logs-header .logs-more:hover{background:var(--primary-soft)}.log-list{display:flex;flex-direction:column;gap:6px;min-width:0}.log-row{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#fafbfc;border-radius:8px;font-size:13px;min-width:0;max-width:100%;transition:all .2s ease;position:relative}.log-row.low-volume .log-volume{color:#f59e0b;font-weight:900}.log-row.high-volume .log-volume{color:#10b981;font-weight:900}.log-row:hover{background:#f1f5f9;transform:translateY(-1px);box-shadow:0 2px 8px #0000000d}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.log-time{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;min-width:42px}.log-volume{font-weight:800;color:var(--primary);font-size:16px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}.log-gap{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;background:#f3f4f6;color:#6b7280;border-radius:999px;font-size:10.5px;font-weight:600;white-space:nowrap;flex-shrink:0;font-variant-numeric:tabular-nums;transition:all .2s ease}.log-gap i{font-size:9px;opacity:.7}.log-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.log-gap.feed-gap-warn i{opacity:.95;color:inherit}.log-note{color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.log-actions{margin-left:auto;display:inline-flex;align-items:center;gap:2px}.log-edit,.log-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:4px 6px;border-radius:6px;font-size:12px;transition:all .2s ease}.log-edit:hover{color:var(--primary);background:var(--primary-soft)}.log-del:hover{color:var(--rose);background:var(--rose-soft)}.empty-mini{padding:12px;text-align:center;color:var(--muted-2);font-size:13px;font-style:italic}.countdown-hero{display:flex;flex-direction:column;gap:16px;padding:18px 20px;background:#fff;border-radius:16px;border:1px solid #e6e9ff;transition:all .3s ease;margin-bottom:20px}.countdown-hero .hero-main{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px}.countdown-hero .feeding-time-card{flex:1;min-width:0;text-align:center}.countdown-hero .feeding-time-card .time-header{margin-bottom:12px;display:flex;justify-content:center}.countdown-hero .feeding-time-card .time-header .status-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;padding:6px 12px;border-radius:14px;white-space:nowrap}.countdown-hero .feeding-time-card .time-header .status-badge i{font-size:11px}.countdown-hero .feeding-time-card .time-header .status-badge.status-waiting{background:var(--primary-soft);color:var(--primary)}.countdown-hero .feeding-time-card .time-header .status-badge.status-now{background:#fee2e2;color:#dc2626;animation:pulse-gentle 2s ease-in-out infinite;font-size:11px;text-transform:none;font-weight:700}.countdown-hero .feeding-time-card .time-header .status-badge.status-late{background:var(--rose-soft);color:var(--rose)}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:42px;font-weight:800;line-height:1;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.02em;margin-bottom:4px}.countdown-hero .feeding-time-card .time-display .time-remaining{font-size:15px;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}.countdown-hero .countdown-next{display:flex;flex-direction:column;gap:8px;padding-left:16px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:0}.countdown-hero .next-item{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.countdown-hero .next-item i{color:var(--primary);font-size:13px}.countdown-hero .next-item strong{font-weight:700}.countdown-hero .next-item .predict-label,.countdown-hero .next-item .recommend-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);margin-left:4px}.countdown-hero .next-item .recommend-label{color:var(--mint)}.countdown-hero .volume-info{display:flex;flex-direction:column;gap:8px;padding-left:20px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:160px;transition:all .3s ease}.countdown-hero .volume-info .volume-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.countdown-hero .volume-info .volume-row .volume-prefix{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);flex-shrink:0}.countdown-hero .volume-info .volume-row .volume-amount{font-size:22px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1;text-align:right}.countdown-hero .volume-info .volume-row.predicted-row .volume-prefix,.countdown-hero .volume-info .volume-row.predicted-row .volume-amount{color:var(--primary)}.countdown-hero .volume-info .volume-row.recommended-row .volume-prefix,.countdown-hero .volume-info .volume-row.recommended-row .volume-amount{color:var(--mint)}.countdown-hero[data-status=waiting]{border-color:#bbf7d0;background:linear-gradient(135deg,#f7fef8,#f0fdf4)}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .predicted-time{color:#16a34a}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .time-remaining{color:#15803d}.countdown-hero[data-status=waiting] .feeding-time-card .status-badge.status-waiting{background:#dcfce7!important;color:#15803d!important;border:1px solid #86efac}.countdown-hero[data-status=waiting] .volume-info .volume-amount{color:#16a34a}.countdown-hero[data-status=waiting] .volume-info .volume-prefix{color:#15803d}.countdown-hero[data-status=late]{border-color:#fca5a5;background:linear-gradient(135deg,#fef2f2,#fee2e2)}.countdown-hero[data-status=late] .feeding-time-card .time-display .predicted-time{color:#dc2626}.countdown-hero[data-status=late] .feeding-time-card .time-display .time-remaining{color:#b91c1c}.countdown-hero[data-status=late] .feeding-time-card .status-badge.status-late{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5}.countdown-hero[data-status=late] .volume-info .volume-amount{color:#dc2626}.countdown-hero[data-status=late] .volume-info .volume-prefix{color:#b91c1c}.countdown-hero[data-status=now]{border-color:#fbbf24;background:linear-gradient(135deg,#fefce8,#fef3c7);animation:pulse-card 2s ease-in-out infinite}.countdown-hero[data-status=now] .feeding-time-card .time-display .predicted-time{color:#92400e}.countdown-hero[data-status=now] .feeding-time-card .time-display .time-remaining{color:#a16207}.countdown-hero[data-status=now] .feeding-time-card .status-badge.status-now{background:#fef3c7!important;color:#b45309!important;border:1px solid #f59e0b!important}.countdown-hero[data-status=now] .volume-info .volume-amount{color:#92400e}.countdown-hero[data-status=now] .volume-info .volume-prefix{color:#a16207}@media(max-width:540px){.countdown-hero .hero-main{flex-direction:column;align-items:stretch;gap:12px}.countdown-hero .hero-main .volume-info{border-left:none;border-top:1px dashed #d8dcff;padding-left:0;padding-top:12px;min-width:auto}.countdown-hero .hero-main .volume-info .volume-row{justify-content:space-between;padding:0 4px}.countdown-hero .hero-main .feeding-time-card .time-display .predicted-time{font-size:36px}.countdown-hero .hero-main .feeding-time-card .volume-info .volume-amount{font-size:20px}}@media(max-width:400px){.countdown-hero .feeding-time-card .status-badge{font-size:10px;padding:4px 10px}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:32px}.countdown-hero .volume-info .volume-amount{font-size:18px}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.65}}@keyframes pulse-gentle{0%,to{opacity:1}50%{opacity:.75}}@keyframes pulse-urgent{0%,to{opacity:1;text-shadow:0 0 0 rgba(239,68,68,0)}50%{opacity:.8;text-shadow:0 0 8px rgba(239,68,68,.25)}}@keyframes pulse-card{0%,to{transform:scale(1);box-shadow:0 1px 3px #0000001a,0 0 #f8717100}50%{transform:scale(1.02);box-shadow:0 4px 12px #00000026,0 0 0 2px #f871714d}}.burp-warning{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin:0 0 16px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1px solid #f59e0b;border-radius:14px;box-shadow:0 2px 8px #f59e0b33;animation:burp-pulse 2s ease-in-out infinite}.burp-warning__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:#f59e0b;border-radius:50%;color:#fff;font-size:18px;animation:burp-icon-bounce 1.5s ease-in-out infinite}.burp-warning__content{flex:1;min-width:0}.burp-warning__title{font-size:15px;font-weight:700;color:#92400e;margin-bottom:4px}.burp-warning__text{font-size:13px;color:#78350f;line-height:1.5;margin-bottom:8px}.burp-warning__text strong{font-weight:600;color:#92400e}.burp-warning__countdown{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:#ffffffb3;border-radius:8px;font-size:13px;color:#92400e;font-weight:500}.burp-warning__countdown i{font-size:14px;color:#f59e0b}.burp-warning__countdown strong{font-size:16px;font-weight:700;color:#d97706}@keyframes burp-pulse{0%,to{box-shadow:0 2px 8px #f59e0b33}50%{box-shadow:0 4px 16px #f59e0b59}}@keyframes burp-icon-bounce{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.countdown-progress{margin-top:10px}.countdown-progress .cp-track{position:relative;height:6px;background:#ffffffbf;border-radius:999px;overflow:hidden}.countdown-progress .cp-fill{height:100%;background:linear-gradient(90deg,var(--primary),var(--accent));border-radius:999px;transition:width .4s ease}.countdown-progress .cp-fill[data-status=waiting]{background:linear-gradient(90deg,#22c55e,#16a34a)}.countdown-progress .cp-fill[data-status=late]{background:linear-gradient(90deg,#f87171,#dc2626)}.countdown-progress .cp-fill[data-status=now]{background:linear-gradient(90deg,#fbbf24,#d97706)}.countdown-progress .cp-labels{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px;font-size:11.5px;color:var(--muted)}.countdown-progress .cp-labels i{font-size:10px;margin-right:2px;color:var(--muted-2)}.countdown-progress .cp-labels .muted{color:var(--muted-2)}.countdown-progress .cp-labels .cp-right{font-weight:600;color:var(--muted);white-space:nowrap}.predict-head{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;margin-bottom:16px}.predict-head .eyebrow{color:var(--primary);display:flex;align-items:center;gap:4px}.predict-head .eyebrow.sparkle i{animation:sparkle 2s ease-in-out infinite}.predict-head h2{margin:4px 0 0;font-size:20px;font-weight:700}.last-feed{display:flex;align-items:center;gap:12px;padding:12px 14px;background:#fff;border-radius:12px;margin-bottom:14px;border:1px solid #e8e9ff}.last-feed .last-feed-icon{width:38px;height:38px;border-radius:50%;background:var(--primary-soft);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.last-feed .last-feed-icon i{color:var(--primary);font-size:16px}.last-feed .last-feed-body{flex:1;min-width:0}.last-feed .last-feed-label{font-size:10px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em;margin-bottom:2px}.last-feed .last-feed-value{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.last-feed .last-feed-value strong{color:var(--primary);font-size:18px;font-weight:800}.last-feed .last-feed-value .sep{color:var(--muted-2)}.last-feed .last-feed-value .ago{color:var(--accent);font-weight:600;font-size:13px}.last-feed .last-feed-note{margin-top:4px;font-size:12px;color:var(--muted);font-style:italic;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.predict-empty{padding:14px 16px;background:#fff;border-radius:12px;color:var(--muted);font-size:13px;display:flex;align-items:center;gap:8px}.predict-empty i{color:var(--primary)}.predict-body{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:14px}@media(max-width:640px){.predict-body{grid-template-columns:1fr}}.predict-metric{background:#fff;border-radius:12px;padding:14px;text-align:center}.predict-metric .metric-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;display:flex;align-items:center;justify-content:center;gap:5px}.predict-metric .metric-label i{color:var(--primary);font-size:12px}.predict-metric .metric-value{font-size:28px;font-weight:800;line-height:1;color:var(--primary);font-variant-numeric:tabular-nums}.predict-metric .metric-value .ml-unit{font-size:14px;color:var(--muted);font-weight:500;margin-left:2px}.predict-metric .metric-sub{margin-top:6px;font-size:12px;color:var(--muted)}.reasoning{background:#fff;border-radius:12px;padding:12px 14px;margin-bottom:12px}.reasoning .reasoning-head{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;margin-bottom:6px}.reasoning .reasoning-head i{color:var(--primary);margin-right:4px}.reasoning ul{margin:0;padding-left:18px;list-style:disc}.reasoning li{font-size:13px;color:var(--text);line-height:1.55;margin:2px 0}.predict-actions{display:flex;justify-content:flex-end}.feed-list-card{margin-bottom:16px}.feed-list-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.feed-list-head h2{margin:4px 0 0;font-size:18px;font-weight:700}.feed-list-compact{display:flex;flex-direction:column;gap:6px}.feed-row{display:grid;grid-template-columns:68px 80px 1fr auto;gap:10px;align-items:center;padding:10px 12px;transition:all .2s ease;position:relative}.feed-row.low-volume .feed-row-volume strong{color:#f59e0b;font-weight:900}.feed-row.high-volume .feed-row-volume strong{color:#10b981;font-weight:900}.feed-row{background:#fafbfc;border-radius:10px;border:1px solid var(--border)}.feed-row .feed-row-time{display:flex;flex-direction:column;line-height:1.2;font-variant-numeric:tabular-nums;gap:3px}.feed-row .feed-row-time .time{font-weight:700;color:var(--text);font-size:14px}.feed-row .feed-row-time .date{font-size:11px;color:var(--muted-2)}.feed-row .feed-row-time .feed-row-gap{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;font-weight:600;color:#6b7280;background:#f3f4f6;padding:1px 6px;border-radius:999px;width:fit-content;white-space:nowrap;font-variant-numeric:tabular-nums}.feed-row .feed-row-time .feed-row-gap i{font-size:8px;opacity:.7}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn i{opacity:.95;color:inherit}.feed-row .feed-row-volume{font-variant-numeric:tabular-nums}.feed-row .feed-row-volume strong{color:var(--primary);font-size:19px;font-weight:800;letter-spacing:-.01em}.feed-row .feed-row-volume .u{font-size:11px;color:var(--muted);margin-left:2px}.feed-row .feed-row-note{font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.feed-row .feed-row-note.muted{color:var(--muted-2);font-style:italic}.feed-row .feed-row-actions{display:inline-flex;align-items:center;gap:2px;margin-left:auto}.feed-row .feed-row-edit,.feed-row .feed-row-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:6px 8px;border-radius:6px;font-size:13px}.feed-row .feed-row-edit:hover{color:var(--primary, #5b6cff);background:#5b6cff1a}.feed-row .feed-row-del:hover{color:var(--rose);background:var(--rose-soft)}@media(max-width:480px){.feed-row{grid-template-columns:58px 70px 1fr auto;gap:8px;padding:8px 10px}.feed-row .feed-row-time .time{font-size:13px}}.dialog-wide{max-width:720px;display:flex;flex-direction:column;padding:0;overflow:hidden}@media(max-width:640px){.dialog-wide{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 72px);border-radius:14px}}.dialog-wide .dialog-head{position:sticky;top:0;z-index:5;background:var(--surface);padding:16px 20px;margin:0;border-bottom:1px solid var(--border);-webkit-backdrop-filter:saturate(1.2) blur(6px);backdrop-filter:saturate(1.2) blur(6px)}@media(max-width:640px){.dialog-wide .dialog-head{padding:12px 14px}}.dialog-wide .dialog-close{width:40px;height:40px;font-size:16px;background:#f1f3f7;color:var(--text)}.dialog-wide .dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:18px 20px 24px;overflow-y:auto;flex:1;min-height:0}@media(max-width:640px){.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:14px 14px 20px}}.history-body{display:flex;flex-direction:column;gap:18px}.history-paging-hint{margin:0;font-size:12px;line-height:1.45}.history-load-more{display:grid;justify-items:center;width:100%;box-sizing:border-box;padding:8px 0 12px}.history-load-more .btn{width:auto;max-width:min(320px,100%);min-width:0;margin-inline:auto;justify-content:center}.tips-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;background:#f1f3f7;border-radius:12px;margin-bottom:16px}.nav-arrow{width:40px;height:40px;border-radius:50%;border:none;background:#fff;color:var(--primary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px;transition:all .15s;flex-shrink:0}.nav-arrow:hover:not(:disabled){background:var(--primary);color:#fff;transform:scale(1.05)}.nav-arrow:active:not(:disabled){transform:scale(.95)}.nav-arrow:disabled{opacity:.35;cursor:not-allowed}.tips-now-btn{flex:1;padding:9px 16px;border:none;background:transparent;border-radius:999px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s}.tips-now-btn i{font-size:12px}.tips-now-btn:hover{color:var(--primary);background:#fff}.tips-now-btn.active{background:var(--primary);color:#fff;pointer-events:none}.tips-dialog-body .period-summary{font-size:14px;color:var(--muted);line-height:1.6;margin:0 0 14px}.tips-dialog-body .schedule-grid{grid-template-columns:repeat(2,1fr);margin-bottom:16px}@media(max-width:560px){.tips-dialog-body .schedule-grid{grid-template-columns:1fr}}.dialog-tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.history-group .history-group-head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:var(--primary-soft);border-radius:10px;margin-bottom:8px}.history-group .history-group-head .h-date{font-size:14px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-day{font-size:11px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.05em}.history-group .history-group-head .h-total{font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-total strong{color:var(--primary);font-size:20px;font-weight:800}.history-group .history-group-head .h-total .h-count{color:var(--muted);font-size:12px;font-weight:500;margin-left:4px}.history-group .history-group-head .h-total .h-avg{color:var(--primary);font-size:12px;font-weight:600;margin-left:4px;font-variant-numeric:tabular-nums}.history-group .history-group-rows{display:flex;flex-direction:column;gap:4px}.tips-section-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;padding:4px 4px 0}.tips-section-head .eyebrow i{margin-right:4px}.tips-section-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text)}.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}.filter-chip{padding:7px 14px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;font-weight:500;cursor:pointer;color:var(--muted);display:inline-flex;align-items:center;gap:5px;transition:all .15s;font-family:inherit}.filter-chip i{font-size:12px}.filter-chip:hover{border-color:var(--cat-color, var(--primary));color:var(--cat-color, var(--primary))}.filter-chip.active{background:var(--cat-color, var(--primary));border-color:var(--cat-color, var(--primary));color:#fff}.tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-bottom:20px}.tip-card{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--cat-color, var(--primary));border-radius:12px;padding:16px;transition:transform .15s}.tip-card:hover{transform:translateY(-2px)}.tip-card .tip-head{margin-bottom:8px}.tip-card .tip-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;background:var(--cat-color, var(--primary));color:#fff;font-size:11px;font-weight:600;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.tip-card .tip-pill i{font-size:10px}.tip-card .tip-title{margin:6px 0 8px;font-size:15px;font-weight:700;line-height:1.35;color:var(--text)}.tip-card .tip-body{margin:0;font-size:13px;line-height:1.55;color:var(--muted)}.empty{grid-column:1/-1;padding:40px 20px;text-align:center;color:var(--muted-2)}.empty i{font-size:32px;margin-bottom:8px;display:block}.upcoming{margin-bottom:24px}.upcoming h3{margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}.upcoming h3 i{color:var(--primary)}.upcoming-grid{display:flex;gap:8px;flex-wrap:wrap}.upcoming-item{padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:10px;font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:6px}.upcoming-item i{color:var(--primary);font-size:12px}.disclaimer{padding:16px;background:var(--amber-soft);border-radius:12px;color:#78350f;font-size:13px;line-height:1.5;margin-bottom:24px;display:flex;gap:8px;align-items:flex-start}.disclaimer i{color:var(--amber);margin-top:2px}.toast{position:fixed;bottom:100px;left:50%;transform:translate(-50%);z-index:90;padding:12px 18px;border-radius:12px;font-size:14px;font-weight:500;box-shadow:0 6px 24px #0f172a1f;display:flex;align-items:center;gap:8px;animation:toastIn .3s cubic-bezier(.4,0,.2,1)}.toast.success{background:var(--mint);color:#fff}.toast.error{background:var(--rose);color:#fff}@keyframes toastIn{0%{transform:translate(-50%,20px);opacity:0}to{transform:translate(-50%);opacity:1}}.fab{position:fixed;bottom:calc(80px + env(safe-area-inset-bottom,0px));right:20px;z-index:96;display:inline-flex;align-items:center;gap:10px;padding:14px 22px 14px 18px;background:var(--primary);color:#fff;border:none;border-radius:999px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;box-shadow:0 8px 24px #5b6cff73;transition:transform .15s,box-shadow .15s,background .15s}.fab i{font-size:18px;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:#ffffff2e}.fab .fab-label{white-space:nowrap}.fab:hover{background:var(--primary-2);transform:translateY(-2px);box-shadow:0 12px 28px #5b6cff8c}.fab:active{transform:translateY(0)}@media(max-width:480px){.fab{padding:14px}.fab .fab-label{display:none}.fab i{font-size:22px;width:28px;height:28px}}.dialog-overlay{position:fixed;inset:0;background:#0f172a8c;z-index:110;display:flex;align-items:center;justify-content:center;padding:20px;animation:overlayIn .2s ease-out}.dialog-overlay.dialog-overlay--front{z-index:400}.dialog-overlay.dialog-overlay--event-reminder{z-index:410}@media(max-width:640px){.dialog-overlay{padding:calc(env(safe-area-inset-top,0px) + 16px) 12px calc(env(safe-area-inset-bottom,0px) + 16px);align-items:flex-start}}@keyframes overlayIn{0%{opacity:0}to{opacity:1}}.dialog{background:var(--surface);border-radius:20px;width:100%;max-width:560px;max-height:min(100vh - 40px,100dvh - 40px);overflow-y:auto;padding:24px;animation:dialogIn .25s cubic-bezier(.4,0,.2,1);box-shadow:0 20px 60px #0f172a40}@media(max-width:640px){.dialog{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 32px);border-radius:16px}}@keyframes dialogIn{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:18px}.dialog-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}.dialog-head h2 i{color:var(--primary)}.dialog-close{background:#f1f3f7;border:none;width:36px;height:36px;border-radius:50%;cursor:pointer;color:var(--muted);font-size:14px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s,color .15s}.dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.log-form{display:flex;flex-direction:column;gap:14px}.log-volume-field{margin:0}.log-volume-field .log-volume-input-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 10px;min-width:0}.log-volume-field .log-volume-input-row>input[type=number]{flex:0 0 118px;width:118px;max-width:min(118px,100%);min-width:0}.log-volume-field .predict-hint--inline{flex:1 1 auto;min-width:0;margin:0;padding:6px 10px;gap:6px;font-size:12px;border-radius:8px}.log-volume-field .predict-hint--inline>i{font-size:13px;flex-shrink:0}.log-volume-field .predict-hint--inline .hint-text{flex:1 1 auto;min-width:0;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-volume-field .predict-hint--inline .btn.small{flex-shrink:0;padding:4px 8px;font-size:11px}@media(max-width:400px){.log-volume-field .log-volume-input-row>input[type=number]{flex:1 1 100px;width:100%;max-width:none}.log-volume-field .predict-hint--inline{flex:1 1 100%;white-space:normal}.log-volume-field .predict-hint--inline .hint-text{white-space:normal;overflow:visible;text-overflow:unset}}.edit-date-banner{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 14px;background:linear-gradient(135deg,#eef2ff,#f5f3ff);border:1px solid #dbe1ff;border-radius:12px;font-size:13px;color:var(--text)}.edit-date-banner>i.pi-calendar{color:var(--primary, #5b6cff);font-size:15px}.edit-date-banner .edb-label{color:var(--muted, #6b7280);font-weight:500}.edit-date-banner .edb-value{font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.edit-date-banner .edb-lock{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:50%;background:#ffffffe0;border:1px solid #e2e8f0;color:var(--muted, #64748b)}.edit-date-banner .edb-lock i{font-size:14px;line-height:1}.log-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.log-grid .field.full{grid-column:1/-1}.log-grid.log-grid--date-time{grid-template-columns:repeat(2,minmax(0,1fr))}@media(max-width:560px){.log-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:380px){.log-grid{grid-template-columns:1fr}}.quick-volumes{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.quick-volumes .quick-label{font-size:12px;color:var(--muted);font-weight:600;margin-right:4px}.quick-chip{padding:7px 13px;background:#f1f3f7;border:none;border-radius:999px;font-size:12px;font-weight:600;cursor:pointer;color:var(--muted);transition:all .15s;font-family:inherit}.quick-chip:hover{background:var(--primary-soft);color:var(--primary)}.quick-chip.now-chip{background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;gap:4px}.quick-chip.now-chip:hover{background:var(--primary);color:#fff}.quick-chip.formula-chip{display:inline-flex;align-items:center;gap:5px;background:#fff7ed;color:#c2410c;border:1px solid #fed7aa}.quick-chip.formula-chip i{font-size:11px;opacity:.9}.quick-chip.formula-chip:hover{background:#ffedd5;color:#9a3412;border-color:#fdba74}.quick-chip.formula-chip.active{background:#c2410c;color:#fff;border-color:#c2410c;box-shadow:0 2px 8px #c2410c40}.quick-chip.formula-chip.active:hover{background:#9a3412;border-color:#9a3412;color:#fff}.bottle-prep{background:#f0f9ff;border:1px solid #bae6fd;border-radius:12px;padding:8px 12px;margin-bottom:16px;display:flex;flex-direction:column;gap:8px;transition:background .2s,border-color .2s}.bottle-prep:has(.bp-warning){background:#fffbeb;border-color:#fde68a}.bottle-prep:has(.bp-warning) .bp-info>i{color:#f59e0b}.bottle-prep:has(.bp-warning) .bp-info strong{color:#d97706}.bottle-prep:has(.bp-warning) .bp-prep-user{color:#92400e;background:#fbbf246b}.bottle-prep:has(.bp-warning) .bp-prep-time{color:#c2410c;background:linear-gradient(180deg,#fff7ed,#ffedd5);border-color:#fb923c73}.bottle-prep:has(.bp-form){align-items:stretch;padding:10px 12px 12px}.bottle-prep .bp-warning{display:flex;align-items:center;justify-content:space-between;gap:10px;animation:slideDown .2s ease;flex-wrap:wrap}.bottle-prep .bp-warning__content{display:flex;align-items:center;gap:8px;font-size:13px;flex:1;min-width:0}.bottle-prep .bp-warning__content i{font-size:16px;flex-shrink:0}.bottle-prep .bp-warning__content strong{font-weight:700}.bottle-prep .bp-warning--expired{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:8px 10px;color:#991b1b}.bottle-prep .bp-warning--expired i{color:#dc2626}.bottle-prep .bp-warning--soon{background:#fefce8;border:1px solid #fde68a;border-radius:8px;padding:8px 10px;color:#92400e}.bottle-prep .bp-warning--soon i{color:#f59e0b}.bottle-prep .bp-warning__btn{flex-shrink:0;display:inline-flex;align-items:center;gap:5px;padding:7px 12px;font-size:12px;font-weight:700;font-family:inherit;color:#fff;background:linear-gradient(180deg,#f59e0b,#d97706);border:none;border-radius:8px;cursor:pointer;box-shadow:0 2px 4px #f59e0b40;transition:all .15s ease}.bottle-prep .bp-warning__btn i{font-size:12px}.bottle-prep .bp-warning__btn:hover{background:linear-gradient(180deg,#fbbf24,#f59e0b);transform:translateY(-1px);box-shadow:0 4px 8px #f59e0b59}.bottle-prep .bp-warning__btn:active{transform:translateY(0)}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.bottle-prep .bp-info-row{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%}.bottle-prep:not(:has(.bp-warning)):not(:has(.bp-form)){flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.bottle-prep .bp-info{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--text);flex:1;min-width:0}.bottle-prep .bp-info>i{color:#0ea5e9;font-size:16px;margin-top:2px;flex-shrink:0}.bottle-prep .bp-info .bp-text{line-height:1.45;min-width:0}.bottle-prep .bp-info .bp-useby{display:inline-block;margin-top:2px;font-size:13px}.bottle-prep .bp-info strong{color:#0284c7;font-weight:800;font-variant-numeric:tabular-nums}.bottle-prep .bp-info .muted{color:var(--muted);font-size:13px}.bottle-prep .bp-actions{display:flex;gap:4px}.bottle-prep .bp-add{flex:1;background:transparent;border:none;padding:6px 4px;font-size:13px;font-weight:600;color:#0284c7;cursor:pointer;display:flex;align-items:center;gap:8px;font-family:inherit;text-align:left}.bottle-prep .bp-add>i{font-size:15px}.bottle-prep .bp-add:hover{color:#0369a1}.bottle-prep .bp-form{display:flex;flex-direction:column;gap:8px;width:100%}.bottle-prep .bp-form-head{display:flex;align-items:center;gap:6px}.bottle-prep .bp-form-head>i{color:#0ea5e9;font-size:15px}.bottle-prep .bp-form-head .bp-label{font-size:13px;color:var(--muted);font-weight:600;white-space:nowrap}.bottle-prep .bp-input-wrap{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid #7dd3fc;border-radius:10px;padding:3px;transition:border-color .15s,box-shadow .15s}.bottle-prep .bp-input-wrap:focus-within{border-color:#0ea5e9;box-shadow:0 0 0 3px #0ea5e926}.bottle-prep .bp-input-wrap .bp-step{width:30px;height:30px;flex-shrink:0;border-radius:8px;border:none;background:#e0f2fe;color:#0369a1;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;transition:all .12s}.bottle-prep .bp-input-wrap .bp-step:hover{background:#bae6fd;color:#0284c7}.bottle-prep .bp-input-wrap .bp-step:active{transform:scale(.94)}.bottle-prep .bp-input-wrap .bp-step:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.bottle-prep .bp-input-wrap .bp-input{flex:1;min-width:60px;padding:4px 6px;font-size:18px;font-weight:800;border:none;background:transparent;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706;text-align:center;letter-spacing:-.01em}.bottle-prep .bp-input-wrap .bp-input:focus{outline:none}.bottle-prep .bp-input-wrap .bp-input:disabled{opacity:.55;cursor:not-allowed}.bottle-prep .bp-input-wrap .bp-unit{font-size:12px;color:var(--muted);font-weight:600;padding-right:4px}.bottle-prep .bp-quick{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.bottle-prep .bp-quick .bp-quick-label{font-size:11px;color:var(--muted);font-weight:600;margin-right:2px}.bottle-prep .bp-quick .bp-quick-chip{padding:4px 9px;background:#f0f9ff;border:1px solid #7dd3fc;border-radius:999px;font-size:11.5px;font-weight:700;color:#0369a1;cursor:pointer;font-family:inherit;font-variant-numeric:tabular-nums;transition:all .12s}.bottle-prep .bp-quick .bp-quick-chip:hover{background:#e0f2fe;border-color:#38bdf8;color:#0284c7}.bottle-prep .bp-quick .bp-quick-chip:active{transform:scale(.95)}.bottle-prep .bp-quick .bp-quick-chip:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-form-actions{display:flex;gap:6px;justify-content:flex-end}.bottle-prep .bp-btn{width:32px;height:32px;border-radius:8px;border:none;background:#fff;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:all .15s;flex-shrink:0}.bottle-prep .bp-btn:hover:not(:disabled){background:#e0f2fe;color:#0284c7}.bottle-prep .bp-btn:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-btn.primary{background:#0ea5e9;color:#fff}.bottle-prep .bp-btn.primary:hover:not(:disabled){background:#0284c7}.bottle-prep .bp-btn.danger:hover{background:var(--rose-soft);color:var(--rose)}.calc-block{border:1px solid #fde7c2;background:#fffaf0;border-radius:12px;overflow:hidden}.calc-block .calc-head{padding:10px 14px 0}.calc-block .prep-info{display:flex;align-items:flex-start;gap:6px;font-size:13px;color:var(--text)}.calc-block .prep-info>i{color:#f59e0b;font-size:14px;margin-top:2px;flex-shrink:0}.calc-block .prep-info .prep-info-text{line-height:1.45;min-width:0}.calc-block .prep-info strong{color:#d97706;font-weight:800;font-variant-numeric:tabular-nums}.calc-block .prep-info .muted{color:var(--muted-2);font-size:12px;display:inline-block;margin-top:2px}.calc-block .calc-body{padding:8px 14px 14px;display:flex;flex-direction:column;gap:10px}.calc-block .field.mini.remaining-input{position:relative;margin:0;display:flex;flex-direction:column}.calc-block .field.mini.remaining-input .label{font-size:11px;font-weight:600;color:var(--muted);margin-bottom:4px}.calc-block .field.mini.remaining-input input{width:100%;padding:10px 36px 10px 12px;font-size:16px;font-weight:700;border:1px solid #fadf9e;border-radius:8px;background:#fff;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706}.calc-block .field.mini.remaining-input input:focus{outline:none;border-color:#f59e0b;box-shadow:0 0 0 3px #f59e0b26}.calc-block .field.mini.remaining-input .suffix{position:absolute;right:12px;top:50%;transform:translateY(4px);font-size:12px;color:var(--muted-2);pointer-events:none}.calc-block .calc-result-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.calc-block .calc-result{display:flex;align-items:baseline;gap:4px;font-variant-numeric:tabular-nums}.calc-block .calc-result .calc-label{font-size:13px;color:var(--muted);font-weight:600}.calc-block .calc-result strong{font-size:22px;font-weight:800;color:var(--primary)}.calc-block .calc-result .u{font-size:12px;color:var(--primary);margin-left:2px}.calc-block .calc-result .muted{color:var(--muted-2);font-size:18px}.calc-block .calc-apply{flex-shrink:0}.calc-block .calc-apply:disabled{background:#e5e7eb;color:var(--muted-2);cursor:not-allowed;box-shadow:none}.predict-hint{display:flex;align-items:center;gap:10px;padding:10px 14px;background:linear-gradient(135deg,var(--primary-soft) 0%,var(--accent-soft) 100%);border-radius:10px;font-size:13px}.predict-hint>i{color:var(--primary);font-size:16px}.predict-hint .hint-text{flex:1;color:var(--text)}.predict-hint .hint-text strong{color:var(--primary);font-weight:700}.predict-hint.predict-hint--inline .hint-text strong{font-weight:800;font-variant-numeric:tabular-nums}.sync-error{padding:10px 14px;background:var(--rose-soft);color:#991b1b;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px}.sync-success{padding:10px 14px;background:var(--mint-soft);color:#166534;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px;animation:fadeIn .2s ease}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}.feeding-settings-dialog .feeding-settings-hint{margin:0 0 14px;font-size:13px;line-height:1.45}.feeding-settings-dialog .feeding-settings-hint code{font-size:11px;padding:1px 5px;border-radius:4px;background:#f1f5f9}.feeding-settings-dialog .feeding-settings-grid{grid-template-columns:1fr;gap:16px}.feeding-settings-dialog .field-hint{display:block;margin-top:8px;font-size:12px;line-height:1.4;padding:8px 12px;background:#f8fafc;border-radius:8px;border-left:3px solid #e2e8f0}.feeding-settings-dialog .label{position:relative}.feeding-settings-dialog .label code{font-size:11px;font-weight:600;color:var(--primary);background:var(--primary-soft);padding:2px 6px;border-radius:4px}.feeding-settings-dialog .label .notification-badge{display:inline-flex;align-items:center;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#fff;background:linear-gradient(135deg,#10b981,#059669);padding:2px 6px;border-radius:10px;margin-left:6px;box-shadow:0 1px 2px #10b98133;animation:gentle-pulse 2s ease-in-out infinite}.feeding-settings-dialog .notification-setting-field{position:relative}.feeding-settings-dialog .notification-setting-field input{border:1.5px solid #10b981;background:linear-gradient(to right,#fff,#f0fdf4)}.feeding-settings-dialog .notification-setting-field input:focus{border-color:#059669;box-shadow:0 0 0 3px #10b9811a;background:#fff}.feeding-settings-dialog .notification-setting-field input::placeholder{color:#6b7280;opacity:.7}.feeding-settings-dialog .notification-setting-field .field-hint{border-left-color:#10b981;background:linear-gradient(to right,#f0fdf4,#f8fafc)}@keyframes gentle-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.85;transform:scale(1.02)}}.log-time--with-badge,.feed-row-time .time--with-badge{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.log-merge-badge,.feed-merge-badge{font:inherit;font-size:10px;font-weight:700;padding:2px 7px;border-radius:999px;border:1px solid rgba(99,102,241,.35);background:linear-gradient(135deg,#e0e7ff,#c7d2fe);color:#312e81;cursor:pointer;line-height:1.2;white-space:nowrap}.log-merge-badge:hover,.feed-merge-badge:hover{filter:brightness(1.03)}.log-row--clickable,.feed-row--clickable{cursor:pointer}.log-row--clickable:hover,.feed-row--clickable:hover{background:#6366f10f}.log-row--clickable:focus-visible,.feed-row--clickable:focus-visible{outline:2px solid rgba(99,102,241,.45);outline-offset:2px}.dialog-overlay.dialog-overlay--front.feed-group-detail-overlay{z-index:125}.dialog-overlay.dialog-overlay--front.feed-detail-overlay{z-index:140;animation:overlayIn .12s ease-out}.dialog-overlay.dialog-overlay--front.feed-detail-overlay .feed-detail-dialog{animation:feedDetailIn .14s ease-out}@keyframes feedDetailIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.feed-detail-dialog{--fd-accent: 99, 102, 241;--fd-soft: #eef2ff;--fd-strong: #4338ca;max-width:min(400px,96vw);padding:0;overflow:hidden;display:flex;flex-direction:column}.feed-detail-dialog[data-status=low]{--fd-accent: 245, 158, 11;--fd-soft: #fffbeb;--fd-strong: #b45309}.feed-detail-dialog[data-status=high]{--fd-accent: 244, 63, 94;--fd-soft: #fff1f2;--fd-strong: #e11d48}.feed-detail-dialog[data-status=ok]{--fd-accent: 16, 185, 129;--fd-soft: #ecfdf5;--fd-strong: #059669}.feed-detail-top{position:relative;padding:20px 20px 16px;text-align:center;background:radial-gradient(120% 80% at 50% -20%,rgba(var(--fd-accent),.22),transparent 55%),linear-gradient(180deg,var(--fd-soft) 0%,#ffffff 72%);border-bottom:1px solid rgba(var(--fd-accent),.12)}.feed-detail-close{position:absolute;top:12px;right:12px;width:36px;height:36px;border:none;border-radius:50%;background:#ffffffd9;color:#64748b;cursor:pointer;display:grid;place-items:center;box-shadow:0 2px 8px #0f172a14;transition:background .15s ease,color .15s ease}.feed-detail-close:hover{background:#fff;color:#0f172a}.feed-detail-eyebrow{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.feed-detail-metric{display:flex;align-items:flex-end;justify-content:center;gap:4px;line-height:1;margin-bottom:10px}.feed-detail-metric__value{font-size:clamp(48px,14vw,56px);font-weight:800;letter-spacing:-.04em;color:var(--fd-strong);font-variant-numeric:tabular-nums}.feed-detail-metric__unit{font-size:18px;font-weight:700;color:rgba(var(--fd-accent),.85);padding-bottom:8px}.feed-detail-status{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:999px;font-size:12px;font-weight:700;background:#fff;border:1px solid rgba(var(--fd-accent),.28);color:var(--fd-strong);box-shadow:0 2px 6px rgba(var(--fd-accent),.1)}.feed-detail-status .pi{font-size:13px}.feed-detail-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:14px}.feed-detail-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:12px;background:#ffffffeb;border:1px solid #e2e8f0;font-size:12px;font-weight:600;color:#334155;line-height:1.3;box-shadow:0 1px 4px #0f172a0a}.feed-detail-chip .pi{font-size:13px;color:#6366f1;flex-shrink:0}.feed-detail-chip em{font-style:normal;font-weight:500;color:#94a3b8;margin-left:4px}.feed-detail-chip--time .pi{color:#dc2626}.feed-detail-chip--time span{font-variant-numeric:tabular-nums;font-weight:700;color:#dc2626}.feed-detail-chip--muted{background:#f8fafc;border-color:#e2e8f0;color:#64748b;font-weight:500}.feed-detail-chip--muted .pi{color:#94a3b8}.feed-detail-body{padding:16px 18px 8px;overflow-y:auto;flex:1;min-height:0}.feed-detail-section{margin-bottom:14px}.feed-detail-section:last-child{margin-bottom:0}.feed-detail-section__title{margin:0 0 10px;font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;display:flex;align-items:center;gap:7px}.feed-detail-section__title .pi{font-size:13px;color:#6366f1}.feed-detail-cards{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.feed-detail-card{display:flex;align-items:flex-start;gap:12px;padding:12px 13px;border-radius:14px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;box-shadow:0 2px 8px #0f172a0a}.feed-detail-card--warn,.feed-detail-card--gap.feed-detail-card--warn{border-color:#f59e0b66;background:linear-gradient(180deg,#fffbeb,#fff)}.feed-detail-card--warn .feed-detail-card__icon,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card--warn .feed-detail-card__text,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__text{color:#92400e}.feed-detail-card--position{border-color:#8b5cf659;background:linear-gradient(180deg,#f5f3ff,#fff)}.feed-detail-card--average{border-color:#0ea5e959;background:linear-gradient(180deg,#f0f9ff,#fff)}.feed-detail-card--next{border-color:#10b98159;background:linear-gradient(180deg,#ecfdf5,#fff)}.feed-detail-card--gap:not(.feed-detail-card--warn){border-color:#64748b59;background:linear-gradient(180deg,#f8fafc,#fff)}.feed-detail-card__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;background:#eef2ff;color:#4f46e5;font-size:15px}.feed-detail-card__icon[data-kind=position]{background:#ede9fe;color:#7c3aed}.feed-detail-card__icon[data-kind=average]{background:#e0f2fe;color:#0284c7}.feed-detail-card__icon[data-kind=next]{background:#d1fae5;color:#059669}.feed-detail-card__icon[data-kind=gap]{background:#e2e8f0;color:#475569}.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card__body{min-width:0;flex:1}.feed-detail-card__label{display:block;font-size:11px;font-weight:800;color:#64748b;margin-bottom:3px;letter-spacing:.02em}.feed-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:#0f172a}.fdc-position__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin-bottom:8px}.fdc-position__idx{font-size:18px;font-weight:800;color:#6d28d9;letter-spacing:-.02em}.fdc-position__of{font-size:14px;font-weight:700;color:#a78bfa}.fdc-position__tag{font-size:11px;font-weight:600;color:#64748b;padding:2px 8px;border-radius:999px;background:#8b5cf61a}.fdc-progress{height:8px;border-radius:999px;background:#ede9fe;overflow:hidden;margin-bottom:10px}.fdc-progress__fill{height:100%;border-radius:inherit;background:linear-gradient(90deg,#8b5cf6,#6366f1);transition:width .2s ease}.fdc-position__stats{display:flex;flex-wrap:wrap;gap:8px}.fdc-stat{display:inline-flex;align-items:baseline;gap:4px;padding:6px 10px;border-radius:10px;font-size:12px}.fdc-stat strong{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-stat small{font-size:11px;font-weight:600;opacity:.85}.fdc-stat--violet{background:#f5f3ff;color:#5b21b6}.fdc-stat--violet strong{color:#6d28d9}.fdc-stat--indigo{background:#eef2ff;color:#4338ca}.fdc-stat--indigo strong{color:#4f46e5}.fdc-avg,.fdc-next{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}.fdc-pill{display:inline-flex;padding:4px 10px;border-radius:999px;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-pill--up{background:#dcfce7;color:#15803d}.fdc-pill--down{background:#fee2e2;color:#b91c1c}.fdc-pill--neutral{background:#f1f5f9;color:#475569}.fdc-avg__meta{font-size:12px;color:#64748b;line-height:1.4}.fdc-avg__meta strong{color:#0f172a;font-weight:700}.fdc-next__time{font-size:16px;font-weight:800;color:#dc2626;font-variant-numeric:tabular-nums}.fdc-next__vol{font-size:14px;font-weight:800;color:#059669;padding:4px 10px;border-radius:8px;background:#d1fae5}.fdc-next__gap{font-size:12px;font-weight:600;color:#64748b;display:inline-flex;align-items:center;gap:4px}.fdc-gap{display:flex;flex-direction:column;gap:10px}.fdc-gap__prev{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:10px 12px;border-radius:10px;background:#ffffffe6;border:1px solid #e2e8f0}.fdc-gap__prev-label{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;width:100%}.fdc-gap__prev-time{font-size:17px;font-weight:800;color:#0f172a;font-variant-numeric:tabular-nums}.fdc-gap__prev-vol{font-size:14px;font-weight:800;color:#475569;padding:3px 8px;border-radius:8px;background:#f1f5f9}.fdc-gap__prev-date{font-size:11px;font-weight:600;color:#94a3b8}.fdc-gap__row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;min-width:0}.fdc-gap__interval{display:inline-flex;align-items:center;gap:6px;flex:1 1 auto;min-width:0;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700;color:#475569;background:#e2e8f0;white-space:nowrap}.fdc-gap__interval .pi{font-size:13px;flex-shrink:0}.fdc-gap__interval span{overflow:hidden;text-overflow:ellipsis}.fdc-gap__interval--warn{color:#b45309;background:#fde68a;border:1px solid rgba(245,158,11,.35)}.fdc-gap__vol-diff{flex:0 0 auto;white-space:nowrap;font-size:12px;padding:5px 10px}.feed-detail-quote{margin:0;padding:12px 14px 12px 16px;border-radius:0;border-left:3px solid #c7d2fe;background:#f8fafc;font-size:14px;line-height:1.55;color:#334155;font-style:normal}.feed-detail-footer{padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fafbff}.feed-detail-cta{display:flex;gap:8px;justify-content:stretch}.feed-detail-cta .btn{flex:1;justify-content:center}.feed-detail-cta .btn.primary{flex:1.2}.feed-group-detail-dialog .feed-group-detail-summary{margin:0 0 8px;padding:0 2px;font-size:13px;line-height:1.45}.feed-group-detail-dialog .feed-group-detail-hint{margin:0 0 12px;font-size:12px;line-height:1.4;display:flex;align-items:center;gap:6px}.feed-group-detail-dialog .feed-group-detail-history{margin-bottom:4px}.feed-group-detail-dialog .feed-group-detail-rows{max-height:min(58vh,420px);overflow-y:auto;padding-right:2px}.mom-food-card{--mf-tone: 244, 114, 182;--mf-soft: #fdf2f8;--mf-ring: #fbcfe8;--mf-strong: #db2777;position:relative;overflow:hidden;background:radial-gradient(900px 260px at 0% 0%,rgba(var(--mf-tone),.14),transparent 60%),#fff;border-color:rgba(var(--mf-tone),.22);padding:18px;margin-top:16px}.mom-food-card[data-accent=rose]{--mf-tone: 244, 63, 94;--mf-soft: #fff1f2;--mf-ring: #fecdd3;--mf-strong: #e11d48}.mom-food-card[data-accent=peach]{--mf-tone: 251, 146, 60;--mf-soft: #fff7ed;--mf-ring: #fed7aa;--mf-strong: #ea580c}.mom-food-card[data-accent=mint]{--mf-tone: 16, 185, 129;--mf-soft: #ecfdf5;--mf-ring: #a7f3d0;--mf-strong: #059669}.mom-food-card[data-accent=lavender]{--mf-tone: 139, 92, 246;--mf-soft: #f5f3ff;--mf-ring: #ddd6fe;--mf-strong: #7c3aed}.mom-food-card[data-accent=amber]{--mf-tone: 245, 158, 11;--mf-soft: #fffbeb;--mf-ring: #fde68a;--mf-strong: #d97706}.mf-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.mf-head__lead{min-width:0;flex:1}.mf-head .eyebrow{display:inline-flex;align-items:center;gap:6px;color:var(--mf-strong);font-weight:700}.mf-head .eyebrow i{font-size:13px}.mf-head h2{margin:4px 0 0;font-size:18px;font-weight:800;color:var(--text);line-height:1.25}.mf-headline{margin:4px 0 0;font-size:13px;color:var(--mf-strong);font-weight:600}.mf-nav{display:inline-flex;align-items:center;gap:6px;background:var(--mf-soft);border-radius:999px;padding:4px;border:1px solid rgba(var(--mf-tone),.25);flex-shrink:0}.mf-arrow{width:30px;height:30px;border-radius:50%;border:none;background:#fff;color:var(--mf-strong);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .15s ease,background .15s ease}.mf-arrow i{font-size:12px}.mf-arrow:hover:not(:disabled){transform:scale(1.08);background:var(--mf-strong);color:#fff}.mf-arrow:disabled{opacity:.35;cursor:not-allowed}.mf-step{font-variant-numeric:tabular-nums;font-size:12px;color:var(--muted);padding:0 4px}.mf-step strong{color:var(--mf-strong);font-weight:800;font-size:13px}.mf-reset{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--mf-strong);background:transparent;border:1px dashed rgba(var(--mf-tone),.5);border-radius:999px;padding:4px 12px;cursor:pointer;margin-bottom:10px;transition:background .15s ease}.mf-reset:hover{background:var(--mf-soft)}.mf-reset i{font-size:11px}.mf-desc{margin:0 0 14px;font-size:13px;color:var(--muted);line-height:1.55}.mf-columns{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}@media(max-width:640px){.mf-columns{grid-template-columns:1fr}}.mf-col{padding:12px 14px;border-radius:14px;background:#fff;border:1px solid #eef0f3}.mf-col--good{background:linear-gradient(180deg,#f0fdf4,#fff 80%);border-color:#bbf7d0}.mf-col--bad{background:linear-gradient(180deg,#fef2f2,#fff 80%);border-color:#fecaca}.mf-col-title{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:14px;font-weight:800;color:var(--text);letter-spacing:.01em}.mf-dot{width:22px;height:22px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;flex-shrink:0;font-size:11px}.mf-dot i{font-size:10px}.mf-dot.good{background:#10b981;box-shadow:0 2px 6px #10b9814d}.mf-dot.bad{background:#ef4444;box-shadow:0 2px 6px #ef44444d}.mf-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mf-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffffb3;transition:background .15s ease,transform .15s ease}.mf-item:hover{background:#fff;transform:translate(2px)}.mf-item--bad .mf-name{color:#7f1d1d}.mf-item--good .mf-name{color:#064e3b}.mf-emoji{width:28px;height:28px;border-radius:8px;background:#fff;display:inline-grid;place-items:center;font-size:16px;flex-shrink:0;box-shadow:0 1px 2px #0f172a0f}.mf-emoji--x{background:#fef2f2;color:#ef4444}.mf-emoji--x i{font-size:12px}.mf-item-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.mf-name{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.mf-note{font-size:11.5px;color:var(--muted);line-height:1.35}.mf-tip{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;border-radius:12px;background:var(--mf-soft);border:1px solid rgba(var(--mf-tone),.22);font-size:12.5px;color:var(--text);line-height:1.5}.mf-tip i{color:var(--mf-strong);font-size:15px;margin-top:1px;flex-shrink:0}.mom-tab .card{padding:12px 14px;margin-bottom:10px;border-radius:14px}.mom-tab .mom-wellness-section,.mom-tab .mom-tips-week{margin-bottom:10px}.mom-tab .mom-food-card{padding:12px 14px;margin-top:10px}.mom-tab .mf-head{margin-bottom:8px}.mom-tab .mf-head h2{font-size:16px}.mom-tab .mf-desc{margin-bottom:10px;font-size:12px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.mom-tab .mf-columns{gap:8px;margin-bottom:8px}.mom-tab .mf-col{padding:8px 10px}.mom-tab .mf-list{gap:6px}.mom-tab .mf-item{padding:6px 8px}.mom-tab .mf-tip{padding:8px 10px;font-size:12px}.mom-hero{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin-bottom:14px;background:linear-gradient(135deg,#fdf2f8,#faf5ff,#eef2ff);border:1px solid #f9a8d4;border-radius:16px}.mom-hero .mom-hero__icon{font-size:36px;line-height:1;flex-shrink:0}.mom-hero h2{margin:0 0 6px;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em}.mom-hero .mom-hero__sub{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}.mom-hero .mom-hero__sub strong{color:var(--text)}.mom-hero .mom-hero__sub .muted-inline{display:block;margin-top:4px;font-size:12.5px;opacity:.92}.mom-hero--compact{align-items:center;padding:10px 12px;margin-bottom:10px;border-radius:14px;gap:10px}.mom-hero--compact .mom-hero__icon{font-size:26px}.mom-hero--compact h2{margin:0;font-size:16px;letter-spacing:-.01em}.mom-hero--compact .mom-hero__title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px}.mom-hero--compact .mom-hero__baby{font-size:12px;font-weight:700;color:#be185d;padding:2px 8px;border-radius:999px;background:#ffffffbf;border:1px solid rgba(244,114,182,.35)}.mom-hero--compact .mom-hero__sub{margin:4px 0 0;font-size:11.5px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.mom-hero--compact .mom-hero__sub .muted-inline{display:inline;margin-top:0;font-size:11.5px}.section-head--tight{margin-bottom:8px}.section-head--tight h3{margin:0}.mom-disclaimer--compact{font-size:11px;line-height:1.4;padding:8px 10px;margin-top:4px;margin-bottom:6px}.section-head{margin-bottom:14px}.section-head h3{margin:0 0 6px;font-size:16px;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}.section-head h3 i{color:#db2777;font-size:15px}.section-head .section-sub{margin:0;font-size:12.5px;color:var(--muted);line-height:1.45}.mom-wellness-section{margin-bottom:16px}.mom-tab .mom-wellness-grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}.mom-wellness-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.mom-w-card{padding:14px 14px 12px;border-radius:14px;border:1px solid var(--border);background:#fafafa;transition:border-color .15s,box-shadow .15s}.mom-w-card:hover{border-color:#f472b6;box-shadow:0 6px 18px -12px #db277759}.mom-w-card .mom-w-card__icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#fce7f3,#ede9fe);display:flex;align-items:center;justify-content:center;margin-bottom:10px}.mom-w-card .mom-w-card__icon i{font-size:16px;color:#be185d}.mom-w-card h4{margin:0 0 8px;font-size:13.5px;font-weight:800;color:var(--text);line-height:1.3}.mom-w-card p{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-tips-week{margin-bottom:16px}.mom-tab .mom-tips-list{gap:6px}.mom-tab .mom-tips-list li{padding:8px 10px;border-radius:10px}.mom-tab .mom-tips-list li strong{margin-bottom:2px;font-size:12px}.mom-tab .mom-tips-list li span{font-size:11.5px;line-height:1.4}.mom-tips-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.mom-tips-list li{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:12px;background:#fefce8;border:1px solid #fde047}.mom-tips-list li>i{color:#ca8a04;margin-top:2px;flex-shrink:0}.mom-tips-list li strong{display:block;font-size:13px;font-weight:800;color:var(--text);margin-bottom:4px}.mom-tips-list li span{font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-disclaimer{margin-top:8px;margin-bottom:8px}.mom-empty-profile{max-width:520px;margin:24px auto 40px;text-align:center}.mom-empty-profile__inner{padding:28px 22px}.mom-empty-profile__inner .mom-empty-emoji{font-size:48px;display:block;margin-bottom:12px}.mom-empty-profile__inner h2{margin:0 0 10px;font-size:17px;font-weight:800;color:var(--text)}.mom-empty-profile__inner p{margin:0 0 18px;font-size:14px;line-height:1.55;color:var(--muted)}.event-reminder-dialog{max-width:min(520px,100vw - 28px);padding-bottom:8px}.event-reminder-dialog__head{align-items:flex-start;margin-bottom:4px}.event-reminder-dialog__title{margin:0;padding-right:40px;font-size:20px;font-weight:800;line-height:1.3;color:var(--text, #0f172a);display:flex;align-items:center;gap:10px;letter-spacing:-.02em}.event-reminder-dialog__title .pi-bell{color:#f59e0b;font-size:18px;flex-shrink:0}.event-reminder-list{list-style:none;margin:12px 0 0;padding:0;max-height:min(52vh,400px);overflow:auto;display:flex;flex-direction:column;gap:10px}.event-reminder-card{padding:14px 16px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;border-radius:14px;box-shadow:0 2px 8px #0f172a0d}.event-reminder-card__title{margin:0;font-size:16px;font-weight:800;color:var(--text, #0f172a);line-height:1.35}.event-reminder-card__datetime{margin:5px 0 0;font-size:12.5px;font-weight:500;color:#64748b;line-height:1.4}.event-reminder-card__date{color:#475569;font-weight:600}.event-reminder-card__sep{color:#94a3b8}.event-reminder-card__hour{color:#dc2626;font-weight:700;font-variant-numeric:tabular-nums}.event-reminder-card__place{margin:6px 0 0;display:flex;align-items:flex-start;gap:6px;font-size:13px;color:#475569;line-height:1.4}.event-reminder-card__place .pi-map-marker{margin-top:2px;flex-shrink:0;font-size:13px;color:var(--primary, #5b6cff)}.event-reminder-card__content-wrap{margin-top:10px;padding-top:10px;border-top:1px solid #eef1f6}.event-reminder-card__content-label{display:block;margin-bottom:4px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#94a3b8}.event-reminder-card__content{margin:0;font-size:13.5px;color:#334155;line-height:1.5;white-space:pre-wrap}.event-reminder-actions{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border, #e5e7eb)}.event-reminder-actions__link{flex:0 1 auto;min-width:0;padding:6px 4px;border:none;background:transparent;color:#64748b;font-size:13px;font-weight:600;font-family:inherit;text-decoration:underline;text-underline-offset:3px;cursor:pointer}.event-reminder-actions__link:hover:not(:disabled){color:#334155}.event-reminder-actions__link:disabled{opacity:.5;cursor:not-allowed}.event-reminder-actions__primary{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:10px 18px;font-size:15px;font-weight:700;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DocumentsComponent, selector: "app-documents" }, { kind: "component", type: MedicalHistoryComponent, selector: "app-medical-history", inputs: ["user"], outputs: ["openExplorerFile"] }, { kind: "component", type: WeightComponent, selector: "app-weight", inputs: ["user", "birthDate", "gender", "babyName"] }, { kind: "component", type: ActivityLogComponent, selector: "app-activity-log", outputs: ["activityChanged"] }, { kind: "component", type: FeedingScheduleComponent, selector: "app-feeding-schedule", inputs: ["user"] }, { kind: "component", type: FeedingMomComponent, selector: "app-feeding-mom", inputs: ["profile", "editing"], outputs: ["goToFeeding"] }, { kind: "component", type: FeedingChartsComponent, selector: "app-feeding-charts", inputs: ["logs", "now", "nutritionTarget"] }, { kind: "component", type: FeedingNutritionComponent, selector: "app-feeding-nutrition", inputs: ["nutritionTarget", "todayStats", "now", "latestWeightKg", "editing"], outputs: ["goToWeight"] }, { kind: "component", type: FeedingProfileComponent, selector: "app-feeding-profile", inputs: ["user", "profile", "draft", "editing", "maxBirthDate"], outputs: ["save", "cancelEdit", "startEdit", "clearProfile", "draftNameChange", "draftBirthChange", "draftGenderChange"] }, { kind: "component", type: FeedingDialogsComponent, selector: "app-feeding-dialogs", inputs: ["logs", "user", "now", "bottlePrep", "feedingSettings", "prediction", "todayStats", "yesterdayStats", "todayLogs", "yesterdayLogs", "maxBirthDate", "syncError", "syncMessage"], outputs: ["logsChanged", "settingsChanged", "syncErrorChange", "syncMessageChange", "logDialogOpenChange", "bottlePrepClearRequested"] }, { kind: "component", type: FeedingDailyComponent, selector: "app-feeding-daily", inputs: ["logs", "feedingSettings", "now", "nutritionTarget", "user", "syncMessage", "syncError", "logDialogOpen", "loadingLogs", "refreshToken"], outputs: ["logsChanged", "bottlePrepChange", "editLog", "historyOpen", "feedRowClick"] }, { kind: "component", type: HeroHeaderComponent, selector: "app-hero-header", inputs: ["profile", "age", "user", "weightLabel", "isAnyLoading", "showRefresh", "showChat"], outputs: ["refresh", "openMenu", "openChat"] }, { kind: "component", type: HeroStatsStripComponent, selector: "app-hero-stats-strip", inputs: ["currentTime", "dateLabel", "nextFeedTime", "nextFeedCountdown", "weightLabel", "whoStatus"] }, { kind: "component", type: NotificationBannerComponent, selector: "app-notification-banner", inputs: ["notifications", "saving", "currentUser", "dialogOpen"], outputs: ["openDialog", "closeDialog", "acknowledge", "acknowledgeAll", "delete", "openComposer"] }, { kind: "component", type: NotificationListComponent, selector: "app-notification-list", inputs: ["notifications", "currentUser", "saving"], outputs: ["back", "acknowledge", "acknowledgeAll", "delete", "openComposer"] }, { kind: "component", type: SubrouteShellComponent, selector: "app-subroute-shell", inputs: ["title"], outputs: ["back"] }, { kind: "component", type: SettingsMenuComponent, selector: "app-settings-menu", inputs: ["appName", "appVersion", "showDocuments", "unreadCount"], outputs: ["close", "select"] }, { kind: "component", type: ChatShellComponent, selector: "app-chat-shell", inputs: ["context", "disabled"] }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1$1.SlicePipe, name: "slice" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15825
17113
|
}
|
|
15826
17114
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FeedingComponent, decorators: [{
|
|
15827
17115
|
type: Component,
|
|
@@ -15844,7 +17132,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
15844
17132
|
NotificationListComponent,
|
|
15845
17133
|
SubrouteShellComponent,
|
|
15846
17134
|
SettingsMenuComponent,
|
|
15847
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"feeding-root\">\n <div class=\"feeding-page\" #feedingPage>\n <div class=\"backdrop\"></div>\n\n <header class=\"hero\" [class.hero--profile-merged]=\"profile() && !editing() && ageBreakdown()\">\n @if (profile() && !editing() && ageBreakdown(); as age) {\n <!-- \u2550\u2550 REDESIGNED HERO \u2550\u2550 -->\n <app-hero-header\n [profile]=\"profile()!\"\n [age]=\"age\"\n [user]=\"user()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [isAnyLoading]=\"isAnyLoading()\"\n [showRefresh]=\"bottomTab() !== 'schedule'\"\n (openMenu)=\"openSettingsMenu()\"\n (refresh)=\"refreshAll()\"\n >\n <app-activity-log slot=\"activity-log\" (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n </app-hero-header>\n\n <app-hero-stats-strip\n [currentTime]=\"currentTimeStr\"\n [dateLabel]=\"heroDateLabel\"\n [nextFeedTime]=\"prediction()?.nextTime ?? null\"\n [nextFeedCountdown]=\"heroNextFeedCountdown()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [whoStatus]=\"nutritionTarget()?.ageLabel ?? null\"\n ></app-hero-stats-strip>\n\n <div class=\"hero-notif-wrap\">\n <app-notification-banner\n [notifications]=\"visibleNotifications()\"\n [saving]=\"notificationSaving()\"\n [currentUser]=\"user()\"\n [dialogOpen]=\"notifDialogOpen()\"\n (openDialog)=\"openNotificationRoute()\"\n (closeDialog)=\"notifDialogOpen.set(false)\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"notifDialogOpen.set(false); openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"notifDialogOpen.set(false); openNotificationComposerDialog()\"\n ></app-notification-banner>\n </div>\n\n } @else {\n <!-- \u2500\u2500 Fallback: no profile / editing \u2500\u2500 -->\n <div class=\"hero-inner\">\n <div class=\"hero-brand\">\n <div class=\"hero-title-row\">\n <div class=\"hero-title\">\n <div class=\"emoji\" aria-hidden=\"true\">\uD83D\uDC76</div>\n <div class=\"hero-text\">\n <h1>B\u00E9 y\u00EAu c\u1EE7a b\u1EA1n</h1>\n <p class=\"hero-userline\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <strong>{{ user() }}</strong>\n </p>\n </div>\n </div>\n @if (profile() && !editing()) {\n <div class=\"hero-actions\">\n <app-activity-log (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"feedingDialogs.openFeedingSettingsDialog()\"\n title=\"C\u00E0i \u0111\u1EB7t\"\n aria-label=\"C\u00E0i \u0111\u1EB7t c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-sliders-h\"></i>\n </button>\n @if (bottomTab() !== 'schedule') {\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"refreshAll()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"\n isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, C\u00E2n, L\u1ECBch, Y t\u1EBF, T\u00E0i li\u1EC7u'\n \"\n aria-label=\"T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, Th\u1EC3 tr\u1EA1ng, L\u1ECBch, Ti\u1EC1n s\u1EED y t\u1EBF v\u00E0 T\u00E0i li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"hero-datetime\" aria-live=\"polite\">\n <div class=\"hero-datetime-strip\">\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--date\">\n <i class=\"pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__wd\">{{ heroWeekday }}</span>\n <time class=\"hero-datetime-strip__date\" [attr.datetime]=\"todayDateStr()\">{{\n heroDateNumeric\n }}</time>\n </div>\n <span class=\"hero-datetime-strip__divider\" aria-hidden=\"true\"></span>\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--time\">\n <i class=\"pi pi-clock\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__clock\">{{ currentTimeStr }}</span>\n </div>\n </div>\n </div>\n\n <section class=\"hero-notification\" aria-label=\"Th\u00F4ng b\u00E1o n\u1ED9i b\u1ED9\">\n <!-- watermark background icons -->\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bell\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-megaphone\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-comments\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--send\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-send\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--star\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-star\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bolt\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bolt\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--heart\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-heart\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--info\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-info-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--check\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-check-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--eye\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-eye\"></i>\n </span>\n\n <div class=\"hero-notification__head\">\n <label class=\"hero-notification__composer-label\" for=\"hero-notification-input\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n <span>Th\u00F4ng b\u00E1o</span>\n @if (visibleNotifications().length > 0) {\n <span class=\"hero-notification__unread-badge\">{{\n visibleNotifications().length\n }}</span>\n }\n </label>\n <div class=\"hero-notification__head-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button\n type=\"button\"\n class=\"hero-notification__add\"\n (click)=\"openNotificationComposerDialog()\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n title=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (displayedNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of displayedNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">\n {{ notice.user | slice: 0 : 2 | uppercase }}\n </div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{\n formatNotificationCreatedAt(notice.createdAt)\n }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\"\n title=\"\u0110\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button\n type=\"button\"\n class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\"\n title=\"X\u00F3a th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\"\n >M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span\n >\n </div>\n }\n\n @if (hiddenNotificationCount() > 0) {\n <button\n type=\"button\"\n class=\"hero-notification__more\"\n (click)=\"openNotificationListDialog()\"\n >\n Xem th\u00EAm {{ hiddenNotificationCount() }} th\u00F4ng b\u00E1o \u2192\n </button>\n }\n </section>\n\n </div>\n } <!-- end @else no-profile -->\n </header>\n\n <!-- \u2550\u2550 NOTIFICATION DIALOGS \u2014 rendered outside <header> to avoid overflow/stacking-context clipping \u2550\u2550 -->\n @if (notificationComposerDialogOpen()) {\n <div\n class=\"dialog-overlay dialog-overlay--front\"\n (click)=\"closeNotificationComposerDialog()\"\n >\n <div\n class=\"dialog hero-notification-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n (click)=\"$event.stopPropagation()\"\n >\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--send\" aria-hidden=\"true\"><i class=\"pi pi-send\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n\n <div class=\"dialog-head hero-notification-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n </div>\n <div>\n <h2 class=\"hero-notification-dialog__title\">T\u1EA1o th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">Th\u00F4ng b\u00E1o s\u1EBD hi\u1EC3n th\u1ECB cho to\u00E0n b\u1ED9 th\u00E0nh vi\u00EAn</p>\n </div>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationComposerDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <div class=\"hero-notification__composer-panel\">\n <div class=\"hero-notification__composer-row\">\n <div class=\"hero-notification__input-wrap\">\n <textarea\n id=\"hero-notification-input\"\n class=\"hero-notification__input\"\n [value]=\"notificationDraft()\"\n (input)=\"updateNotificationDraft($any($event.target).value)\"\n [disabled]=\"notificationSaving()\"\n maxlength=\"300\"\n placeholder=\"Nh\u1EADp n\u1ED9i dung th\u00F4ng b\u00E1o cho m\u1ECDi ng\u01B0\u1EDDi...\"\n aria-label=\"N\u1ED9i dung th\u00F4ng b\u00E1o\"\n ></textarea>\n <div class=\"hero-notification__char-ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 36 36\">\n <circle class=\"hero-notification__char-ring-track\" cx=\"18\" cy=\"18\" r=\"15\" />\n <circle class=\"hero-notification__char-ring-fill\" cx=\"18\" cy=\"18\" r=\"15\"\n [style.stroke-dashoffset]=\"94 - (notificationDraft().trim().length / 300) * 94\" />\n </svg>\n <span class=\"hero-notification__char-count\">{{ notificationDraft().trim().length }}</span>\n </div>\n </div>\n </div>\n <div class=\"hero-notification__composer-actions\">\n <button type=\"button\" class=\"btn ghost hero-notification__cancel\"\n (click)=\"closeNotificationComposerDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn hero-notification__send\"\n (click)=\"createNotification()\"\n [disabled]=\"notificationSaving() || !notificationDraft().trim()\">\n <i class=\"pi pi-send\"></i>\n <span>G\u1EEDi th\u00F4ng b\u00E1o</span>\n </button>\n </div>\n </div>\n @if (notificationError()) {\n <p class=\"hero-notification__error\">{{ notificationError() }}</p>\n }\n </div>\n </div>\n }\n\n @if (notificationListDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeNotificationListDialog()\">\n <div class=\"dialog hero-notification-list-dialog\" role=\"dialog\" aria-modal=\"true\"\n aria-label=\"Danh s\u00E1ch th\u00F4ng b\u00E1o\" (click)=\"$event.stopPropagation()\">\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--check\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></span>\n\n <div class=\"dialog-head hero-notification-list-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\"><i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i></div>\n <div>\n <h2 class=\"hero-notification-list-dialog__title\">To\u00E0n b\u1ED9 th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">{{ visibleNotifications().length }} th\u00F4ng b\u00E1o</p>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__head-actions\">\n <button type=\"button\" class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationListDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__body\">\n @if (visibleNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of visibleNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">{{ notice.user | slice: 0 : 2 | uppercase }}</div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{ formatNotificationCreatedAt(notice.createdAt) }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button type=\"button\" class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\" title=\"\u0110\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button type=\"button\" class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\" title=\"X\u00F3a th\u00F4ng b\u00E1o\">\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\">M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (acknowledgeAllConfirmDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAcknowledgeAllConfirmDialog()\">\n <div class=\"dialog ack-all-confirm-dialog\" role=\"alertdialog\" aria-modal=\"true\"\n aria-labelledby=\"ack-all-confirm-title\" (click)=\"$event.stopPropagation()\">\n <div class=\"ack-all-confirm-dialog__icon-wrap\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></div>\n <h2 id=\"ack-all-confirm-title\" class=\"ack-all-confirm-dialog__title\">\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc?</h2>\n <p class=\"ack-all-confirm-dialog__desc\">\n T\u1EA5t c\u1EA3 {{ visibleNotifications().length }} th\u00F4ng b\u00E1o s\u1EBD \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u \u0111\u00E3 \u0111\u1ECDc v\u00E0 \u1EA9n kh\u1ECFi danh s\u00E1ch.\n </p>\n <div class=\"ack-all-confirm-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeAcknowledgeAllConfirmDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn ack-all-confirm-dialog__confirm-btn\"\n (click)=\"confirmAcknowledgeAll()\" [disabled]=\"notificationSaving()\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> X\u00E1c nh\u1EADn\n </button>\n </div>\n </div>\n </div>\n }\n\n <main\n class=\"content\"\n [class.is-documents]=\"bottomTab() === 'documents'\"\n [class.is-weight]=\"bottomTab() === 'weight'\"\n [class.is-schedule]=\"bottomTab() === 'schedule'\"\n [class.is-mom]=\"bottomTab() === 'mom'\"\n [class.is-medical]=\"bottomTab() === 'medical'\"\n >\n <!--\n ============ TAB PANES ============\n D\u00F9ng `[hidden]` thay v\u00EC `*ngIf` \u0111\u1EC3 c\u00E1c tab c\u00F9ng gi\u1EEF tr\u1EA1ng th\u00E1i khi\n user qua l\u1EA1i: scroll position, search query \u0111ang g\u00F5, viewMode, folder\n \u0111ang \u0111\u1EE9ng, file \u0111ang ch\u1ECDn, draft \u0111ang nh\u1EADp\u2026 \u0111\u1EC1u kh\u00F4ng b\u1ECB reset. H\u1EC7\n qu\u1EA3: `<app-documents>` \u0111\u01B0\u1EE3c mount 1 l\u1EA7n \u1EDF app load \u2014 `loadEntries()`\n ch\u1EA1y 1 l\u1EA7n, c\u00E1c l\u1EA7n switch tab k\u1EBF ti\u1EBFp kh\u00F4ng g\u1ECDi API th\u00EAm.\n -->\n <!-- ============ TAB: DOCUMENTS \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: WEIGHT ============ -->\n <div class=\"tab-pane tab-pane--weight\" [hidden]=\"bottomTab() !== 'weight'\">\n <app-weight\n [user]=\"user()\"\n [birthDate]=\"profile()?.birthDate\"\n [gender]=\"profile()?.gender\"\n [babyName]=\"profile()?.babyName\"\n ></app-weight>\n </div>\n\n <!-- ============ TAB: L\u1ECACH \u2014 Google Sheet tab \"Event\" ============ -->\n <div class=\"tab-pane tab-pane--schedule\" [hidden]=\"bottomTab() !== 'schedule'\">\n <app-feeding-schedule [user]=\"user()\"></app-feeding-schedule>\n </div>\n\n <!-- ============ TAB: TI\u1EC0N S\u1EEC Y T\u1EBE \u2014 Medical history V2 ============ -->\n <div class=\"tab-pane tab-pane--medical\" [hidden]=\"bottomTab() !== 'medical'\">\n <app-medical-history\n [user]=\"user()\"\n (openExplorerFile)=\"openMedicalAttachmentInDocuments($event)\"\n ></app-medical-history>\n </div>\n\n <!-- ============ TAB: M\u1EB8 \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: FEEDING ============ -->\n <div class=\"tab-pane tab-pane--feeding\" [hidden]=\"bottomTab() !== 'feeding'\">\n <app-feeding-profile\n [user]=\"user()\"\n [profile]=\"profile()\"\n [draft]=\"draft()\"\n [editing]=\"editing()\"\n [maxBirthDate]=\"maxBirthDate\"\n (save)=\"save()\"\n (cancelEdit)=\"cancelEdit()\"\n (startEdit)=\"startEdit()\"\n (clearProfile)=\"clearProfile()\"\n (draftNameChange)=\"updateDraftName($event)\"\n (draftBirthChange)=\"updateDraftBirth($event)\"\n (draftGenderChange)=\"setDraftGender($event)\"\n />\n\n @if (profile() && !editing() && ageBreakdown()) {\n <app-feeding-daily\n [logs]=\"logs()\"\n [feedingSettings]=\"feedingSettings()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n [user]=\"user()\"\n [syncMessage]=\"syncMessage()\"\n [syncError]=\"syncError()\"\n [logDialogOpen]=\"logDialogOpen()\"\n [loadingLogs]=\"loadingLogs()\"\n [refreshToken]=\"dailyRefreshToken()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (bottlePrepChange)=\"bottlePrep.set($event)\"\n (editLog)=\"feedingDialogs.openEditDialog($event)\"\n (historyOpen)=\"feedingDialogs.openHistoryDialog($event.filter, $event.dateStr)\"\n (feedRowClick)=\"feedingDialogs.onFeedRowClick($event)\"\n >\n <app-feeding-charts\n data-feeding-charts\n [logs]=\"logs()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n />\n </app-feeding-daily>\n <app-feeding-nutrition\n [nutritionTarget]=\"nutritionTarget()\"\n [todayStats]=\"todayStats()\"\n [now]=\"now()\"\n [latestWeightKg]=\"latestWeightKgFromSheet()\"\n [editing]=\"editing()\"\n (goToWeight)=\"setBottomTab('weight')\"\n />\n }\n\n <!-- ============ FAB \u00AB Ghi c\u1EEF b\u00FA \u00BB c\u0169 \u0111\u00E3 \u0111\u01B0\u1EE3c thay b\u1EB1ng FAB gi\u1EEFa bottom-nav ============ -->\n </div>\n <!-- /.tab-pane--feeding -->\n </main>\n </div>\n <!-- /.feeding-page -->\n\n <!-- ============ BOTTOM NAVIGATION (C\u1EEF b\u00FA \u00B7 Th\u1EC3 tr\u1EA1ng \u00B7 + \u00B7 L\u1ECBch \u00B7 Y t\u1EBF) ============ -->\n <nav\n class=\"bottom-nav\"\n role=\"tablist\"\n aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\"\n >\n <div class=\"bn-side bn-side--left\">\n @for (tab of navLeftTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"bn-fab\"\n (click)=\"feedingDialogs.openLogDialog()\"\n [disabled]=\"!profile() || editing()\"\n [title]=\"(!profile() || editing()) ? 'Ch\u1ECDn b\u00E9 tr\u01B0\u1EDBc khi ghi c\u1EEF b\u00FA' : 'Ghi nh\u1EADn c\u1EEF b\u00FA m\u1EDBi'\"\n aria-label=\"Ghi nh\u1EADn c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"bn-side bn-side--right\">\n @for (tab of navRightTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n </nav>\n\n <!-- ============ NOTIFICATION SUBROUTE (full-screen) ============ -->\n @if (notifRouteOpen()) {\n <app-notification-list\n [notifications]=\"allNotifications()\"\n [currentUser]=\"user()\"\n [saving]=\"notificationSaving()\"\n (back)=\"closeNotificationRoute()\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"openNotificationComposerDialog()\"\n />\n }\n\n <!-- ============ SETTINGS FLOAT MENU (full-screen) ============ -->\n @if (settingsMenuOpen()) {\n <app-settings-menu\n [appName]=\"appInfo.name\"\n [appVersion]=\"appInfo.version\"\n [showDocuments]=\"explorerEnabled\"\n [unreadCount]=\"visibleNotifications().length\"\n (close)=\"closeSettingsMenu()\"\n (select)=\"onSettingsMenuSelect($event)\"\n />\n }\n\n <!-- ============ M\u1EB8 SUBROUTE (full-screen) ============ -->\n @if (momRouteOpen()) {\n <app-subroute-shell\n title=\"M\u1EB9\"\n (back)=\"closeMomRoute()\"\n >\n <app-feeding-mom\n [profile]=\"profile()\"\n [editing]=\"editing()\"\n (goToFeeding)=\"closeMomRoute(); setBottomTab('feeding')\"\n ></app-feeding-mom>\n </app-subroute-shell>\n }\n\n <!-- ============ T\u00C0I LI\u1EC6U SUBROUTE (full-screen) ============ -->\n @if (documentsRouteOpen()) {\n <app-subroute-shell\n title=\"T\u00E0i li\u1EC7u\"\n (back)=\"closeDocumentsRoute()\"\n >\n <app-documents></app-documents>\n </app-subroute-shell>\n }\n\n <!-- About -->\n @if (aboutDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAboutDialog()\">\n <div\n class=\"dialog about-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"feeding-about-title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head about-dialog__head\">\n <h2 id=\"feeding-about-title\" class=\"about-dialog__title\">\n <i class=\"pi pi-info-circle\" aria-hidden=\"true\"></i>\n About\n </h2>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeAboutDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <dl class=\"about-dialog__meta\">\n <div class=\"about-dialog__row\">\n <dt>\u1EE8ng d\u1EE5ng</dt>\n <dd>{{ appInfo.name }}</dd>\n </div>\n <div class=\"about-dialog__row\">\n <dt>Phi\u00EAn b\u1EA3n</dt>\n <dd>\n <code>{{ appInfo.version }}</code>\n </dd>\n </div>\n </dl>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn primary\" (click)=\"closeAboutDialog()\">\u0110\u00F3ng</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Nh\u1EAFc s\u1EF1 ki\u1EC7n l\u1ECBch (sheet Event + Settings) -->\n @if (eventReminderDialogOpen() && scheduleReminderEvents().length > 0) {\n <div\n class=\"dialog-overlay dialog-overlay--front dialog-overlay--event-reminder\"\n (click)=\"closeEventReminderDialog()\"\n >\n <div\n class=\"dialog event-reminder-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head event-reminder-dialog__head\">\n <h2 class=\"event-reminder-dialog__title\">\n <i class=\"pi pi-bell\" aria-hidden=\"true\"></i>\n B\u1EA1n c\u00F3 l\u1ECBch s\u1EAFp t\u1EDBi\n </h2>\n <button\n type=\"button\"\n class=\"dialog-close\"\n (click)=\"closeEventReminderDialog()\"\n aria-label=\"\u0110\u00F3ng\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <ul class=\"event-reminder-list\">\n @for (ev of scheduleReminderEvents(); track ev) {\n <li>\n <article class=\"event-reminder-card\">\n <h3 class=\"event-reminder-card__title\">{{ ev.title }}</h3>\n <p class=\"event-reminder-card__datetime\">\n <span class=\"event-reminder-card__date\">{{ formatReminderEventWhen(ev) }}</span>\n <span class=\"event-reminder-card__sep\" aria-hidden=\"true\"> \u2013 </span>\n <span class=\"event-reminder-card__hour\">{{\n formatReminderEventTime(ev.time)\n }}</span>\n </p>\n @if (ev.place) {\n <p class=\"event-reminder-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n <span>{{ ev.place }}</span>\n </p>\n }\n @if (ev.note) {\n <div class=\"event-reminder-card__content-wrap\">\n <span class=\"event-reminder-card__content-label\">N\u1ED9i dung</span>\n <p class=\"event-reminder-card__content\">{{ ev.note }}</p>\n </div>\n }\n </article>\n </li>\n }\n </ul>\n <div class=\"dialog-actions event-reminder-actions\">\n <button\n type=\"button\"\n class=\"event-reminder-actions__link\"\n (click)=\"acknowledgeAllEventReminders()\"\n >\n Kh\u00F4ng nh\u1EAFc l\u1EA1i\n </button>\n <button\n type=\"button\"\n class=\"btn primary event-reminder-actions__primary\"\n (click)=\"skipEventReminderSession()\"\n >\n \u0110\u00E3 hi\u1EC3u\n </button>\n </div>\n </div>\n </div>\n }\n\n <app-feeding-dialogs\n #feedingDialogs\n [logs]=\"logs()\"\n [user]=\"user()\"\n [now]=\"now()\"\n [bottlePrep]=\"bottlePrep()\"\n [feedingSettings]=\"feedingSettings()\"\n [prediction]=\"prediction()\"\n [todayStats]=\"todayStats()\"\n [yesterdayStats]=\"yesterdayStats()\"\n [todayLogs]=\"todayLogs()\"\n [yesterdayLogs]=\"yesterdayLogs()\"\n [maxBirthDate]=\"maxBirthDate\"\n [syncError]=\"syncError()\"\n [syncMessage]=\"syncMessage()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (settingsChanged)=\"onFeedingSettingsChanged($event)\"\n (syncErrorChange)=\"syncError.set($event)\"\n (syncMessageChange)=\"syncMessage.set($event)\"\n (logDialogOpenChange)=\"logDialogOpen.set($event)\"\n (bottlePrepClearRequested)=\"clearBottlePrep()\"\n />\n</div>\n<!-- /.feeding-root -->\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;position:absolute;inset:0;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--border-strong: #d1d5db;--primary: #5b6cff;--primary-2: #4c5cf0;--primary-soft: #eef0ff;--accent: #ff5c8a;--accent-soft: #ffe7ef;--mint: #10b981;--mint-soft: #d1fae5;--amber: #f59e0b;--amber-soft: #fef3c7;--rose: #ef4444;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8}.feeding-root{flex:1;min-height:0;width:100%;display:flex;flex-direction:column;position:relative}.feeding-root .bp-prep-user{font-weight:800;color:#0369a1;background:#0ea5e933;padding:0 5px;margin-right:2px;border-radius:5px;white-space:nowrap;transition:color .2s,background .2s}.feeding-root .bp-prep-time{font-weight:800;font-variant-numeric:tabular-nums;color:#0284c7;background:linear-gradient(180deg,#f0f9ff,#e0f2fe);border:1px solid rgba(14,165,233,.35);padding:1px 7px;margin-left:2px;border-radius:6px;white-space:nowrap;box-shadow:0 1px #ffffffa6 inset;transition:color .2s,background .2s,border-color .2s}.feeding-page{position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;color:var(--text);padding-bottom:calc(170px + env(safe-area-inset-bottom,0px))}.backdrop{position:fixed;inset:0;background:var(--bg);z-index:-1}.hero-notif-wrap{padding:0 16px 8px}.hero{padding:0;background:#fffffff5;border-bottom:1px solid rgba(226,232,240,.9)}@media(max-width:640px){.hero{padding-top:calc(26px + env(safe-area-inset-top,0px));padding-bottom:10px}}.hero-inner{max-width:1100px;margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:14px 18px}.hero-brand{min-width:0}.hero-title-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;width:100%}.hero-title{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.hero-title .emoji{font-size:36px;line-height:1;flex-shrink:0;filter:drop-shadow(0 2px 4px rgba(15,23,42,.08))}@media(max-width:640px){.hero-title .emoji{font-size:32px}}.hero-title .hero-text{min-width:0}.hero-title h1{margin:0;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em;line-height:1.2}@media(max-width:640px){.hero-title h1{font-size:17px}}.hero-title .hero-userline{margin:5px 0 0;display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:12.5px;color:var(--muted)}.hero-title .hero-userline i{font-size:12px;color:var(--primary);opacity:.9}.hero-title .hero-userline strong{color:var(--primary);font-weight:700;letter-spacing:.01em}.hero-title .hero-userline .hero-last-update{font-size:11px;color:#94a3b8;font-weight:400}.hero--profile-merged{background:var(--primary-soft);padding-bottom:10px;margin-bottom:8px}.hero-title--merged{align-items:flex-start}.hero-title--merged .emoji{font-size:30px}.hero-title--merged h1{font-size:18px;line-height:1.15}.hero-merged-head{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;min-width:0;row-gap:6px}.hero-age-chip{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:lowercase;color:var(--primary);background:var(--primary-soft);padding:3px 9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--primary) 18%,transparent);white-space:nowrap}.hero-merged-stat{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:700;color:var(--muted);background:#94a3b824;border:1px solid rgba(148,163,184,.28);white-space:nowrap;line-height:1.25}.hero-merged-stat>i{font-size:10px;opacity:.88;color:var(--muted-2)}.hero-merged-stat--weight>i{color:var(--accent);opacity:1}.hero-merged-meta{margin:5px 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:11.5px;line-height:1.45;color:var(--muted)}.hero-merged-meta .dot{color:var(--muted-2);font-weight:700;-webkit-user-select:none;user-select:none}.hero-merged-meta i{font-size:10px;margin-right:3px;color:var(--muted-2);opacity:.95}.hero-merged-meta strong{color:var(--primary);font-weight:700}.hero-merged-sync{color:#94a3b8;font-weight:400;font-size:11px}.hero-actions--merged{flex-shrink:0}.hero-profile-menu{position:relative;flex:0 0 auto;list-style:none}.hero-profile-menu__trigger{list-style:none}.hero-profile-menu__trigger::-webkit-details-marker{display:none}.hero-profile-menu__trigger::marker{content:\"\"}.hero-profile-menu__panel{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:180px;padding:6px;border-radius:12px;border:1px solid var(--border);background:#fff;box-shadow:0 8px 24px -8px #0f172a2e}.hero-profile-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;border:0;border-radius:8px;background:transparent;font:inherit;font-size:13px;font-weight:600;color:var(--text);text-align:left;cursor:pointer;transition:background .12s ease,color .12s ease}.hero-profile-menu__item i{font-size:14px;color:var(--muted-2);width:1.1em;text-align:center}.hero-profile-menu__item:hover:not(:disabled){background:var(--primary-soft, #eef0ff);color:var(--primary)}.hero-profile-menu__item:hover:not(:disabled) i{color:var(--primary)}.hero-profile-menu__item--danger:hover:not(:disabled){color:var(--rose);background:var(--rose-soft)}.hero-profile-menu__item--danger:hover:not(:disabled) i{color:var(--rose)}.about-dialog{max-width:360px}.about-dialog__head{margin-bottom:8px}.about-dialog__title{display:flex;align-items:center;gap:8px;margin:0;font-size:18px;font-weight:700}.about-dialog__title i{color:var(--primary)}.about-dialog__meta{margin:0 0 16px;padding:0}.about-dialog__row{display:grid;grid-template-columns:96px 1fr;gap:8px 12px;padding:10px 0;border-bottom:1px solid var(--border)}.about-dialog__row:last-child{border-bottom:0}.about-dialog__row dt{margin:0;font-size:13px;font-weight:600;color:var(--muted-2)}.about-dialog__row dd{margin:0;font-size:14px;font-weight:600;color:var(--text)}.about-dialog__row dd code{font-size:13px;font-weight:600;padding:2px 8px;border-radius:6px;background:var(--primary-soft, #eef0ff);color:var(--primary)}@media(max-width:640px){.hero-title--merged .emoji{font-size:26px}.hero-merged-meta{font-size:11px}.hero-merged-stat{font-size:10.5px;padding:2px 7px}}.hero-datetime{display:flex;flex-wrap:wrap;align-items:center;flex-shrink:0}.hero-datetime-strip{display:inline-flex;align-items:stretch;flex-wrap:nowrap;max-width:100%;min-height:34px;border:1px solid var(--border);border-radius:8px;background:linear-gradient(180deg,#fff,#f1f5f9);box-shadow:0 1px 1px #0f172a0d}.hero-datetime-strip__block{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;min-width:0}.hero-datetime-strip__block>.pi{font-size:12px;color:var(--primary);opacity:.82;flex-shrink:0}.hero-datetime-strip__block--date{flex-wrap:wrap;row-gap:0}.hero-datetime-strip__wd{font-size:10px;font-weight:700;color:var(--muted);text-transform:capitalize;letter-spacing:.02em;line-height:1.15;max-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hero-datetime-strip__date{font-size:12px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--text);letter-spacing:.02em;white-space:nowrap}.hero-datetime-strip__divider{align-self:stretch;width:1px;margin:5px 0;background:#94a3b866;flex-shrink:0}.hero-datetime-strip__clock{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:.04em;color:var(--primary);line-height:1;white-space:nowrap}.hero-datetime-strip__block--time{padding-right:10px}.hero-datetime-strip__block--time>.pi{font-size:11px}@media(max-width:520px){.hero-inner{flex-direction:column;align-items:stretch}.hero-datetime{width:100%;justify-content:flex-start}.hero-datetime-strip{flex:1 1 auto;width:100%;justify-content:space-between}.hero-notification__composer-row{flex-direction:column;align-items:stretch}.hero-notification__send,.hero-notification__cancel{width:100%;justify-content:center}}.hero-notification__bg-icon{position:absolute;pointer-events:none;color:var(--primary);opacity:.13;line-height:1}.hero-notification__bg-icon i{display:block}.hero-notification__bg-icon--bell{top:8%;left:6%;font-size:28px;transform:rotate(-18deg)}.hero-notification__bg-icon--megaphone{top:10%;right:8%;font-size:24px;transform:rotate(12deg)}.hero-notification__bg-icon--comments{bottom:14%;right:10%;font-size:22px;transform:rotate(-8deg)}.hero-notification__bg-icon--send{bottom:10%;left:50%;font-size:20px;transform:rotate(-22deg)}.hero-notification__bg-icon--star{bottom:16%;left:4%;font-size:18px;transform:rotate(20deg)}.hero-notification__bg-icon--bolt{top:46%;right:2%;font-size:20px;transform:rotate(-5deg)}.hero-notification__bg-icon--heart{top:38%;left:14%;font-size:17px;transform:rotate(15deg)}.hero-notification__bg-icon--info{top:4%;left:50%;font-size:18px;transform:rotate(0)}.hero-notification__bg-icon--check{bottom:24%;left:35%;font-size:17px;transform:rotate(8deg)}.hero-notification__bg-icon--eye{top:14%;left:28%;font-size:18px;transform:rotate(-10deg)}.hero-notification{flex:1 1 100%;display:flex;flex-direction:column;gap:10px;margin-top:2px;padding:10px 12px;border:1px solid rgba(148,163,184,.28);border-radius:14px;background:#fff;position:relative;overflow:hidden}.hero-notification__head{display:flex;align-items:center;justify-content:space-between;gap:10px;position:relative}.hero-notification__head-actions{display:inline-flex;align-items:center;gap:6px}.hero-notification__composer-label{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--muted)}.hero-notification__composer-label i{color:var(--primary);font-size:12px}.hero-notification__unread-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--primary);color:#fff;font-size:10px;font-weight:700;line-height:1}.hero-notification__add{width:28px;height:28px;border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.25);border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.hero-notification__add i{font-size:11px}.hero-notification__add:hover:not(:disabled){background:var(--primary);border-color:var(--primary);color:#fff}.hero-notification__mark-all{height:26px;border:none;border-radius:0;background:transparent;color:var(--primary);font-size:11px;font-weight:600;padding:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:4px}.hero-notification__mark-all i{font-size:10px}.hero-notification__mark-all:hover:not(:disabled){opacity:.75}.hero-notification__mark-all:disabled{color:var(--muted-2);cursor:default}.hero-notification__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;position:relative}.hero-notification__item{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;border-radius:10px;background:#f8fafc;border:1px solid #e2e8f0;position:relative;transition:border-color .18s ease,transform .18s ease}@media(hover:hover){.hero-notification__item:hover{border-color:#c7d2fe;transform:translateY(-1px)}}.hero-notification__avatar{width:34px;height:34px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);font-size:11px;font-weight:700;letter-spacing:.5px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification__body{flex:1;min-width:0;padding-right:52px}.hero-notification__body-head{display:flex;align-items:center;gap:6px;margin-bottom:3px}.hero-notification__author{font-size:12px;font-weight:700;color:var(--text)}.hero-notification__content{margin:0;font-size:13.5px;line-height:1.5;color:var(--text);white-space:pre-wrap;word-break:break-word}.hero-notification__created{display:block;margin-top:4px;font-size:11px;color:#94a3b8;font-variant-numeric:tabular-nums}.hero-notification__item-actions{position:absolute;top:7px;right:8px;display:inline-flex;align-items:center;gap:4px}.hero-notification__ack-top{width:24px;height:24px;border:none;border-radius:50%;background:#eff6ff;color:#1d4ed8;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s ease,color .15s ease,transform .15s ease}.hero-notification__ack-top i{font-size:10px}.hero-notification__ack-top:active:not(:disabled){transform:scale(.96)}.hero-notification__ack-top:hover:not(:disabled){background:#dbeafe;color:#1e40af}.hero-notification__delete{width:24px;height:24px;border:none;border-radius:50%;background:transparent;color:#94a3b8;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.hero-notification__delete i{font-size:10px}.hero-notification__delete:hover:not(:disabled){background:#fee2e2;color:#b91c1c}.hero-notification__empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 8px;position:relative}.hero-notification__empty-icon{font-size:28px;color:var(--primary);opacity:.45;margin-bottom:4px}.hero-notification__empty-title{font-size:13px;font-weight:600;color:var(--muted)}.hero-notification__empty-sub{font-size:11px;color:var(--muted-2);text-align:center}.hero-notification__more{align-self:flex-start;border:none;background:transparent;color:var(--primary);font-size:12px;font-weight:700;cursor:pointer;padding:0;font-family:inherit;position:relative}.hero-notification__more:hover{opacity:.75}.hero-notification__composer-row{display:flex;align-items:stretch;gap:8px;min-width:0}.hero-notification__composer-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid #dbe4f3;border-radius:12px;background:#f8fbff}.hero-notification__input-wrap{flex:1;min-width:0;border-radius:11px;border:1.5px solid #cfd8e6;background:#fff;transition:border-color .15s ease;position:relative}.hero-notification__composer-row:focus-within .hero-notification__input-wrap{border-color:var(--primary)}.hero-notification__input{width:100%;min-width:0;min-height:100px;border:0;border-radius:11px;padding:10px 12px 32px;font-size:13px;line-height:1.45;color:var(--text);background:transparent;font-family:inherit;resize:vertical}.hero-notification__input:focus{outline:none}.hero-notification__char-ring{position:absolute;bottom:8px;right:10px;display:flex;align-items:center;gap:4px;pointer-events:none}.hero-notification__char-ring svg{width:22px;height:22px;transform:rotate(-90deg)}.hero-notification__char-ring-track{fill:none;stroke:#e2e8f0;stroke-width:3}.hero-notification__char-ring-fill{fill:none;stroke:var(--primary);stroke-width:3;stroke-dasharray:94 94;stroke-linecap:round;transition:stroke-dashoffset .2s ease}.hero-notification__char-count{font-size:10px;color:var(--muted-2);font-variant-numeric:tabular-nums}.hero-notification__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.hero-notification__send,.hero-notification__cancel{height:36px;border-radius:10px;min-width:86px;padding:0 14px;font-size:12px;font-weight:700}.hero-notification__send{background:var(--primary);color:#fff}.hero-notification__send:not(:disabled):hover{background:var(--primary-2)}.hero-notification__cancel{min-width:72px}.hero-notification__error{margin:0;font-size:12px;color:#b91c1c;position:relative}.hero-notification-dialog{max-width:min(520px,100vw - 24px);overflow:hidden}.hero-notification-dialog .hero-notification__composer-row{align-items:stretch;gap:10px}.hero-notification-dialog .hero-notification__input-wrap{min-height:110px}.hero-notification-dialog__head{margin-bottom:14px}.hero-notification-dialog__title-group{display:flex;align-items:flex-start;gap:10px}.hero-notification-dialog__icon-wrap{width:34px;height:34px;border-radius:10px;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification-dialog__icon-wrap i{font-size:14px}.hero-notification-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-dialog__subtitle{margin:2px 0 0;font-size:11px;color:var(--muted-2)}.hero-notification-list-dialog{max-width:min(620px,100vw - 24px)}.hero-notification-list-dialog__head{margin-bottom:10px;align-items:flex-start}.hero-notification-list-dialog__head-actions{display:inline-flex;align-items:center;gap:8px;flex-shrink:0}.hero-notification-list-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-list-dialog__body{max-height:min(60vh,480px);overflow-y:auto;padding-right:2px}.ack-all-confirm-dialog{max-width:min(380px,100vw - 24px);text-align:center;padding:28px 24px 24px;display:flex;flex-direction:column;align-items:center;gap:10px;overflow:hidden}.ack-all-confirm-dialog__icon-wrap{width:52px;height:52px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.2);color:var(--primary);display:flex;align-items:center;justify-content:center;margin-bottom:2px}.ack-all-confirm-dialog__icon-wrap i{font-size:22px}.ack-all-confirm-dialog__title{margin:0;font-size:16px;font-weight:800;color:var(--text)}.ack-all-confirm-dialog__desc{margin:0;font-size:13px;color:var(--muted);line-height:1.55;max-width:280px}.ack-all-confirm-dialog__actions{display:flex;gap:10px;margin-top:6px;width:100%;justify-content:center}.ack-all-confirm-dialog__actions .btn{min-width:96px;height:38px;border-radius:10px;font-size:13px;font-weight:700}.ack-all-confirm-dialog__confirm-btn{background:var(--primary);color:#fff;display:inline-flex;align-items:center;gap:6px}.ack-all-confirm-dialog__confirm-btn i{font-size:12px}.ack-all-confirm-dialog__confirm-btn:not(:disabled):hover{background:var(--primary-2)}.hero-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;align-self:center}.content{width:100%}.content .tab-pane[hidden]{display:none!important}.content .tab-pane--feeding{max-width:1100px;margin:0 auto;padding:0 20px}.content .tab-pane--documents{max-width:none;padding:0}.content .tab-pane--weight{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 5%,var(--surface, #fff)) 0%,var(--surface, #fff) min(36vh,280px),var(--surface, #fff) 100%)}.content .tab-pane--mom{max-width:1100px;margin:0 auto;padding:0 20px calc(24px + env(safe-area-inset-bottom,0px))}.content .tab-pane--medical{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 7%,var(--surface, #fff)) 0%,var(--surface, #fff) min(42vh,320px),var(--surface, #fff) 100%)}.bottom-nav{--bn-primary: #5b6cff;--bn-primary-rgb: 91, 108, 255;--bn-inactive: #b0b7c3;--bn-height: 62px;position:fixed;left:0;right:0;bottom:0;width:100%;box-sizing:border-box;z-index:90;display:flex;align-items:center;justify-content:space-between;gap:4px;min-height:var(--bn-height);padding:0 4px env(safe-area-inset-bottom,0px);background:#fffffff5;backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);border-top:1px solid rgba(0,0,0,.055);box-shadow:0 -4px 24px -4px #0f172a14}.bottom-nav .bn-side{flex:1;display:flex;align-items:center;justify-content:space-around;min-width:0}.bottom-nav .bn-fab{flex-shrink:0;width:56px;height:56px;margin-top:-20px;border-radius:999px;border:3px solid rgba(255,255,255,.96);background:var(--bn-primary);color:#fff;font-size:22px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:transform .15s ease,background .15s ease,opacity .15s ease}.bottom-nav .bn-fab:hover:not(:disabled){background:var(--primary-2, #4c5cf0);transform:translateY(-2px)}.bottom-nav .bn-fab:active:not(:disabled){transform:translateY(0) scale(.94)}.bottom-nav .bn-fab:disabled{background:var(--bn-inactive);cursor:not-allowed;opacity:.7}.bottom-nav .bn-item{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;height:var(--bn-height);padding:0 2px;background:transparent;border:none;color:var(--bn-inactive);cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:color .22s ease}.bottom-nav .bn-item .bn-icon-wrap{display:flex;align-items:center;justify-content:center;width:32px;height:30px;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.bottom-nav .bn-item .bn-icon{font-size:21px;line-height:1;transition:filter .22s ease;display:block}.bottom-nav .bn-item .bn-label{font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1;transition:font-weight .22s ease}.bottom-nav .bn-item:not(.active):hover{color:rgba(var(--bn-primary-rgb),.7)}.bottom-nav .bn-item:active .bn-icon-wrap{transform:scale(.88)}.bottom-nav .bn-item.active{color:var(--bn-primary)}.bottom-nav .bn-item.active .bn-icon-wrap{transform:translateY(-3px) scale(1.15)}.bottom-nav .bn-item.active .bn-icon{filter:drop-shadow(0 2px 8px rgba(var(--bn-primary-rgb),.4))}.bottom-nav .bn-item.active .bn-label{font-weight:700}.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px;margin-bottom:16px}.card-header{margin-bottom:16px}.card-header h2{margin:0;font-size:18px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:8px}.card-header h2 i{color:var(--primary)}.card-header p{margin:6px 0 0;color:var(--muted);font-size:13px}.eyebrow{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em}.eyebrow i{margin-right:4px}.form{display:flex;flex-direction:column;gap:14px}.field{display:flex;flex-direction:column;gap:6px}.field .label{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.02em;display:flex;align-items:center;gap:6px;flex-wrap:wrap}.field .label .label-hint{font-size:11px;font-weight:500;color:var(--muted-2);font-style:italic;text-transform:none;letter-spacing:0}.field input[type=text],.field input[type=date],.field input[type=time],.field input[type=number]{padding:12px 14px;border:1.5px solid var(--border);border-radius:10px;font-size:14px;font-family:inherit;color:var(--text);background:#fafbfc;width:100%;min-width:0;box-sizing:border-box;appearance:none;-webkit-appearance:none;transition:border-color .15s,background .15s;min-height:46px;font-variant-numeric:tabular-nums}.field input[type=text]:focus,.field input[type=date]:focus,.field input[type=time]:focus,.field input[type=number]:focus{outline:none;border-color:var(--primary);background:#fff}.field input[type=text]::placeholder,.field input[type=date]::placeholder,.field input[type=time]::placeholder,.field input[type=number]::placeholder{color:var(--muted-2)}.field input[type=number]{-moz-appearance:textfield}.field input[type=number]::-webkit-outer-spin-button,.field input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.radio-group{display:flex;gap:8px;flex-wrap:wrap}.chip{padding:9px 16px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;cursor:pointer;color:var(--text);transition:all .15s;font-family:inherit}.chip:hover{border-color:var(--primary);color:var(--primary)}.chip.active{background:var(--primary);border-color:var(--primary);color:#fff}.form-actions{display:flex;gap:10px;margin-top:4px;flex-wrap:wrap}.btn{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;border:none;border-radius:10px;font-size:14px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,color .15s,border-color .15s}.btn:disabled{opacity:.5;cursor:not-allowed}.btn.small{padding:6px 12px;font-size:12px}.btn.primary{background:var(--primary);color:#fff}.btn.primary:not(:disabled):hover{background:var(--primary-2)}.btn.ghost{background:transparent;color:var(--muted);border:1.5px solid var(--border)}.btn.ghost:hover{color:var(--text);border-color:var(--border-strong)}.btn.danger-ghost{background:transparent;color:var(--rose);border:1.5px solid var(--rose-soft)}.btn.danger-ghost:hover{background:var(--rose-soft)}.btn.icon-btn{padding:0;width:36px;height:36px;justify-content:center;background:transparent;color:var(--muted);border:1.5px solid var(--border);border-radius:10px}.btn.icon-btn i{font-size:14px}.btn.icon-btn:not(:disabled):hover{color:var(--primary);border-color:var(--primary);background:var(--primary-soft)}.btn.icon-btn .spin{animation:spin-refresh .9s linear infinite}@keyframes spin-refresh{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.age-stats{display:flex;align-items:baseline;justify-content:flex-start;gap:16px;padding:14px 16px;background:var(--primary-soft);border-radius:12px}.age-stats .stat{text-align:center}.age-stats .stat .num{font-size:26px;font-weight:700;color:var(--primary);line-height:1;font-variant-numeric:tabular-nums}.age-stats .stat .unit{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-top:3px}.age-stats .dot{color:var(--muted-2);font-size:20px}.period-card{margin-bottom:0}.period-card h2{margin:6px 0 8px;font-size:20px;font-weight:700}.period-card .period-summary{color:var(--muted);font-size:14px;margin:0 0 14px;line-height:1.55}.schedule-grid{display:grid;grid-template-columns:1fr;gap:10px}.schedule{display:flex;gap:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.schedule i{color:var(--primary);font-size:18px;margin-top:2px}.schedule .schedule-title{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px}.schedule .schedule-text{font-size:14px;color:var(--text);line-height:1.5}.compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}@media(max-width:768px){.compare-grid{grid-template-columns:1fr}}.day-card{margin-bottom:0;min-width:0;overflow:hidden}.day-card.today{border-color:#3b82f6;background:linear-gradient(180deg,#dbeafe,#fff 50%)}.day-card.yesterday{background:#fafbfc}.day-date-nav-wrap{position:relative;margin-bottom:12px}.day-date-nav-reset{position:absolute;top:-8px;right:-8px;z-index:1;width:24px;height:24px;border:1px solid var(--border);border-radius:50%;background:#fff;color:var(--muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;box-shadow:0 1px 3px #0f172a14}.day-date-nav-reset i{font-size:10px}.day-date-nav-reset:hover{background:#fee2e2;border-color:#fecaca;color:#dc2626}.day-date-nav-reset:active{background:#fee2e2;color:#dc2626}.day-date-strip{display:grid;grid-template-columns:32px 1fr 32px;gap:6px;align-items:stretch;padding:6px 8px;border-radius:10px;background:#fff;border:1px solid var(--border);touch-action:manipulation}.day-date-strip-scroller{display:flex;gap:4px;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:thin;padding:1px 2px}.day-date-strip-scroller::-webkit-scrollbar{height:4px}.day-date-strip-scroller::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px}.day-date-strip-day{flex:0 0 64px;scroll-snap-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;min-width:0;padding:6px 4px;border:1px solid transparent;border-radius:8px;background:#f8fafc;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-day:hover{background:#eef2ff;border-color:#c7d2fe}.day-date-strip-day.active{background:#dbeafe;border-color:#3b82f6;box-shadow:inset 0 0 0 1px #3b82f626}.day-date-strip-date{font-size:12px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml{font-size:11px;font-weight:600;color:#1d4ed8;font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml.empty{color:var(--muted);font-weight:500}.day-date-strip-arrow{width:32px;min-height:52px;flex-shrink:0;border:none;border-radius:8px;background:#f1f5f9;color:var(--text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-arrow i{font-size:12px}.day-date-strip-arrow:hover:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:active:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:disabled{opacity:.35;cursor:not-allowed}.day-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px}.day-head h3{margin:4px 0 0;font-size:16px;color:var(--text);font-variant-numeric:tabular-nums}.big-total{display:flex;align-items:baseline;gap:3px}.big-total .num{font-size:32px;font-weight:800;color:var(--text);line-height:1;font-variant-numeric:tabular-nums}.big-total .unit{font-size:14px;color:var(--muted);font-weight:500}.day-stats{display:flex;flex-wrap:wrap;gap:10px 16px;padding:10px 0 0;border-top:1px solid var(--border)}.day-stats>div{font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:5px}.day-stats>div i{color:var(--primary);font-size:12px}.compare-banner{margin-top:12px;padding:12px;background:var(--primary-soft);border-radius:12px;font-size:12px;color:var(--text);display:flex;flex-direction:column;gap:8px}.compare-banner strong{color:var(--primary);font-variant-numeric:tabular-nums;font-weight:700}.compare-banner .compare-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.compare-banner .compare-row:not(:last-child){padding-bottom:8px;border-bottom:1px dashed rgba(15,23,42,.1)}.compare-banner .compare-stats{display:flex;align-items:center;gap:6px;flex:1}.compare-banner .compare-stats .label{color:var(--muted);font-weight:500;font-size:11px}.compare-banner .compare-stats .count{color:var(--muted-2);font-size:11px}.compare-banner .compare-change{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;flex-shrink:0}.compare-banner .compare-change i{font-size:10px}.compare-banner .compare-change i.up{color:var(--mint)}.compare-banner .compare-change i.down{color:var(--amber)}.compare-banner .compare-change strong{font-size:12px}.compare-banner .compare-change span{color:var(--muted-2);font-weight:500}.today-logs{margin-top:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.logs-header{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}.logs-header span{display:flex;align-items:center;gap:6px}.logs-header span:before{content:\"\\1f37c\";font-size:14px}.logs-header .logs-more{text-transform:none;letter-spacing:0;background:transparent;border:none;color:var(--primary);font-size:12px;font-weight:600;padding:4px 8px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:3px;transition:background .2s ease}.logs-header .logs-more i{font-size:10px}.logs-header .logs-more:hover{background:var(--primary-soft)}.log-list{display:flex;flex-direction:column;gap:6px;min-width:0}.log-row{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#fafbfc;border-radius:8px;font-size:13px;min-width:0;max-width:100%;transition:all .2s ease;position:relative}.log-row.low-volume .log-volume{color:#f59e0b;font-weight:900}.log-row.high-volume .log-volume{color:#10b981;font-weight:900}.log-row:hover{background:#f1f5f9;transform:translateY(-1px);box-shadow:0 2px 8px #0000000d}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.log-time{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;min-width:42px}.log-volume{font-weight:800;color:var(--primary);font-size:16px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}.log-gap{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;background:#f3f4f6;color:#6b7280;border-radius:999px;font-size:10.5px;font-weight:600;white-space:nowrap;flex-shrink:0;font-variant-numeric:tabular-nums;transition:all .2s ease}.log-gap i{font-size:9px;opacity:.7}.log-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.log-gap.feed-gap-warn i{opacity:.95;color:inherit}.log-note{color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.log-actions{margin-left:auto;display:inline-flex;align-items:center;gap:2px}.log-edit,.log-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:4px 6px;border-radius:6px;font-size:12px;transition:all .2s ease}.log-edit:hover{color:var(--primary);background:var(--primary-soft)}.log-del:hover{color:var(--rose);background:var(--rose-soft)}.empty-mini{padding:12px;text-align:center;color:var(--muted-2);font-size:13px;font-style:italic}.countdown-hero{display:flex;flex-direction:column;gap:16px;padding:18px 20px;background:#fff;border-radius:16px;border:1px solid #e6e9ff;transition:all .3s ease;margin-bottom:20px}.countdown-hero .hero-main{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px}.countdown-hero .feeding-time-card{flex:1;min-width:0;text-align:center}.countdown-hero .feeding-time-card .time-header{margin-bottom:12px;display:flex;justify-content:center}.countdown-hero .feeding-time-card .time-header .status-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;padding:6px 12px;border-radius:14px;white-space:nowrap}.countdown-hero .feeding-time-card .time-header .status-badge i{font-size:11px}.countdown-hero .feeding-time-card .time-header .status-badge.status-waiting{background:var(--primary-soft);color:var(--primary)}.countdown-hero .feeding-time-card .time-header .status-badge.status-now{background:#fee2e2;color:#dc2626;animation:pulse-gentle 2s ease-in-out infinite;font-size:11px;text-transform:none;font-weight:700}.countdown-hero .feeding-time-card .time-header .status-badge.status-late{background:var(--rose-soft);color:var(--rose)}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:42px;font-weight:800;line-height:1;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.02em;margin-bottom:4px}.countdown-hero .feeding-time-card .time-display .time-remaining{font-size:15px;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}.countdown-hero .countdown-next{display:flex;flex-direction:column;gap:8px;padding-left:16px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:0}.countdown-hero .next-item{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.countdown-hero .next-item i{color:var(--primary);font-size:13px}.countdown-hero .next-item strong{font-weight:700}.countdown-hero .next-item .predict-label,.countdown-hero .next-item .recommend-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);margin-left:4px}.countdown-hero .next-item .recommend-label{color:var(--mint)}.countdown-hero .volume-info{display:flex;flex-direction:column;gap:8px;padding-left:20px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:160px;transition:all .3s ease}.countdown-hero .volume-info .volume-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.countdown-hero .volume-info .volume-row .volume-prefix{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);flex-shrink:0}.countdown-hero .volume-info .volume-row .volume-amount{font-size:22px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1;text-align:right}.countdown-hero .volume-info .volume-row.predicted-row .volume-prefix,.countdown-hero .volume-info .volume-row.predicted-row .volume-amount{color:var(--primary)}.countdown-hero .volume-info .volume-row.recommended-row .volume-prefix,.countdown-hero .volume-info .volume-row.recommended-row .volume-amount{color:var(--mint)}.countdown-hero[data-status=waiting]{border-color:#bbf7d0;background:linear-gradient(135deg,#f7fef8,#f0fdf4)}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .predicted-time{color:#16a34a}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .time-remaining{color:#15803d}.countdown-hero[data-status=waiting] .feeding-time-card .status-badge.status-waiting{background:#dcfce7!important;color:#15803d!important;border:1px solid #86efac}.countdown-hero[data-status=waiting] .volume-info .volume-amount{color:#16a34a}.countdown-hero[data-status=waiting] .volume-info .volume-prefix{color:#15803d}.countdown-hero[data-status=late]{border-color:#fca5a5;background:linear-gradient(135deg,#fef2f2,#fee2e2)}.countdown-hero[data-status=late] .feeding-time-card .time-display .predicted-time{color:#dc2626}.countdown-hero[data-status=late] .feeding-time-card .time-display .time-remaining{color:#b91c1c}.countdown-hero[data-status=late] .feeding-time-card .status-badge.status-late{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5}.countdown-hero[data-status=late] .volume-info .volume-amount{color:#dc2626}.countdown-hero[data-status=late] .volume-info .volume-prefix{color:#b91c1c}.countdown-hero[data-status=now]{border-color:#fbbf24;background:linear-gradient(135deg,#fefce8,#fef3c7);animation:pulse-card 2s ease-in-out infinite}.countdown-hero[data-status=now] .feeding-time-card .time-display .predicted-time{color:#92400e}.countdown-hero[data-status=now] .feeding-time-card .time-display .time-remaining{color:#a16207}.countdown-hero[data-status=now] .feeding-time-card .status-badge.status-now{background:#fef3c7!important;color:#b45309!important;border:1px solid #f59e0b!important}.countdown-hero[data-status=now] .volume-info .volume-amount{color:#92400e}.countdown-hero[data-status=now] .volume-info .volume-prefix{color:#a16207}@media(max-width:540px){.countdown-hero .hero-main{flex-direction:column;align-items:stretch;gap:12px}.countdown-hero .hero-main .volume-info{border-left:none;border-top:1px dashed #d8dcff;padding-left:0;padding-top:12px;min-width:auto}.countdown-hero .hero-main .volume-info .volume-row{justify-content:space-between;padding:0 4px}.countdown-hero .hero-main .feeding-time-card .time-display .predicted-time{font-size:36px}.countdown-hero .hero-main .feeding-time-card .volume-info .volume-amount{font-size:20px}}@media(max-width:400px){.countdown-hero .feeding-time-card .status-badge{font-size:10px;padding:4px 10px}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:32px}.countdown-hero .volume-info .volume-amount{font-size:18px}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.65}}@keyframes pulse-gentle{0%,to{opacity:1}50%{opacity:.75}}@keyframes pulse-urgent{0%,to{opacity:1;text-shadow:0 0 0 rgba(239,68,68,0)}50%{opacity:.8;text-shadow:0 0 8px rgba(239,68,68,.25)}}@keyframes pulse-card{0%,to{transform:scale(1);box-shadow:0 1px 3px #0000001a,0 0 #f8717100}50%{transform:scale(1.02);box-shadow:0 4px 12px #00000026,0 0 0 2px #f871714d}}.burp-warning{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin:0 0 16px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1px solid #f59e0b;border-radius:14px;box-shadow:0 2px 8px #f59e0b33;animation:burp-pulse 2s ease-in-out infinite}.burp-warning__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:#f59e0b;border-radius:50%;color:#fff;font-size:18px;animation:burp-icon-bounce 1.5s ease-in-out infinite}.burp-warning__content{flex:1;min-width:0}.burp-warning__title{font-size:15px;font-weight:700;color:#92400e;margin-bottom:4px}.burp-warning__text{font-size:13px;color:#78350f;line-height:1.5;margin-bottom:8px}.burp-warning__text strong{font-weight:600;color:#92400e}.burp-warning__countdown{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:#ffffffb3;border-radius:8px;font-size:13px;color:#92400e;font-weight:500}.burp-warning__countdown i{font-size:14px;color:#f59e0b}.burp-warning__countdown strong{font-size:16px;font-weight:700;color:#d97706}@keyframes burp-pulse{0%,to{box-shadow:0 2px 8px #f59e0b33}50%{box-shadow:0 4px 16px #f59e0b59}}@keyframes burp-icon-bounce{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.countdown-progress{margin-top:10px}.countdown-progress .cp-track{position:relative;height:6px;background:#ffffffbf;border-radius:999px;overflow:hidden}.countdown-progress .cp-fill{height:100%;background:linear-gradient(90deg,var(--primary),var(--accent));border-radius:999px;transition:width .4s ease}.countdown-progress .cp-fill[data-status=waiting]{background:linear-gradient(90deg,#22c55e,#16a34a)}.countdown-progress .cp-fill[data-status=late]{background:linear-gradient(90deg,#f87171,#dc2626)}.countdown-progress .cp-fill[data-status=now]{background:linear-gradient(90deg,#fbbf24,#d97706)}.countdown-progress .cp-labels{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px;font-size:11.5px;color:var(--muted)}.countdown-progress .cp-labels i{font-size:10px;margin-right:2px;color:var(--muted-2)}.countdown-progress .cp-labels .muted{color:var(--muted-2)}.countdown-progress .cp-labels .cp-right{font-weight:600;color:var(--muted);white-space:nowrap}.predict-head{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;margin-bottom:16px}.predict-head .eyebrow{color:var(--primary);display:flex;align-items:center;gap:4px}.predict-head .eyebrow.sparkle i{animation:sparkle 2s ease-in-out infinite}.predict-head h2{margin:4px 0 0;font-size:20px;font-weight:700}.last-feed{display:flex;align-items:center;gap:12px;padding:12px 14px;background:#fff;border-radius:12px;margin-bottom:14px;border:1px solid #e8e9ff}.last-feed .last-feed-icon{width:38px;height:38px;border-radius:50%;background:var(--primary-soft);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.last-feed .last-feed-icon i{color:var(--primary);font-size:16px}.last-feed .last-feed-body{flex:1;min-width:0}.last-feed .last-feed-label{font-size:10px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em;margin-bottom:2px}.last-feed .last-feed-value{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.last-feed .last-feed-value strong{color:var(--primary);font-size:18px;font-weight:800}.last-feed .last-feed-value .sep{color:var(--muted-2)}.last-feed .last-feed-value .ago{color:var(--accent);font-weight:600;font-size:13px}.last-feed .last-feed-note{margin-top:4px;font-size:12px;color:var(--muted);font-style:italic;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.predict-empty{padding:14px 16px;background:#fff;border-radius:12px;color:var(--muted);font-size:13px;display:flex;align-items:center;gap:8px}.predict-empty i{color:var(--primary)}.predict-body{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:14px}@media(max-width:640px){.predict-body{grid-template-columns:1fr}}.predict-metric{background:#fff;border-radius:12px;padding:14px;text-align:center}.predict-metric .metric-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;display:flex;align-items:center;justify-content:center;gap:5px}.predict-metric .metric-label i{color:var(--primary);font-size:12px}.predict-metric .metric-value{font-size:28px;font-weight:800;line-height:1;color:var(--primary);font-variant-numeric:tabular-nums}.predict-metric .metric-value .ml-unit{font-size:14px;color:var(--muted);font-weight:500;margin-left:2px}.predict-metric .metric-sub{margin-top:6px;font-size:12px;color:var(--muted)}.reasoning{background:#fff;border-radius:12px;padding:12px 14px;margin-bottom:12px}.reasoning .reasoning-head{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;margin-bottom:6px}.reasoning .reasoning-head i{color:var(--primary);margin-right:4px}.reasoning ul{margin:0;padding-left:18px;list-style:disc}.reasoning li{font-size:13px;color:var(--text);line-height:1.55;margin:2px 0}.predict-actions{display:flex;justify-content:flex-end}.feed-list-card{margin-bottom:16px}.feed-list-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.feed-list-head h2{margin:4px 0 0;font-size:18px;font-weight:700}.feed-list-compact{display:flex;flex-direction:column;gap:6px}.feed-row{display:grid;grid-template-columns:68px 80px 1fr auto;gap:10px;align-items:center;padding:10px 12px;transition:all .2s ease;position:relative}.feed-row.low-volume .feed-row-volume strong{color:#f59e0b;font-weight:900}.feed-row.high-volume .feed-row-volume strong{color:#10b981;font-weight:900}.feed-row{background:#fafbfc;border-radius:10px;border:1px solid var(--border)}.feed-row .feed-row-time{display:flex;flex-direction:column;line-height:1.2;font-variant-numeric:tabular-nums;gap:3px}.feed-row .feed-row-time .time{font-weight:700;color:var(--text);font-size:14px}.feed-row .feed-row-time .date{font-size:11px;color:var(--muted-2)}.feed-row .feed-row-time .feed-row-gap{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;font-weight:600;color:#6b7280;background:#f3f4f6;padding:1px 6px;border-radius:999px;width:fit-content;white-space:nowrap;font-variant-numeric:tabular-nums}.feed-row .feed-row-time .feed-row-gap i{font-size:8px;opacity:.7}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn i{opacity:.95;color:inherit}.feed-row .feed-row-volume{font-variant-numeric:tabular-nums}.feed-row .feed-row-volume strong{color:var(--primary);font-size:19px;font-weight:800;letter-spacing:-.01em}.feed-row .feed-row-volume .u{font-size:11px;color:var(--muted);margin-left:2px}.feed-row .feed-row-note{font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.feed-row .feed-row-note.muted{color:var(--muted-2);font-style:italic}.feed-row .feed-row-actions{display:inline-flex;align-items:center;gap:2px;margin-left:auto}.feed-row .feed-row-edit,.feed-row .feed-row-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:6px 8px;border-radius:6px;font-size:13px}.feed-row .feed-row-edit:hover{color:var(--primary, #5b6cff);background:#5b6cff1a}.feed-row .feed-row-del:hover{color:var(--rose);background:var(--rose-soft)}@media(max-width:480px){.feed-row{grid-template-columns:58px 70px 1fr auto;gap:8px;padding:8px 10px}.feed-row .feed-row-time .time{font-size:13px}}.dialog-wide{max-width:720px;display:flex;flex-direction:column;padding:0;overflow:hidden}@media(max-width:640px){.dialog-wide{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 72px);border-radius:14px}}.dialog-wide .dialog-head{position:sticky;top:0;z-index:5;background:var(--surface);padding:16px 20px;margin:0;border-bottom:1px solid var(--border);-webkit-backdrop-filter:saturate(1.2) blur(6px);backdrop-filter:saturate(1.2) blur(6px)}@media(max-width:640px){.dialog-wide .dialog-head{padding:12px 14px}}.dialog-wide .dialog-close{width:40px;height:40px;font-size:16px;background:#f1f3f7;color:var(--text)}.dialog-wide .dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:18px 20px 24px;overflow-y:auto;flex:1;min-height:0}@media(max-width:640px){.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:14px 14px 20px}}.history-body{display:flex;flex-direction:column;gap:18px}.history-paging-hint{margin:0;font-size:12px;line-height:1.45}.history-load-more{display:grid;justify-items:center;width:100%;box-sizing:border-box;padding:8px 0 12px}.history-load-more .btn{width:auto;max-width:min(320px,100%);min-width:0;margin-inline:auto;justify-content:center}.tips-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;background:#f1f3f7;border-radius:12px;margin-bottom:16px}.nav-arrow{width:40px;height:40px;border-radius:50%;border:none;background:#fff;color:var(--primary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px;transition:all .15s;flex-shrink:0}.nav-arrow:hover:not(:disabled){background:var(--primary);color:#fff;transform:scale(1.05)}.nav-arrow:active:not(:disabled){transform:scale(.95)}.nav-arrow:disabled{opacity:.35;cursor:not-allowed}.tips-now-btn{flex:1;padding:9px 16px;border:none;background:transparent;border-radius:999px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s}.tips-now-btn i{font-size:12px}.tips-now-btn:hover{color:var(--primary);background:#fff}.tips-now-btn.active{background:var(--primary);color:#fff;pointer-events:none}.tips-dialog-body .period-summary{font-size:14px;color:var(--muted);line-height:1.6;margin:0 0 14px}.tips-dialog-body .schedule-grid{grid-template-columns:repeat(2,1fr);margin-bottom:16px}@media(max-width:560px){.tips-dialog-body .schedule-grid{grid-template-columns:1fr}}.dialog-tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.history-group .history-group-head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:var(--primary-soft);border-radius:10px;margin-bottom:8px}.history-group .history-group-head .h-date{font-size:14px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-day{font-size:11px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.05em}.history-group .history-group-head .h-total{font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-total strong{color:var(--primary);font-size:20px;font-weight:800}.history-group .history-group-head .h-total .h-count{color:var(--muted);font-size:12px;font-weight:500;margin-left:4px}.history-group .history-group-head .h-total .h-avg{color:var(--primary);font-size:12px;font-weight:600;margin-left:4px;font-variant-numeric:tabular-nums}.history-group .history-group-rows{display:flex;flex-direction:column;gap:4px}.tips-section-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;padding:4px 4px 0}.tips-section-head .eyebrow i{margin-right:4px}.tips-section-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text)}.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}.filter-chip{padding:7px 14px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;font-weight:500;cursor:pointer;color:var(--muted);display:inline-flex;align-items:center;gap:5px;transition:all .15s;font-family:inherit}.filter-chip i{font-size:12px}.filter-chip:hover{border-color:var(--cat-color, var(--primary));color:var(--cat-color, var(--primary))}.filter-chip.active{background:var(--cat-color, var(--primary));border-color:var(--cat-color, var(--primary));color:#fff}.tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-bottom:20px}.tip-card{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--cat-color, var(--primary));border-radius:12px;padding:16px;transition:transform .15s}.tip-card:hover{transform:translateY(-2px)}.tip-card .tip-head{margin-bottom:8px}.tip-card .tip-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;background:var(--cat-color, var(--primary));color:#fff;font-size:11px;font-weight:600;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.tip-card .tip-pill i{font-size:10px}.tip-card .tip-title{margin:6px 0 8px;font-size:15px;font-weight:700;line-height:1.35;color:var(--text)}.tip-card .tip-body{margin:0;font-size:13px;line-height:1.55;color:var(--muted)}.empty{grid-column:1/-1;padding:40px 20px;text-align:center;color:var(--muted-2)}.empty i{font-size:32px;margin-bottom:8px;display:block}.upcoming{margin-bottom:24px}.upcoming h3{margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}.upcoming h3 i{color:var(--primary)}.upcoming-grid{display:flex;gap:8px;flex-wrap:wrap}.upcoming-item{padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:10px;font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:6px}.upcoming-item i{color:var(--primary);font-size:12px}.disclaimer{padding:16px;background:var(--amber-soft);border-radius:12px;color:#78350f;font-size:13px;line-height:1.5;margin-bottom:24px;display:flex;gap:8px;align-items:flex-start}.disclaimer i{color:var(--amber);margin-top:2px}.toast{position:fixed;bottom:100px;left:50%;transform:translate(-50%);z-index:90;padding:12px 18px;border-radius:12px;font-size:14px;font-weight:500;box-shadow:0 6px 24px #0f172a1f;display:flex;align-items:center;gap:8px;animation:toastIn .3s cubic-bezier(.4,0,.2,1)}.toast.success{background:var(--mint);color:#fff}.toast.error{background:var(--rose);color:#fff}@keyframes toastIn{0%{transform:translate(-50%,20px);opacity:0}to{transform:translate(-50%);opacity:1}}.fab{position:fixed;bottom:calc(80px + env(safe-area-inset-bottom,0px));right:20px;z-index:96;display:inline-flex;align-items:center;gap:10px;padding:14px 22px 14px 18px;background:var(--primary);color:#fff;border:none;border-radius:999px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;box-shadow:0 8px 24px #5b6cff73;transition:transform .15s,box-shadow .15s,background .15s}.fab i{font-size:18px;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:#ffffff2e}.fab .fab-label{white-space:nowrap}.fab:hover{background:var(--primary-2);transform:translateY(-2px);box-shadow:0 12px 28px #5b6cff8c}.fab:active{transform:translateY(0)}@media(max-width:480px){.fab{padding:14px}.fab .fab-label{display:none}.fab i{font-size:22px;width:28px;height:28px}}.dialog-overlay{position:fixed;inset:0;background:#0f172a8c;z-index:110;display:flex;align-items:center;justify-content:center;padding:20px;animation:overlayIn .2s ease-out}.dialog-overlay.dialog-overlay--front{z-index:400}.dialog-overlay.dialog-overlay--event-reminder{z-index:410}@media(max-width:640px){.dialog-overlay{padding:calc(env(safe-area-inset-top,0px) + 16px) 12px calc(env(safe-area-inset-bottom,0px) + 16px);align-items:flex-start}}@keyframes overlayIn{0%{opacity:0}to{opacity:1}}.dialog{background:var(--surface);border-radius:20px;width:100%;max-width:560px;max-height:min(100vh - 40px,100dvh - 40px);overflow-y:auto;padding:24px;animation:dialogIn .25s cubic-bezier(.4,0,.2,1);box-shadow:0 20px 60px #0f172a40}@media(max-width:640px){.dialog{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 32px);border-radius:16px}}@keyframes dialogIn{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:18px}.dialog-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}.dialog-head h2 i{color:var(--primary)}.dialog-close{background:#f1f3f7;border:none;width:36px;height:36px;border-radius:50%;cursor:pointer;color:var(--muted);font-size:14px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s,color .15s}.dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.log-form{display:flex;flex-direction:column;gap:14px}.log-volume-field{margin:0}.log-volume-field .log-volume-input-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 10px;min-width:0}.log-volume-field .log-volume-input-row>input[type=number]{flex:0 0 118px;width:118px;max-width:min(118px,100%);min-width:0}.log-volume-field .predict-hint--inline{flex:1 1 auto;min-width:0;margin:0;padding:6px 10px;gap:6px;font-size:12px;border-radius:8px}.log-volume-field .predict-hint--inline>i{font-size:13px;flex-shrink:0}.log-volume-field .predict-hint--inline .hint-text{flex:1 1 auto;min-width:0;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-volume-field .predict-hint--inline .btn.small{flex-shrink:0;padding:4px 8px;font-size:11px}@media(max-width:400px){.log-volume-field .log-volume-input-row>input[type=number]{flex:1 1 100px;width:100%;max-width:none}.log-volume-field .predict-hint--inline{flex:1 1 100%;white-space:normal}.log-volume-field .predict-hint--inline .hint-text{white-space:normal;overflow:visible;text-overflow:unset}}.edit-date-banner{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 14px;background:linear-gradient(135deg,#eef2ff,#f5f3ff);border:1px solid #dbe1ff;border-radius:12px;font-size:13px;color:var(--text)}.edit-date-banner>i.pi-calendar{color:var(--primary, #5b6cff);font-size:15px}.edit-date-banner .edb-label{color:var(--muted, #6b7280);font-weight:500}.edit-date-banner .edb-value{font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.edit-date-banner .edb-lock{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:50%;background:#ffffffe0;border:1px solid #e2e8f0;color:var(--muted, #64748b)}.edit-date-banner .edb-lock i{font-size:14px;line-height:1}.log-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.log-grid .field.full{grid-column:1/-1}.log-grid.log-grid--date-time{grid-template-columns:repeat(2,minmax(0,1fr))}@media(max-width:560px){.log-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:380px){.log-grid{grid-template-columns:1fr}}.quick-volumes{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.quick-volumes .quick-label{font-size:12px;color:var(--muted);font-weight:600;margin-right:4px}.quick-chip{padding:7px 13px;background:#f1f3f7;border:none;border-radius:999px;font-size:12px;font-weight:600;cursor:pointer;color:var(--muted);transition:all .15s;font-family:inherit}.quick-chip:hover{background:var(--primary-soft);color:var(--primary)}.quick-chip.now-chip{background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;gap:4px}.quick-chip.now-chip:hover{background:var(--primary);color:#fff}.quick-chip.formula-chip{display:inline-flex;align-items:center;gap:5px;background:#fff7ed;color:#c2410c;border:1px solid #fed7aa}.quick-chip.formula-chip i{font-size:11px;opacity:.9}.quick-chip.formula-chip:hover{background:#ffedd5;color:#9a3412;border-color:#fdba74}.quick-chip.formula-chip.active{background:#c2410c;color:#fff;border-color:#c2410c;box-shadow:0 2px 8px #c2410c40}.quick-chip.formula-chip.active:hover{background:#9a3412;border-color:#9a3412;color:#fff}.bottle-prep{background:#f0f9ff;border:1px solid #bae6fd;border-radius:12px;padding:8px 12px;margin-bottom:16px;display:flex;flex-direction:column;gap:8px;transition:background .2s,border-color .2s}.bottle-prep:has(.bp-warning){background:#fffbeb;border-color:#fde68a}.bottle-prep:has(.bp-warning) .bp-info>i{color:#f59e0b}.bottle-prep:has(.bp-warning) .bp-info strong{color:#d97706}.bottle-prep:has(.bp-warning) .bp-prep-user{color:#92400e;background:#fbbf246b}.bottle-prep:has(.bp-warning) .bp-prep-time{color:#c2410c;background:linear-gradient(180deg,#fff7ed,#ffedd5);border-color:#fb923c73}.bottle-prep:has(.bp-form){align-items:stretch;padding:10px 12px 12px}.bottle-prep .bp-warning{display:flex;align-items:center;justify-content:space-between;gap:10px;animation:slideDown .2s ease;flex-wrap:wrap}.bottle-prep .bp-warning__content{display:flex;align-items:center;gap:8px;font-size:13px;flex:1;min-width:0}.bottle-prep .bp-warning__content i{font-size:16px;flex-shrink:0}.bottle-prep .bp-warning__content strong{font-weight:700}.bottle-prep .bp-warning--expired{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:8px 10px;color:#991b1b}.bottle-prep .bp-warning--expired i{color:#dc2626}.bottle-prep .bp-warning--soon{background:#fefce8;border:1px solid #fde68a;border-radius:8px;padding:8px 10px;color:#92400e}.bottle-prep .bp-warning--soon i{color:#f59e0b}.bottle-prep .bp-warning__btn{flex-shrink:0;display:inline-flex;align-items:center;gap:5px;padding:7px 12px;font-size:12px;font-weight:700;font-family:inherit;color:#fff;background:linear-gradient(180deg,#f59e0b,#d97706);border:none;border-radius:8px;cursor:pointer;box-shadow:0 2px 4px #f59e0b40;transition:all .15s ease}.bottle-prep .bp-warning__btn i{font-size:12px}.bottle-prep .bp-warning__btn:hover{background:linear-gradient(180deg,#fbbf24,#f59e0b);transform:translateY(-1px);box-shadow:0 4px 8px #f59e0b59}.bottle-prep .bp-warning__btn:active{transform:translateY(0)}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.bottle-prep .bp-info-row{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%}.bottle-prep:not(:has(.bp-warning)):not(:has(.bp-form)){flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.bottle-prep .bp-info{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--text);flex:1;min-width:0}.bottle-prep .bp-info>i{color:#0ea5e9;font-size:16px;margin-top:2px;flex-shrink:0}.bottle-prep .bp-info .bp-text{line-height:1.45;min-width:0}.bottle-prep .bp-info .bp-useby{display:inline-block;margin-top:2px;font-size:13px}.bottle-prep .bp-info strong{color:#0284c7;font-weight:800;font-variant-numeric:tabular-nums}.bottle-prep .bp-info .muted{color:var(--muted);font-size:13px}.bottle-prep .bp-actions{display:flex;gap:4px}.bottle-prep .bp-add{flex:1;background:transparent;border:none;padding:6px 4px;font-size:13px;font-weight:600;color:#0284c7;cursor:pointer;display:flex;align-items:center;gap:8px;font-family:inherit;text-align:left}.bottle-prep .bp-add>i{font-size:15px}.bottle-prep .bp-add:hover{color:#0369a1}.bottle-prep .bp-form{display:flex;flex-direction:column;gap:8px;width:100%}.bottle-prep .bp-form-head{display:flex;align-items:center;gap:6px}.bottle-prep .bp-form-head>i{color:#0ea5e9;font-size:15px}.bottle-prep .bp-form-head .bp-label{font-size:13px;color:var(--muted);font-weight:600;white-space:nowrap}.bottle-prep .bp-input-wrap{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid #7dd3fc;border-radius:10px;padding:3px;transition:border-color .15s,box-shadow .15s}.bottle-prep .bp-input-wrap:focus-within{border-color:#0ea5e9;box-shadow:0 0 0 3px #0ea5e926}.bottle-prep .bp-input-wrap .bp-step{width:30px;height:30px;flex-shrink:0;border-radius:8px;border:none;background:#e0f2fe;color:#0369a1;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;transition:all .12s}.bottle-prep .bp-input-wrap .bp-step:hover{background:#bae6fd;color:#0284c7}.bottle-prep .bp-input-wrap .bp-step:active{transform:scale(.94)}.bottle-prep .bp-input-wrap .bp-step:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.bottle-prep .bp-input-wrap .bp-input{flex:1;min-width:60px;padding:4px 6px;font-size:18px;font-weight:800;border:none;background:transparent;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706;text-align:center;letter-spacing:-.01em}.bottle-prep .bp-input-wrap .bp-input:focus{outline:none}.bottle-prep .bp-input-wrap .bp-input:disabled{opacity:.55;cursor:not-allowed}.bottle-prep .bp-input-wrap .bp-unit{font-size:12px;color:var(--muted);font-weight:600;padding-right:4px}.bottle-prep .bp-quick{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.bottle-prep .bp-quick .bp-quick-label{font-size:11px;color:var(--muted);font-weight:600;margin-right:2px}.bottle-prep .bp-quick .bp-quick-chip{padding:4px 9px;background:#f0f9ff;border:1px solid #7dd3fc;border-radius:999px;font-size:11.5px;font-weight:700;color:#0369a1;cursor:pointer;font-family:inherit;font-variant-numeric:tabular-nums;transition:all .12s}.bottle-prep .bp-quick .bp-quick-chip:hover{background:#e0f2fe;border-color:#38bdf8;color:#0284c7}.bottle-prep .bp-quick .bp-quick-chip:active{transform:scale(.95)}.bottle-prep .bp-quick .bp-quick-chip:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-form-actions{display:flex;gap:6px;justify-content:flex-end}.bottle-prep .bp-btn{width:32px;height:32px;border-radius:8px;border:none;background:#fff;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:all .15s;flex-shrink:0}.bottle-prep .bp-btn:hover:not(:disabled){background:#e0f2fe;color:#0284c7}.bottle-prep .bp-btn:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-btn.primary{background:#0ea5e9;color:#fff}.bottle-prep .bp-btn.primary:hover:not(:disabled){background:#0284c7}.bottle-prep .bp-btn.danger:hover{background:var(--rose-soft);color:var(--rose)}.calc-block{border:1px solid #fde7c2;background:#fffaf0;border-radius:12px;overflow:hidden}.calc-block .calc-head{padding:10px 14px 0}.calc-block .prep-info{display:flex;align-items:flex-start;gap:6px;font-size:13px;color:var(--text)}.calc-block .prep-info>i{color:#f59e0b;font-size:14px;margin-top:2px;flex-shrink:0}.calc-block .prep-info .prep-info-text{line-height:1.45;min-width:0}.calc-block .prep-info strong{color:#d97706;font-weight:800;font-variant-numeric:tabular-nums}.calc-block .prep-info .muted{color:var(--muted-2);font-size:12px;display:inline-block;margin-top:2px}.calc-block .calc-body{padding:8px 14px 14px;display:flex;flex-direction:column;gap:10px}.calc-block .field.mini.remaining-input{position:relative;margin:0;display:flex;flex-direction:column}.calc-block .field.mini.remaining-input .label{font-size:11px;font-weight:600;color:var(--muted);margin-bottom:4px}.calc-block .field.mini.remaining-input input{width:100%;padding:10px 36px 10px 12px;font-size:16px;font-weight:700;border:1px solid #fadf9e;border-radius:8px;background:#fff;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706}.calc-block .field.mini.remaining-input input:focus{outline:none;border-color:#f59e0b;box-shadow:0 0 0 3px #f59e0b26}.calc-block .field.mini.remaining-input .suffix{position:absolute;right:12px;top:50%;transform:translateY(4px);font-size:12px;color:var(--muted-2);pointer-events:none}.calc-block .calc-result-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.calc-block .calc-result{display:flex;align-items:baseline;gap:4px;font-variant-numeric:tabular-nums}.calc-block .calc-result .calc-label{font-size:13px;color:var(--muted);font-weight:600}.calc-block .calc-result strong{font-size:22px;font-weight:800;color:var(--primary)}.calc-block .calc-result .u{font-size:12px;color:var(--primary);margin-left:2px}.calc-block .calc-result .muted{color:var(--muted-2);font-size:18px}.calc-block .calc-apply{flex-shrink:0}.calc-block .calc-apply:disabled{background:#e5e7eb;color:var(--muted-2);cursor:not-allowed;box-shadow:none}.predict-hint{display:flex;align-items:center;gap:10px;padding:10px 14px;background:linear-gradient(135deg,var(--primary-soft) 0%,var(--accent-soft) 100%);border-radius:10px;font-size:13px}.predict-hint>i{color:var(--primary);font-size:16px}.predict-hint .hint-text{flex:1;color:var(--text)}.predict-hint .hint-text strong{color:var(--primary);font-weight:700}.predict-hint.predict-hint--inline .hint-text strong{font-weight:800;font-variant-numeric:tabular-nums}.sync-error{padding:10px 14px;background:var(--rose-soft);color:#991b1b;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px}.sync-success{padding:10px 14px;background:var(--mint-soft);color:#166534;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px;animation:fadeIn .2s ease}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}.feeding-settings-dialog .feeding-settings-hint{margin:0 0 14px;font-size:13px;line-height:1.45}.feeding-settings-dialog .feeding-settings-hint code{font-size:11px;padding:1px 5px;border-radius:4px;background:#f1f5f9}.feeding-settings-dialog .feeding-settings-grid{grid-template-columns:1fr;gap:16px}.feeding-settings-dialog .field-hint{display:block;margin-top:8px;font-size:12px;line-height:1.4;padding:8px 12px;background:#f8fafc;border-radius:8px;border-left:3px solid #e2e8f0}.feeding-settings-dialog .label{position:relative}.feeding-settings-dialog .label code{font-size:11px;font-weight:600;color:var(--primary);background:var(--primary-soft);padding:2px 6px;border-radius:4px}.feeding-settings-dialog .label .notification-badge{display:inline-flex;align-items:center;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#fff;background:linear-gradient(135deg,#10b981,#059669);padding:2px 6px;border-radius:10px;margin-left:6px;box-shadow:0 1px 2px #10b98133;animation:gentle-pulse 2s ease-in-out infinite}.feeding-settings-dialog .notification-setting-field{position:relative}.feeding-settings-dialog .notification-setting-field input{border:1.5px solid #10b981;background:linear-gradient(to right,#fff,#f0fdf4)}.feeding-settings-dialog .notification-setting-field input:focus{border-color:#059669;box-shadow:0 0 0 3px #10b9811a;background:#fff}.feeding-settings-dialog .notification-setting-field input::placeholder{color:#6b7280;opacity:.7}.feeding-settings-dialog .notification-setting-field .field-hint{border-left-color:#10b981;background:linear-gradient(to right,#f0fdf4,#f8fafc)}@keyframes gentle-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.85;transform:scale(1.02)}}.log-time--with-badge,.feed-row-time .time--with-badge{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.log-merge-badge,.feed-merge-badge{font:inherit;font-size:10px;font-weight:700;padding:2px 7px;border-radius:999px;border:1px solid rgba(99,102,241,.35);background:linear-gradient(135deg,#e0e7ff,#c7d2fe);color:#312e81;cursor:pointer;line-height:1.2;white-space:nowrap}.log-merge-badge:hover,.feed-merge-badge:hover{filter:brightness(1.03)}.log-row--clickable,.feed-row--clickable{cursor:pointer}.log-row--clickable:hover,.feed-row--clickable:hover{background:#6366f10f}.log-row--clickable:focus-visible,.feed-row--clickable:focus-visible{outline:2px solid rgba(99,102,241,.45);outline-offset:2px}.dialog-overlay.dialog-overlay--front.feed-group-detail-overlay{z-index:125}.dialog-overlay.dialog-overlay--front.feed-detail-overlay{z-index:140;animation:overlayIn .12s ease-out}.dialog-overlay.dialog-overlay--front.feed-detail-overlay .feed-detail-dialog{animation:feedDetailIn .14s ease-out}@keyframes feedDetailIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.feed-detail-dialog{--fd-accent: 99, 102, 241;--fd-soft: #eef2ff;--fd-strong: #4338ca;max-width:min(400px,96vw);padding:0;overflow:hidden;display:flex;flex-direction:column}.feed-detail-dialog[data-status=low]{--fd-accent: 245, 158, 11;--fd-soft: #fffbeb;--fd-strong: #b45309}.feed-detail-dialog[data-status=high]{--fd-accent: 244, 63, 94;--fd-soft: #fff1f2;--fd-strong: #e11d48}.feed-detail-dialog[data-status=ok]{--fd-accent: 16, 185, 129;--fd-soft: #ecfdf5;--fd-strong: #059669}.feed-detail-top{position:relative;padding:20px 20px 16px;text-align:center;background:radial-gradient(120% 80% at 50% -20%,rgba(var(--fd-accent),.22),transparent 55%),linear-gradient(180deg,var(--fd-soft) 0%,#ffffff 72%);border-bottom:1px solid rgba(var(--fd-accent),.12)}.feed-detail-close{position:absolute;top:12px;right:12px;width:36px;height:36px;border:none;border-radius:50%;background:#ffffffd9;color:#64748b;cursor:pointer;display:grid;place-items:center;box-shadow:0 2px 8px #0f172a14;transition:background .15s ease,color .15s ease}.feed-detail-close:hover{background:#fff;color:#0f172a}.feed-detail-eyebrow{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.feed-detail-metric{display:flex;align-items:flex-end;justify-content:center;gap:4px;line-height:1;margin-bottom:10px}.feed-detail-metric__value{font-size:clamp(48px,14vw,56px);font-weight:800;letter-spacing:-.04em;color:var(--fd-strong);font-variant-numeric:tabular-nums}.feed-detail-metric__unit{font-size:18px;font-weight:700;color:rgba(var(--fd-accent),.85);padding-bottom:8px}.feed-detail-status{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:999px;font-size:12px;font-weight:700;background:#fff;border:1px solid rgba(var(--fd-accent),.28);color:var(--fd-strong);box-shadow:0 2px 6px rgba(var(--fd-accent),.1)}.feed-detail-status .pi{font-size:13px}.feed-detail-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:14px}.feed-detail-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:12px;background:#ffffffeb;border:1px solid #e2e8f0;font-size:12px;font-weight:600;color:#334155;line-height:1.3;box-shadow:0 1px 4px #0f172a0a}.feed-detail-chip .pi{font-size:13px;color:#6366f1;flex-shrink:0}.feed-detail-chip em{font-style:normal;font-weight:500;color:#94a3b8;margin-left:4px}.feed-detail-chip--time .pi{color:#dc2626}.feed-detail-chip--time span{font-variant-numeric:tabular-nums;font-weight:700;color:#dc2626}.feed-detail-chip--muted{background:#f8fafc;border-color:#e2e8f0;color:#64748b;font-weight:500}.feed-detail-chip--muted .pi{color:#94a3b8}.feed-detail-body{padding:16px 18px 8px;overflow-y:auto;flex:1;min-height:0}.feed-detail-section{margin-bottom:14px}.feed-detail-section:last-child{margin-bottom:0}.feed-detail-section__title{margin:0 0 10px;font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;display:flex;align-items:center;gap:7px}.feed-detail-section__title .pi{font-size:13px;color:#6366f1}.feed-detail-cards{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.feed-detail-card{display:flex;align-items:flex-start;gap:12px;padding:12px 13px;border-radius:14px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;box-shadow:0 2px 8px #0f172a0a}.feed-detail-card--warn,.feed-detail-card--gap.feed-detail-card--warn{border-color:#f59e0b66;background:linear-gradient(180deg,#fffbeb,#fff)}.feed-detail-card--warn .feed-detail-card__icon,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card--warn .feed-detail-card__text,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__text{color:#92400e}.feed-detail-card--position{border-color:#8b5cf659;background:linear-gradient(180deg,#f5f3ff,#fff)}.feed-detail-card--average{border-color:#0ea5e959;background:linear-gradient(180deg,#f0f9ff,#fff)}.feed-detail-card--next{border-color:#10b98159;background:linear-gradient(180deg,#ecfdf5,#fff)}.feed-detail-card--gap:not(.feed-detail-card--warn){border-color:#64748b59;background:linear-gradient(180deg,#f8fafc,#fff)}.feed-detail-card__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;background:#eef2ff;color:#4f46e5;font-size:15px}.feed-detail-card__icon[data-kind=position]{background:#ede9fe;color:#7c3aed}.feed-detail-card__icon[data-kind=average]{background:#e0f2fe;color:#0284c7}.feed-detail-card__icon[data-kind=next]{background:#d1fae5;color:#059669}.feed-detail-card__icon[data-kind=gap]{background:#e2e8f0;color:#475569}.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card__body{min-width:0;flex:1}.feed-detail-card__label{display:block;font-size:11px;font-weight:800;color:#64748b;margin-bottom:3px;letter-spacing:.02em}.feed-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:#0f172a}.fdc-position__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin-bottom:8px}.fdc-position__idx{font-size:18px;font-weight:800;color:#6d28d9;letter-spacing:-.02em}.fdc-position__of{font-size:14px;font-weight:700;color:#a78bfa}.fdc-position__tag{font-size:11px;font-weight:600;color:#64748b;padding:2px 8px;border-radius:999px;background:#8b5cf61a}.fdc-progress{height:8px;border-radius:999px;background:#ede9fe;overflow:hidden;margin-bottom:10px}.fdc-progress__fill{height:100%;border-radius:inherit;background:linear-gradient(90deg,#8b5cf6,#6366f1);transition:width .2s ease}.fdc-position__stats{display:flex;flex-wrap:wrap;gap:8px}.fdc-stat{display:inline-flex;align-items:baseline;gap:4px;padding:6px 10px;border-radius:10px;font-size:12px}.fdc-stat strong{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-stat small{font-size:11px;font-weight:600;opacity:.85}.fdc-stat--violet{background:#f5f3ff;color:#5b21b6}.fdc-stat--violet strong{color:#6d28d9}.fdc-stat--indigo{background:#eef2ff;color:#4338ca}.fdc-stat--indigo strong{color:#4f46e5}.fdc-avg,.fdc-next{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}.fdc-pill{display:inline-flex;padding:4px 10px;border-radius:999px;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-pill--up{background:#dcfce7;color:#15803d}.fdc-pill--down{background:#fee2e2;color:#b91c1c}.fdc-pill--neutral{background:#f1f5f9;color:#475569}.fdc-avg__meta{font-size:12px;color:#64748b;line-height:1.4}.fdc-avg__meta strong{color:#0f172a;font-weight:700}.fdc-next__time{font-size:16px;font-weight:800;color:#dc2626;font-variant-numeric:tabular-nums}.fdc-next__vol{font-size:14px;font-weight:800;color:#059669;padding:4px 10px;border-radius:8px;background:#d1fae5}.fdc-next__gap{font-size:12px;font-weight:600;color:#64748b;display:inline-flex;align-items:center;gap:4px}.fdc-gap{display:flex;flex-direction:column;gap:10px}.fdc-gap__prev{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:10px 12px;border-radius:10px;background:#ffffffe6;border:1px solid #e2e8f0}.fdc-gap__prev-label{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;width:100%}.fdc-gap__prev-time{font-size:17px;font-weight:800;color:#0f172a;font-variant-numeric:tabular-nums}.fdc-gap__prev-vol{font-size:14px;font-weight:800;color:#475569;padding:3px 8px;border-radius:8px;background:#f1f5f9}.fdc-gap__prev-date{font-size:11px;font-weight:600;color:#94a3b8}.fdc-gap__row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;min-width:0}.fdc-gap__interval{display:inline-flex;align-items:center;gap:6px;flex:1 1 auto;min-width:0;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700;color:#475569;background:#e2e8f0;white-space:nowrap}.fdc-gap__interval .pi{font-size:13px;flex-shrink:0}.fdc-gap__interval span{overflow:hidden;text-overflow:ellipsis}.fdc-gap__interval--warn{color:#b45309;background:#fde68a;border:1px solid rgba(245,158,11,.35)}.fdc-gap__vol-diff{flex:0 0 auto;white-space:nowrap;font-size:12px;padding:5px 10px}.feed-detail-quote{margin:0;padding:12px 14px 12px 16px;border-radius:0;border-left:3px solid #c7d2fe;background:#f8fafc;font-size:14px;line-height:1.55;color:#334155;font-style:normal}.feed-detail-footer{padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fafbff}.feed-detail-cta{display:flex;gap:8px;justify-content:stretch}.feed-detail-cta .btn{flex:1;justify-content:center}.feed-detail-cta .btn.primary{flex:1.2}.feed-group-detail-dialog .feed-group-detail-summary{margin:0 0 8px;padding:0 2px;font-size:13px;line-height:1.45}.feed-group-detail-dialog .feed-group-detail-hint{margin:0 0 12px;font-size:12px;line-height:1.4;display:flex;align-items:center;gap:6px}.feed-group-detail-dialog .feed-group-detail-history{margin-bottom:4px}.feed-group-detail-dialog .feed-group-detail-rows{max-height:min(58vh,420px);overflow-y:auto;padding-right:2px}.mom-food-card{--mf-tone: 244, 114, 182;--mf-soft: #fdf2f8;--mf-ring: #fbcfe8;--mf-strong: #db2777;position:relative;overflow:hidden;background:radial-gradient(900px 260px at 0% 0%,rgba(var(--mf-tone),.14),transparent 60%),#fff;border-color:rgba(var(--mf-tone),.22);padding:18px;margin-top:16px}.mom-food-card[data-accent=rose]{--mf-tone: 244, 63, 94;--mf-soft: #fff1f2;--mf-ring: #fecdd3;--mf-strong: #e11d48}.mom-food-card[data-accent=peach]{--mf-tone: 251, 146, 60;--mf-soft: #fff7ed;--mf-ring: #fed7aa;--mf-strong: #ea580c}.mom-food-card[data-accent=mint]{--mf-tone: 16, 185, 129;--mf-soft: #ecfdf5;--mf-ring: #a7f3d0;--mf-strong: #059669}.mom-food-card[data-accent=lavender]{--mf-tone: 139, 92, 246;--mf-soft: #f5f3ff;--mf-ring: #ddd6fe;--mf-strong: #7c3aed}.mom-food-card[data-accent=amber]{--mf-tone: 245, 158, 11;--mf-soft: #fffbeb;--mf-ring: #fde68a;--mf-strong: #d97706}.mf-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.mf-head__lead{min-width:0;flex:1}.mf-head .eyebrow{display:inline-flex;align-items:center;gap:6px;color:var(--mf-strong);font-weight:700}.mf-head .eyebrow i{font-size:13px}.mf-head h2{margin:4px 0 0;font-size:18px;font-weight:800;color:var(--text);line-height:1.25}.mf-headline{margin:4px 0 0;font-size:13px;color:var(--mf-strong);font-weight:600}.mf-nav{display:inline-flex;align-items:center;gap:6px;background:var(--mf-soft);border-radius:999px;padding:4px;border:1px solid rgba(var(--mf-tone),.25);flex-shrink:0}.mf-arrow{width:30px;height:30px;border-radius:50%;border:none;background:#fff;color:var(--mf-strong);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .15s ease,background .15s ease}.mf-arrow i{font-size:12px}.mf-arrow:hover:not(:disabled){transform:scale(1.08);background:var(--mf-strong);color:#fff}.mf-arrow:disabled{opacity:.35;cursor:not-allowed}.mf-step{font-variant-numeric:tabular-nums;font-size:12px;color:var(--muted);padding:0 4px}.mf-step strong{color:var(--mf-strong);font-weight:800;font-size:13px}.mf-reset{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--mf-strong);background:transparent;border:1px dashed rgba(var(--mf-tone),.5);border-radius:999px;padding:4px 12px;cursor:pointer;margin-bottom:10px;transition:background .15s ease}.mf-reset:hover{background:var(--mf-soft)}.mf-reset i{font-size:11px}.mf-desc{margin:0 0 14px;font-size:13px;color:var(--muted);line-height:1.55}.mf-columns{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}@media(max-width:640px){.mf-columns{grid-template-columns:1fr}}.mf-col{padding:12px 14px;border-radius:14px;background:#fff;border:1px solid #eef0f3}.mf-col--good{background:linear-gradient(180deg,#f0fdf4,#fff 80%);border-color:#bbf7d0}.mf-col--bad{background:linear-gradient(180deg,#fef2f2,#fff 80%);border-color:#fecaca}.mf-col-title{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:14px;font-weight:800;color:var(--text);letter-spacing:.01em}.mf-dot{width:22px;height:22px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;flex-shrink:0;font-size:11px}.mf-dot i{font-size:10px}.mf-dot.good{background:#10b981;box-shadow:0 2px 6px #10b9814d}.mf-dot.bad{background:#ef4444;box-shadow:0 2px 6px #ef44444d}.mf-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mf-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffffb3;transition:background .15s ease,transform .15s ease}.mf-item:hover{background:#fff;transform:translate(2px)}.mf-item--bad .mf-name{color:#7f1d1d}.mf-item--good .mf-name{color:#064e3b}.mf-emoji{width:28px;height:28px;border-radius:8px;background:#fff;display:inline-grid;place-items:center;font-size:16px;flex-shrink:0;box-shadow:0 1px 2px #0f172a0f}.mf-emoji--x{background:#fef2f2;color:#ef4444}.mf-emoji--x i{font-size:12px}.mf-item-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.mf-name{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.mf-note{font-size:11.5px;color:var(--muted);line-height:1.35}.mf-tip{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;border-radius:12px;background:var(--mf-soft);border:1px solid rgba(var(--mf-tone),.22);font-size:12.5px;color:var(--text);line-height:1.5}.mf-tip i{color:var(--mf-strong);font-size:15px;margin-top:1px;flex-shrink:0}.mom-tab .card{padding:12px 14px;margin-bottom:10px;border-radius:14px}.mom-tab .mom-wellness-section,.mom-tab .mom-tips-week{margin-bottom:10px}.mom-tab .mom-food-card{padding:12px 14px;margin-top:10px}.mom-tab .mf-head{margin-bottom:8px}.mom-tab .mf-head h2{font-size:16px}.mom-tab .mf-desc{margin-bottom:10px;font-size:12px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.mom-tab .mf-columns{gap:8px;margin-bottom:8px}.mom-tab .mf-col{padding:8px 10px}.mom-tab .mf-list{gap:6px}.mom-tab .mf-item{padding:6px 8px}.mom-tab .mf-tip{padding:8px 10px;font-size:12px}.mom-hero{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin-bottom:14px;background:linear-gradient(135deg,#fdf2f8,#faf5ff,#eef2ff);border:1px solid #f9a8d4;border-radius:16px}.mom-hero .mom-hero__icon{font-size:36px;line-height:1;flex-shrink:0}.mom-hero h2{margin:0 0 6px;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em}.mom-hero .mom-hero__sub{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}.mom-hero .mom-hero__sub strong{color:var(--text)}.mom-hero .mom-hero__sub .muted-inline{display:block;margin-top:4px;font-size:12.5px;opacity:.92}.mom-hero--compact{align-items:center;padding:10px 12px;margin-bottom:10px;border-radius:14px;gap:10px}.mom-hero--compact .mom-hero__icon{font-size:26px}.mom-hero--compact h2{margin:0;font-size:16px;letter-spacing:-.01em}.mom-hero--compact .mom-hero__title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px}.mom-hero--compact .mom-hero__baby{font-size:12px;font-weight:700;color:#be185d;padding:2px 8px;border-radius:999px;background:#ffffffbf;border:1px solid rgba(244,114,182,.35)}.mom-hero--compact .mom-hero__sub{margin:4px 0 0;font-size:11.5px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.mom-hero--compact .mom-hero__sub .muted-inline{display:inline;margin-top:0;font-size:11.5px}.section-head--tight{margin-bottom:8px}.section-head--tight h3{margin:0}.mom-disclaimer--compact{font-size:11px;line-height:1.4;padding:8px 10px;margin-top:4px;margin-bottom:6px}.section-head{margin-bottom:14px}.section-head h3{margin:0 0 6px;font-size:16px;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}.section-head h3 i{color:#db2777;font-size:15px}.section-head .section-sub{margin:0;font-size:12.5px;color:var(--muted);line-height:1.45}.mom-wellness-section{margin-bottom:16px}.mom-tab .mom-wellness-grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}.mom-wellness-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.mom-w-card{padding:14px 14px 12px;border-radius:14px;border:1px solid var(--border);background:#fafafa;transition:border-color .15s,box-shadow .15s}.mom-w-card:hover{border-color:#f472b6;box-shadow:0 6px 18px -12px #db277759}.mom-w-card .mom-w-card__icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#fce7f3,#ede9fe);display:flex;align-items:center;justify-content:center;margin-bottom:10px}.mom-w-card .mom-w-card__icon i{font-size:16px;color:#be185d}.mom-w-card h4{margin:0 0 8px;font-size:13.5px;font-weight:800;color:var(--text);line-height:1.3}.mom-w-card p{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-tips-week{margin-bottom:16px}.mom-tab .mom-tips-list{gap:6px}.mom-tab .mom-tips-list li{padding:8px 10px;border-radius:10px}.mom-tab .mom-tips-list li strong{margin-bottom:2px;font-size:12px}.mom-tab .mom-tips-list li span{font-size:11.5px;line-height:1.4}.mom-tips-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.mom-tips-list li{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:12px;background:#fefce8;border:1px solid #fde047}.mom-tips-list li>i{color:#ca8a04;margin-top:2px;flex-shrink:0}.mom-tips-list li strong{display:block;font-size:13px;font-weight:800;color:var(--text);margin-bottom:4px}.mom-tips-list li span{font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-disclaimer{margin-top:8px;margin-bottom:8px}.mom-empty-profile{max-width:520px;margin:24px auto 40px;text-align:center}.mom-empty-profile__inner{padding:28px 22px}.mom-empty-profile__inner .mom-empty-emoji{font-size:48px;display:block;margin-bottom:12px}.mom-empty-profile__inner h2{margin:0 0 10px;font-size:17px;font-weight:800;color:var(--text)}.mom-empty-profile__inner p{margin:0 0 18px;font-size:14px;line-height:1.55;color:var(--muted)}.event-reminder-dialog{max-width:min(520px,100vw - 28px);padding-bottom:8px}.event-reminder-dialog__head{align-items:flex-start;margin-bottom:4px}.event-reminder-dialog__title{margin:0;padding-right:40px;font-size:20px;font-weight:800;line-height:1.3;color:var(--text, #0f172a);display:flex;align-items:center;gap:10px;letter-spacing:-.02em}.event-reminder-dialog__title .pi-bell{color:#f59e0b;font-size:18px;flex-shrink:0}.event-reminder-list{list-style:none;margin:12px 0 0;padding:0;max-height:min(52vh,400px);overflow:auto;display:flex;flex-direction:column;gap:10px}.event-reminder-card{padding:14px 16px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;border-radius:14px;box-shadow:0 2px 8px #0f172a0d}.event-reminder-card__title{margin:0;font-size:16px;font-weight:800;color:var(--text, #0f172a);line-height:1.35}.event-reminder-card__datetime{margin:5px 0 0;font-size:12.5px;font-weight:500;color:#64748b;line-height:1.4}.event-reminder-card__date{color:#475569;font-weight:600}.event-reminder-card__sep{color:#94a3b8}.event-reminder-card__hour{color:#dc2626;font-weight:700;font-variant-numeric:tabular-nums}.event-reminder-card__place{margin:6px 0 0;display:flex;align-items:flex-start;gap:6px;font-size:13px;color:#475569;line-height:1.4}.event-reminder-card__place .pi-map-marker{margin-top:2px;flex-shrink:0;font-size:13px;color:var(--primary, #5b6cff)}.event-reminder-card__content-wrap{margin-top:10px;padding-top:10px;border-top:1px solid #eef1f6}.event-reminder-card__content-label{display:block;margin-bottom:4px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#94a3b8}.event-reminder-card__content{margin:0;font-size:13.5px;color:#334155;line-height:1.5;white-space:pre-wrap}.event-reminder-actions{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border, #e5e7eb)}.event-reminder-actions__link{flex:0 1 auto;min-width:0;padding:6px 4px;border:none;background:transparent;color:#64748b;font-size:13px;font-weight:600;font-family:inherit;text-decoration:underline;text-underline-offset:3px;cursor:pointer}.event-reminder-actions__link:hover:not(:disabled){color:#334155}.event-reminder-actions__link:disabled{opacity:.5;cursor:not-allowed}.event-reminder-actions__primary{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:10px 18px;font-size:15px;font-weight:700;text-align:center}\n"] }]
|
|
17135
|
+
ChatShellComponent,
|
|
17136
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"feeding-root\">\n <div class=\"feeding-page\" #feedingPage>\n <div class=\"backdrop\"></div>\n\n <header class=\"hero\" [class.hero--profile-merged]=\"profile() && !editing() && ageBreakdown()\">\n @if (profile() && !editing() && ageBreakdown(); as age) {\n <!-- \u2550\u2550 REDESIGNED HERO \u2550\u2550 -->\n <app-hero-header\n [profile]=\"profile()!\"\n [age]=\"age\"\n [user]=\"user()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [isAnyLoading]=\"isAnyLoading()\"\n [showRefresh]=\"bottomTab() !== 'schedule'\"\n (openMenu)=\"openSettingsMenu()\"\n (openChat)=\"openChatRoute()\"\n (refresh)=\"refreshAll()\"\n >\n <app-activity-log slot=\"activity-log\" (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n </app-hero-header>\n\n <app-hero-stats-strip\n [currentTime]=\"currentTimeStr\"\n [dateLabel]=\"heroDateLabel\"\n [nextFeedTime]=\"prediction()?.nextTime ?? null\"\n [nextFeedCountdown]=\"heroNextFeedCountdown()\"\n [weightLabel]=\"profileHeroWeightLabel()\"\n [whoStatus]=\"nutritionTarget()?.ageLabel ?? null\"\n ></app-hero-stats-strip>\n\n <div class=\"hero-notif-wrap\">\n <app-notification-banner\n [notifications]=\"visibleNotifications()\"\n [saving]=\"notificationSaving()\"\n [currentUser]=\"user()\"\n [dialogOpen]=\"notifDialogOpen()\"\n (openDialog)=\"openNotificationRoute()\"\n (closeDialog)=\"notifDialogOpen.set(false)\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"notifDialogOpen.set(false); openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"notifDialogOpen.set(false); openNotificationComposerDialog()\"\n ></app-notification-banner>\n </div>\n\n } @else {\n <!-- \u2500\u2500 Fallback: no profile / editing \u2500\u2500 -->\n <div class=\"hero-inner\">\n <div class=\"hero-brand\">\n <div class=\"hero-title-row\">\n <div class=\"hero-title\">\n <div class=\"emoji\" aria-hidden=\"true\">\uD83D\uDC76</div>\n <div class=\"hero-text\">\n <h1>B\u00E9 y\u00EAu c\u1EE7a b\u1EA1n</h1>\n <p class=\"hero-userline\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <strong>{{ user() }}</strong>\n </p>\n </div>\n </div>\n @if (profile() && !editing()) {\n <div class=\"hero-actions\">\n <app-activity-log (activityChanged)=\"onActivityChanged()\"></app-activity-log>\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"feedingDialogs.openFeedingSettingsDialog()\"\n title=\"C\u00E0i \u0111\u1EB7t\"\n aria-label=\"C\u00E0i \u0111\u1EB7t c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-sliders-h\"></i>\n </button>\n @if (bottomTab() !== 'schedule') {\n <button\n class=\"btn icon-btn\"\n type=\"button\"\n (click)=\"refreshAll()\"\n [disabled]=\"isAnyLoading()\"\n [title]=\"\n isAnyLoading() ? '\u0110ang t\u1EA3i\u2026' : 'T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, C\u00E2n, L\u1ECBch, Y t\u1EBF, T\u00E0i li\u1EC7u'\n \"\n aria-label=\"T\u1EA3i l\u1EA1i C\u1EEF b\u00FA, Th\u1EC3 tr\u1EA1ng, L\u1ECBch, Ti\u1EC1n s\u1EED y t\u1EBF v\u00E0 T\u00E0i li\u1EC7u\"\n >\n <i class=\"pi pi-refresh\" [class.spin]=\"isAnyLoading()\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"hero-datetime\" aria-live=\"polite\">\n <div class=\"hero-datetime-strip\">\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--date\">\n <i class=\"pi pi-calendar\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__wd\">{{ heroWeekday }}</span>\n <time class=\"hero-datetime-strip__date\" [attr.datetime]=\"todayDateStr()\">{{\n heroDateNumeric\n }}</time>\n </div>\n <span class=\"hero-datetime-strip__divider\" aria-hidden=\"true\"></span>\n <div class=\"hero-datetime-strip__block hero-datetime-strip__block--time\">\n <i class=\"pi pi-clock\" aria-hidden=\"true\"></i>\n <span class=\"hero-datetime-strip__clock\">{{ currentTimeStr }}</span>\n </div>\n </div>\n </div>\n\n <section class=\"hero-notification\" aria-label=\"Th\u00F4ng b\u00E1o n\u1ED9i b\u1ED9\">\n <!-- watermark background icons -->\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bell\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-megaphone\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-comments\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--send\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-send\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--star\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-star\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--bolt\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-bolt\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--heart\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-heart\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--info\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-info-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--check\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-check-circle\"></i>\n </span>\n <span\n class=\"hero-notification__bg-icon hero-notification__bg-icon--eye\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-eye\"></i>\n </span>\n\n <div class=\"hero-notification__head\">\n <label class=\"hero-notification__composer-label\" for=\"hero-notification-input\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n <span>Th\u00F4ng b\u00E1o</span>\n @if (visibleNotifications().length > 0) {\n <span class=\"hero-notification__unread-badge\">{{\n visibleNotifications().length\n }}</span>\n }\n </label>\n <div class=\"hero-notification__head-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button\n type=\"button\"\n class=\"hero-notification__add\"\n (click)=\"openNotificationComposerDialog()\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n title=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n\n @if (displayedNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of displayedNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">\n {{ notice.user | slice: 0 : 2 | uppercase }}\n </div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{\n formatNotificationCreatedAt(notice.createdAt)\n }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button\n type=\"button\"\n class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\"\n title=\"\u0110\u00E3 \u0111\u1ECDc\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button\n type=\"button\"\n class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\"\n [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\"\n title=\"X\u00F3a th\u00F4ng b\u00E1o\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\"\n >M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span\n >\n </div>\n }\n\n @if (hiddenNotificationCount() > 0) {\n <button\n type=\"button\"\n class=\"hero-notification__more\"\n (click)=\"openNotificationListDialog()\"\n >\n Xem th\u00EAm {{ hiddenNotificationCount() }} th\u00F4ng b\u00E1o \u2192\n </button>\n }\n </section>\n\n </div>\n } <!-- end @else no-profile -->\n </header>\n\n <main\n class=\"content\"\n [class.is-documents]=\"bottomTab() === 'documents'\"\n [class.is-weight]=\"bottomTab() === 'weight'\"\n [class.is-schedule]=\"bottomTab() === 'schedule'\"\n [class.is-mom]=\"bottomTab() === 'mom'\"\n [class.is-medical]=\"bottomTab() === 'medical'\"\n >\n <!--\n ============ TAB PANES ============\n D\u00F9ng `[hidden]` thay v\u00EC `*ngIf` \u0111\u1EC3 c\u00E1c tab c\u00F9ng gi\u1EEF tr\u1EA1ng th\u00E1i khi\n user qua l\u1EA1i: scroll position, search query \u0111ang g\u00F5, viewMode, folder\n \u0111ang \u0111\u1EE9ng, file \u0111ang ch\u1ECDn, draft \u0111ang nh\u1EADp\u2026 \u0111\u1EC1u kh\u00F4ng b\u1ECB reset. H\u1EC7\n qu\u1EA3: `<app-documents>` \u0111\u01B0\u1EE3c mount 1 l\u1EA7n \u1EDF app load \u2014 `loadEntries()`\n ch\u1EA1y 1 l\u1EA7n, c\u00E1c l\u1EA7n switch tab k\u1EBF ti\u1EBFp kh\u00F4ng g\u1ECDi API th\u00EAm.\n -->\n <!-- ============ TAB: DOCUMENTS \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: WEIGHT ============ -->\n <div class=\"tab-pane tab-pane--weight\" [hidden]=\"bottomTab() !== 'weight'\">\n <app-weight\n [user]=\"user()\"\n [birthDate]=\"profile()?.birthDate\"\n [gender]=\"profile()?.gender\"\n [babyName]=\"profile()?.babyName\"\n ></app-weight>\n </div>\n\n <!-- ============ TAB: L\u1ECACH \u2014 Google Sheet tab \"Event\" ============ -->\n <div class=\"tab-pane tab-pane--schedule\" [hidden]=\"bottomTab() !== 'schedule'\">\n <app-feeding-schedule [user]=\"user()\"></app-feeding-schedule>\n </div>\n\n <!-- ============ TAB: TI\u1EC0N S\u1EEC Y T\u1EBE \u2014 Medical history V2 ============ -->\n <div class=\"tab-pane tab-pane--medical\" [hidden]=\"bottomTab() !== 'medical'\">\n <app-medical-history\n [user]=\"user()\"\n (openExplorerFile)=\"openMedicalAttachmentInDocuments($event)\"\n ></app-medical-history>\n </div>\n\n <!-- ============ TAB: M\u1EB8 \u2014 \u0111\u00E3 chuy\u1EC3n sang subroute (m\u1EDF t\u1EEB Settings menu) ============ -->\n\n <!-- ============ TAB: FEEDING ============ -->\n <div class=\"tab-pane tab-pane--feeding\" [hidden]=\"bottomTab() !== 'feeding'\">\n <app-feeding-profile\n [user]=\"user()\"\n [profile]=\"profile()\"\n [draft]=\"draft()\"\n [editing]=\"editing()\"\n [maxBirthDate]=\"maxBirthDate\"\n (save)=\"save()\"\n (cancelEdit)=\"cancelEdit()\"\n (startEdit)=\"startEdit()\"\n (clearProfile)=\"clearProfile()\"\n (draftNameChange)=\"updateDraftName($event)\"\n (draftBirthChange)=\"updateDraftBirth($event)\"\n (draftGenderChange)=\"setDraftGender($event)\"\n />\n\n @if (profile() && !editing() && ageBreakdown()) {\n <app-feeding-daily\n [logs]=\"logs()\"\n [feedingSettings]=\"feedingSettings()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n [user]=\"user()\"\n [syncMessage]=\"syncMessage()\"\n [syncError]=\"syncError()\"\n [logDialogOpen]=\"logDialogOpen()\"\n [loadingLogs]=\"loadingLogs()\"\n [refreshToken]=\"dailyRefreshToken()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (bottlePrepChange)=\"bottlePrep.set($event)\"\n (editLog)=\"feedingDialogs.openEditDialog($event)\"\n (historyOpen)=\"feedingDialogs.openHistoryDialog($event.filter, $event.dateStr)\"\n (feedRowClick)=\"feedingDialogs.onFeedRowClick($event)\"\n >\n <app-feeding-charts\n data-feeding-charts\n [logs]=\"logs()\"\n [now]=\"now()\"\n [nutritionTarget]=\"nutritionTarget()\"\n />\n </app-feeding-daily>\n <app-feeding-nutrition\n [nutritionTarget]=\"nutritionTarget()\"\n [todayStats]=\"todayStats()\"\n [now]=\"now()\"\n [latestWeightKg]=\"latestWeightKgFromSheet()\"\n [editing]=\"editing()\"\n (goToWeight)=\"setBottomTab('weight')\"\n />\n }\n\n <!-- ============ FAB \u00AB Ghi c\u1EEF b\u00FA \u00BB c\u0169 \u0111\u00E3 \u0111\u01B0\u1EE3c thay b\u1EB1ng FAB gi\u1EEFa bottom-nav ============ -->\n </div>\n <!-- /.tab-pane--feeding -->\n </main>\n </div>\n <!-- /.feeding-page -->\n\n <!-- ============ BOTTOM NAVIGATION (C\u1EEF b\u00FA \u00B7 Th\u1EC3 tr\u1EA1ng \u00B7 + \u00B7 L\u1ECBch \u00B7 Y t\u1EBF) ============ -->\n <nav\n class=\"bottom-nav\"\n role=\"tablist\"\n aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\"\n >\n <div class=\"bn-side bn-side--left\">\n @for (tab of navLeftTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"bn-fab\"\n (click)=\"feedingDialogs.openLogDialog()\"\n [disabled]=\"!profile() || editing()\"\n [title]=\"(!profile() || editing()) ? 'Ch\u1ECDn b\u00E9 tr\u01B0\u1EDBc khi ghi c\u1EEF b\u00FA' : 'Ghi nh\u1EADn c\u1EEF b\u00FA m\u1EDBi'\"\n aria-label=\"Ghi nh\u1EADn c\u1EEF b\u00FA\"\n >\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"bn-side bn-side--right\">\n @for (tab of navRightTabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"bn-item\"\n role=\"tab\"\n [attr.aria-selected]=\"bottomTab() === tab.id\"\n [class.active]=\"bottomTab() === tab.id\"\n (click)=\"setBottomTab(tab.id)\"\n >\n <span class=\"bn-icon-wrap\">\n <i class=\"pi {{ tab.icon }} bn-icon\" aria-hidden=\"true\"></i>\n </span>\n <span class=\"bn-label\">{{ tab.label }}</span>\n </button>\n }\n </div>\n </nav>\n\n <!-- ============ NOTIFICATION SUBROUTE (full-screen) ============ -->\n @if (notifRouteOpen()) {\n <app-notification-list\n [notifications]=\"allNotifications()\"\n [currentUser]=\"user()\"\n [saving]=\"notificationSaving()\"\n (back)=\"closeNotificationRoute()\"\n (acknowledge)=\"acknowledgeNotification($event)\"\n (acknowledgeAll)=\"openAcknowledgeAllConfirmDialog()\"\n (delete)=\"deleteNotification($event)\"\n (openComposer)=\"openNotificationComposerDialog()\"\n />\n }\n\n <!-- \u2550\u2550 NOTIFICATION DIALOGS \u2014 rendered as siblings of .feeding-page (outside the\n overflow/stacking-context scroll zone) so they always overlay the\n full-screen notification subroute. See .feeding-page overflow-y:auto +\n -webkit-overflow-scrolling:touch in feeding.component.scss. \u2550\u2550 -->\n @if (notificationComposerDialogOpen()) {\n <div\n class=\"dialog-overlay dialog-overlay--front\"\n (click)=\"closeNotificationComposerDialog()\"\n >\n <div\n class=\"dialog hero-notification-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"T\u1EA1o th\u00F4ng b\u00E1o\"\n (click)=\"$event.stopPropagation()\"\n >\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--send\" aria-hidden=\"true\"><i class=\"pi pi-send\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n\n <div class=\"dialog-head hero-notification-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\">\n <i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i>\n </div>\n <div>\n <h2 class=\"hero-notification-dialog__title\">T\u1EA1o th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">Th\u00F4ng b\u00E1o s\u1EBD hi\u1EC3n th\u1ECB cho to\u00E0n b\u1ED9 th\u00E0nh vi\u00EAn</p>\n </div>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationComposerDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <div class=\"hero-notification__composer-panel\">\n <div class=\"hero-notification__composer-row\">\n <div class=\"hero-notification__input-wrap\">\n <textarea\n id=\"hero-notification-input\"\n class=\"hero-notification__input\"\n [value]=\"notificationDraft()\"\n (input)=\"updateNotificationDraft($any($event.target).value)\"\n [disabled]=\"notificationSaving()\"\n maxlength=\"300\"\n placeholder=\"Nh\u1EADp n\u1ED9i dung th\u00F4ng b\u00E1o cho m\u1ECDi ng\u01B0\u1EDDi...\"\n aria-label=\"N\u1ED9i dung th\u00F4ng b\u00E1o\"\n ></textarea>\n <div class=\"hero-notification__char-ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 36 36\">\n <circle class=\"hero-notification__char-ring-track\" cx=\"18\" cy=\"18\" r=\"15\" />\n <circle class=\"hero-notification__char-ring-fill\" cx=\"18\" cy=\"18\" r=\"15\"\n [style.stroke-dashoffset]=\"94 - (notificationDraft().trim().length / 300) * 94\" />\n </svg>\n <span class=\"hero-notification__char-count\">{{ notificationDraft().trim().length }}</span>\n </div>\n </div>\n </div>\n <div class=\"hero-notification__composer-actions\">\n <button type=\"button\" class=\"btn ghost hero-notification__cancel\"\n (click)=\"closeNotificationComposerDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn hero-notification__send\"\n (click)=\"createNotification()\"\n [disabled]=\"notificationSaving() || !notificationDraft().trim()\">\n <i class=\"pi pi-send\"></i>\n <span>G\u1EEDi th\u00F4ng b\u00E1o</span>\n </button>\n </div>\n </div>\n @if (notificationError()) {\n <p class=\"hero-notification__error\">{{ notificationError() }}</p>\n }\n </div>\n </div>\n }\n\n @if (notificationListDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeNotificationListDialog()\">\n <div class=\"dialog hero-notification-list-dialog\" role=\"dialog\" aria-modal=\"true\"\n aria-label=\"Danh s\u00E1ch th\u00F4ng b\u00E1o\" (click)=\"$event.stopPropagation()\">\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--bell\" aria-hidden=\"true\"><i class=\"pi pi-bell\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--megaphone\" aria-hidden=\"true\"><i class=\"pi pi-megaphone\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--comments\" aria-hidden=\"true\"><i class=\"pi pi-comments\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--star\" aria-hidden=\"true\"><i class=\"pi pi-star\"></i></span>\n <span class=\"hero-notification__bg-icon hero-notification__bg-icon--check\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></span>\n\n <div class=\"dialog-head hero-notification-list-dialog__head\">\n <div class=\"hero-notification-dialog__title-group\">\n <div class=\"hero-notification-dialog__icon-wrap\"><i class=\"pi pi-megaphone\" aria-hidden=\"true\"></i></div>\n <div>\n <h2 class=\"hero-notification-list-dialog__title\">To\u00E0n b\u1ED9 th\u00F4ng b\u00E1o</h2>\n <p class=\"hero-notification-dialog__subtitle\">{{ visibleNotifications().length }} th\u00F4ng b\u00E1o</p>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__head-actions\">\n <button type=\"button\" class=\"hero-notification__mark-all\"\n (click)=\"openAcknowledgeAllConfirmDialog()\"\n [disabled]=\"notificationSaving() || visibleNotifications().length === 0\"\n title=\"\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> \u0110\u00E3 \u0111\u1ECDc t\u1EA5t c\u1EA3\n </button>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeNotificationListDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"hero-notification-list-dialog__body\">\n @if (visibleNotifications().length > 0) {\n <ul class=\"hero-notification__list\">\n @for (notice of visibleNotifications(); track notice) {\n <li class=\"hero-notification__item\">\n <div class=\"hero-notification__avatar\" aria-hidden=\"true\">{{ notice.user | slice: 0 : 2 | uppercase }}</div>\n <div class=\"hero-notification__body\">\n <div class=\"hero-notification__body-head\">\n <span class=\"hero-notification__author\">{{ notice.user }}</span>\n </div>\n <p class=\"hero-notification__content\">{{ notice.content }}</p>\n @if (notice.createdAt) {\n <span class=\"hero-notification__created\">{{ formatNotificationCreatedAt(notice.createdAt) }}</span>\n }\n </div>\n <div class=\"hero-notification__item-actions\">\n <button type=\"button\" class=\"hero-notification__ack-top\"\n (click)=\"acknowledgeNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"\u0110\u00E3 \u0111\u1ECDc th\u00F4ng b\u00E1o\" title=\"\u0110\u00E3 \u0111\u1ECDc\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n @if (canDeleteNotification(notice)) {\n <button type=\"button\" class=\"hero-notification__delete\"\n (click)=\"deleteNotification(notice)\" [disabled]=\"notificationSaving()\"\n aria-label=\"X\u00F3a th\u00F4ng b\u00E1o\" title=\"X\u00F3a th\u00F4ng b\u00E1o\">\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div class=\"hero-notification__empty\">\n <i class=\"pi pi-bell hero-notification__empty-icon\" aria-hidden=\"true\"></i>\n <span class=\"hero-notification__empty-title\">Kh\u00F4ng c\u00F3 th\u00F4ng b\u00E1o m\u1EDBi</span>\n <span class=\"hero-notification__empty-sub\">M\u1ECDi th\u1EE9 \u0111\u1EC1u \u1ED5n. Th\u00F4ng b\u00E1o m\u1EDBi s\u1EBD xu\u1EA5t hi\u1EC7n \u1EDF \u0111\u00E2y.</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (acknowledgeAllConfirmDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAcknowledgeAllConfirmDialog()\">\n <div class=\"dialog ack-all-confirm-dialog\" role=\"alertdialog\" aria-modal=\"true\"\n aria-labelledby=\"ack-all-confirm-title\" (click)=\"$event.stopPropagation()\">\n <div class=\"ack-all-confirm-dialog__icon-wrap\" aria-hidden=\"true\"><i class=\"pi pi-check-circle\"></i></div>\n <h2 id=\"ack-all-confirm-title\" class=\"ack-all-confirm-dialog__title\">\u0110\u00E1nh d\u1EA5u t\u1EA5t c\u1EA3 \u0111\u00E3 \u0111\u1ECDc?</h2>\n <p class=\"ack-all-confirm-dialog__desc\">\n T\u1EA5t c\u1EA3 {{ visibleNotifications().length }} th\u00F4ng b\u00E1o s\u1EBD \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u \u0111\u00E3 \u0111\u1ECDc v\u00E0 \u1EA9n kh\u1ECFi danh s\u00E1ch.\n </p>\n <div class=\"ack-all-confirm-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeAcknowledgeAllConfirmDialog()\" [disabled]=\"notificationSaving()\">H\u1EE7y</button>\n <button type=\"button\" class=\"btn ack-all-confirm-dialog__confirm-btn\"\n (click)=\"confirmAcknowledgeAll()\" [disabled]=\"notificationSaving()\">\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i> X\u00E1c nh\u1EADn\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- ============ SETTINGS FLOAT MENU (full-screen) ============ -->\n @if (settingsMenuOpen()) {\n <app-settings-menu\n [appName]=\"appInfo.name\"\n [appVersion]=\"appInfo.version\"\n [showDocuments]=\"explorerEnabled\"\n [unreadCount]=\"visibleNotifications().length\"\n (close)=\"closeSettingsMenu()\"\n (select)=\"onSettingsMenuSelect($event)\"\n />\n }\n\n <!-- ============ M\u1EB8 SUBROUTE (full-screen) ============ -->\n @if (momRouteOpen()) {\n <app-subroute-shell\n title=\"M\u1EB9\"\n (back)=\"closeMomRoute()\"\n >\n <app-feeding-mom\n [profile]=\"profile()\"\n [editing]=\"editing()\"\n (goToFeeding)=\"closeMomRoute(); setBottomTab('feeding')\"\n ></app-feeding-mom>\n </app-subroute-shell>\n }\n\n <!-- ============ T\u00C0I LI\u1EC6U SUBROUTE (full-screen) ============ -->\n @if (documentsRouteOpen()) {\n <app-subroute-shell\n title=\"T\u00E0i li\u1EC7u\"\n (back)=\"closeDocumentsRoute()\"\n >\n <app-documents></app-documents>\n </app-subroute-shell>\n }\n\n <!-- ============ TR\u1EE2 L\u00DD AI SUBROUTE (full-screen, m\u1EDF t\u1EEB n\u00FAt chat tr\u00EAn hero) ============ -->\n @if (chatRouteOpen()) {\n <app-subroute-shell\n title=\"Tr\u1EE3 l\u00FD AI\"\n (back)=\"closeChatRoute()\"\n >\n <app-chat-shell\n [context]=\"chatContext()\"\n [disabled]=\"chatDisabled()\"\n ></app-chat-shell>\n </app-subroute-shell>\n }\n\n <!-- About -->\n @if (aboutDialogOpen()) {\n <div class=\"dialog-overlay dialog-overlay--front\" (click)=\"closeAboutDialog()\">\n <div\n class=\"dialog about-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"feeding-about-title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head about-dialog__head\">\n <h2 id=\"feeding-about-title\" class=\"about-dialog__title\">\n <i class=\"pi pi-info-circle\" aria-hidden=\"true\"></i>\n About\n </h2>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeAboutDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <dl class=\"about-dialog__meta\">\n <div class=\"about-dialog__row\">\n <dt>\u1EE8ng d\u1EE5ng</dt>\n <dd>{{ appInfo.name }}</dd>\n </div>\n <div class=\"about-dialog__row\">\n <dt>Phi\u00EAn b\u1EA3n</dt>\n <dd>\n <code>{{ appInfo.version }}</code>\n </dd>\n </div>\n </dl>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn primary\" (click)=\"closeAboutDialog()\">\u0110\u00F3ng</button>\n </div>\n </div>\n </div>\n }\n\n <!-- Nh\u1EAFc s\u1EF1 ki\u1EC7n l\u1ECBch (sheet Event + Settings) -->\n @if (eventReminderDialogOpen() && scheduleReminderEvents().length > 0) {\n <div\n class=\"dialog-overlay dialog-overlay--front dialog-overlay--event-reminder\"\n (click)=\"closeEventReminderDialog()\"\n >\n <div\n class=\"dialog event-reminder-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head event-reminder-dialog__head\">\n <h2 class=\"event-reminder-dialog__title\">\n <i class=\"pi pi-bell\" aria-hidden=\"true\"></i>\n B\u1EA1n c\u00F3 l\u1ECBch s\u1EAFp t\u1EDBi\n </h2>\n <button\n type=\"button\"\n class=\"dialog-close\"\n (click)=\"closeEventReminderDialog()\"\n aria-label=\"\u0110\u00F3ng\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n <ul class=\"event-reminder-list\">\n @for (ev of scheduleReminderEvents(); track ev) {\n <li>\n <article class=\"event-reminder-card\">\n <h3 class=\"event-reminder-card__title\">{{ ev.title }}</h3>\n <p class=\"event-reminder-card__datetime\">\n <span class=\"event-reminder-card__date\">{{ formatReminderEventWhen(ev) }}</span>\n <span class=\"event-reminder-card__sep\" aria-hidden=\"true\"> \u2013 </span>\n <span class=\"event-reminder-card__hour\">{{\n formatReminderEventTime(ev.time)\n }}</span>\n </p>\n @if (ev.place) {\n <p class=\"event-reminder-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n <span>{{ ev.place }}</span>\n </p>\n }\n @if (ev.note) {\n <div class=\"event-reminder-card__content-wrap\">\n <span class=\"event-reminder-card__content-label\">N\u1ED9i dung</span>\n <p class=\"event-reminder-card__content\">{{ ev.note }}</p>\n </div>\n }\n </article>\n </li>\n }\n </ul>\n <div class=\"dialog-actions event-reminder-actions\">\n <button\n type=\"button\"\n class=\"event-reminder-actions__link\"\n (click)=\"acknowledgeAllEventReminders()\"\n >\n Kh\u00F4ng nh\u1EAFc l\u1EA1i\n </button>\n <button\n type=\"button\"\n class=\"btn primary event-reminder-actions__primary\"\n (click)=\"skipEventReminderSession()\"\n >\n \u0110\u00E3 hi\u1EC3u\n </button>\n </div>\n </div>\n </div>\n }\n\n <app-feeding-dialogs\n #feedingDialogs\n [logs]=\"logs()\"\n [user]=\"user()\"\n [now]=\"now()\"\n [bottlePrep]=\"bottlePrep()\"\n [feedingSettings]=\"feedingSettings()\"\n [prediction]=\"prediction()\"\n [todayStats]=\"todayStats()\"\n [yesterdayStats]=\"yesterdayStats()\"\n [todayLogs]=\"todayLogs()\"\n [yesterdayLogs]=\"yesterdayLogs()\"\n [maxBirthDate]=\"maxBirthDate\"\n [syncError]=\"syncError()\"\n [syncMessage]=\"syncMessage()\"\n (logsChanged)=\"onFeedingLogsChanged()\"\n (settingsChanged)=\"onFeedingSettingsChanged($event)\"\n (syncErrorChange)=\"syncError.set($event)\"\n (syncMessageChange)=\"syncMessage.set($event)\"\n (logDialogOpenChange)=\"logDialogOpen.set($event)\"\n (bottlePrepClearRequested)=\"clearBottlePrep()\"\n />\n</div>\n<!-- /.feeding-root -->\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;position:absolute;inset:0;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--border-strong: #d1d5db;--primary: #5b6cff;--primary-2: #4c5cf0;--primary-soft: #eef0ff;--accent: #ff5c8a;--accent-soft: #ffe7ef;--mint: #10b981;--mint-soft: #d1fae5;--amber: #f59e0b;--amber-soft: #fef3c7;--rose: #ef4444;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8}.feeding-root{flex:1;min-height:0;width:100%;display:flex;flex-direction:column;position:relative}.feeding-root .bp-prep-user{font-weight:800;color:#0369a1;background:#0ea5e933;padding:0 5px;margin-right:2px;border-radius:5px;white-space:nowrap;transition:color .2s,background .2s}.feeding-root .bp-prep-time{font-weight:800;font-variant-numeric:tabular-nums;color:#0284c7;background:linear-gradient(180deg,#f0f9ff,#e0f2fe);border:1px solid rgba(14,165,233,.35);padding:1px 7px;margin-left:2px;border-radius:6px;white-space:nowrap;box-shadow:0 1px #ffffffa6 inset;transition:color .2s,background .2s,border-color .2s}.feeding-page{position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;color:var(--text);padding-bottom:calc(170px + env(safe-area-inset-bottom,0px))}.backdrop{position:fixed;inset:0;background:var(--bg);z-index:-1}.hero-notif-wrap{padding:0 16px 8px}.hero{padding:0;background:#fffffff5;border-bottom:1px solid rgba(226,232,240,.9)}@media(max-width:640px){.hero{padding-top:calc(26px + env(safe-area-inset-top,0px));padding-bottom:10px}}.hero-inner{max-width:1100px;margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:14px 18px}.hero-brand{min-width:0}.hero-title-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;width:100%}.hero-title{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.hero-title .emoji{font-size:36px;line-height:1;flex-shrink:0;filter:drop-shadow(0 2px 4px rgba(15,23,42,.08))}@media(max-width:640px){.hero-title .emoji{font-size:32px}}.hero-title .hero-text{min-width:0}.hero-title h1{margin:0;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em;line-height:1.2}@media(max-width:640px){.hero-title h1{font-size:17px}}.hero-title .hero-userline{margin:5px 0 0;display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:12.5px;color:var(--muted)}.hero-title .hero-userline i{font-size:12px;color:var(--primary);opacity:.9}.hero-title .hero-userline strong{color:var(--primary);font-weight:700;letter-spacing:.01em}.hero-title .hero-userline .hero-last-update{font-size:11px;color:#94a3b8;font-weight:400}.hero--profile-merged{background:var(--primary-soft);padding-bottom:10px;margin-bottom:8px}.hero-title--merged{align-items:flex-start}.hero-title--merged .emoji{font-size:30px}.hero-title--merged h1{font-size:18px;line-height:1.15}.hero-merged-head{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;min-width:0;row-gap:6px}.hero-age-chip{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:lowercase;color:var(--primary);background:var(--primary-soft);padding:3px 9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--primary) 18%,transparent);white-space:nowrap}.hero-merged-stat{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-size:11px;font-weight:700;color:var(--muted);background:#94a3b824;border:1px solid rgba(148,163,184,.28);white-space:nowrap;line-height:1.25}.hero-merged-stat>i{font-size:10px;opacity:.88;color:var(--muted-2)}.hero-merged-stat--weight>i{color:var(--accent);opacity:1}.hero-merged-meta{margin:5px 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:11.5px;line-height:1.45;color:var(--muted)}.hero-merged-meta .dot{color:var(--muted-2);font-weight:700;-webkit-user-select:none;user-select:none}.hero-merged-meta i{font-size:10px;margin-right:3px;color:var(--muted-2);opacity:.95}.hero-merged-meta strong{color:var(--primary);font-weight:700}.hero-merged-sync{color:#94a3b8;font-weight:400;font-size:11px}.hero-actions--merged{flex-shrink:0}.hero-profile-menu{position:relative;flex:0 0 auto;list-style:none}.hero-profile-menu__trigger{list-style:none}.hero-profile-menu__trigger::-webkit-details-marker{display:none}.hero-profile-menu__trigger::marker{content:\"\"}.hero-profile-menu__panel{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:180px;padding:6px;border-radius:12px;border:1px solid var(--border);background:#fff;box-shadow:0 8px 24px -8px #0f172a2e}.hero-profile-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;border:0;border-radius:8px;background:transparent;font:inherit;font-size:13px;font-weight:600;color:var(--text);text-align:left;cursor:pointer;transition:background .12s ease,color .12s ease}.hero-profile-menu__item i{font-size:14px;color:var(--muted-2);width:1.1em;text-align:center}.hero-profile-menu__item:hover:not(:disabled){background:var(--primary-soft, #eef0ff);color:var(--primary)}.hero-profile-menu__item:hover:not(:disabled) i{color:var(--primary)}.hero-profile-menu__item--danger:hover:not(:disabled){color:var(--rose);background:var(--rose-soft)}.hero-profile-menu__item--danger:hover:not(:disabled) i{color:var(--rose)}.about-dialog{max-width:360px}.about-dialog__head{margin-bottom:8px}.about-dialog__title{display:flex;align-items:center;gap:8px;margin:0;font-size:18px;font-weight:700}.about-dialog__title i{color:var(--primary)}.about-dialog__meta{margin:0 0 16px;padding:0}.about-dialog__row{display:grid;grid-template-columns:96px 1fr;gap:8px 12px;padding:10px 0;border-bottom:1px solid var(--border)}.about-dialog__row:last-child{border-bottom:0}.about-dialog__row dt{margin:0;font-size:13px;font-weight:600;color:var(--muted-2)}.about-dialog__row dd{margin:0;font-size:14px;font-weight:600;color:var(--text)}.about-dialog__row dd code{font-size:13px;font-weight:600;padding:2px 8px;border-radius:6px;background:var(--primary-soft, #eef0ff);color:var(--primary)}@media(max-width:640px){.hero-title--merged .emoji{font-size:26px}.hero-merged-meta{font-size:11px}.hero-merged-stat{font-size:10.5px;padding:2px 7px}}.hero-datetime{display:flex;flex-wrap:wrap;align-items:center;flex-shrink:0}.hero-datetime-strip{display:inline-flex;align-items:stretch;flex-wrap:nowrap;max-width:100%;min-height:34px;border:1px solid var(--border);border-radius:8px;background:linear-gradient(180deg,#fff,#f1f5f9);box-shadow:0 1px 1px #0f172a0d}.hero-datetime-strip__block{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;min-width:0}.hero-datetime-strip__block>.pi{font-size:12px;color:var(--primary);opacity:.82;flex-shrink:0}.hero-datetime-strip__block--date{flex-wrap:wrap;row-gap:0}.hero-datetime-strip__wd{font-size:10px;font-weight:700;color:var(--muted);text-transform:capitalize;letter-spacing:.02em;line-height:1.15;max-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hero-datetime-strip__date{font-size:12px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--text);letter-spacing:.02em;white-space:nowrap}.hero-datetime-strip__divider{align-self:stretch;width:1px;margin:5px 0;background:#94a3b866;flex-shrink:0}.hero-datetime-strip__clock{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:.04em;color:var(--primary);line-height:1;white-space:nowrap}.hero-datetime-strip__block--time{padding-right:10px}.hero-datetime-strip__block--time>.pi{font-size:11px}@media(max-width:520px){.hero-inner{flex-direction:column;align-items:stretch}.hero-datetime{width:100%;justify-content:flex-start}.hero-datetime-strip{flex:1 1 auto;width:100%;justify-content:space-between}.hero-notification__composer-row{flex-direction:column;align-items:stretch}.hero-notification__send,.hero-notification__cancel{width:100%;justify-content:center}}.hero-notification__bg-icon{position:absolute;pointer-events:none;color:var(--primary);opacity:.13;line-height:1}.hero-notification__bg-icon i{display:block}.hero-notification__bg-icon--bell{top:8%;left:6%;font-size:28px;transform:rotate(-18deg)}.hero-notification__bg-icon--megaphone{top:10%;right:8%;font-size:24px;transform:rotate(12deg)}.hero-notification__bg-icon--comments{bottom:14%;right:10%;font-size:22px;transform:rotate(-8deg)}.hero-notification__bg-icon--send{bottom:10%;left:50%;font-size:20px;transform:rotate(-22deg)}.hero-notification__bg-icon--star{bottom:16%;left:4%;font-size:18px;transform:rotate(20deg)}.hero-notification__bg-icon--bolt{top:46%;right:2%;font-size:20px;transform:rotate(-5deg)}.hero-notification__bg-icon--heart{top:38%;left:14%;font-size:17px;transform:rotate(15deg)}.hero-notification__bg-icon--info{top:4%;left:50%;font-size:18px;transform:rotate(0)}.hero-notification__bg-icon--check{bottom:24%;left:35%;font-size:17px;transform:rotate(8deg)}.hero-notification__bg-icon--eye{top:14%;left:28%;font-size:18px;transform:rotate(-10deg)}.hero-notification{flex:1 1 100%;display:flex;flex-direction:column;gap:10px;margin-top:2px;padding:10px 12px;border:1px solid rgba(148,163,184,.28);border-radius:14px;background:#fff;position:relative;overflow:hidden}.hero-notification__head{display:flex;align-items:center;justify-content:space-between;gap:10px;position:relative}.hero-notification__head-actions{display:inline-flex;align-items:center;gap:6px}.hero-notification__composer-label{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--muted)}.hero-notification__composer-label i{color:var(--primary);font-size:12px}.hero-notification__unread-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--primary);color:#fff;font-size:10px;font-weight:700;line-height:1}.hero-notification__add{width:28px;height:28px;border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.25);border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.hero-notification__add i{font-size:11px}.hero-notification__add:hover:not(:disabled){background:var(--primary);border-color:var(--primary);color:#fff}.hero-notification__mark-all{height:26px;border:none;border-radius:0;background:transparent;color:var(--primary);font-size:11px;font-weight:600;padding:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:4px}.hero-notification__mark-all i{font-size:10px}.hero-notification__mark-all:hover:not(:disabled){opacity:.75}.hero-notification__mark-all:disabled{color:var(--muted-2);cursor:default}.hero-notification__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;position:relative}.hero-notification__item{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;border-radius:10px;background:#f8fafc;border:1px solid #e2e8f0;position:relative;transition:border-color .18s ease,transform .18s ease}@media(hover:hover){.hero-notification__item:hover{border-color:#c7d2fe;transform:translateY(-1px)}}.hero-notification__avatar{width:34px;height:34px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);font-size:11px;font-weight:700;letter-spacing:.5px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification__body{flex:1;min-width:0;padding-right:52px}.hero-notification__body-head{display:flex;align-items:center;gap:6px;margin-bottom:3px}.hero-notification__author{font-size:12px;font-weight:700;color:var(--text)}.hero-notification__content{margin:0;font-size:13.5px;line-height:1.5;color:var(--text);white-space:pre-wrap;word-break:break-word}.hero-notification__created{display:block;margin-top:4px;font-size:11px;color:#94a3b8;font-variant-numeric:tabular-nums}.hero-notification__item-actions{position:absolute;top:7px;right:8px;display:inline-flex;align-items:center;gap:4px}.hero-notification__ack-top{width:24px;height:24px;border:none;border-radius:50%;background:#eff6ff;color:#1d4ed8;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s ease,color .15s ease,transform .15s ease}.hero-notification__ack-top i{font-size:10px}.hero-notification__ack-top:active:not(:disabled){transform:scale(.96)}.hero-notification__ack-top:hover:not(:disabled){background:#dbeafe;color:#1e40af}.hero-notification__delete{width:24px;height:24px;border:none;border-radius:50%;background:transparent;color:#94a3b8;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.hero-notification__delete i{font-size:10px}.hero-notification__delete:hover:not(:disabled){background:#fee2e2;color:#b91c1c}.hero-notification__empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 8px;position:relative}.hero-notification__empty-icon{font-size:28px;color:var(--primary);opacity:.45;margin-bottom:4px}.hero-notification__empty-title{font-size:13px;font-weight:600;color:var(--muted)}.hero-notification__empty-sub{font-size:11px;color:var(--muted-2);text-align:center}.hero-notification__more{align-self:flex-start;border:none;background:transparent;color:var(--primary);font-size:12px;font-weight:700;cursor:pointer;padding:0;font-family:inherit;position:relative}.hero-notification__more:hover{opacity:.75}.hero-notification__composer-row{display:flex;align-items:stretch;gap:8px;min-width:0}.hero-notification__composer-panel{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid #dbe4f3;border-radius:12px;background:#f8fbff}.hero-notification__input-wrap{flex:1;min-width:0;border-radius:11px;border:1.5px solid #cfd8e6;background:#fff;transition:border-color .15s ease;position:relative}.hero-notification__composer-row:focus-within .hero-notification__input-wrap{border-color:var(--primary)}.hero-notification__input{width:100%;min-width:0;min-height:100px;border:0;border-radius:11px;padding:10px 12px 32px;font-size:13px;line-height:1.45;color:var(--text);background:transparent;font-family:inherit;resize:vertical}.hero-notification__input:focus{outline:none}.hero-notification__char-ring{position:absolute;bottom:8px;right:10px;display:flex;align-items:center;gap:4px;pointer-events:none}.hero-notification__char-ring svg{width:22px;height:22px;transform:rotate(-90deg)}.hero-notification__char-ring-track{fill:none;stroke:#e2e8f0;stroke-width:3}.hero-notification__char-ring-fill{fill:none;stroke:var(--primary);stroke-width:3;stroke-dasharray:94 94;stroke-linecap:round;transition:stroke-dashoffset .2s ease}.hero-notification__char-count{font-size:10px;color:var(--muted-2);font-variant-numeric:tabular-nums}.hero-notification__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.hero-notification__send,.hero-notification__cancel{height:36px;border-radius:10px;min-width:86px;padding:0 14px;font-size:12px;font-weight:700}.hero-notification__send{background:var(--primary);color:#fff}.hero-notification__send:not(:disabled):hover{background:var(--primary-2)}.hero-notification__cancel{min-width:72px}.hero-notification__error{margin:0;font-size:12px;color:#b91c1c;position:relative}.hero-notification-dialog{max-width:min(520px,100vw - 24px);overflow:hidden}.hero-notification-dialog .hero-notification__composer-row{align-items:stretch;gap:10px}.hero-notification-dialog .hero-notification__input-wrap{min-height:110px}.hero-notification-dialog__head{margin-bottom:14px}.hero-notification-dialog__title-group{display:flex;align-items:flex-start;gap:10px}.hero-notification-dialog__icon-wrap{width:34px;height:34px;border-radius:10px;background:rgba(var(--primary-rgb, 76, 92, 240),.12);border:1px solid rgba(var(--primary-rgb, 76, 92, 240),.22);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.hero-notification-dialog__icon-wrap i{font-size:14px}.hero-notification-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-dialog__subtitle{margin:2px 0 0;font-size:11px;color:var(--muted-2)}.hero-notification-list-dialog{max-width:min(620px,100vw - 24px)}.hero-notification-list-dialog__head{margin-bottom:10px;align-items:flex-start}.hero-notification-list-dialog__head-actions{display:inline-flex;align-items:center;gap:8px;flex-shrink:0}.hero-notification-list-dialog__title{margin:0;font-size:15px;font-weight:800;color:var(--text);line-height:1.3}.hero-notification-list-dialog__body{max-height:min(60vh,480px);overflow-y:auto;padding-right:2px}.ack-all-confirm-dialog{max-width:min(380px,100vw - 24px);text-align:center;padding:28px 24px 24px;display:flex;flex-direction:column;align-items:center;gap:10px;overflow:hidden}.ack-all-confirm-dialog__icon-wrap{width:52px;height:52px;border-radius:50%;background:rgba(var(--primary-rgb, 76, 92, 240),.1);border:1.5px solid rgba(var(--primary-rgb, 76, 92, 240),.2);color:var(--primary);display:flex;align-items:center;justify-content:center;margin-bottom:2px}.ack-all-confirm-dialog__icon-wrap i{font-size:22px}.ack-all-confirm-dialog__title{margin:0;font-size:16px;font-weight:800;color:var(--text)}.ack-all-confirm-dialog__desc{margin:0;font-size:13px;color:var(--muted);line-height:1.55;max-width:280px}.ack-all-confirm-dialog__actions{display:flex;gap:10px;margin-top:6px;width:100%;justify-content:center}.ack-all-confirm-dialog__actions .btn{min-width:96px;height:38px;border-radius:10px;font-size:13px;font-weight:700}.ack-all-confirm-dialog__confirm-btn{background:var(--primary);color:#fff;display:inline-flex;align-items:center;gap:6px}.ack-all-confirm-dialog__confirm-btn i{font-size:12px}.ack-all-confirm-dialog__confirm-btn:not(:disabled):hover{background:var(--primary-2)}.hero-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;align-self:center}.content{width:100%}.content .tab-pane[hidden]{display:none!important}.content .tab-pane--feeding{max-width:1100px;margin:0 auto;padding:0 20px}.content .tab-pane--documents{max-width:none;padding:0}.content .tab-pane--weight{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 5%,var(--surface, #fff)) 0%,var(--surface, #fff) min(36vh,280px),var(--surface, #fff) 100%)}.content .tab-pane--mom{max-width:1100px;margin:0 auto;padding:0 20px calc(24px + env(safe-area-inset-bottom,0px))}.content .tab-pane--medical{max-width:none;padding:0;background:linear-gradient(180deg,color-mix(in srgb,var(--primary, #5b6cff) 7%,var(--surface, #fff)) 0%,var(--surface, #fff) min(42vh,320px),var(--surface, #fff) 100%)}.bottom-nav{--bn-primary: #5b6cff;--bn-primary-rgb: 91, 108, 255;--bn-inactive: #b0b7c3;--bn-height: 62px;position:fixed;left:0;right:0;bottom:0;width:100%;box-sizing:border-box;z-index:90;display:flex;align-items:center;justify-content:space-between;gap:4px;min-height:var(--bn-height);padding:0 4px env(safe-area-inset-bottom,0px);background:#fffffff5;backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);border-top:1px solid rgba(0,0,0,.055);box-shadow:0 -4px 24px -4px #0f172a14}.bottom-nav .bn-side{flex:1;display:flex;align-items:center;justify-content:space-around;min-width:0}.bottom-nav .bn-fab{flex-shrink:0;width:56px;height:56px;margin-top:-20px;border-radius:999px;border:3px solid rgba(255,255,255,.96);background:var(--bn-primary);color:#fff;font-size:22px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:transform .15s ease,background .15s ease,opacity .15s ease}.bottom-nav .bn-fab:hover:not(:disabled){background:var(--primary-2, #4c5cf0);transform:translateY(-2px)}.bottom-nav .bn-fab:active:not(:disabled){transform:translateY(0) scale(.94)}.bottom-nav .bn-fab:disabled{background:var(--bn-inactive);cursor:not-allowed;opacity:.7}.bottom-nav .bn-item{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;height:var(--bn-height);padding:0 2px;background:transparent;border:none;color:var(--bn-inactive);cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:color .22s ease}.bottom-nav .bn-item .bn-icon-wrap{display:flex;align-items:center;justify-content:center;width:32px;height:30px;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.bottom-nav .bn-item .bn-icon{font-size:21px;line-height:1;transition:filter .22s ease;display:block}.bottom-nav .bn-item .bn-label{font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1;transition:font-weight .22s ease}.bottom-nav .bn-item:not(.active):hover{color:rgba(var(--bn-primary-rgb),.7)}.bottom-nav .bn-item:active .bn-icon-wrap{transform:scale(.88)}.bottom-nav .bn-item.active{color:var(--bn-primary)}.bottom-nav .bn-item.active .bn-icon-wrap{transform:translateY(-3px) scale(1.15)}.bottom-nav .bn-item.active .bn-icon{filter:drop-shadow(0 2px 8px rgba(var(--bn-primary-rgb),.4))}.bottom-nav .bn-item.active .bn-label{font-weight:700}.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px;margin-bottom:16px}.card-header{margin-bottom:16px}.card-header h2{margin:0;font-size:18px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:8px}.card-header h2 i{color:var(--primary)}.card-header p{margin:6px 0 0;color:var(--muted);font-size:13px}.eyebrow{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em}.eyebrow i{margin-right:4px}.form{display:flex;flex-direction:column;gap:14px}.field{display:flex;flex-direction:column;gap:6px}.field .label{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.02em;display:flex;align-items:center;gap:6px;flex-wrap:wrap}.field .label .label-hint{font-size:11px;font-weight:500;color:var(--muted-2);font-style:italic;text-transform:none;letter-spacing:0}.field input[type=text],.field input[type=date],.field input[type=time],.field input[type=number]{padding:12px 14px;border:1.5px solid var(--border);border-radius:10px;font-size:14px;font-family:inherit;color:var(--text);background:#fafbfc;width:100%;min-width:0;box-sizing:border-box;appearance:none;-webkit-appearance:none;transition:border-color .15s,background .15s;min-height:46px;font-variant-numeric:tabular-nums}.field input[type=text]:focus,.field input[type=date]:focus,.field input[type=time]:focus,.field input[type=number]:focus{outline:none;border-color:var(--primary);background:#fff}.field input[type=text]::placeholder,.field input[type=date]::placeholder,.field input[type=time]::placeholder,.field input[type=number]::placeholder{color:var(--muted-2)}.field input[type=number]{-moz-appearance:textfield}.field input[type=number]::-webkit-outer-spin-button,.field input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.radio-group{display:flex;gap:8px;flex-wrap:wrap}.chip{padding:9px 16px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;cursor:pointer;color:var(--text);transition:all .15s;font-family:inherit}.chip:hover{border-color:var(--primary);color:var(--primary)}.chip.active{background:var(--primary);border-color:var(--primary);color:#fff}.form-actions{display:flex;gap:10px;margin-top:4px;flex-wrap:wrap}.btn{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;border:none;border-radius:10px;font-size:14px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .15s,color .15s,border-color .15s}.btn:disabled{opacity:.5;cursor:not-allowed}.btn.small{padding:6px 12px;font-size:12px}.btn.primary{background:var(--primary);color:#fff}.btn.primary:not(:disabled):hover{background:var(--primary-2)}.btn.ghost{background:transparent;color:var(--muted);border:1.5px solid var(--border)}.btn.ghost:hover{color:var(--text);border-color:var(--border-strong)}.btn.danger-ghost{background:transparent;color:var(--rose);border:1.5px solid var(--rose-soft)}.btn.danger-ghost:hover{background:var(--rose-soft)}.btn.icon-btn{padding:0;width:36px;height:36px;justify-content:center;background:transparent;color:var(--muted);border:1.5px solid var(--border);border-radius:10px}.btn.icon-btn i{font-size:14px}.btn.icon-btn:not(:disabled):hover{color:var(--primary);border-color:var(--primary);background:var(--primary-soft)}.btn.icon-btn .spin{animation:spin-refresh .9s linear infinite}@keyframes spin-refresh{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.age-stats{display:flex;align-items:baseline;justify-content:flex-start;gap:16px;padding:14px 16px;background:var(--primary-soft);border-radius:12px}.age-stats .stat{text-align:center}.age-stats .stat .num{font-size:26px;font-weight:700;color:var(--primary);line-height:1;font-variant-numeric:tabular-nums}.age-stats .stat .unit{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-top:3px}.age-stats .dot{color:var(--muted-2);font-size:20px}.period-card{margin-bottom:0}.period-card h2{margin:6px 0 8px;font-size:20px;font-weight:700}.period-card .period-summary{color:var(--muted);font-size:14px;margin:0 0 14px;line-height:1.55}.schedule-grid{display:grid;grid-template-columns:1fr;gap:10px}.schedule{display:flex;gap:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.schedule i{color:var(--primary);font-size:18px;margin-top:2px}.schedule .schedule-title{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:2px}.schedule .schedule-text{font-size:14px;color:var(--text);line-height:1.5}.compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}@media(max-width:768px){.compare-grid{grid-template-columns:1fr}}.day-card{margin-bottom:0;min-width:0;overflow:hidden}.day-card.today{border-color:#3b82f6;background:linear-gradient(180deg,#dbeafe,#fff 50%)}.day-card.yesterday{background:#fafbfc}.day-date-nav-wrap{position:relative;margin-bottom:12px}.day-date-nav-reset{position:absolute;top:-8px;right:-8px;z-index:1;width:24px;height:24px;border:1px solid var(--border);border-radius:50%;background:#fff;color:var(--muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;box-shadow:0 1px 3px #0f172a14}.day-date-nav-reset i{font-size:10px}.day-date-nav-reset:hover{background:#fee2e2;border-color:#fecaca;color:#dc2626}.day-date-nav-reset:active{background:#fee2e2;color:#dc2626}.day-date-strip{display:grid;grid-template-columns:32px 1fr 32px;gap:6px;align-items:stretch;padding:6px 8px;border-radius:10px;background:#fff;border:1px solid var(--border);touch-action:manipulation}.day-date-strip-scroller{display:flex;gap:4px;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:thin;padding:1px 2px}.day-date-strip-scroller::-webkit-scrollbar{height:4px}.day-date-strip-scroller::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px}.day-date-strip-day{flex:0 0 64px;scroll-snap-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;min-width:0;padding:6px 4px;border:1px solid transparent;border-radius:8px;background:#f8fafc;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-day:hover{background:#eef2ff;border-color:#c7d2fe}.day-date-strip-day.active{background:#dbeafe;border-color:#3b82f6;box-shadow:inset 0 0 0 1px #3b82f626}.day-date-strip-date{font-size:12px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml{font-size:11px;font-weight:600;color:#1d4ed8;font-variant-numeric:tabular-nums;line-height:1.2}.day-date-strip-ml.empty{color:var(--muted);font-weight:500}.day-date-strip-arrow{width:32px;min-height:52px;flex-shrink:0;border:none;border-radius:8px;background:#f1f5f9;color:var(--text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.day-date-strip-arrow i{font-size:12px}.day-date-strip-arrow:hover:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:active:not(:disabled){background:var(--primary);color:#fff}.day-date-strip-arrow:disabled{opacity:.35;cursor:not-allowed}.day-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px}.day-head h3{margin:4px 0 0;font-size:16px;color:var(--text);font-variant-numeric:tabular-nums}.big-total{display:flex;align-items:baseline;gap:3px}.big-total .num{font-size:32px;font-weight:800;color:var(--text);line-height:1;font-variant-numeric:tabular-nums}.big-total .unit{font-size:14px;color:var(--muted);font-weight:500}.day-stats{display:flex;flex-wrap:wrap;gap:10px 16px;padding:10px 0 0;border-top:1px solid var(--border)}.day-stats>div{font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:5px}.day-stats>div i{color:var(--primary);font-size:12px}.compare-banner{margin-top:12px;padding:12px;background:var(--primary-soft);border-radius:12px;font-size:12px;color:var(--text);display:flex;flex-direction:column;gap:8px}.compare-banner strong{color:var(--primary);font-variant-numeric:tabular-nums;font-weight:700}.compare-banner .compare-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.compare-banner .compare-row:not(:last-child){padding-bottom:8px;border-bottom:1px dashed rgba(15,23,42,.1)}.compare-banner .compare-stats{display:flex;align-items:center;gap:6px;flex:1}.compare-banner .compare-stats .label{color:var(--muted);font-weight:500;font-size:11px}.compare-banner .compare-stats .count{color:var(--muted-2);font-size:11px}.compare-banner .compare-change{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:600;flex-shrink:0}.compare-banner .compare-change i{font-size:10px}.compare-banner .compare-change i.up{color:var(--mint)}.compare-banner .compare-change i.down{color:var(--amber)}.compare-banner .compare-change strong{font-size:12px}.compare-banner .compare-change span{color:var(--muted-2);font-weight:500}.today-logs{margin-top:12px;padding:12px;background:#fafbfc;border:1px solid var(--border);border-radius:12px}.logs-header{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}.logs-header span{display:flex;align-items:center;gap:6px}.logs-header span:before{content:\"\\1f37c\";font-size:14px}.logs-header .logs-more{text-transform:none;letter-spacing:0;background:transparent;border:none;color:var(--primary);font-size:12px;font-weight:600;padding:4px 8px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:3px;transition:background .2s ease}.logs-header .logs-more i{font-size:10px}.logs-header .logs-more:hover{background:var(--primary-soft)}.log-list{display:flex;flex-direction:column;gap:6px;min-width:0}.log-row{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#fafbfc;border-radius:8px;font-size:13px;min-width:0;max-width:100%;transition:all .2s ease;position:relative}.log-row.low-volume .log-volume{color:#f59e0b;font-weight:900}.log-row.high-volume .log-volume{color:#10b981;font-weight:900}.log-row:hover{background:#f1f5f9;transform:translateY(-1px);box-shadow:0 2px 8px #0000000d}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.log-time{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;min-width:42px}.log-volume{font-weight:800;color:var(--primary);font-size:16px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}.log-gap{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;background:#f3f4f6;color:#6b7280;border-radius:999px;font-size:10.5px;font-weight:600;white-space:nowrap;flex-shrink:0;font-variant-numeric:tabular-nums;transition:all .2s ease}.log-gap i{font-size:9px;opacity:.7}.log-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.log-gap.feed-gap-warn i{opacity:.95;color:inherit}.log-note{color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.log-actions{margin-left:auto;display:inline-flex;align-items:center;gap:2px}.log-edit,.log-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:4px 6px;border-radius:6px;font-size:12px;transition:all .2s ease}.log-edit:hover{color:var(--primary);background:var(--primary-soft)}.log-del:hover{color:var(--rose);background:var(--rose-soft)}.empty-mini{padding:12px;text-align:center;color:var(--muted-2);font-size:13px;font-style:italic}.countdown-hero{display:flex;flex-direction:column;gap:16px;padding:18px 20px;background:#fff;border-radius:16px;border:1px solid #e6e9ff;transition:all .3s ease;margin-bottom:20px}.countdown-hero .hero-main{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px}.countdown-hero .feeding-time-card{flex:1;min-width:0;text-align:center}.countdown-hero .feeding-time-card .time-header{margin-bottom:12px;display:flex;justify-content:center}.countdown-hero .feeding-time-card .time-header .status-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;padding:6px 12px;border-radius:14px;white-space:nowrap}.countdown-hero .feeding-time-card .time-header .status-badge i{font-size:11px}.countdown-hero .feeding-time-card .time-header .status-badge.status-waiting{background:var(--primary-soft);color:var(--primary)}.countdown-hero .feeding-time-card .time-header .status-badge.status-now{background:#fee2e2;color:#dc2626;animation:pulse-gentle 2s ease-in-out infinite;font-size:11px;text-transform:none;font-weight:700}.countdown-hero .feeding-time-card .time-header .status-badge.status-late{background:var(--rose-soft);color:var(--rose)}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:42px;font-weight:800;line-height:1;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.02em;margin-bottom:4px}.countdown-hero .feeding-time-card .time-display .time-remaining{font-size:15px;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}.countdown-hero .countdown-next{display:flex;flex-direction:column;gap:8px;padding-left:16px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:0}.countdown-hero .next-item{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.countdown-hero .next-item i{color:var(--primary);font-size:13px}.countdown-hero .next-item strong{font-weight:700}.countdown-hero .next-item .predict-label,.countdown-hero .next-item .recommend-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);margin-left:4px}.countdown-hero .next-item .recommend-label{color:var(--mint)}.countdown-hero .volume-info{display:flex;flex-direction:column;gap:8px;padding-left:20px;border-left:1px dashed #d8dcff;flex-shrink:0;min-width:160px;transition:all .3s ease}.countdown-hero .volume-info .volume-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.countdown-hero .volume-info .volume-row .volume-prefix{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2);flex-shrink:0}.countdown-hero .volume-info .volume-row .volume-amount{font-size:22px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;line-height:1;text-align:right}.countdown-hero .volume-info .volume-row.predicted-row .volume-prefix,.countdown-hero .volume-info .volume-row.predicted-row .volume-amount{color:var(--primary)}.countdown-hero .volume-info .volume-row.recommended-row .volume-prefix,.countdown-hero .volume-info .volume-row.recommended-row .volume-amount{color:var(--mint)}.countdown-hero[data-status=waiting]{border-color:#bbf7d0;background:linear-gradient(135deg,#f7fef8,#f0fdf4)}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .predicted-time{color:#16a34a}.countdown-hero[data-status=waiting] .feeding-time-card .time-display .time-remaining{color:#15803d}.countdown-hero[data-status=waiting] .feeding-time-card .status-badge.status-waiting{background:#dcfce7!important;color:#15803d!important;border:1px solid #86efac}.countdown-hero[data-status=waiting] .volume-info .volume-amount{color:#16a34a}.countdown-hero[data-status=waiting] .volume-info .volume-prefix{color:#15803d}.countdown-hero[data-status=late]{border-color:#fca5a5;background:linear-gradient(135deg,#fef2f2,#fee2e2)}.countdown-hero[data-status=late] .feeding-time-card .time-display .predicted-time{color:#dc2626}.countdown-hero[data-status=late] .feeding-time-card .time-display .time-remaining{color:#b91c1c}.countdown-hero[data-status=late] .feeding-time-card .status-badge.status-late{background:#fee2e2;color:#dc2626;border:1px solid #fca5a5}.countdown-hero[data-status=late] .volume-info .volume-amount{color:#dc2626}.countdown-hero[data-status=late] .volume-info .volume-prefix{color:#b91c1c}.countdown-hero[data-status=now]{border-color:#fbbf24;background:linear-gradient(135deg,#fefce8,#fef3c7);animation:pulse-card 2s ease-in-out infinite}.countdown-hero[data-status=now] .feeding-time-card .time-display .predicted-time{color:#92400e}.countdown-hero[data-status=now] .feeding-time-card .time-display .time-remaining{color:#a16207}.countdown-hero[data-status=now] .feeding-time-card .status-badge.status-now{background:#fef3c7!important;color:#b45309!important;border:1px solid #f59e0b!important}.countdown-hero[data-status=now] .volume-info .volume-amount{color:#92400e}.countdown-hero[data-status=now] .volume-info .volume-prefix{color:#a16207}@media(max-width:540px){.countdown-hero .hero-main{flex-direction:column;align-items:stretch;gap:12px}.countdown-hero .hero-main .volume-info{border-left:none;border-top:1px dashed #d8dcff;padding-left:0;padding-top:12px;min-width:auto}.countdown-hero .hero-main .volume-info .volume-row{justify-content:space-between;padding:0 4px}.countdown-hero .hero-main .feeding-time-card .time-display .predicted-time{font-size:36px}.countdown-hero .hero-main .feeding-time-card .volume-info .volume-amount{font-size:20px}}@media(max-width:400px){.countdown-hero .feeding-time-card .status-badge{font-size:10px;padding:4px 10px}.countdown-hero .feeding-time-card .time-display .predicted-time{font-size:32px}.countdown-hero .volume-info .volume-amount{font-size:18px}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.65}}@keyframes pulse-gentle{0%,to{opacity:1}50%{opacity:.75}}@keyframes pulse-urgent{0%,to{opacity:1;text-shadow:0 0 0 rgba(239,68,68,0)}50%{opacity:.8;text-shadow:0 0 8px rgba(239,68,68,.25)}}@keyframes pulse-card{0%,to{transform:scale(1);box-shadow:0 1px 3px #0000001a,0 0 #f8717100}50%{transform:scale(1.02);box-shadow:0 4px 12px #00000026,0 0 0 2px #f871714d}}.burp-warning{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin:0 0 16px;background:linear-gradient(135deg,#fef3c7,#fde68a);border:1px solid #f59e0b;border-radius:14px;box-shadow:0 2px 8px #f59e0b33;animation:burp-pulse 2s ease-in-out infinite}.burp-warning__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:#f59e0b;border-radius:50%;color:#fff;font-size:18px;animation:burp-icon-bounce 1.5s ease-in-out infinite}.burp-warning__content{flex:1;min-width:0}.burp-warning__title{font-size:15px;font-weight:700;color:#92400e;margin-bottom:4px}.burp-warning__text{font-size:13px;color:#78350f;line-height:1.5;margin-bottom:8px}.burp-warning__text strong{font-weight:600;color:#92400e}.burp-warning__countdown{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:#ffffffb3;border-radius:8px;font-size:13px;color:#92400e;font-weight:500}.burp-warning__countdown i{font-size:14px;color:#f59e0b}.burp-warning__countdown strong{font-size:16px;font-weight:700;color:#d97706}@keyframes burp-pulse{0%,to{box-shadow:0 2px 8px #f59e0b33}50%{box-shadow:0 4px 16px #f59e0b59}}@keyframes burp-icon-bounce{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.countdown-progress{margin-top:10px}.countdown-progress .cp-track{position:relative;height:6px;background:#ffffffbf;border-radius:999px;overflow:hidden}.countdown-progress .cp-fill{height:100%;background:linear-gradient(90deg,var(--primary),var(--accent));border-radius:999px;transition:width .4s ease}.countdown-progress .cp-fill[data-status=waiting]{background:linear-gradient(90deg,#22c55e,#16a34a)}.countdown-progress .cp-fill[data-status=late]{background:linear-gradient(90deg,#f87171,#dc2626)}.countdown-progress .cp-fill[data-status=now]{background:linear-gradient(90deg,#fbbf24,#d97706)}.countdown-progress .cp-labels{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:6px;font-size:11.5px;color:var(--muted)}.countdown-progress .cp-labels i{font-size:10px;margin-right:2px;color:var(--muted-2)}.countdown-progress .cp-labels .muted{color:var(--muted-2)}.countdown-progress .cp-labels .cp-right{font-weight:600;color:var(--muted);white-space:nowrap}.predict-head{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;margin-bottom:16px}.predict-head .eyebrow{color:var(--primary);display:flex;align-items:center;gap:4px}.predict-head .eyebrow.sparkle i{animation:sparkle 2s ease-in-out infinite}.predict-head h2{margin:4px 0 0;font-size:20px;font-weight:700}.last-feed{display:flex;align-items:center;gap:12px;padding:12px 14px;background:#fff;border-radius:12px;margin-bottom:14px;border:1px solid #e8e9ff}.last-feed .last-feed-icon{width:38px;height:38px;border-radius:50%;background:var(--primary-soft);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.last-feed .last-feed-icon i{color:var(--primary);font-size:16px}.last-feed .last-feed-body{flex:1;min-width:0}.last-feed .last-feed-label{font-size:10px;font-weight:700;text-transform:uppercase;color:var(--muted-2);letter-spacing:.06em;margin-bottom:2px}.last-feed .last-feed-value{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.last-feed .last-feed-value strong{color:var(--primary);font-size:18px;font-weight:800}.last-feed .last-feed-value .sep{color:var(--muted-2)}.last-feed .last-feed-value .ago{color:var(--accent);font-weight:600;font-size:13px}.last-feed .last-feed-note{margin-top:4px;font-size:12px;color:var(--muted);font-style:italic;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.predict-empty{padding:14px 16px;background:#fff;border-radius:12px;color:var(--muted);font-size:13px;display:flex;align-items:center;gap:8px}.predict-empty i{color:var(--primary)}.predict-body{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:14px}@media(max-width:640px){.predict-body{grid-template-columns:1fr}}.predict-metric{background:#fff;border-radius:12px;padding:14px;text-align:center}.predict-metric .metric-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;display:flex;align-items:center;justify-content:center;gap:5px}.predict-metric .metric-label i{color:var(--primary);font-size:12px}.predict-metric .metric-value{font-size:28px;font-weight:800;line-height:1;color:var(--primary);font-variant-numeric:tabular-nums}.predict-metric .metric-value .ml-unit{font-size:14px;color:var(--muted);font-weight:500;margin-left:2px}.predict-metric .metric-sub{margin-top:6px;font-size:12px;color:var(--muted)}.reasoning{background:#fff;border-radius:12px;padding:12px 14px;margin-bottom:12px}.reasoning .reasoning-head{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;margin-bottom:6px}.reasoning .reasoning-head i{color:var(--primary);margin-right:4px}.reasoning ul{margin:0;padding-left:18px;list-style:disc}.reasoning li{font-size:13px;color:var(--text);line-height:1.55;margin:2px 0}.predict-actions{display:flex;justify-content:flex-end}.feed-list-card{margin-bottom:16px}.feed-list-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.feed-list-head h2{margin:4px 0 0;font-size:18px;font-weight:700}.feed-list-compact{display:flex;flex-direction:column;gap:6px}.feed-row{display:grid;grid-template-columns:68px 80px 1fr auto;gap:10px;align-items:center;padding:10px 12px;transition:all .2s ease;position:relative}.feed-row.low-volume .feed-row-volume strong{color:#f59e0b;font-weight:900}.feed-row.high-volume .feed-row-volume strong{color:#10b981;font-weight:900}.feed-row{background:#fafbfc;border-radius:10px;border:1px solid var(--border)}.feed-row .feed-row-time{display:flex;flex-direction:column;line-height:1.2;font-variant-numeric:tabular-nums;gap:3px}.feed-row .feed-row-time .time{font-weight:700;color:var(--text);font-size:14px}.feed-row .feed-row-time .date{font-size:11px;color:var(--muted-2)}.feed-row .feed-row-time .feed-row-gap{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;font-weight:600;color:#6b7280;background:#f3f4f6;padding:1px 6px;border-radius:999px;width:fit-content;white-space:nowrap;font-variant-numeric:tabular-nums}.feed-row .feed-row-time .feed-row-gap i{font-size:8px;opacity:.7}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn{background:linear-gradient(135deg,#f59e0b,#ea580c);color:#fff;font-weight:700;border:1px solid rgba(234,88,12,.45);box-shadow:0 0 0 1px #fff3 inset,0 2px 8px #ea580c59}.feed-row .feed-row-time .feed-row-gap.feed-gap-warn i{opacity:.95;color:inherit}.feed-row .feed-row-volume{font-variant-numeric:tabular-nums}.feed-row .feed-row-volume strong{color:var(--primary);font-size:19px;font-weight:800;letter-spacing:-.01em}.feed-row .feed-row-volume .u{font-size:11px;color:var(--muted);margin-left:2px}.feed-row .feed-row-note{font-size:13px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.feed-row .feed-row-note.muted{color:var(--muted-2);font-style:italic}.feed-row .feed-row-actions{display:inline-flex;align-items:center;gap:2px;margin-left:auto}.feed-row .feed-row-edit,.feed-row .feed-row-del{background:transparent;border:none;color:var(--muted-2);cursor:pointer;padding:6px 8px;border-radius:6px;font-size:13px}.feed-row .feed-row-edit:hover{color:var(--primary, #5b6cff);background:#5b6cff1a}.feed-row .feed-row-del:hover{color:var(--rose);background:var(--rose-soft)}@media(max-width:480px){.feed-row{grid-template-columns:58px 70px 1fr auto;gap:8px;padding:8px 10px}.feed-row .feed-row-time .time{font-size:13px}}.dialog-wide{max-width:720px;display:flex;flex-direction:column;padding:0;overflow:hidden}@media(max-width:640px){.dialog-wide{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 72px);border-radius:14px}}.dialog-wide .dialog-head{position:sticky;top:0;z-index:5;background:var(--surface);padding:16px 20px;margin:0;border-bottom:1px solid var(--border);-webkit-backdrop-filter:saturate(1.2) blur(6px);backdrop-filter:saturate(1.2) blur(6px)}@media(max-width:640px){.dialog-wide .dialog-head{padding:12px 14px}}.dialog-wide .dialog-close{width:40px;height:40px;font-size:16px;background:#f1f3f7;color:var(--text)}.dialog-wide .dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:18px 20px 24px;overflow-y:auto;flex:1;min-height:0}@media(max-width:640px){.dialog-wide .history-body,.dialog-wide .tips-dialog-body{padding:14px 14px 20px}}.history-body{display:flex;flex-direction:column;gap:18px}.history-paging-hint{margin:0;font-size:12px;line-height:1.45}.history-load-more{display:grid;justify-items:center;width:100%;box-sizing:border-box;padding:8px 0 12px}.history-load-more .btn{width:auto;max-width:min(320px,100%);min-width:0;margin-inline:auto;justify-content:center}.tips-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;background:#f1f3f7;border-radius:12px;margin-bottom:16px}.nav-arrow{width:40px;height:40px;border-radius:50%;border:none;background:#fff;color:var(--primary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px;transition:all .15s;flex-shrink:0}.nav-arrow:hover:not(:disabled){background:var(--primary);color:#fff;transform:scale(1.05)}.nav-arrow:active:not(:disabled){transform:scale(.95)}.nav-arrow:disabled{opacity:.35;cursor:not-allowed}.tips-now-btn{flex:1;padding:9px 16px;border:none;background:transparent;border-radius:999px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s}.tips-now-btn i{font-size:12px}.tips-now-btn:hover{color:var(--primary);background:#fff}.tips-now-btn.active{background:var(--primary);color:#fff;pointer-events:none}.tips-dialog-body .period-summary{font-size:14px;color:var(--muted);line-height:1.6;margin:0 0 14px}.tips-dialog-body .schedule-grid{grid-template-columns:repeat(2,1fr);margin-bottom:16px}@media(max-width:560px){.tips-dialog-body .schedule-grid{grid-template-columns:1fr}}.dialog-tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.history-group .history-group-head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:var(--primary-soft);border-radius:10px;margin-bottom:8px}.history-group .history-group-head .h-date{font-size:14px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-day{font-size:11px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.05em}.history-group .history-group-head .h-total{font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}.history-group .history-group-head .h-total strong{color:var(--primary);font-size:20px;font-weight:800}.history-group .history-group-head .h-total .h-count{color:var(--muted);font-size:12px;font-weight:500;margin-left:4px}.history-group .history-group-head .h-total .h-avg{color:var(--primary);font-size:12px;font-weight:600;margin-left:4px;font-variant-numeric:tabular-nums}.history-group .history-group-rows{display:flex;flex-direction:column;gap:4px}.tips-section-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;padding:4px 4px 0}.tips-section-head .eyebrow i{margin-right:4px}.tips-section-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text)}.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}.filter-chip{padding:7px 14px;border:1.5px solid var(--border);background:#fff;border-radius:999px;font-size:13px;font-weight:500;cursor:pointer;color:var(--muted);display:inline-flex;align-items:center;gap:5px;transition:all .15s;font-family:inherit}.filter-chip i{font-size:12px}.filter-chip:hover{border-color:var(--cat-color, var(--primary));color:var(--cat-color, var(--primary))}.filter-chip.active{background:var(--cat-color, var(--primary));border-color:var(--cat-color, var(--primary));color:#fff}.tips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-bottom:20px}.tip-card{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--cat-color, var(--primary));border-radius:12px;padding:16px;transition:transform .15s}.tip-card:hover{transform:translateY(-2px)}.tip-card .tip-head{margin-bottom:8px}.tip-card .tip-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;background:var(--cat-color, var(--primary));color:#fff;font-size:11px;font-weight:600;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.tip-card .tip-pill i{font-size:10px}.tip-card .tip-title{margin:6px 0 8px;font-size:15px;font-weight:700;line-height:1.35;color:var(--text)}.tip-card .tip-body{margin:0;font-size:13px;line-height:1.55;color:var(--muted)}.empty{grid-column:1/-1;padding:40px 20px;text-align:center;color:var(--muted-2)}.empty i{font-size:32px;margin-bottom:8px;display:block}.upcoming{margin-bottom:24px}.upcoming h3{margin:0 0 12px;font-size:16px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}.upcoming h3 i{color:var(--primary)}.upcoming-grid{display:flex;gap:8px;flex-wrap:wrap}.upcoming-item{padding:10px 14px;background:var(--surface);border:1px solid var(--border);border-radius:10px;font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:6px}.upcoming-item i{color:var(--primary);font-size:12px}.disclaimer{padding:16px;background:var(--amber-soft);border-radius:12px;color:#78350f;font-size:13px;line-height:1.5;margin-bottom:24px;display:flex;gap:8px;align-items:flex-start}.disclaimer i{color:var(--amber);margin-top:2px}.toast{position:fixed;bottom:100px;left:50%;transform:translate(-50%);z-index:90;padding:12px 18px;border-radius:12px;font-size:14px;font-weight:500;box-shadow:0 6px 24px #0f172a1f;display:flex;align-items:center;gap:8px;animation:toastIn .3s cubic-bezier(.4,0,.2,1)}.toast.success{background:var(--mint);color:#fff}.toast.error{background:var(--rose);color:#fff}@keyframes toastIn{0%{transform:translate(-50%,20px);opacity:0}to{transform:translate(-50%);opacity:1}}.fab{position:fixed;bottom:calc(80px + env(safe-area-inset-bottom,0px));right:20px;z-index:96;display:inline-flex;align-items:center;gap:10px;padding:14px 22px 14px 18px;background:var(--primary);color:#fff;border:none;border-radius:999px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;box-shadow:0 8px 24px #5b6cff73;transition:transform .15s,box-shadow .15s,background .15s}.fab i{font-size:18px;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:#ffffff2e}.fab .fab-label{white-space:nowrap}.fab:hover{background:var(--primary-2);transform:translateY(-2px);box-shadow:0 12px 28px #5b6cff8c}.fab:active{transform:translateY(0)}@media(max-width:480px){.fab{padding:14px}.fab .fab-label{display:none}.fab i{font-size:22px;width:28px;height:28px}}.dialog-overlay{position:fixed;inset:0;background:#0f172a8c;z-index:110;display:flex;align-items:center;justify-content:center;padding:20px;animation:overlayIn .2s ease-out}.dialog-overlay.dialog-overlay--front{z-index:400}.dialog-overlay.dialog-overlay--event-reminder{z-index:410}@media(max-width:640px){.dialog-overlay{padding:calc(env(safe-area-inset-top,0px) + 16px) 12px calc(env(safe-area-inset-bottom,0px) + 16px);align-items:flex-start}}@keyframes overlayIn{0%{opacity:0}to{opacity:1}}.dialog{background:var(--surface);border-radius:20px;width:100%;max-width:560px;max-height:min(100vh - 40px,100dvh - 40px);overflow-y:auto;padding:24px;animation:dialogIn .25s cubic-bezier(.4,0,.2,1);box-shadow:0 20px 60px #0f172a40}@media(max-width:640px){.dialog{max-height:calc(100dvh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px) - 32px);border-radius:16px}}@keyframes dialogIn{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:18px}.dialog-head h2{margin:4px 0 0;font-size:20px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}.dialog-head h2 i{color:var(--primary)}.dialog-close{background:#f1f3f7;border:none;width:36px;height:36px;border-radius:50%;cursor:pointer;color:var(--muted);font-size:14px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s,color .15s}.dialog-close:hover{background:var(--rose-soft);color:var(--rose)}.log-form{display:flex;flex-direction:column;gap:14px}.log-volume-field{margin:0}.log-volume-field .log-volume-input-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px 10px;min-width:0}.log-volume-field .log-volume-input-row>input[type=number]{flex:0 0 118px;width:118px;max-width:min(118px,100%);min-width:0}.log-volume-field .predict-hint--inline{flex:1 1 auto;min-width:0;margin:0;padding:6px 10px;gap:6px;font-size:12px;border-radius:8px}.log-volume-field .predict-hint--inline>i{font-size:13px;flex-shrink:0}.log-volume-field .predict-hint--inline .hint-text{flex:1 1 auto;min-width:0;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-volume-field .predict-hint--inline .btn.small{flex-shrink:0;padding:4px 8px;font-size:11px}@media(max-width:400px){.log-volume-field .log-volume-input-row>input[type=number]{flex:1 1 100px;width:100%;max-width:none}.log-volume-field .predict-hint--inline{flex:1 1 100%;white-space:normal}.log-volume-field .predict-hint--inline .hint-text{white-space:normal;overflow:visible;text-overflow:unset}}.edit-date-banner{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 14px;background:linear-gradient(135deg,#eef2ff,#f5f3ff);border:1px solid #dbe1ff;border-radius:12px;font-size:13px;color:var(--text)}.edit-date-banner>i.pi-calendar{color:var(--primary, #5b6cff);font-size:15px}.edit-date-banner .edb-label{color:var(--muted, #6b7280);font-weight:500}.edit-date-banner .edb-value{font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.01em}.edit-date-banner .edb-lock{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:50%;background:#ffffffe0;border:1px solid #e2e8f0;color:var(--muted, #64748b)}.edit-date-banner .edb-lock i{font-size:14px;line-height:1}.log-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.log-grid .field.full{grid-column:1/-1}.log-grid.log-grid--date-time{grid-template-columns:repeat(2,minmax(0,1fr))}@media(max-width:560px){.log-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:380px){.log-grid{grid-template-columns:1fr}}.quick-volumes{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.quick-volumes .quick-label{font-size:12px;color:var(--muted);font-weight:600;margin-right:4px}.quick-chip{padding:7px 13px;background:#f1f3f7;border:none;border-radius:999px;font-size:12px;font-weight:600;cursor:pointer;color:var(--muted);transition:all .15s;font-family:inherit}.quick-chip:hover{background:var(--primary-soft);color:var(--primary)}.quick-chip.now-chip{background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;gap:4px}.quick-chip.now-chip:hover{background:var(--primary);color:#fff}.quick-chip.formula-chip{display:inline-flex;align-items:center;gap:5px;background:#fff7ed;color:#c2410c;border:1px solid #fed7aa}.quick-chip.formula-chip i{font-size:11px;opacity:.9}.quick-chip.formula-chip:hover{background:#ffedd5;color:#9a3412;border-color:#fdba74}.quick-chip.formula-chip.active{background:#c2410c;color:#fff;border-color:#c2410c;box-shadow:0 2px 8px #c2410c40}.quick-chip.formula-chip.active:hover{background:#9a3412;border-color:#9a3412;color:#fff}.bottle-prep{background:#f0f9ff;border:1px solid #bae6fd;border-radius:12px;padding:8px 12px;margin-bottom:16px;display:flex;flex-direction:column;gap:8px;transition:background .2s,border-color .2s}.bottle-prep:has(.bp-warning){background:#fffbeb;border-color:#fde68a}.bottle-prep:has(.bp-warning) .bp-info>i{color:#f59e0b}.bottle-prep:has(.bp-warning) .bp-info strong{color:#d97706}.bottle-prep:has(.bp-warning) .bp-prep-user{color:#92400e;background:#fbbf246b}.bottle-prep:has(.bp-warning) .bp-prep-time{color:#c2410c;background:linear-gradient(180deg,#fff7ed,#ffedd5);border-color:#fb923c73}.bottle-prep:has(.bp-form){align-items:stretch;padding:10px 12px 12px}.bottle-prep .bp-warning{display:flex;align-items:center;justify-content:space-between;gap:10px;animation:slideDown .2s ease;flex-wrap:wrap}.bottle-prep .bp-warning__content{display:flex;align-items:center;gap:8px;font-size:13px;flex:1;min-width:0}.bottle-prep .bp-warning__content i{font-size:16px;flex-shrink:0}.bottle-prep .bp-warning__content strong{font-weight:700}.bottle-prep .bp-warning--expired{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:8px 10px;color:#991b1b}.bottle-prep .bp-warning--expired i{color:#dc2626}.bottle-prep .bp-warning--soon{background:#fefce8;border:1px solid #fde68a;border-radius:8px;padding:8px 10px;color:#92400e}.bottle-prep .bp-warning--soon i{color:#f59e0b}.bottle-prep .bp-warning__btn{flex-shrink:0;display:inline-flex;align-items:center;gap:5px;padding:7px 12px;font-size:12px;font-weight:700;font-family:inherit;color:#fff;background:linear-gradient(180deg,#f59e0b,#d97706);border:none;border-radius:8px;cursor:pointer;box-shadow:0 2px 4px #f59e0b40;transition:all .15s ease}.bottle-prep .bp-warning__btn i{font-size:12px}.bottle-prep .bp-warning__btn:hover{background:linear-gradient(180deg,#fbbf24,#f59e0b);transform:translateY(-1px);box-shadow:0 4px 8px #f59e0b59}.bottle-prep .bp-warning__btn:active{transform:translateY(0)}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.bottle-prep .bp-info-row{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%}.bottle-prep:not(:has(.bp-warning)):not(:has(.bp-form)){flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.bottle-prep .bp-info{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--text);flex:1;min-width:0}.bottle-prep .bp-info>i{color:#0ea5e9;font-size:16px;margin-top:2px;flex-shrink:0}.bottle-prep .bp-info .bp-text{line-height:1.45;min-width:0}.bottle-prep .bp-info .bp-useby{display:inline-block;margin-top:2px;font-size:13px}.bottle-prep .bp-info strong{color:#0284c7;font-weight:800;font-variant-numeric:tabular-nums}.bottle-prep .bp-info .muted{color:var(--muted);font-size:13px}.bottle-prep .bp-actions{display:flex;gap:4px}.bottle-prep .bp-add{flex:1;background:transparent;border:none;padding:6px 4px;font-size:13px;font-weight:600;color:#0284c7;cursor:pointer;display:flex;align-items:center;gap:8px;font-family:inherit;text-align:left}.bottle-prep .bp-add>i{font-size:15px}.bottle-prep .bp-add:hover{color:#0369a1}.bottle-prep .bp-form{display:flex;flex-direction:column;gap:8px;width:100%}.bottle-prep .bp-form-head{display:flex;align-items:center;gap:6px}.bottle-prep .bp-form-head>i{color:#0ea5e9;font-size:15px}.bottle-prep .bp-form-head .bp-label{font-size:13px;color:var(--muted);font-weight:600;white-space:nowrap}.bottle-prep .bp-input-wrap{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid #7dd3fc;border-radius:10px;padding:3px;transition:border-color .15s,box-shadow .15s}.bottle-prep .bp-input-wrap:focus-within{border-color:#0ea5e9;box-shadow:0 0 0 3px #0ea5e926}.bottle-prep .bp-input-wrap .bp-step{width:30px;height:30px;flex-shrink:0;border-radius:8px;border:none;background:#e0f2fe;color:#0369a1;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;transition:all .12s}.bottle-prep .bp-input-wrap .bp-step:hover{background:#bae6fd;color:#0284c7}.bottle-prep .bp-input-wrap .bp-step:active{transform:scale(.94)}.bottle-prep .bp-input-wrap .bp-step:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.bottle-prep .bp-input-wrap .bp-input{flex:1;min-width:60px;padding:4px 6px;font-size:18px;font-weight:800;border:none;background:transparent;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706;text-align:center;letter-spacing:-.01em}.bottle-prep .bp-input-wrap .bp-input:focus{outline:none}.bottle-prep .bp-input-wrap .bp-input:disabled{opacity:.55;cursor:not-allowed}.bottle-prep .bp-input-wrap .bp-unit{font-size:12px;color:var(--muted);font-weight:600;padding-right:4px}.bottle-prep .bp-quick{display:flex;flex-wrap:wrap;align-items:center;gap:5px}.bottle-prep .bp-quick .bp-quick-label{font-size:11px;color:var(--muted);font-weight:600;margin-right:2px}.bottle-prep .bp-quick .bp-quick-chip{padding:4px 9px;background:#f0f9ff;border:1px solid #7dd3fc;border-radius:999px;font-size:11.5px;font-weight:700;color:#0369a1;cursor:pointer;font-family:inherit;font-variant-numeric:tabular-nums;transition:all .12s}.bottle-prep .bp-quick .bp-quick-chip:hover{background:#e0f2fe;border-color:#38bdf8;color:#0284c7}.bottle-prep .bp-quick .bp-quick-chip:active{transform:scale(.95)}.bottle-prep .bp-quick .bp-quick-chip:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-form-actions{display:flex;gap:6px;justify-content:flex-end}.bottle-prep .bp-btn{width:32px;height:32px;border-radius:8px;border:none;background:#fff;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:all .15s;flex-shrink:0}.bottle-prep .bp-btn:hover:not(:disabled){background:#e0f2fe;color:#0284c7}.bottle-prep .bp-btn:disabled{opacity:.45;cursor:not-allowed}.bottle-prep .bp-btn.primary{background:#0ea5e9;color:#fff}.bottle-prep .bp-btn.primary:hover:not(:disabled){background:#0284c7}.bottle-prep .bp-btn.danger:hover{background:var(--rose-soft);color:var(--rose)}.calc-block{border:1px solid #fde7c2;background:#fffaf0;border-radius:12px;overflow:hidden}.calc-block .calc-head{padding:10px 14px 0}.calc-block .prep-info{display:flex;align-items:flex-start;gap:6px;font-size:13px;color:var(--text)}.calc-block .prep-info>i{color:#f59e0b;font-size:14px;margin-top:2px;flex-shrink:0}.calc-block .prep-info .prep-info-text{line-height:1.45;min-width:0}.calc-block .prep-info strong{color:#d97706;font-weight:800;font-variant-numeric:tabular-nums}.calc-block .prep-info .muted{color:var(--muted-2);font-size:12px;display:inline-block;margin-top:2px}.calc-block .calc-body{padding:8px 14px 14px;display:flex;flex-direction:column;gap:10px}.calc-block .field.mini.remaining-input{position:relative;margin:0;display:flex;flex-direction:column}.calc-block .field.mini.remaining-input .label{font-size:11px;font-weight:600;color:var(--muted);margin-bottom:4px}.calc-block .field.mini.remaining-input input{width:100%;padding:10px 36px 10px 12px;font-size:16px;font-weight:700;border:1px solid #fadf9e;border-radius:8px;background:#fff;font-variant-numeric:tabular-nums;font-family:inherit;color:#d97706}.calc-block .field.mini.remaining-input input:focus{outline:none;border-color:#f59e0b;box-shadow:0 0 0 3px #f59e0b26}.calc-block .field.mini.remaining-input .suffix{position:absolute;right:12px;top:50%;transform:translateY(4px);font-size:12px;color:var(--muted-2);pointer-events:none}.calc-block .calc-result-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.calc-block .calc-result{display:flex;align-items:baseline;gap:4px;font-variant-numeric:tabular-nums}.calc-block .calc-result .calc-label{font-size:13px;color:var(--muted);font-weight:600}.calc-block .calc-result strong{font-size:22px;font-weight:800;color:var(--primary)}.calc-block .calc-result .u{font-size:12px;color:var(--primary);margin-left:2px}.calc-block .calc-result .muted{color:var(--muted-2);font-size:18px}.calc-block .calc-apply{flex-shrink:0}.calc-block .calc-apply:disabled{background:#e5e7eb;color:var(--muted-2);cursor:not-allowed;box-shadow:none}.predict-hint{display:flex;align-items:center;gap:10px;padding:10px 14px;background:linear-gradient(135deg,var(--primary-soft) 0%,var(--accent-soft) 100%);border-radius:10px;font-size:13px}.predict-hint>i{color:var(--primary);font-size:16px}.predict-hint .hint-text{flex:1;color:var(--text)}.predict-hint .hint-text strong{color:var(--primary);font-weight:700}.predict-hint.predict-hint--inline .hint-text strong{font-weight:800;font-variant-numeric:tabular-nums}.sync-error{padding:10px 14px;background:var(--rose-soft);color:#991b1b;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px}.sync-success{padding:10px 14px;background:var(--mint-soft);color:#166534;border-radius:10px;font-size:13px;display:flex;align-items:center;gap:6px;animation:fadeIn .2s ease}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}.feeding-settings-dialog .feeding-settings-hint{margin:0 0 14px;font-size:13px;line-height:1.45}.feeding-settings-dialog .feeding-settings-hint code{font-size:11px;padding:1px 5px;border-radius:4px;background:#f1f5f9}.feeding-settings-dialog .feeding-settings-grid{grid-template-columns:1fr;gap:16px}.feeding-settings-dialog .field-hint{display:block;margin-top:8px;font-size:12px;line-height:1.4;padding:8px 12px;background:#f8fafc;border-radius:8px;border-left:3px solid #e2e8f0}.feeding-settings-dialog .label{position:relative}.feeding-settings-dialog .label code{font-size:11px;font-weight:600;color:var(--primary);background:var(--primary-soft);padding:2px 6px;border-radius:4px}.feeding-settings-dialog .label .notification-badge{display:inline-flex;align-items:center;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#fff;background:linear-gradient(135deg,#10b981,#059669);padding:2px 6px;border-radius:10px;margin-left:6px;box-shadow:0 1px 2px #10b98133;animation:gentle-pulse 2s ease-in-out infinite}.feeding-settings-dialog .notification-setting-field{position:relative}.feeding-settings-dialog .notification-setting-field input{border:1.5px solid #10b981;background:linear-gradient(to right,#fff,#f0fdf4)}.feeding-settings-dialog .notification-setting-field input:focus{border-color:#059669;box-shadow:0 0 0 3px #10b9811a;background:#fff}.feeding-settings-dialog .notification-setting-field input::placeholder{color:#6b7280;opacity:.7}.feeding-settings-dialog .notification-setting-field .field-hint{border-left-color:#10b981;background:linear-gradient(to right,#f0fdf4,#f8fafc)}@keyframes gentle-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.85;transform:scale(1.02)}}.log-time--with-badge,.feed-row-time .time--with-badge{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.log-merge-badge,.feed-merge-badge{font:inherit;font-size:10px;font-weight:700;padding:2px 7px;border-radius:999px;border:1px solid rgba(99,102,241,.35);background:linear-gradient(135deg,#e0e7ff,#c7d2fe);color:#312e81;cursor:pointer;line-height:1.2;white-space:nowrap}.log-merge-badge:hover,.feed-merge-badge:hover{filter:brightness(1.03)}.log-row--clickable,.feed-row--clickable{cursor:pointer}.log-row--clickable:hover,.feed-row--clickable:hover{background:#6366f10f}.log-row--clickable:focus-visible,.feed-row--clickable:focus-visible{outline:2px solid rgba(99,102,241,.45);outline-offset:2px}.dialog-overlay.dialog-overlay--front.feed-group-detail-overlay{z-index:125}.dialog-overlay.dialog-overlay--front.feed-detail-overlay{z-index:140;animation:overlayIn .12s ease-out}.dialog-overlay.dialog-overlay--front.feed-detail-overlay .feed-detail-dialog{animation:feedDetailIn .14s ease-out}@keyframes feedDetailIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.feed-detail-dialog{--fd-accent: 99, 102, 241;--fd-soft: #eef2ff;--fd-strong: #4338ca;max-width:min(400px,96vw);padding:0;overflow:hidden;display:flex;flex-direction:column}.feed-detail-dialog[data-status=low]{--fd-accent: 245, 158, 11;--fd-soft: #fffbeb;--fd-strong: #b45309}.feed-detail-dialog[data-status=high]{--fd-accent: 244, 63, 94;--fd-soft: #fff1f2;--fd-strong: #e11d48}.feed-detail-dialog[data-status=ok]{--fd-accent: 16, 185, 129;--fd-soft: #ecfdf5;--fd-strong: #059669}.feed-detail-top{position:relative;padding:20px 20px 16px;text-align:center;background:radial-gradient(120% 80% at 50% -20%,rgba(var(--fd-accent),.22),transparent 55%),linear-gradient(180deg,var(--fd-soft) 0%,#ffffff 72%);border-bottom:1px solid rgba(var(--fd-accent),.12)}.feed-detail-close{position:absolute;top:12px;right:12px;width:36px;height:36px;border:none;border-radius:50%;background:#ffffffd9;color:#64748b;cursor:pointer;display:grid;place-items:center;box-shadow:0 2px 8px #0f172a14;transition:background .15s ease,color .15s ease}.feed-detail-close:hover{background:#fff;color:#0f172a}.feed-detail-eyebrow{margin:0 0 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.feed-detail-metric{display:flex;align-items:flex-end;justify-content:center;gap:4px;line-height:1;margin-bottom:10px}.feed-detail-metric__value{font-size:clamp(48px,14vw,56px);font-weight:800;letter-spacing:-.04em;color:var(--fd-strong);font-variant-numeric:tabular-nums}.feed-detail-metric__unit{font-size:18px;font-weight:700;color:rgba(var(--fd-accent),.85);padding-bottom:8px}.feed-detail-status{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:999px;font-size:12px;font-weight:700;background:#fff;border:1px solid rgba(var(--fd-accent),.28);color:var(--fd-strong);box-shadow:0 2px 6px rgba(var(--fd-accent),.1)}.feed-detail-status .pi{font-size:13px}.feed-detail-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:14px}.feed-detail-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:12px;background:#ffffffeb;border:1px solid #e2e8f0;font-size:12px;font-weight:600;color:#334155;line-height:1.3;box-shadow:0 1px 4px #0f172a0a}.feed-detail-chip .pi{font-size:13px;color:#6366f1;flex-shrink:0}.feed-detail-chip em{font-style:normal;font-weight:500;color:#94a3b8;margin-left:4px}.feed-detail-chip--time .pi{color:#dc2626}.feed-detail-chip--time span{font-variant-numeric:tabular-nums;font-weight:700;color:#dc2626}.feed-detail-chip--muted{background:#f8fafc;border-color:#e2e8f0;color:#64748b;font-weight:500}.feed-detail-chip--muted .pi{color:#94a3b8}.feed-detail-body{padding:16px 18px 8px;overflow-y:auto;flex:1;min-height:0}.feed-detail-section{margin-bottom:14px}.feed-detail-section:last-child{margin-bottom:0}.feed-detail-section__title{margin:0 0 10px;font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;display:flex;align-items:center;gap:7px}.feed-detail-section__title .pi{font-size:13px;color:#6366f1}.feed-detail-cards{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.feed-detail-card{display:flex;align-items:flex-start;gap:12px;padding:12px 13px;border-radius:14px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;box-shadow:0 2px 8px #0f172a0a}.feed-detail-card--warn,.feed-detail-card--gap.feed-detail-card--warn{border-color:#f59e0b66;background:linear-gradient(180deg,#fffbeb,#fff)}.feed-detail-card--warn .feed-detail-card__icon,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card--warn .feed-detail-card__text,.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__text{color:#92400e}.feed-detail-card--position{border-color:#8b5cf659;background:linear-gradient(180deg,#f5f3ff,#fff)}.feed-detail-card--average{border-color:#0ea5e959;background:linear-gradient(180deg,#f0f9ff,#fff)}.feed-detail-card--next{border-color:#10b98159;background:linear-gradient(180deg,#ecfdf5,#fff)}.feed-detail-card--gap:not(.feed-detail-card--warn){border-color:#64748b59;background:linear-gradient(180deg,#f8fafc,#fff)}.feed-detail-card__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;background:#eef2ff;color:#4f46e5;font-size:15px}.feed-detail-card__icon[data-kind=position]{background:#ede9fe;color:#7c3aed}.feed-detail-card__icon[data-kind=average]{background:#e0f2fe;color:#0284c7}.feed-detail-card__icon[data-kind=next]{background:#d1fae5;color:#059669}.feed-detail-card__icon[data-kind=gap]{background:#e2e8f0;color:#475569}.feed-detail-card--gap.feed-detail-card--warn .feed-detail-card__icon{background:#fef3c7;color:#b45309}.feed-detail-card__body{min-width:0;flex:1}.feed-detail-card__label{display:block;font-size:11px;font-weight:800;color:#64748b;margin-bottom:3px;letter-spacing:.02em}.feed-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:#0f172a}.fdc-position__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin-bottom:8px}.fdc-position__idx{font-size:18px;font-weight:800;color:#6d28d9;letter-spacing:-.02em}.fdc-position__of{font-size:14px;font-weight:700;color:#a78bfa}.fdc-position__tag{font-size:11px;font-weight:600;color:#64748b;padding:2px 8px;border-radius:999px;background:#8b5cf61a}.fdc-progress{height:8px;border-radius:999px;background:#ede9fe;overflow:hidden;margin-bottom:10px}.fdc-progress__fill{height:100%;border-radius:inherit;background:linear-gradient(90deg,#8b5cf6,#6366f1);transition:width .2s ease}.fdc-position__stats{display:flex;flex-wrap:wrap;gap:8px}.fdc-stat{display:inline-flex;align-items:baseline;gap:4px;padding:6px 10px;border-radius:10px;font-size:12px}.fdc-stat strong{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-stat small{font-size:11px;font-weight:600;opacity:.85}.fdc-stat--violet{background:#f5f3ff;color:#5b21b6}.fdc-stat--violet strong{color:#6d28d9}.fdc-stat--indigo{background:#eef2ff;color:#4338ca}.fdc-stat--indigo strong{color:#4f46e5}.fdc-avg,.fdc-next{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}.fdc-pill{display:inline-flex;padding:4px 10px;border-radius:999px;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums}.fdc-pill--up{background:#dcfce7;color:#15803d}.fdc-pill--down{background:#fee2e2;color:#b91c1c}.fdc-pill--neutral{background:#f1f5f9;color:#475569}.fdc-avg__meta{font-size:12px;color:#64748b;line-height:1.4}.fdc-avg__meta strong{color:#0f172a;font-weight:700}.fdc-next__time{font-size:16px;font-weight:800;color:#dc2626;font-variant-numeric:tabular-nums}.fdc-next__vol{font-size:14px;font-weight:800;color:#059669;padding:4px 10px;border-radius:8px;background:#d1fae5}.fdc-next__gap{font-size:12px;font-weight:600;color:#64748b;display:inline-flex;align-items:center;gap:4px}.fdc-gap{display:flex;flex-direction:column;gap:10px}.fdc-gap__prev{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:10px 12px;border-radius:10px;background:#ffffffe6;border:1px solid #e2e8f0}.fdc-gap__prev-label{font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#64748b;width:100%}.fdc-gap__prev-time{font-size:17px;font-weight:800;color:#0f172a;font-variant-numeric:tabular-nums}.fdc-gap__prev-vol{font-size:14px;font-weight:800;color:#475569;padding:3px 8px;border-radius:8px;background:#f1f5f9}.fdc-gap__prev-date{font-size:11px;font-weight:600;color:#94a3b8}.fdc-gap__row{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;min-width:0}.fdc-gap__interval{display:inline-flex;align-items:center;gap:6px;flex:1 1 auto;min-width:0;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700;color:#475569;background:#e2e8f0;white-space:nowrap}.fdc-gap__interval .pi{font-size:13px;flex-shrink:0}.fdc-gap__interval span{overflow:hidden;text-overflow:ellipsis}.fdc-gap__interval--warn{color:#b45309;background:#fde68a;border:1px solid rgba(245,158,11,.35)}.fdc-gap__vol-diff{flex:0 0 auto;white-space:nowrap;font-size:12px;padding:5px 10px}.feed-detail-quote{margin:0;padding:12px 14px 12px 16px;border-radius:0;border-left:3px solid #c7d2fe;background:#f8fafc;font-size:14px;line-height:1.55;color:#334155;font-style:normal}.feed-detail-footer{padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fafbff}.feed-detail-cta{display:flex;gap:8px;justify-content:stretch}.feed-detail-cta .btn{flex:1;justify-content:center}.feed-detail-cta .btn.primary{flex:1.2}.feed-group-detail-dialog .feed-group-detail-summary{margin:0 0 8px;padding:0 2px;font-size:13px;line-height:1.45}.feed-group-detail-dialog .feed-group-detail-hint{margin:0 0 12px;font-size:12px;line-height:1.4;display:flex;align-items:center;gap:6px}.feed-group-detail-dialog .feed-group-detail-history{margin-bottom:4px}.feed-group-detail-dialog .feed-group-detail-rows{max-height:min(58vh,420px);overflow-y:auto;padding-right:2px}.mom-food-card{--mf-tone: 244, 114, 182;--mf-soft: #fdf2f8;--mf-ring: #fbcfe8;--mf-strong: #db2777;position:relative;overflow:hidden;background:radial-gradient(900px 260px at 0% 0%,rgba(var(--mf-tone),.14),transparent 60%),#fff;border-color:rgba(var(--mf-tone),.22);padding:18px;margin-top:16px}.mom-food-card[data-accent=rose]{--mf-tone: 244, 63, 94;--mf-soft: #fff1f2;--mf-ring: #fecdd3;--mf-strong: #e11d48}.mom-food-card[data-accent=peach]{--mf-tone: 251, 146, 60;--mf-soft: #fff7ed;--mf-ring: #fed7aa;--mf-strong: #ea580c}.mom-food-card[data-accent=mint]{--mf-tone: 16, 185, 129;--mf-soft: #ecfdf5;--mf-ring: #a7f3d0;--mf-strong: #059669}.mom-food-card[data-accent=lavender]{--mf-tone: 139, 92, 246;--mf-soft: #f5f3ff;--mf-ring: #ddd6fe;--mf-strong: #7c3aed}.mom-food-card[data-accent=amber]{--mf-tone: 245, 158, 11;--mf-soft: #fffbeb;--mf-ring: #fde68a;--mf-strong: #d97706}.mf-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}.mf-head__lead{min-width:0;flex:1}.mf-head .eyebrow{display:inline-flex;align-items:center;gap:6px;color:var(--mf-strong);font-weight:700}.mf-head .eyebrow i{font-size:13px}.mf-head h2{margin:4px 0 0;font-size:18px;font-weight:800;color:var(--text);line-height:1.25}.mf-headline{margin:4px 0 0;font-size:13px;color:var(--mf-strong);font-weight:600}.mf-nav{display:inline-flex;align-items:center;gap:6px;background:var(--mf-soft);border-radius:999px;padding:4px;border:1px solid rgba(var(--mf-tone),.25);flex-shrink:0}.mf-arrow{width:30px;height:30px;border-radius:50%;border:none;background:#fff;color:var(--mf-strong);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .15s ease,background .15s ease}.mf-arrow i{font-size:12px}.mf-arrow:hover:not(:disabled){transform:scale(1.08);background:var(--mf-strong);color:#fff}.mf-arrow:disabled{opacity:.35;cursor:not-allowed}.mf-step{font-variant-numeric:tabular-nums;font-size:12px;color:var(--muted);padding:0 4px}.mf-step strong{color:var(--mf-strong);font-weight:800;font-size:13px}.mf-reset{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--mf-strong);background:transparent;border:1px dashed rgba(var(--mf-tone),.5);border-radius:999px;padding:4px 12px;cursor:pointer;margin-bottom:10px;transition:background .15s ease}.mf-reset:hover{background:var(--mf-soft)}.mf-reset i{font-size:11px}.mf-desc{margin:0 0 14px;font-size:13px;color:var(--muted);line-height:1.55}.mf-columns{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}@media(max-width:640px){.mf-columns{grid-template-columns:1fr}}.mf-col{padding:12px 14px;border-radius:14px;background:#fff;border:1px solid #eef0f3}.mf-col--good{background:linear-gradient(180deg,#f0fdf4,#fff 80%);border-color:#bbf7d0}.mf-col--bad{background:linear-gradient(180deg,#fef2f2,#fff 80%);border-color:#fecaca}.mf-col-title{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:14px;font-weight:800;color:var(--text);letter-spacing:.01em}.mf-dot{width:22px;height:22px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;flex-shrink:0;font-size:11px}.mf-dot i{font-size:10px}.mf-dot.good{background:#10b981;box-shadow:0 2px 6px #10b9814d}.mf-dot.bad{background:#ef4444;box-shadow:0 2px 6px #ef44444d}.mf-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mf-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px;border-radius:10px;background:#ffffffb3;transition:background .15s ease,transform .15s ease}.mf-item:hover{background:#fff;transform:translate(2px)}.mf-item--bad .mf-name{color:#7f1d1d}.mf-item--good .mf-name{color:#064e3b}.mf-emoji{width:28px;height:28px;border-radius:8px;background:#fff;display:inline-grid;place-items:center;font-size:16px;flex-shrink:0;box-shadow:0 1px 2px #0f172a0f}.mf-emoji--x{background:#fef2f2;color:#ef4444}.mf-emoji--x i{font-size:12px}.mf-item-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.mf-name{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.mf-note{font-size:11.5px;color:var(--muted);line-height:1.35}.mf-tip{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;border-radius:12px;background:var(--mf-soft);border:1px solid rgba(var(--mf-tone),.22);font-size:12.5px;color:var(--text);line-height:1.5}.mf-tip i{color:var(--mf-strong);font-size:15px;margin-top:1px;flex-shrink:0}.mom-tab .card{padding:12px 14px;margin-bottom:10px;border-radius:14px}.mom-tab .mom-wellness-section,.mom-tab .mom-tips-week{margin-bottom:10px}.mom-tab .mom-food-card{padding:12px 14px;margin-top:10px}.mom-tab .mf-head{margin-bottom:8px}.mom-tab .mf-head h2{font-size:16px}.mom-tab .mf-desc{margin-bottom:10px;font-size:12px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.mom-tab .mf-columns{gap:8px;margin-bottom:8px}.mom-tab .mf-col{padding:8px 10px}.mom-tab .mf-list{gap:6px}.mom-tab .mf-item{padding:6px 8px}.mom-tab .mf-tip{padding:8px 10px;font-size:12px}.mom-hero{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;margin-bottom:14px;background:linear-gradient(135deg,#fdf2f8,#faf5ff,#eef2ff);border:1px solid #f9a8d4;border-radius:16px}.mom-hero .mom-hero__icon{font-size:36px;line-height:1;flex-shrink:0}.mom-hero h2{margin:0 0 6px;font-size:19px;font-weight:800;color:var(--text);letter-spacing:-.02em}.mom-hero .mom-hero__sub{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}.mom-hero .mom-hero__sub strong{color:var(--text)}.mom-hero .mom-hero__sub .muted-inline{display:block;margin-top:4px;font-size:12.5px;opacity:.92}.mom-hero--compact{align-items:center;padding:10px 12px;margin-bottom:10px;border-radius:14px;gap:10px}.mom-hero--compact .mom-hero__icon{font-size:26px}.mom-hero--compact h2{margin:0;font-size:16px;letter-spacing:-.01em}.mom-hero--compact .mom-hero__title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px}.mom-hero--compact .mom-hero__baby{font-size:12px;font-weight:700;color:#be185d;padding:2px 8px;border-radius:999px;background:#ffffffbf;border:1px solid rgba(244,114,182,.35)}.mom-hero--compact .mom-hero__sub{margin:4px 0 0;font-size:11.5px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.mom-hero--compact .mom-hero__sub .muted-inline{display:inline;margin-top:0;font-size:11.5px}.section-head--tight{margin-bottom:8px}.section-head--tight h3{margin:0}.mom-disclaimer--compact{font-size:11px;line-height:1.4;padding:8px 10px;margin-top:4px;margin-bottom:6px}.section-head{margin-bottom:14px}.section-head h3{margin:0 0 6px;font-size:16px;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}.section-head h3 i{color:#db2777;font-size:15px}.section-head .section-sub{margin:0;font-size:12.5px;color:var(--muted);line-height:1.45}.mom-wellness-section{margin-bottom:16px}.mom-tab .mom-wellness-grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}.mom-wellness-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.mom-w-card{padding:14px 14px 12px;border-radius:14px;border:1px solid var(--border);background:#fafafa;transition:border-color .15s,box-shadow .15s}.mom-w-card:hover{border-color:#f472b6;box-shadow:0 6px 18px -12px #db277759}.mom-w-card .mom-w-card__icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#fce7f3,#ede9fe);display:flex;align-items:center;justify-content:center;margin-bottom:10px}.mom-w-card .mom-w-card__icon i{font-size:16px;color:#be185d}.mom-w-card h4{margin:0 0 8px;font-size:13.5px;font-weight:800;color:var(--text);line-height:1.3}.mom-w-card p{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-tips-week{margin-bottom:16px}.mom-tab .mom-tips-list{gap:6px}.mom-tab .mom-tips-list li{padding:8px 10px;border-radius:10px}.mom-tab .mom-tips-list li strong{margin-bottom:2px;font-size:12px}.mom-tab .mom-tips-list li span{font-size:11.5px;line-height:1.4}.mom-tips-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.mom-tips-list li{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:12px;background:#fefce8;border:1px solid #fde047}.mom-tips-list li>i{color:#ca8a04;margin-top:2px;flex-shrink:0}.mom-tips-list li strong{display:block;font-size:13px;font-weight:800;color:var(--text);margin-bottom:4px}.mom-tips-list li span{font-size:12.5px;line-height:1.5;color:var(--muted)}.mom-disclaimer{margin-top:8px;margin-bottom:8px}.mom-empty-profile{max-width:520px;margin:24px auto 40px;text-align:center}.mom-empty-profile__inner{padding:28px 22px}.mom-empty-profile__inner .mom-empty-emoji{font-size:48px;display:block;margin-bottom:12px}.mom-empty-profile__inner h2{margin:0 0 10px;font-size:17px;font-weight:800;color:var(--text)}.mom-empty-profile__inner p{margin:0 0 18px;font-size:14px;line-height:1.55;color:var(--muted)}.event-reminder-dialog{max-width:min(520px,100vw - 28px);padding-bottom:8px}.event-reminder-dialog__head{align-items:flex-start;margin-bottom:4px}.event-reminder-dialog__title{margin:0;padding-right:40px;font-size:20px;font-weight:800;line-height:1.3;color:var(--text, #0f172a);display:flex;align-items:center;gap:10px;letter-spacing:-.02em}.event-reminder-dialog__title .pi-bell{color:#f59e0b;font-size:18px;flex-shrink:0}.event-reminder-list{list-style:none;margin:12px 0 0;padding:0;max-height:min(52vh,400px);overflow:auto;display:flex;flex-direction:column;gap:10px}.event-reminder-card{padding:14px 16px;background:linear-gradient(180deg,#fafbff,#fff);border:1px solid #e2e8f0;border-radius:14px;box-shadow:0 2px 8px #0f172a0d}.event-reminder-card__title{margin:0;font-size:16px;font-weight:800;color:var(--text, #0f172a);line-height:1.35}.event-reminder-card__datetime{margin:5px 0 0;font-size:12.5px;font-weight:500;color:#64748b;line-height:1.4}.event-reminder-card__date{color:#475569;font-weight:600}.event-reminder-card__sep{color:#94a3b8}.event-reminder-card__hour{color:#dc2626;font-weight:700;font-variant-numeric:tabular-nums}.event-reminder-card__place{margin:6px 0 0;display:flex;align-items:flex-start;gap:6px;font-size:13px;color:#475569;line-height:1.4}.event-reminder-card__place .pi-map-marker{margin-top:2px;flex-shrink:0;font-size:13px;color:var(--primary, #5b6cff)}.event-reminder-card__content-wrap{margin-top:10px;padding-top:10px;border-top:1px solid #eef1f6}.event-reminder-card__content-label{display:block;margin-bottom:4px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#94a3b8}.event-reminder-card__content{margin:0;font-size:13.5px;color:#334155;line-height:1.5;white-space:pre-wrap}.event-reminder-actions{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border, #e5e7eb)}.event-reminder-actions__link{flex:0 1 auto;min-width:0;padding:6px 4px;border:none;background:transparent;color:#64748b;font-size:13px;font-weight:600;font-family:inherit;text-decoration:underline;text-underline-offset:3px;cursor:pointer}.event-reminder-actions__link:hover:not(:disabled){color:#334155}.event-reminder-actions__link:disabled{opacity:.5;cursor:not-allowed}.event-reminder-actions__primary{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:10px 18px;font-size:15px;font-weight:700;text-align:center}\n"] }]
|
|
15848
17137
|
}], ctorParameters: () => [], propDecorators: { feedingPageRef: [{
|
|
15849
17138
|
type: ViewChild,
|
|
15850
17139
|
args: ['feedingPage', { static: true }]
|
|
@@ -15876,5 +17165,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
15876
17165
|
* Generated bundle index. Do not edit.
|
|
15877
17166
|
*/
|
|
15878
17167
|
|
|
15879
|
-
export { ACTIVITY_EVENT, APP_INFO_TOKEN, ActivityLogComponent, ActivityLogService, BABY_TIMELINE, BABY_TIMELINE_CARE_GUIDES, DEFAULT_FEEDING_CONFIG, DocumentsComponent, EventLogService, ExplorerService, FEEDING_CONFIG_TOKEN, FeedingComponent, FeedingDailyComponent, FeedingLogService, FeedingMomComponent, FeedingNutritionComponent, FeedingScheduleComponent, GoogleSheetsService, MedicalHistoryComponent, MedicalHistoryService, NotificationLogService, WeightComponent, WeightLogService, buildActivityLogMessageHtml, createFeedingConfig, evaluateWeightForAge, formatLogContent, getCurrentMilestone, getDailySummaries, getMilestoneState, getNutritionTarget, getTimelineCareGuide, groupLogsByProximity, parseFeedingSettingsFromRows, predictNextFeeding, provideFeedingConfig, resolveAppsScriptUrl };
|
|
17168
|
+
export { ACTIVITY_EVENT, APP_INFO_TOKEN, ActivityLogComponent, ActivityLogService, BABY_TIMELINE, BABY_TIMELINE_CARE_GUIDES, ChatComposerComponent, ChatMessageComponent, ChatShellComponent, DEFAULT_FEEDING_CONFIG, DocumentsComponent, EventLogService, ExplorerService, FEEDING_CONFIG_TOKEN, FeedingComponent, FeedingDailyComponent, FeedingLogService, FeedingMomComponent, FeedingNutritionComponent, FeedingScheduleComponent, GoogleSheetsService, HfAssistantService, MedicalHistoryComponent, MedicalHistoryService, NotificationLogService, TOOL_NAMES, ToolArgs, ToolRegistry, WeightComponent, WeightLogService, buildActivityLogMessageHtml, createFeedingConfig, evaluateWeightForAge, formatLogContent, getCurrentMilestone, getDailySummaries, getMilestoneState, getNutritionTarget, getTimelineCareGuide, groupLogsByProximity, isToolName, parseFeedingSettingsFromRows, parseHfReply, predictNextFeeding, provideFeedingConfig, resolveAppsScriptUrl };
|
|
15880
17169
|
//# sourceMappingURL=hbslovely-baby-feeding.mjs.map
|