/* ============================================================================
   AKILI Theme — "Calm & Confident" (Apple-inspired)
   Phase 1: typography + brand palette + softened depth.

   Loaded AFTER Radzen's material(.dark).css so these custom-property overrides
   win. Brand-level tokens (teal accent, font, radius, shadows) live in :root and
   apply to BOTH light and dark. Neutral grays are scoped per data-theme so the
   light surface palette never leaks into dark mode (and vice-versa).
   ============================================================================ */

/* ── Inter (self-hosted, offline-safe for MAUI) ───────────────────────────── */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-display: swap;
    font-weight: 100 900;
    src: url('../fonts/InterVariable.woff2') format('woff2');
}

/* ── Montserrat (self-hosted, offline-safe for MAUI) ──────────────────────── */
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-display: swap;
    font-weight: 400 800;
    src: url('../fonts/MontserratVariable.woff2') format('woff2');
}

/* ── Material Symbols (raw <span class="material-symbols-outlined"> icons) ──
   Pages render icons as bare ligature spans (e.g. "arrow_back", "alternate_email")
   instead of <RadzenIcon>, but nothing ever defined the class or loaded a font for
   it — the glyph name showed up as literal text. Radzen.Blazor already ships the
   same Google "Material Symbols" variable font (self-hosted, offline-safe for MAUI)
   under the font-family "Material Symbols" for its own <RadzenIcon>/.rzi elements
   (see material-base.css), so reuse it here instead of pulling Google Fonts from a
   CDN. */
.material-symbols-outlined {
    font-family: 'Material Symbols';
    font-weight: normal;
    font-style: normal;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: inline-block;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}

/* ── Brand tokens — identical in light & dark ─────────────────────────────── */
:root {
    /* Typography: Inter first, then platform system fonts as graceful fallback */
    --rz-text-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

    /* Modal dialogs (DialogService.Confirm/Alert) must sit ABOVE the app's sheets.
       Radzen defaults to 1001/1000, which is below every sheet overlay in this app
       (OnboardingSheet / ReminderEditSheet / SaleSuccessSheet all use 1200) and below
       BottomTabBar (1050) — a confirm opened from inside a sheet rendered behind it and
       looked like nothing happened (hit while wiring "Reset to default" in HomeEditSheet). */
    --rz-dialog-zindex: 1300;
    --rz-dialog-mask-zindex: 1299;

    /* Primary — "Harbor Blue": deep, confident steel blue (rgb(25, 74, 113)).
       Used sparingly: actions / active states / brand accents. */
    --rz-primary: #194a71;
    --rz-primary-rgb: 25, 74, 113;
    --rz-primary-light: #2f6b9a;
    --rz-primary-lighter: rgba(25, 74, 113, 0.12);
    --rz-primary-dark: #123a5b;
    --rz-primary-darker: #0c2b45;
    --rz-on-primary: #ffffff;
    --rz-on-primary-light: #ffffff;
    --rz-on-primary-lighter: #194a71;
    --rz-on-primary-dark: #ffffff;
    --rz-on-primary-darker: #ffffff;

    /* Secondary — neutral slate so no loud magenta accents leak through */
    --rz-secondary: #475569;
    --rz-secondary-light: #64748b;
    --rz-secondary-lighter: rgba(71, 85, 105, 0.12);
    --rz-secondary-dark: #334155;
    --rz-secondary-darker: #1e293b;

    /* Links read as the brand teal, not magenta */
    --rz-link-color: var(--rz-primary);
    --rz-link-hover-color: var(--rz-primary-dark);

    /* Status — Apple system colors (friendly but professional) */
    --rz-info: #0a84ff;
    --rz-success: #34c759;
    --rz-warning: #ff9500;
    --rz-danger: #ff3b30;
    --rz-on-danger: #ffffff;

    /* Softer, more precise corners (Apple-medium, not bubbly) */
    --rz-border-radius: 10px;

    /* Hairline separator color. Radzen's material theme CONSUMES --rz-border-color
       (tab strip, sticky bars, tile-layout overlay) but never defines it, and ~120
       component rules in this app reference it too — so it must be defined here or
       every bare `1px solid var(--rz-border-color)` collapses to no border at all
       (invalid at computed-value time → border-style: none), while the rules that
       carry a hardcoded light-gray fallback paint a border that vanishes on dark.
       Derived from --rz-text-color so it inverts with the theme automatically and
       can never become a foreground-unsafe tenant color (rule 1 / rule 9). */
    --rz-border-color: color-mix(in srgb, var(--rz-text-color) 14%, transparent);

    /* Semantic fill scale — "slightly off the current surface", for pills, hover
       and :active states, chat bubbles, skeletons, input shells and raised cards.
       These exist because --rz-base-100/200/300 CANNOT be used for that: the dark
       theme deliberately keeps the same scale direction as light (see the comment
       on :root[data-theme="dark"] below — Radzen indexes into it), so base-100 is
       #d1d1d6 and base-200 is #aeaeb2 in DARK mode. A component reaching for a
       low index as a "subtle fill" therefore painted a LIGHT gray block and put
       --rz-text-color (near-white) on top of it — about 1.8:1, unreadable. The
       three steps mirror base-100/200/300 so existing layering is preserved.
       Mixed over --rz-base-background-color (not transparent) so they stay OPAQUE:
       several consumers are position:sticky and would let content scroll through. */
    --rz-fill-subtle: color-mix(in srgb, var(--rz-text-color) 6%, var(--rz-base-background-color));
    --rz-fill-muted: color-mix(in srgb, var(--rz-text-color) 11%, var(--rz-base-background-color));
    --rz-fill-strong: color-mix(in srgb, var(--rz-text-color) 18%, var(--rz-base-background-color));

    /* Subtle, single-direction shadows — calm depth, never heavy */
    --rz-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.06);
    --rz-shadow-2: 0 1px 3px rgba(0, 0, 0, 0.06), 0 2px 6px rgba(0, 0, 0, 0.07);
    --rz-shadow-3: 0 2px 6px rgba(0, 0, 0, 0.07), 0 6px 16px rgba(0, 0, 0, 0.08);
    --rz-shadow-4: 0 4px 12px rgba(0, 0, 0, 0.08), 0 10px 24px rgba(0, 0, 0, 0.08);
    --rz-shadow-5: 0 8px 24px rgba(0, 0, 0, 0.10), 0 16px 40px rgba(0, 0, 0, 0.10);

    /* Brand data-viz series, blue-led, calm and coherent */
    --rz-series-1: #194a71;
    --rz-series-2: #2f6b9a;
    --rz-series-3: #0a84ff;
    --rz-series-4: #ff9500;
    --rz-series-5: #5856d6;
    --rz-series-6: #34c759;
    --rz-series-7: #64748b;
    --rz-series-8: #ff3b30;
    --rz-series-9: #123a5b;
}

/* ── Light mode — Apple-style cool neutrals ───────────────────────────────── */
:root[data-theme="light"] {
    --rz-base: #f5f5f7;
    --rz-base-50: #fbfbfd;
    --rz-base-100: #f5f5f7;
    --rz-base-200: #e8e8ed;
    --rz-base-300: #d2d2d7;
    --rz-base-400: #aeaeb2;
    --rz-base-500: #8e8e93;
    --rz-base-600: #6e6e73;
    --rz-base-700: #48484a;
    --rz-base-800: #2c2c2e;
    --rz-base-900: #1d1d1f;
    --rz-base-light: #f5f5f7;
    --rz-base-lighter: #ffffff;
    --rz-base-dark: #6e6e73;
    --rz-base-darker: #1d1d1f;

    --rz-body-background-color: #f5f5f7;
    --rz-base-background-color: #ffffff;
    --rz-text-title-color: #1d1d1f;
    --rz-text-color: #2c2c2e;
    --rz-text-secondary-color: #6e6e73;
    --rz-text-tertiary-color: #8e8e93;
}

/* ── Banner foreground token (akili-ui-conventions rule 1) ────────────────────
   The top banner is TWO different surfaces depending on host, so every banner
   foreground (title, action buttons, icons) resolves through this one token:
   - Web: .rz-header is a SOLID surface painted with the tenant's brand color →
     the WCAG-computed on-primary (--rz-header-color) is the only correct fg.
   - MAUI: .rz-header.app-header--maui is frosted glass over the page (see
     UnitechRMSClient.Maui/wwwroot/index.html) → the page's own text color is
     the only correct fg.
   Picking either color var directly breaks the other host — that regression
   has shipped twice. Never bypass the token in banner rules. */
.rz-header { --banner-fg: var(--rz-header-color); }
.rz-header.app-header--maui { --banner-fg: var(--rz-text-color); }

/* ── Web header/sidebar stacking ──────────────────────────────────────────────
   On web the header is a STATIC grid row, so its z-index was inert and anything
   opened from it (the avatar dropdown, absolute z:1000 inside the z:2
   .app-header__user cluster) was trapped at level 2 in the root context — a
   page-level fixed shell like the Assistant's .chat-shell (fixed, z:5) painted
   over the dropdown AND over the opened sidebar. MAUI never showed this because
   its frosted header is position:fixed z:1000 (index.html), which also wins the
   position:fixed !important battle over the `relative` here. Same tier as MAUI:
   header 1000 — under the FAB (1100), dialogs (1300), sheets (2000).
   The sidebar sits above page shells but under everything else. */
.rz-layout .rz-header {
    position: relative;
    z-index: 1000;
}
.rz-layout .rz-sidebar {
    position: relative;
    z-index: 20;
}

/* ── Dark mode — Apple-style near-black surfaces ──────────────────────────────
   IMPORTANT: Radzen's dark theme keeps the SAME scale direction as light —
   base-50 is the LIGHTEST (used for text), base-900 the DARKEST (used for the
   body background). Components reference these by index (e.g. the header uses
   base-700 as a dark surface), so the order must be preserved or surfaces
   invert (a light header in dark mode). */
:root[data-theme="dark"] {
    --rz-base: #2c2c2e;
    --rz-base-50: #f5f5f7;   /* lightest — primary text */
    --rz-base-100: #d1d1d6;
    --rz-base-200: #aeaeb2;
    --rz-base-300: #8e8e93;
    --rz-base-400: #636366;
    --rz-base-500: #48484a;
    --rz-base-600: #3a3a3c;
    --rz-base-700: #2c2c2e;  /* header / raised surface */
    --rz-base-800: #1c1c1e;  /* card surface */
    --rz-base-900: #000000;  /* darkest — body background */
    --rz-base-light: #2c2c2e;
    --rz-base-lighter: #1c1c1e;
    --rz-base-dark: #d1d1d6;
    --rz-base-darker: #f5f5f7;

    --rz-body-background-color: #000000;
    --rz-base-background-color: #1c1c1e;
    --rz-text-title-color: #f5f5f7;
    --rz-text-color: #ebebf0;
    --rz-text-secondary-color: #aeaeb2;
    --rz-text-tertiary-color: #8e8e93;
}

/* Apply the brand font globally (covers non-Radzen elements too). */
html, body {
    font-family: var(--rz-text-font-family);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ============================================================================
   MOTION & MICRO-INTERACTIONS (Phase 2)
   Apple-style "delight through polish": subtle press/hover feedback, smooth
   transitions and gentle entrance animations. Hover-lifts are gated behind
   (hover: hover) so touch devices only get the press feedback (no sticky
   hover). Everything is disabled under prefers-reduced-motion.
   ============================================================================ */

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.rz-button {
    transition: transform 0.12s ease, box-shadow 0.18s ease, background-color 0.18s ease, filter 0.18s ease;
    will-change: transform;
}
.rz-button:active {
    transform: scale(0.97);
}
@media (hover: hover) {
    .rz-button:not(.rz-state-disabled):hover {
        transform: translateY(-1px);
        filter: brightness(1.03);
    }
}

/* ── Cards & raised surfaces ──────────────────────────────────────────────── */
.rz-card {
    transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

/* Opt-in: add class "akili-interactive" to clickable cards for a hover lift */
@media (hover: hover) {
    .akili-interactive:hover {
        transform: translateY(-2px);
        box-shadow: var(--rz-shadow-3);
        cursor: pointer;
    }
}

/* ── Inputs & form fields ─────────────────────────────────────────────────── */
.rz-textbox, .rz-dropdown, .rz-numeric, .rz-textarea, .rz-datepicker,
.rz-form-field, .rz-chkbox-box, .rz-switch {
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}

/* ── Navigation: sidebar menu + bottom tab bar ────────────────────────────── */
.rz-panel-menu .rz-navigation-item-link {
    transition: background-color 0.15s ease, color 0.15s ease, padding-left 0.15s ease;
}
@media (hover: hover) {
    .rz-panel-menu .rz-navigation-item-link:hover {
        padding-left: calc(var(--rz-panel-menu-item-padding, 1rem) + 3px);
    }
}
.bottom-tab-bar button, .bottom-tab-bar a {
    transition: transform 0.12s ease, color 0.15s ease;
}
.bottom-tab-bar button:active, .bottom-tab-bar a:active {
    transform: scale(0.92);
}

/* Radzen's own theme (material.css / material-dark.css) sets
   `.rz-layout .rz-body { transform: translateZ(0); }` as a scroll-perf hint.
   Any transform — even an identity one — makes .rz-body a new containing
   block for `position: fixed` descendants (CSS spec), so every fixed-position
   overlay anchored to the viewport (the immersive chat composer, full-bleed
   mobile shells, etc.) resolves its `bottom: 0` against .rz-body's own box
   instead of the real viewport, floating well short of the screen edge.
   This file loads after Radzen's theme CSS on every host, so the same
   selector here wins the cascade and removes the accidental containing block. */
.rz-layout .rz-body {
    transform: none;
}

/* Fixes a CSS Grid "blowout" on the web host: Radzen sizes .rz-layout's body
   row as a bare `1fr` (grid-template-rows: auto 1fr auto), but measured live
   here the browser resolves that row to .rz-layout's FULL height (not the
   remainder after the header/footer rows) — verified empirically by forcing
   `minmax(0, 1fr)` at runtime, which immediately corrected it. Root cause
   isn't the usual "item's automatic minimum size" grid gotcha (.rz-body
   already has its own overflow:auto and min-height:0 makes no difference
   here) — it's the TRACK sizing itself. Net effect either way: the body row
   grows to the container's full height, pushing .rz-footer (grid-area
   rz-footer, the row after rz-body) past .rz-layout's own `overflow: hidden`
   bottom edge — clipped and permanently unreachable, even though .rz-body
   itself still scrolls its own content just fine. `minmax(0, 1fr)` clamps the
   track to the actual remaining space so the footer lands back on-screen.
   Selector mirrors Radzen's own (header + sidebar-start + footer, the only
   shape MainLayout.razor uses) so it wins the cascade without !important. */
.rz-layout:has(> .rz-sidebar-start):has(> .rz-header):has(> .rz-footer) {
    grid-template-rows: auto minmax(0, 1fr) auto;
}

/* ── Accessible focus ring (keyboard users) ───────────────────────────────── */
:focus-visible {
    outline: 2px solid var(--rz-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

/* ── Entrance animations (opt-in utility classes) ─────────────────────────── */
@keyframes akiliFadeInUp {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes akiliFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.akili-fade-in    { animation: akiliFadeIn 0.3s ease both; }
.akili-fade-in-up { animation: akiliFadeInUp 0.35s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* Route/page transition: replayed on every navigation via a route-keyed wrapper
   (see PageTransition.razor). Soft fade for an iOS-like feel.
   NOTE: deliberately opacity-only — this class is toggled on .rz-body itself
   (see akiliPlayPageTransition in fabDrag.js), and a `transform` here would
   reintroduce the exact containing-block hazard documented above for the
   whole 0.5s of every navigation, floating the immersive chat composer and
   other full-bleed fixed shells short of the real viewport bottom. */
@keyframes akiliPageEnter {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.akili-page-enter {
    animation: akiliPageEnter 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

/* Stagger helper: apply to a container; children fade in sequentially. */
.akili-stagger > * { animation: akiliFadeInUp 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; }
.akili-stagger > *:nth-child(1) { animation-delay: 0.02s; }
.akili-stagger > *:nth-child(2) { animation-delay: 0.06s; }
.akili-stagger > *:nth-child(3) { animation-delay: 0.10s; }
.akili-stagger > *:nth-child(4) { animation-delay: 0.14s; }
.akili-stagger > *:nth-child(5) { animation-delay: 0.18s; }
.akili-stagger > *:nth-child(6) { animation-delay: 0.22s; }
.akili-stagger > *:nth-child(7) { animation-delay: 0.26s; }
.akili-stagger > *:nth-child(8) { animation-delay: 0.30s; }

/* ── Skeleton loader shimmer (used by the Skeleton component) ─────────────── */
.akili-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--rz-fill-muted);
    border-radius: 8px;
}
.akili-skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(
        90deg,
        transparent 0,
        rgba(255, 255, 255, 0.18) 50%,
        transparent 100%
    );
    animation: akiliShimmer 1.3s infinite;
}
:root[data-theme="dark"] .akili-skeleton::after {
    background: linear-gradient(
        90deg,
        transparent 0,
        rgba(255, 255, 255, 0.06) 50%,
        transparent 100%
    );
}
@keyframes akiliShimmer {
    100% { transform: translateX(100%); }
}

/* ── Empty states ─────────────────────────────────────────────────────────── */
.akili-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 0.5rem;
    padding: 2.5rem 1.5rem;
    width: 100%;
}
.akili-empty-state__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    margin-bottom: 0.5rem;
    border-radius: 50%;
    background: var(--rz-fill-muted);
    color: var(--rz-text-tertiary-color);
}
.akili-empty-state__icon .rzi {
    font-size: 32px;
    line-height: 1;
}
.akili-empty-state__title {
    color: var(--rz-text-color);
}
.akili-empty-state__msg {
    color: var(--rz-text-secondary-color);
    max-width: 28rem;
}
.akili-empty-state__action {
    margin-top: 0.75rem;
}

/* ============================================================================
   LIQUID GLASS SYSTEM (Phase 3 — "Akili Glass")
   Reusable frosted-glass surfaces with a specular top edge. Use .akili-glass
   for bars/pills and .akili-glass-card for content cards.
   ============================================================================ */
.akili-glass {
    background: rgba(255, 255, 255, 0.55);
    -webkit-backdrop-filter: blur(28px) saturate(190%);
    backdrop-filter: blur(28px) saturate(190%);
    border: 1px solid rgba(255, 255, 255, 0.55);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.65),
        0 8px 32px rgba(var(--rz-primary-rgb, 25, 74, 113), 0.10);
}
:root[data-theme="dark"] .akili-glass {
    background: rgba(28, 28, 30, 0.55);
    border-color: rgba(255, 255, 255, 0.10);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.08),
        0 8px 32px rgba(0, 0, 0, 0.45);
}

.akili-glass-card {
    background: var(--rz-base-background-color);
    border: 1px solid rgba(127, 127, 127, 0.12);
    border-radius: 16px;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.45),
        var(--rz-shadow-2);
}
:root[data-theme="dark"] .akili-glass-card {
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.05),
        var(--rz-shadow-2);
}

/* ── Sidebar: calm panel + rounded active pills ───────────────────────────── */
.rz-sidebar {
    border-right: 1px solid rgba(127, 127, 127, 0.12);
    /* .rz-sidebar is always `position: static` here (`.rz-layout .rz-sidebar` from Radzen's
       own CSS outranks the base `.rz-sidebar { position: fixed }` rule via specificity), so
       it's a genuine grid item sharing .rz-layout's body row with .rz-body — it should just
       stretch to fill that row (CSS Grid's default align-self) and let its own flex column
       (below) bound the menu list's scrollable height. A previous fix here pinned an
       explicit `height: 100dvh` instead, to work around the SAME grid-row "blowout" now
       fixed at the source in the `.rz-layout:has(...)` rule above (that 1fr row was
       resolving to the layout's full height rather than the remainder after header/footer).
       With the row itself fixed, `height: 100dvh` became stale and actively harmful: since
       .rz-sidebar starts below the header (top offset > 0) but 100dvh measures from the
       viewport, not the row, it now overshot the row by exactly the header's height and got
       clipped by .rz-layout's own `overflow: hidden` — silently cutting the last sidebar
       items off, indistinguishable from the sidebar not scrolling at all. Letting it stretch
       naturally keeps it correctly bounded to the row regardless of header height. */
    display: flex;
    flex-direction: column;
}
/* .rz-sidebar is a CSS Grid item sitting in .rz-layout's "auto" column track. Whenever the
   panel-menu content is tall enough to overflow (exactly the scrollable case above), that
   "auto" track's sizing algorithm collapses to ~1px — Radzen sets width via the
   --rz-sidebar-width custom property, but nothing pins a MINIMUM, so the auto-track
   collapse wins over the (still class-applied) width. Reproduced directly against the
   live DOM: same bug with none of the rules above present, so it's pre-existing, not
   something introduced by the scroll fix — min-width simply forces the track to respect
   the sidebar's real width regardless of how the auto-sizing algorithm treats overflow. */
.rz-sidebar-expanded {
    min-width: var(--rz-sidebar-width, 300px);
}
.rz-panel-menu {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
.rz-panel-menu .rz-navigation-item {
    background: transparent;
}
.rz-panel-menu .rz-navigation-item-wrapper {
    border-radius: 10px;
    margin: 1px 8px;
    overflow: hidden;
}
.rz-panel-menu .rz-navigation-item-link {
    border-radius: 10px;
}
@media (hover: hover) {
    .rz-panel-menu .rz-navigation-item-link:hover {
        background-color: rgba(var(--rz-primary-rgb, 25, 74, 113), 0.07);
    }
}
/* Selected entry: soft tinted pill instead of a hard block. The pill and its label are drawn
   from --rz-text-color, never from --rz-primary: the tenant primary is a foreground-unsafe
   colour (a dark navy by default), so on the dark theme's dark drawer the selected item's
   label went black-on-black and vanished. Text colour + weight carry the selected state. */
.rz-panel-menu .rz-navigation-item-wrapper-active > .rz-navigation-item-link,
.rz-panel-menu .rz-navigation-item-link.rz-state-active,
.rz-panel-menu .rz-navigation-item-link[aria-current="page"] {
    background-color: color-mix(in srgb, var(--rz-text-color) 12%, transparent);
    color: var(--rz-text-color);
    font-weight: 600;
}

/* ── Respect reduced-motion ───────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .rz-button,
    .rz-button:active,
    .rz-button:hover,
    .akili-interactive:hover,
    .bottom-tab-bar button:active,
    .bottom-tab-bar a:active,
    .rz-panel-menu .rz-navigation-item-link:hover {
        transform: none !important;
    }
    .akili-fade-in,
    .akili-fade-in-up,
    .akili-page-enter,
    .akili-stagger > *,
    .akili-skeleton::after {
        animation: none !important;
    }
}

/* ============================================================
   Scroll affordance — fade cue at top/bottom of a scroll area
   so the user knows there's more content. Driven by
   scrollAffordance.js (sets data-can-scroll-up / -down).
   Uses a CONTRAST cue: a dark shadow on light mode, a light
   glow on dark mode, so it reads against the content either way.
   ============================================================ */
.scroll-affordance {
    position: relative;
    --scroll-fade-color: rgba(0, 0, 0, .18); /* light: dark shadow */
}

[data-theme="dark"] .scroll-affordance {
    --scroll-fade-color: rgba(255, 255, 255, .16); /* dark: light glow */
}

.scroll-affordance__fade {
    position: absolute;
    left: 0;
    right: 0;
    height: 26px;
    pointer-events: none;
    z-index: 4;
    opacity: 0;
    transition: opacity .18s ease;
}

.scroll-affordance__fade--top {
    top: 0;
    background: linear-gradient(to bottom, var(--scroll-fade-color) 0%, rgba(0, 0, 0, 0) 100%);
}

.scroll-affordance__fade--bottom {
    bottom: 0;
    background: linear-gradient(to top, var(--scroll-fade-color) 0%, rgba(0, 0, 0, 0) 100%);
}

.scroll-affordance[data-can-scroll-up="true"] .scroll-affordance__fade--top {
    opacity: 1;
}

.scroll-affordance[data-can-scroll-down="true"] .scroll-affordance__fade--bottom {
    opacity: 1;
}

/* Dashed "add line/product" button (.popup-add-dashed is styled in each popup's
   non-scoped <style>). It uses --rz-primary (dark navy) which does NOT lighten in
   the dark theme, so it read as disabled on dark backgrounds. Neutralize to a
   light dashed button in dark mode. */
[data-theme="dark"] .popup-add-dashed {
    border-color: rgba(255, 255, 255, .28);
    background: rgba(255, 255, 255, .05);
    color: var(--rz-text-title-color, #eaeaec);
}

[data-theme="dark"] .popup-add-dashed:active {
    background: rgba(255, 255, 255, .10);
}

/* ============================================================
   Keyboard-open (mobile) — set by index.html when the iOS keyboard
   is up. Lift the sticky invoice summary bar out of the way so the
   focused list field (e.g. a quantity input) isn't covered, and
   shrink the list's bottom reserve so it can scroll above the
   keyboard. Keep the bar in place when the field being edited is the
   shipping input that lives INSIDE it.
   ============================================================ */
.keyboard-open .invoice-summary-bar {
    transition: transform .2s ease, opacity .2s ease;
}

.keyboard-open:not(:has(.invoice-summary-bar :focus)) .invoice-summary-bar {
    transform: translateY(140%);
    opacity: 0;
    pointer-events: none;
}

.keyboard-open .parentInvoice,
.keyboard-open .stock-invoice-stack,
.keyboard-open .expense-invoice-row {
    padding-bottom: 24px !important;
}

/* ============================================================
   Toast notifications (Radzen NotificationService outlet)
   ------------------------------------------------------------
   Radzen hardcodes `.rz-notification { z-index: 1002; inset-block-start: 100px }`
   and exposes no custom property for it — unlike dialogs, which is why
   --rz-dialog-zindex (above) could simply be raised to 1300. 1002 sits UNDER
   this app's whole overlay tier (InvoiceSummaryBar 1040, BottomTabBar 1050,
   assistant FAB 1100, dialogs 1300, AppGate 1500/2000, every sheet 2000/2001)
   and, on the MAUI host, under the fixed frosted header — which is
   50px + safe-top tall, i.e. taller on a notched iPhone than the 100px offset
   Radzen positions the toast at. Toasts are this app's only channel for error
   feedback, so they get the same explicit treatment dialogs already got.
   ============================================================ */
.rz-notification {
    /* Above every overlay this app renders (highest is TourOverlay's 2001). */
    z-index: 2100;
    /* Keep Radzen's 100px baseline (it already clears the web app header) and push
       further down by the notch inset, which is what the MAUI host's fixed frosted
       header is padded by. --safe-top exists only in the MAUI index.html; on web it
       falls back to 0 and the outlet stays exactly where Radzen put it. */
    inset-block-start: calc(100px + var(--safe-top, env(safe-area-inset-top, 0px)));
    /* The outlet is a bare wrapper with no backdrop of its own — never let it
       (or the gap between stacked toasts) swallow taps meant for the page. */
    pointer-events: none;
}

.rz-notification .rz-notification-item-wrapper {
    pointer-events: auto;
}

/* Radzen fades a toast in with `.rz-open { animation: <duration> rz-open }`,
   whose 0% keyframe is `opacity: 0` and whose fill-mode is `none`. That makes
   the toast's visibility depend on the document's animation timeline actually
   running: while that timeline is suspended — the state a WKWebView reports
   when it considers the document hidden — the element sticks on that first
   keyframe, so the toast is in the DOM, laid out, and completely invisible.
   Radzen dialogs and this app's own sheets are unaffected because they animate
   with transitions, which degrade to an instant jump to the final value rather
   than freezing on a keyframe. Toast visibility must never depend on an
   animation having run. */
.rz-notification .rz-notification-item-wrapper.rz-open {
    animation: none;
    opacity: 1;
}

/* Phone: anchor at the bottom, iOS-style — by the thumb, and clear of the
   frosted header entirely. Radzen's 20rem min-width would overflow a narrow
   device once both side insets apply, so let the item size to the container. */
@media (max-width: 767.98px) {
    .rz-notification {
        inset-block-start: auto;
        inset-inline-start: 0.75rem;
        inset-inline-end: 0.75rem;
        inset-block-end: calc(var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 0.75rem);
    }

    .rz-notification .rz-notification-item-wrapper {
        min-width: 0;
        width: auto;
    }

    /* Nothing may sit under the MAUI bottom tab dock. max(), not a sum: when the
       keyboard is up it covers the dock, so the larger inset is the right one. */
    body.has-bottom-tab-bar .rz-notification {
        inset-block-end: max(var(--kb-inset, 0px),
                             calc(var(--maui-tabbar-height, 56px) + var(--safe-bottom, 0px)));
    }
}
