/* Source Sans 3 variable font: the existing CSP already permits jsDelivr for both
   styles and fonts, so this does not widen the application's external origins. */
@font-face {
    font-family: "Source Sans 3 Variable";
    font-style: normal;
    font-display: swap;
    font-weight: 200 900;
    src: url("https://cdn.jsdelivr.net/fontsource/fonts/source-sans-3:vf@5.2.9/latin-wght-normal.woff2") format("woff2-variations");
}

/* ==========================================================================
   MAIN CSS FILE: Base Styles & Accessibility Fixes
   ========================================================================== */
html, body {
    font-family: "Source Sans 3 Variable", "Source Sans 3", "Segoe UI", sans-serif;
    height: 100%;
    overflow-y: hidden;
    /* Blocks horizontal overflows to lock the viewport size on mobile devices */
    max-width: 100% !important;
    overflow-x: hidden !important;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

/* :focus-visible (not plain :focus) so this ring only appears for real keyboard navigation - a
   mouse click or touch tap satisfies :focus too, but a WebView (MAUI) has no mouse to move away
   afterward and can leave that ring looking permanently "stuck" on whatever button was last tapped.
   :focus-visible is the browser's own semantically-correct way to tell the two apart. */
.btn:focus-visible, .btn:active:focus-visible, .btn-link.nav-link:focus-visible, .form-control:focus-visible, .form-check-input:focus-visible {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
/* Drag and Drop Safety Pointer Mask */
.is-dragging td * {
    pointer-events: none !important;
}

/* FIX: Kills the ugly black box Blazor draws around headers when navigating */
h1:focus, h2:focus, [tabindex="-1"]:focus {
    outline: none !important;
    box-shadow: none !important;
    border: none !important;
}

.hover-clickable {
    cursor: pointer !important;
    transition: all 0.2s ease-in-out !important;
}

    .hover-clickable:hover {
        transform: translateY(-2px) scale(1.03) !important;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important;
        opacity: 1 !important;
    }

/* Instant tap feedback for server-rendered buttons (e.g. Add Task modal's bed/shortcut/group
   pickers) - the visual "pressed" state below is pure CSS, so it appears before the SignalR
   round-trip resolves the real selected state, masking perceived latency. */
.transition-all {
    transition: transform 0.08s ease-out, opacity 0.08s ease-out !important;
}

.hover-clickable:active,
.transition-all:active {
    transform: scale(0.96) !important;
    opacity: 0.85 !important;
}

/* ==========================================================================
   Dashboard Layout (Light/Dark Mode Supported)
   ========================================================================== */
.sidebar {
    width: 260px !important;
    min-width: 260px !important;
    background-color: var(--bs-body-bg);
    border-right: 1px solid var(--bs-border-color);
    z-index: 1040;
    height: 100vh;
}

    /* Force rounded corners and nice spacing on the links */
    .sidebar .nav-link {
        color: var(--bs-body-color) !important;
        font-weight: 500 !important;
        padding: 0.75rem 1.25rem !important;
        border-radius: 0.5rem !important; /* Forces the nice pill shape */
        margin-bottom: 0.25rem !important;
        display: flex !important;
        align-items: center !important;
        opacity: 0.85;
        transition: all 0.2s ease-in-out;
    }

        .sidebar .nav-link i {
            color: var(--bs-primary) !important;
        }

        .sidebar .nav-link:hover, .sidebar .nav-link:focus {
            opacity: 1;
            background-color: var(--bs-secondary-bg) !important;
        }

        /* The Active State (Blue Pill) */
        .sidebar .nav-link.active {
            color: #fff !important;
            background-color: #0d6efd !important;
            box-shadow: 0 4px 6px -1px rgba(13, 110, 253, 0.2) !important;
            opacity: 1;
        }

            .sidebar .nav-link.active i {
                color: #fff !important;
            }
.main-content {
    flex-grow: 1;
    /* Without this, a flex item's default min-width:auto lets wide content (e.g. a many-column
       grid table) force this whole area wider than its allotted space, which then gets silently
       clipped by overflow-x:hidden below instead of being reachable via a scrollbar. */
    min-width: 0;
    overflow-y: auto;
    height: 100vh;
    /* On a real mobile browser (not the MAUI WebView, which doesn't have this problem), 100vh is
       computed against the LARGEST possible viewport (address bar collapsed) rather than whatever is
       actually visible right now - this container ends up taller than the visible area, and since
       html/body below has overflow-y:hidden, there is no outer scroll to recover the clipped part.
       Reported on TimePlanner specifically: unable to scroll back to the top of the page or reach the
       header's menu button on a mobile browser. First fix tried 100dvh (dynamic viewport height,
       tracking whatever is CURRENTLY visible) - that introduced a NEW regression on this same page
       (unable to scroll all the way to the bottom / reach the Anytime row): dvh is a moving target
       that recalculates as the browser's own chrome animates in/out DURING an active scroll gesture
       (a documented iOS Safari quirk), which can make a scroll container's own maximum scroll
       position shift mid-gesture and settle short of the true bottom. 100svh (small viewport height -
       the height guaranteed visible with the browser's chrome shown, i.e. the smallest/most
       conservative value) fixes the original top-clipping bug the same way (never taller than what's
       actually visible) without being a moving target during scroll - it's fixed for the life of the
       page load instead of recalculating mid-gesture. Older browsers without svh support simply
       ignore this second declaration and keep the 100vh value above. */
    height: 100svh;
    background-color: var(--nt-page-bg) !important;
    max-width: 100% !important;
    overflow-x: hidden !important;
}

/* ==========================================================================
   CLINICAL DESIGN SYSTEM
   ========================================================================== */
:root,
[data-bs-theme="light"] {
    --nt-primary: #176b87;
    --nt-primary-hover: #12576e;
    --nt-primary-soft: #e8f3f7;
    --nt-success: #168c80;
    --nt-success-hover: #117267;
    --nt-success-soft: #e5f5f2;
    --nt-warning: #d79a2b;
    --nt-danger: #c34b4f;
    /* Softer, amber-leaning red for a single-item destructive action (delete one bed, delete one
       task) - reserves the full --nt-danger red for bulk/whole-shift actions (Discharge All, Clear
       All), so the strongest warning color is only ever spent on the biggest blast radius. */
    --nt-danger-soft: #bd7a4a;
    --nt-info: #3d8fa8;
    --nt-neutral: #68757d;
    --nt-page-bg: #f7f5f0;
    --nt-card-bg: #fffefa;
    --nt-surface-muted: #f3f5f4;
    --nt-card-border: rgba(34, 69, 82, 0.12);
    --nt-card-shadow: 0 4px 16px rgba(25, 55, 68, 0.07);
    --nt-card-shadow-hover: 0 10px 26px rgba(25, 55, 68, 0.13);
    --nt-row-stripe: rgba(23, 107, 135, 0.035);
    --nt-row-hover: rgba(23, 107, 135, 0.105);
    --bs-primary: var(--nt-primary);
    --bs-primary-rgb: 23, 107, 135;
    --bs-success: var(--nt-success);
    --bs-success-rgb: 22, 140, 128;
    --bs-warning: var(--nt-warning);
    --bs-warning-rgb: 215, 154, 43;
    --bs-danger: var(--nt-danger);
    --bs-danger-rgb: 195, 75, 79;
    --bs-info: var(--nt-info);
    --bs-info-rgb: 61, 143, 168;
}

[data-bs-theme="dark"] {
    --nt-primary: #55b3d0;
    --nt-primary-hover: #76c4db;
    --nt-primary-soft: rgba(85, 179, 208, 0.14);
    --nt-success: #45b8a9;
    --nt-success-hover: #68c8bb;
    --nt-success-soft: rgba(69, 184, 169, 0.14);
    --nt-warning: #e0aa49;
    --nt-danger: #dc6c70;
    --nt-danger-soft: #d1935f;
    --nt-info: #68b6cc;
    --nt-neutral: #7c8992;
    --nt-page-bg: #151c20;
    --nt-card-bg: #1d272c;
    --nt-surface-muted: rgba(255, 255, 255, 0.045);
    /* Bumped from 0.1 - too close to --nt-card-bg (#1d272c) to reliably separate stacked/adjacent
       cards from each other or the page background at a glance in dark mode. */
    --nt-card-border: rgba(255, 255, 255, 0.16);
    --nt-card-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    --nt-card-shadow-hover: 0 10px 26px rgba(0, 0, 0, 0.3);
    --nt-row-stripe: rgba(85, 179, 208, 0.045);
    --nt-row-hover: rgba(85, 179, 208, 0.14);
    /* --bs-*-rgb triples were only ever defined in the light-mode block above, never redefined
       here - every rgba(var(--bs-*-rgb), X) rule in this file (dozens: the app-primary-action
       glow, overdue-pulse, the Handover panel's info tint, table-hover shadows, the mobile FAB
       pulse, and more) was silently rendering with LIGHT-mode colors while in dark mode, since an
       undefined custom property here just inherits the :root value instead of failing. Computed
       directly from this block's own --nt-* hex values above, so they can never drift apart again. */
    --bs-primary-rgb: 85, 179, 208;
    --bs-success-rgb: 69, 184, 169;
    --bs-warning-rgb: 224, 170, 73;
    --bs-danger-rgb: 220, 108, 112;
    --bs-info-rgb: 104, 182, 204;
}

/* Bootstrap's .btn-outline-secondary and .btn-light hardcode literal light-mode colors
   (--bs-btn-color: #6c757d / #000) that never adapt via [data-bs-theme="dark"] - neither this
   app's own dark-mode block above nor Bootstrap's own dark-mode CSS redefines these secondary/light
   -specific button variables. Without this override, every secondary/light-styled button (toolbar
   dropdown toggles, full-screen/theme toggle, panel show/hide toggles, etc.) renders low-contrast
   grey-on-grey or black-on-grey text against this app's dark surfaces. Overriding the same
   `--bs-btn-*` custom properties Bootstrap's own rules already read keeps hover/active/disabled
   states working for free instead of re-deriving them. */
[data-bs-theme="dark"] .btn-outline-secondary {
    --bs-btn-color: #adb5bd;
    --bs-btn-border-color: #6c757d;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #495057;
    --bs-btn-hover-border-color: #6c757d;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #495057;
    --bs-btn-active-border-color: #6c757d;
    --bs-btn-disabled-color: #6c757d;
}

/* A touch WebView (MAUI) has no mouse to move away after a tap, so :hover/:active can visibly
   "stick" on a button - most noticeable on the light/dark mode toggle, which otherwise looks
   permanently pressed/highlighted right after use. blur()-ing the element (see ToggleTheme in
   MainLayout.razor) only clears :focus, not :hover/:active, and is timing-sensitive in a WebView -
   this CSS-only reset removes the possibility entirely on any device that reports no real hover
   capability, regardless of whether the pseudo-class technically cleared. Scoped to
   .btn-outline-secondary generally since the same stuck-look affects every icon button using it,
   not just the theme toggle. */
@media (hover: none) {
    .btn-outline-secondary:hover,
    .btn-outline-secondary:active {
        background-color: transparent !important;
        border-color: var(--bs-btn-border-color) !important;
        color: var(--bs-btn-color) !important;
    }
}

/* Bootstrap's .text-secondary reads --bs-secondary-rgb (the fixed #6c757d Sass value), unlike
   .text-muted which already reads the theme-reactive --bs-secondary-color - so despite looking like
   siblings, only one of the two adapts to dark mode. .text-secondary is used for labels/body text
   throughout this app, and #6c757d against the app's very dark card/page backgrounds reads as
   "grey on dark grey" - remapping it to the same reactive variable .text-muted already uses fixes
   every occurrence app-wide in one place instead of hunting down each individual label. */
[data-bs-theme="dark"] .text-secondary {
    color: var(--bs-secondary-color) !important;
}

[data-bs-theme="dark"] .btn-light {
    --bs-btn-color: #dee2e6;
    --bs-btn-bg: #343a40;
    --bs-btn-border-color: #343a40;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #495057;
    --bs-btn-hover-border-color: #495057;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #495057;
    --bs-btn-active-border-color: #495057;
    --bs-btn-disabled-color: #adb5bd;
    --bs-btn-disabled-bg: #343a40;
    --bs-btn-disabled-border-color: #343a40;
}

body {
    font-size: 1rem;
    line-height: 1.5;
    font-weight: 400;
    background-color: var(--nt-page-bg);
}

h1, .h1, .page-title {
    font-size: clamp(1.75rem, 2.5vw, 2rem);
    line-height: 1.2;
    font-weight: 700;
}

.page-title {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    padding-bottom: 0.65rem;
    letter-spacing: -0.015em;
}

.page-title::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 3rem;
    height: 3px;
    border-radius: 999px;
    background: var(--nt-primary);
}

.page-title.page-title-centered::after {
    left: 50%;
    transform: translateX(-50%);
}

.page-title > .bi {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    margin: 0 !important;
    border-radius: 0.75rem;
    color: var(--nt-primary) !important;
    background: var(--nt-primary-soft);
    font-size: 1.25rem;
}

/* Home's personalized greeting ("Good morning, Jane") deliberately does NOT reuse .page-title -
   that class was sized for short, static page names ("Administration Panel"), and a name-bearing
   greeting ("Good afternoon, Jonathan") ran noticeably larger/heavier at the same size, made worse
   by sitting directly above the .lead subtitle at the very top of the page on both Web and the
   MAUI phone layout. No underline accent bar either - that reads as a "page you're on" marker,
   which doesn't fit a greeting. */
.home-greeting {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-size: clamp(1.15rem, 1.8vw, 1.4rem);
    font-weight: 700;
    line-height: 1.2;
}

.home-greeting > .bi {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 0.6rem;
    color: var(--nt-primary) !important;
    background: var(--nt-primary-soft);
    font-size: 0.95rem;
}

/* Smaller sibling of .page-title's boxed icon, scaled for card/section-level headers (h4-h6)
   rather than the main page title - standardizes what used to be a mix of "boxed icon", "bare
   inline icon with no background", and "no icon at all" across near-identical section headers
   (Admin's per-tab "Manage X" headings in particular). Deliberately distinct from modal titles,
   which keep their own established bare-icon convention (see CLAUDE.md's design system section) -
   this is only for headers inside page content, not modal headers. */
.section-header-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    margin: 0 0.6rem 0 0 !important;
    border-radius: 0.6rem;
    color: var(--nt-primary) !important;
    background: var(--nt-primary-soft);
    font-size: 1.05rem;
    flex-shrink: 0;
}

h2, .h2 {
    font-size: clamp(1.5rem, 2vw, 1.75rem);
    line-height: 1.25;
    font-weight: 700;
}

h3, .h3 {
    font-size: 1.45rem;
    line-height: 1.3;
    font-weight: 600;
}

h4, .h4 {
    font-size: 1.25rem;
    line-height: 1.35;
    font-weight: 600;
}

h5, .h5 {
    font-size: 1.1rem;
    line-height: 1.4;
    font-weight: 600;
}

h6, .h6, .section-heading {
    font-size: 0.95rem;
    line-height: 1.4;
    font-weight: 600;
}

.fw-bold {
    font-weight: 600 !important;
}

h1.fw-bold,
h2.fw-bold,
.page-title.fw-bold {
    font-weight: 700 !important;
}

.form-label {
    margin-bottom: 0.35rem;
    font-size: 0.8125rem;
    line-height: 1.35;
    font-weight: 600 !important;
    letter-spacing: 0.025em;
}

.form-text,
.helper-text,
small,
.small {
    font-size: 0.8125rem;
    line-height: 1.4;
}

.text-xs {
    font-size: 0.75rem !important;
    line-height: 1.35;
}

.text-primary {
    color: var(--nt-primary) !important;
}

.text-success {
    color: var(--nt-success) !important;
}

.lead,
.card-text,
.modal-body,
.table > tbody {
    font-weight: 400;
}

.btn {
    --nt-button-radius: 0.625rem;
    min-height: 38px;
    border-radius: var(--nt-button-radius);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    font-weight: 500;
    line-height: 1.2;
    transition: color 0.18s ease, background-color 0.18s ease, border-color 0.18s ease,
        box-shadow 0.18s ease, transform 0.18s ease;
}

.btn:hover:not(:disabled) {
    transform: translateY(-1px);
}

.btn-sm {
    min-height: 34px;
}

.btn-xs {
    min-height: 30px;
}

.btn-lg {
    min-height: 44px;
    border-radius: 0.625rem !important;
    font-weight: 600;
}

/* The single "main commit" action per screen/modal (Save, Apply, Add, Login) previously shared
   .btn-lg's plain sizing rule with no distinct visual weight, reading as "just a bigger button"
   rather than the one action that actually matters on the page. A soft primary-colored glow (using
   Bootstrap's own --bs-primary-rgb, same pattern as the shared bg-opacity overrides) gives it real
   priority without introducing a new color. */
.app-primary-action {
    min-height: 44px;
    border-radius: 0.625rem !important;
    font-weight: 600;
    box-shadow: 0 2px 10px rgba(var(--bs-primary-rgb), 0.35);
}

.app-primary-action:hover:not(:disabled) {
    box-shadow: 0 4px 16px rgba(var(--bs-primary-rgb), 0.45);
    transform: translateY(-2px);
}

.modal-footer .btn-primary,
.modal-footer .btn-success,
.modal-footer .btn-danger {
    min-height: 40px;
    border-radius: 0.625rem !important;
    font-weight: 600;
}

/* Every modal's Cancel button across the app used solid .btn-secondary - the exact same "opaque,
   filled, bold-ish" visual weight class as the commit button beside it (.btn-primary/.btn-success/
   .btn-danger above), just a different hue. Restyled to a ghost/outline treatment scoped to
   .modal-footer only (so .btn-secondary keeps its normal solid look everywhere else it's used) -
   the commit action should always read as the one that matters, Cancel as the quiet way out. */
.modal-footer .btn-secondary {
    background-color: transparent;
    border-color: var(--bs-border-color);
    color: var(--bs-secondary-color);
    font-weight: 500;
}

.modal-footer .btn-secondary:hover,
.modal-footer .btn-secondary:focus {
    background-color: var(--nt-surface-muted);
    border-color: var(--bs-secondary-color);
    color: var(--bs-body-color);
}

.btn .bi {
    flex: 0 0 auto;
    margin-left: 0 !important;
    margin-right: 0 !important;
    line-height: 1;
}

.btn-primary {
    color: #fff;
    background-color: var(--nt-primary);
    border-color: var(--nt-primary);
}

.btn-primary:hover,
.btn-primary:focus {
    color: #fff;
    background-color: var(--nt-primary-hover);
    border-color: var(--nt-primary-hover);
}

.btn-success {
    color: #fff;
    background-color: var(--nt-success);
    border-color: var(--nt-success);
}

.btn-success:hover,
.btn-success:focus {
    color: #fff;
    background-color: var(--nt-success-hover);
    border-color: var(--nt-success-hover);
}

.btn-outline-primary {
    color: var(--nt-primary);
    border-color: var(--nt-primary);
}

.btn-outline-primary:hover,
.btn-outline-primary:focus {
    color: #fff;
    background-color: var(--nt-primary);
    border-color: var(--nt-primary);
}

.btn-outline-success {
    color: var(--nt-success);
    border-color: var(--nt-success);
}

.btn-outline-success:hover,
.btn-outline-success:focus {
    color: #fff;
    background-color: var(--nt-success);
    border-color: var(--nt-success);
}

/* Soft-danger tier: single-item destructive actions (delete one bed, delete one task) - the full
   .text-danger/.btn-outline-danger red is reserved for bulk/whole-shift actions. */
.text-danger-soft {
    color: var(--nt-danger-soft) !important;
}

.btn-outline-danger-soft {
    color: var(--nt-danger-soft);
    border-color: var(--nt-danger-soft);
}

.btn-outline-danger-soft:hover,
.btn-outline-danger-soft:focus {
    color: #fff;
    background-color: var(--nt-danger-soft);
    border-color: var(--nt-danger-soft);
}

/* Icon-only controls must opt in explicitly. Do not infer this with :only-child:
   CSS ignores text nodes there, so a labelled "<i> + text" button is otherwise
   misclassified and collapsed to a square. */
.btn-icon {
    width: 38px;
    min-width: 38px;
    height: 38px;
    min-height: 38px;
    padding: 0 !important;
    border-radius: 0.625rem !important;
}

/* Loading spinners were styled ad hoc per call site across TimePlanner/BedBoard/Admin - some with
   text-primary, some without, some with me-1, some me-2 - so the same "something is loading"
   signal looked subtly different from screen to screen. Overriding the shared Bootstrap classes
   directly here (rather than touching 25+ individual markup call sites) standardizes color and
   spacing everywhere at once; app.css loads after Bootstrap's own utility classes, so this wins
   the cascade regardless of which combination of text-primary/me-1/me-2 a given instance has. */
.spinner-border-sm {
    color: var(--nt-primary);
    margin-right: 0.4rem;
}

.spinner-border {
    color: var(--nt-primary);
}

/* Admin's ApplyFilterAndReload deliberately skips the page-wide LoadingData overlay for a filter
   change (keeps the tab/search box mounted) - previously that meant zero visual feedback at all
   while the new page of rows was in flight, the old rows just sat there until they were quietly
   swapped out. A gentle dim+pulse on the table body signals "this is refreshing" without the
   heavier full-page block. See Admin.razor.cs's IsFilterReloading. */
.table-refreshing table > tbody {
    opacity: 0.5;
    animation: tableRefreshPulse 1.1s ease-in-out infinite;
    pointer-events: none;
}

@keyframes tableRefreshPulse {
    0%, 100% { opacity: 0.5; }
    50% { opacity: 0.7; }
}

@media (prefers-reduced-motion: reduce) {
    .table-refreshing table > tbody {
        animation: none !important;
        opacity: 0.6;
    }
}

/* Sidebar section collapse/expand chevrons previously swapped between two different icon glyphs
   (bi-chevron-up/bi-chevron-down) - a DOM content swap isn't animatable, so expanding a nav
   section looked like a hard icon cut rather than one continuous motion. Now always the same
   bi-chevron-down icon, rotated 180deg via CSS when the section is expanded. */
.sidebar-chevron {
    display: inline-block;
    transition: transform 0.2s ease;
}

.sidebar-chevron.rotated {
    transform: rotate(180deg);
}

.btn-icon.btn-sm,
.btn-icon.btn-xs {
    width: 36px;
    min-width: 36px;
    height: 36px;
    min-height: 36px;
}

/* Shared sizing for the Edit/Delete/Duplicate icon-only buttons repeated in every Admin paged
   table's Actions column - previously each table's action column grew its own slightly different
   combination of size/padding/margin classes, so the same conceptual control felt like a different
   widget from one table to the next. A fixed square target plus a consistent inter-button gap (via
   the parent's flex gap, not per-button margin) makes every table's action column read as the same
   shared control. */
.btn-table-action {
    width: 30px;
    min-width: 30px;
    height: 30px;
    min-height: 30px;
    padding: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.btn-close {
    position: relative;
    box-sizing: border-box;
    width: 38px;
    min-width: 38px;
    height: 38px;
    min-height: 38px;
    padding: 0.7rem;
    border-radius: 0.625rem;
}

/* Bootstrap close buttons have no visible label; this supplies a consistent hover
   tooltip without requiring dozens of modal-specific implementations. */
.btn-close::after {
    content: "Close";
    position: absolute;
    right: 0;
    bottom: calc(100% + 0.35rem);
    z-index: 1200;
    padding: 0.25rem 0.45rem;
    border-radius: 0.35rem;
    color: #fff;
    background: #26343b;
    font-size: 0.72rem;
    line-height: 1;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transform: translateY(2px);
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.btn-close:hover::after,
.btn-close:focus-visible::after {
    opacity: 1;
    transform: translateY(0);
}

.badge {
    border-radius: 0.45rem !important;
    padding: 0.35em 0.6em;
    font-weight: 600;
    letter-spacing: 0.01em;
}

.badge.bg-success,
.bg-success {
    background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important;
}

.badge.bg-warning,
.bg-warning {
    background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important;
}

.badge.bg-danger,
.bg-danger {
    background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important;
}

.badge.bg-info,
.bg-info {
    background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important;
}

.badge.bg-primary,
.bg-primary {
    background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important;
}

.btn-content-safe {
    width: auto !important;
    min-width: max-content;
    height: auto !important;
    min-height: 36px !important;
    padding: 0.42rem 0.75rem !important;
    font-size: 0.8125rem !important;
    line-height: 1.2 !important;
    white-space: nowrap;
}

.btn-content-wrap {
    min-height: 40px !important;
    padding: 0.5rem 0.85rem !important;
    line-height: 1.25 !important;
    white-space: normal;
}

#tour-planner-views {
    margin-bottom: 0.75rem !important;
    padding: 0.5rem 0.75rem;
}

#tour-planner-views .btn-group {
    height: auto !important;
    min-height: 38px;
}

.admin-action-cell {
    vertical-align: middle !important;
    text-align: right;
}

.admin-action-group {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.admin-audit-btn {
    min-width: 72px;
}

/* Every dropdown menu in the app uses this shared elevation instead of Bootstrap's default .shadow
   utility (a flatter, generic box-shadow that didn't match the softer, theme-aware depth used on
   cards/modals elsewhere) - individual dropdowns no longer need their own `shadow` class or an
   inline box-shadow override. */
.dropdown-menu {
    box-shadow: var(--nt-card-shadow) !important;
}

.dropdown-menu .dropdown-item {
    min-height: 38px;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-top: 0.45rem;
    padding-bottom: 0.45rem;
}

.dropdown-menu .dropdown-item .bi {
    margin-right: 0 !important;
}

.status-active,
.status-completed,
.status-accepted {
    color: #fff;
    background-color: var(--nt-success) !important;
}

.status-deferred,
.status-expired,
.status-pending {
    color: #2c2415;
    background-color: var(--nt-warning) !important;
}

.status-shared,
.status-sent {
    color: #fff;
    background-color: var(--nt-info) !important;
}

.status-cancelled,
.status-declined {
    color: #fff;
    background-color: var(--nt-neutral) !important;
}

.status-deleted {
    color: #fff;
    background-color: var(--nt-danger) !important;
}

.card,
.app-card {
    border: 1px solid var(--nt-card-border);
    border-radius: 0.75rem !important;
    background-color: var(--nt-card-bg);
    box-shadow: var(--nt-card-shadow);
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}

.app-card,
.dashboard-card {
    border: 1px solid var(--nt-card-border) !important;
    background-color: var(--nt-card-bg) !important;
}

.card > .card-body {
    padding: 1.25rem;
}

.card > .card-header,
.card > .card-footer {
    border-color: var(--nt-card-border);
}

.app-card > .card-body,
.dashboard-card > .card-body {
    padding: 1.25rem;
}

.dashboard-card {
    cursor: default;
}

.dashboard-card:hover,
.transition-hover:hover {
    transform: translateY(-4px);
    border-color: rgba(var(--bs-primary-rgb), 0.25) !important;
    box-shadow: var(--nt-card-shadow-hover) !important;
}

.nt-chart-gridlines {
    background-image: repeating-linear-gradient(to top,
        transparent 0,
        transparent calc(25% - 1px),
        var(--bs-border-color) calc(25% - 1px),
        var(--bs-border-color) 25%);
}

.dashboard-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    margin-bottom: 1rem;
    border-radius: 0.75rem;
    font-size: 1.45rem;
}

.table {
    --bs-table-striped-bg: var(--nt-row-stripe);
    --bs-table-hover-bg: var(--nt-row-hover);
    border-color: var(--bs-border-color);
}

.table > tbody > tr > td {
    padding-top: 0.7rem;
    padding-bottom: 0.7rem;
    transition: background-color 0.16s ease, box-shadow 0.16s ease;
}

.table > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 2;
    color: var(--bs-secondary-color);
    background: var(--bs-tertiary-bg);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    box-shadow: inset 0 -1px 0 var(--bs-border-color);
}

/* Sortable column headers (Admin's paged tables, ~40 columns across all entity tables) only ever
   had cursor:pointer as a clickability hint, with no actual hover/focus feedback - the sort
   icon alone was easy to read as decorative rather than interactive. Matches on the inline
   cursor:pointer style every sortable <th> already carries instead of requiring a dedicated class
   on every one of them. */
.table > thead > tr > th[style*="cursor: pointer"]:hover {
    background: var(--nt-row-hover);
    color: var(--bs-body-color);
}

.table > thead > tr > th[style*="cursor: pointer"]:focus-visible {
    outline: 2px solid var(--nt-primary);
    outline-offset: -2px;
}

.table > tbody > tr:nth-of-type(even) > * {
    background-color: var(--nt-row-stripe);
}

.table-hover > tbody > tr:hover > * {
    background-color: var(--nt-row-hover) !important;
    box-shadow: inset 0 1px 0 rgba(var(--bs-primary-rgb), 0.08),
        inset 0 -1px 0 rgba(var(--bs-primary-rgb), 0.08);
}

.modal-content {
    overflow: hidden;
    border: 1px solid var(--nt-card-border) !important;
    border-radius: 0.75rem !important;
    background-color: var(--nt-card-bg);
    box-shadow: 0 18px 50px rgba(16, 37, 46, 0.22);
    animation: nt-modal-enter 0.18s ease-out;
}

/* TimePlanner's Add Task modal (see its own inline comment in TimePlanner.razor) - Bootstrap sizes
   a modal to fit its content, not the available viewport, so on desktop (above the md breakpoint,
   where modal-fullscreen-md-down no longer forces full height) it rendered noticeably shorter than
   the screen with a lot of unused space below the quick-task list. Stretching .modal-content itself
   (rather than .modal-dialog) keeps modal-dialog-centered's centering math correct while still
   giving .modal-body genuine room to show more quick tasks at once without scrolling as often.
   max-height only (not a fixed height) deliberately - a fixed height was tried first and forced the
   modal to ALWAYS be 85vh tall regardless of which tab was active, leaving a large empty gap below
   "Enable Shift Recurrence" whenever the current tab (Custom Task, or Quick Tasks/Groups with only a
   few items) didn't actually need that much room. max-height lets the modal size to its real content
   - short when there's little, up to 85vh when there's enough (e.g. a long quick-task list) to
   warrant it, scrolling via the existing modal-dialog-scrollable class past that point. */
@media (min-width: 768px) {
    .planner-add-task-modal .modal-content {
        max-height: 85vh;
    }
}

.modal-header {
    min-height: 4.25rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 1px solid var(--nt-card-border);
    background: linear-gradient(135deg, var(--nt-primary-soft), transparent 72%);
}

.modal-header .modal-title {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    margin: 0;
    color: var(--bs-body-color);
}

.modal-header .modal-title > .bi {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    margin: 0 !important;
    border-radius: 0.65rem;
    color: var(--nt-primary) !important;
    background: rgba(var(--bs-primary-rgb), 0.12);
}

.modal-header.bg-primary .modal-title,
.modal-header.bg-success .modal-title,
.modal-header.bg-danger .modal-title,
.modal-header.bg-dark .modal-title {
    color: #fff;
}

.modal-header.bg-primary .modal-title > .bi,
.modal-header.bg-success .modal-title > .bi,
.modal-header.bg-danger .modal-title > .bi,
.modal-header.bg-dark .modal-title > .bi {
    color: #fff !important;
    background: rgba(255, 255, 255, 0.16);
}

.modal-header-copy {
    min-width: 0;
}

.modal-header-description {
    display: block;
    margin-top: 0.15rem;
    color: var(--bs-secondary-color);
    font-size: 0.78rem;
    font-weight: 400;
    line-height: 1.3;
}

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 7rem;
    padding: 1.5rem;
    color: var(--bs-secondary-color);
    text-align: center;
    border: 1px dashed var(--nt-card-border);
    border-radius: 0.75rem;
    background: var(--nt-surface-muted);
}

.empty-state.compact {
    min-height: 4.5rem;
    padding: 1rem;
}

.empty-state > .bi {
    color: var(--nt-primary);
    font-size: 1.65rem;
}

.empty-state-title {
    color: var(--bs-body-color);
    font-weight: 600;
}

/* Administration panel's "Getting Started" checklist (ward -> beds -> invite staff) - see
   Admin.razor's ShowSetupChecklist. */
.setup-checklist-card {
    background: var(--nt-surface-muted);
    border: 1px solid var(--nt-card-border) !important;
}

.setup-checklist-step {
    border: 1px solid var(--nt-card-border);
    border-radius: 0.75rem;
    background: var(--nt-card-bg);
    transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.setup-checklist-step:hover {
    border-color: var(--nt-primary);
    transform: translateY(-1px);
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.06);
}

.setup-checklist-step-done {
    opacity: 0.75;
}

.nav-link,
.dropdown-menu,
.collapse,
.collapsing,
.page-toolbar {
    transition-duration: 0.18s !important;
    transition-timing-function: ease !important;
}

@keyframes nt-modal-enter {
    from {
        opacity: 0;
        transform: translateY(8px) scale(0.985);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

@keyframes nt-result-fade-in {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.nt-result-fade-in {
    animation: nt-result-fade-in 0.25s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .btn,
    .card,
    .app-card,
    .nav-link,
    .dropdown-menu,
    .collapse,
    .collapsing,
    .page-toolbar,
    .modal-content,
    .nt-result-fade-in {
        animation: none !important;
        transition: none !important;
    }
}

.page-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
    padding: 0.75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.75rem;
    background: var(--nt-card-bg);
    box-shadow: 0 2px 8px rgba(20, 56, 70, 0.06);
}

.page-toolbar-primary,
.page-toolbar-secondary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.clinical-page-header-copy {
    min-width: 0;
}

.clinical-page-header-title {
    margin: 0;
    color: var(--bs-body-color);
    font-size: 1rem;
    line-height: 1.15;
    font-weight: 700;
}

.clinical-page-header-description {
    display: block;
    max-width: 48rem;
    margin-top: 0.1rem;
    color: var(--bs-secondary-color);
    font-size: 0.75rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-icon-active {
    display: none;
}

.sidebar .nav-link.active .nav-icon-outline {
    display: none;
}

.sidebar .nav-link.active .nav-icon-active {
    display: inline-block;
}

.sidebar .nav-link.active {
    color: #fff !important;
    background-color: var(--nt-primary) !important;
    box-shadow: 0 4px 10px rgba(var(--bs-primary-rgb), 0.2) !important;
    position: relative;
}

/* An additional "you are here" cue beyond the fill color alone - most useful where the filled pill
   is narrower or partly obscured (a collapsed/icon-only sidebar state, the MAUI app's tighter nav
   width). A bright accent bar reads clearly even at a glance or in peripheral vision. */
.sidebar .nav-link.active::before {
    content: "";
    position: absolute;
    left: -0.5rem;
    top: 15%;
    bottom: 15%;
    width: 4px;
    border-radius: 0 4px 4px 0;
    background: #fff;
}

/* :not(.active) is required, not cosmetic - clicking a nav link both activates it (Blazor's NavLink
   routing) AND gives it native browser focus at the same moment. Without this exclusion, this rule
   has the exact same specificity as ".sidebar .nav-link.active" below and comes later in the
   cascade, so it silently won and painted the just-clicked, now-active item with this pale "soft"
   hover color instead of the solid active color - looking barely visible/greyed out immediately
   after every click, until focus moved elsewhere (switching windows, a hard reload) and let the
   .active rule apply cleanly again. */
.sidebar .nav-link:not(.active):hover,
.sidebar .nav-link:not(.active):focus {
    background-color: var(--nt-primary-soft) !important;
}

.nav-tabs .nav-link {
    color: var(--bs-secondary-color);
    font-weight: 500;
}

.nav-tabs .nav-link.active {
    color: var(--nt-primary);
    border-bottom-color: var(--nt-primary);
    font-weight: 600;
}

/* Administration panel's two-row grouped navigation (User Management, Organisation Setup, System,
   Location Setup as row 1; that group's own tabs as row 2) - see Admin.razor's
   GetAdminMenuGroups/SelectAdminGroup. Both rows share this same class deliberately, so row 2 looks
   identical to row 1 rather than reading as a visually distinct sub-level. */
.admin-menu-bar {
    border-bottom: 1px solid var(--bs-border-color);
}

.admin-menu-bar .nav-link {
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--bs-secondary-color);
    font-weight: 500;
    padding-bottom: 0.6rem;
}

.admin-menu-bar .nav-link.active {
    color: var(--nt-primary);
    border-bottom-color: var(--nt-primary);
    font-weight: 600;
}

.admin-menu-bar .nav-link:hover,
.admin-menu-bar .nav-link:focus {
    color: var(--nt-primary);
}

/* Row 2 (the active group's own tabs) sits directly under row 1 with only a hairline gap - close
   enough to read as "these belong to the group above," without a heavier visual treatment that
   would contradict the two rows deliberately looking alike. */
.admin-menu-subbar {
    margin-top: 0.35rem;
}

@media (max-width: 768px) {
    .clinical-page-header-description {
        display: none;
    }

    .page-toolbar,
    .page-toolbar-primary,
    .page-toolbar-secondary {
        width: 100%;
    }
}

/* MAUI's WebView does not consistently implement the browser Fullscreen API. The
   planner still needs a reliable app-level distraction-free mode. */
body.nursing-tools-planner-fullscreen .top-navbar,
body.nursing-tools-planner-fullscreen > .container-fluid > .d-flex > .sidebar {
    display: none !important;
}
body.nursing-tools-planner-fullscreen .main-content {
    width: 100% !important;
    height: 100vh !important;
    height: 100dvh !important;
    max-height: 100vh !important;
    max-height: 100dvh !important;
    overflow: hidden !important;
    overscroll-behavior: none;
}
body.nursing-tools-planner-fullscreen,
body.nursing-tools-planner-fullscreen > .container-fluid,
body.nursing-tools-planner-fullscreen > .container-fluid > .d-flex {
    height: 100vh !important;
    height: 100dvh !important;
    max-height: 100vh !important;
    max-height: 100dvh !important;
    overflow: hidden !important;
}
body.nursing-tools-planner-fullscreen #timeplanner-root {
    height: 100% !important;
    max-height: 100% !important;
    overflow: hidden !important;
    overscroll-behavior: none;
}

/* Stacked directly above .planner-mobile-fab's own bottom-right corner (20px/56px tall, so 20+56+12
   gap = 88px) rather than pinned to the viewport's LEFT edge - "left: 1rem" ignored the persistent
   260px .sidebar entirely (position:fixed is relative to the viewport, not .main-content), so on any
   screen wide/large enough to keep the sidebar visible (ordinary desktop, and any tablet not narrow
   enough to fall into the mobile-overlay sidebar breakpoint) this button rendered on top of the
   sidebar's own bottom nav items instead of near the planner content, reading as "why is scroll-to-
   top in the menu area". Right-anchoring next to the other floating planner control avoids the
   sidebar column entirely regardless of its width/collapsed state. */
.planner-scroll-top {
    position: fixed;
    right: 20px;
    bottom: 88px;
    width: 42px;
    height: 42px;
    z-index: 1055;
}

.planner-fullscreen-exit {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 2000;
}

/* Grid/Timeline view toggle, relocated here from the main toolbar below the md breakpoint (see
   TimePlanner.razor) - real estate is too tight for it in the toolbar on a phone/MAUI-width screen. */
.planner-mobile-view-bar {
    padding: 0.5rem 0.75rem calc(0.5rem + env(safe-area-inset-bottom, 0px));
    z-index: 1020; /* below planner-scroll-top/modals/offcanvas, above ordinary page content */
}

.planner-mobile-view-bar .btn {
    border-radius: 0.6rem;
}

/* Grid/Timeline toggle buttons specifically, now sharing the consolidated bottom row with the
   scroll-to-top and Add Task icon buttons (see TimePlanner.razor) - tighter than the shared
   .btn-content-safe sizing used everywhere else so all four controls comfortably fit one row on a
   narrow phone screen. */
.planner-mobile-view-btn {
    padding: 0.3rem 0.4rem !important;
    font-size: 0.75rem !important;
    min-height: 36px !important;
    white-space: nowrap;
}

/* .planner-scroll-top/.planner-mobile-fab only ever render at these narrow widths in FullScreenMode
   (ordinary mode hides both below md in favour of .planner-mobile-view-bar's own inline controls) -
   and .planner-mobile-view-bar itself never renders in FullScreenMode either (see TimePlanner.razor),
   so there's no bar for the stacked bottom-right pair to clear here; their normal 20px/88px bottom
   offsets already avoid it without a narrow-width-specific shift. */
@media (max-width: 767.98px) {
    /* Keeps the last panel/row on the page from being hidden behind the fixed
       .planner-mobile-view-bar footprint at the very bottom of the viewport. */
    #timeplanner-root {
        padding-bottom: calc(3.75rem + env(safe-area-inset-bottom, 0px));
    }
}

/* The mobile/desktop split above (.planner-mobile-view-bar, .planner-scroll-top,
   .planner-mobile-fab) is driven by Bootstrap's d-md-none/d-md-inline-flex/d-md-flex utilities,
   which key purely off viewport WIDTH (the 768px "md" breakpoint) - a phone or 7" tablet turned to
   LANDSCAPE can easily cross that width while still being a small touchscreen, not a real desktop
   browser. That flipped the bottom bar to the scattered desktop layout (separate floating
   scroll-top/Add Task buttons, no consolidated Grid/Timeline toggle) mid-session just from rotating
   the device - reported as "why does the bottom action bar change?". (pointer: coarse) matches
   touch capability instead of width, so this keeps the mobile layout on any touchscreen regardless
   of orientation-driven width changes; a real mouse/trackpad desktop browser (pointer: fine) is
   untouched. Scoped to the .d-md-* combination specifically so fullscreen mode - which already
   drops those Bootstrap classes via Razor so these controls stay visible on every device - isn't
   affected by this override at all. Capped at 1023.98px to stay in lockstep with the sidebar's own
   matching "(pointer: coarse) and (max-width: 1023.98px)" override further down - without the same
   cap here, a genuinely large (10"+) touch tablet would get the desktop sidebar but still this
   mobile bottom bar, reintroducing the exact overlap this was meant to fix, just at a different
   width. */
@media (pointer: coarse) and (max-width: 1023.98px) {
    .planner-mobile-view-bar.d-md-none {
        display: block !important;
    }

    .planner-scroll-top.d-md-inline-flex {
        display: none !important;
    }

    .planner-mobile-fab.d-md-flex {
        display: none !important;
    }
}

.top-navbar {
    background-color: var(--bs-body-bg) !important;
    border-bottom: 1px solid var(--bs-border-color);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03); /* Subtle shadow to separate it */
}

/* ==========================================================================
   Blazor Form Validations & Error Boundaries
   Previously just a colored outline with no icon or inline message styling -
   the rest of the app's forms are considerably more polished than this.
   ========================================================================== */
.valid.modified:not([type=checkbox]) {
    outline: none;
    border-color: var(--nt-success) !important;
    box-shadow: 0 0 0 1px var(--nt-success);
}

.invalid {
    outline: none;
    border-color: var(--nt-danger) !important;
    box-shadow: 0 0 0 1px var(--nt-danger);
    background-color: rgba(var(--bs-danger-rgb), 0.05);
}

.validation-message {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--nt-danger);
    font-size: 0.8rem;
    font-weight: 600;
    margin-top: 0.35rem;
}

.validation-message::before {
    font-family: "bootstrap-icons" !important;
    content: "\f33a"; /* bi-exclamation-triangle-fill */
    font-size: 0.85rem;
    flex-shrink: 0;
}

/* Restyled to match the app's clinical palette instead of the framework's default lightyellow
   banner, which clashed badly next to the rest of the design system. */
#blazor-error-ui {
    background: var(--nt-danger);
    color: #fff;
    bottom: 0;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.25);
    box-sizing: border-box;
    display: none;
    align-items: center;
    gap: 0.6rem;
    left: 0;
    padding: 0.75rem 3rem 0.75rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 2500;
    font-weight: 600;
    font-size: 0.9rem;
}

    #blazor-error-ui::before {
        font-family: "bootstrap-icons" !important;
        content: "\f33a"; /* bi-exclamation-triangle-fill */
        font-size: 1.1rem;
        flex-shrink: 0;
    }

    #blazor-error-ui .reload {
        color: #fff;
        text-decoration: underline;
        font-weight: 700;
        margin-left: 0.25rem;
    }

    #blazor-error-ui .reload:hover {
        color: rgba(255, 255, 255, 0.85);
    }

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 1rem;
        top: 50%;
        transform: translateY(-50%);
        width: 26px;
        height: 26px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.15);
        font-size: 0.75rem;
        transition: background-color 150ms ease;
    }

    #blazor-error-ui .dismiss:hover {
        background: rgba(255, 255, 255, 0.3);
    }

.blazor-error-boundary {
    background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMTQgMjU5LjQ3MyA1My42Mjk0IDI1OS45NjEgNTIuNzk4NSAyNjAuNDA3IDUzLjYyODNaTTI2My41ODYgNjYuMDE4M0MyNjAuNzM3IDY2LjAxODMgMjU5LjMxMyA2Ny4xMjQ1IDI1OS4zMTMgNjkuMzM3IDI1OS4zMTMgNjkuNjEwMiAyNTkuMzMyIDY5Ljg2MDggMjU5LjM3MSA3MC4wODg3TDI2MS43OTUgODQuMDE2MSAyNjUuMzggODQuMDE2MSAyNjcuODIxIDY5Ljc0NzVD") no-repeat;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}
.qr-svg-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 1rem auto;
}

/* Shared Planner uses the same planner typography, scale and component rules above. Only its
   share-page fullscreen shell needs a distinct layout rule. */
.shared-fullscreen-mode > .card:not(.shared-planner-content),
.shared-fullscreen-mode > .alert {
    display: none !important;
}

.shared-fullscreen-mode .shared-planner-content {
    margin: 0 !important;
    border-radius: 0 !important;
    height: 100vh;
    overflow: auto;
}

.qr-svg-wrapper svg {
    max-width: 200px !important;
    max-height: 200px !important;
    width: 100% !important;
    height: 100% !important;
    display: block;
}

/* ========================================================
   GRID TIMELINE LAYOUT SCALES (REQ 2 & REQ 3)
   ======================================================== */

/* Compact Presets */
.grid-scale-Compact th,
.grid-scale-Compact td {
    padding: 0.15rem 0.25rem !important;
    font-size: 0.72rem !important;
}

.grid-scale-Compact .btn-sm {
    padding: 0.1rem 0.3rem !important;
    font-size: 0.68rem !important;
}

/* Normal Presets (Default) */
.grid-scale-Normal th,
.grid-scale-Normal td {
    padding: 0.5rem 0.5rem !important;
    font-size: 0.85rem !important;
}

/* Spacious Presets */
.grid-scale-Spacious th,
.grid-scale-Spacious td {
    padding: 1rem 0.75rem !important;
    font-size: 0.98rem !important;
}

.grid-scale-Spacious .btn-sm {
    padding: 0.35rem 0.75rem !important;
    font-size: 0.9rem !important;
}

/* Timeline's own hour-label/task-chip/location-label previously hardcoded fixed inline font-sizes
   in TimePlanner.razor regardless of GridScale - inline styles always beat a class selector's
   specificity, so the grid-scale-@GridScale class already present on Timeline's own <table> (see
   the Grid rules above, which DO apply to Timeline via the shared th/td rule) had no effect on
   these specific elements, reported as "Timeline doesn't obey spacious/view settings". Markup now
   uses these classes with no inline font-size instead. */
.grid-scale-Compact .planner-timeline-hour-label,
.grid-scale-Compact .planner-timeline-task-chip {
    font-size: 0.7rem !important;
}
.grid-scale-Compact .planner-timeline-task-location {
    font-size: 0.55rem !important;
}

.grid-scale-Normal .planner-timeline-hour-label,
.grid-scale-Normal .planner-timeline-task-chip {
    font-size: 0.8rem !important;
}
.grid-scale-Normal .planner-timeline-task-location {
    font-size: 0.62rem !important;
}

.grid-scale-Spacious .planner-timeline-hour-label,
.grid-scale-Spacious .planner-timeline-task-chip {
    font-size: 0.92rem !important;
}
.grid-scale-Spacious .planner-timeline-task-location {
    font-size: 0.72rem !important;
}

/* Task chips: shorter/squarer boxes that wrap up to 3 per column instead of one full-width row each.
   Each chip's flex-basis is computed inline (GetTaskChipFlexBasis) from how many tasks share the
   cell: 100% / 50% / 33% (capped at 33%), so a 4th+ task wraps to the next row rather than
   shrinking further. Task text itself is truncated to a single line with an ellipsis rather than
   wrapping - a chip that's narrower than its text (e.g. 3 per row under Fit to Screen) used to
   wrap word-by-word until it ran out of room and started breaking individual characters onto
   their own line. The full text is available via the chip's title attribute (hover) or by
   clicking the chip to open it (EditTaskNotes). */
.planner-task-list {
    align-content: flex-start;
    /* Overrides the markup's own .gap-1 (0.25rem) - adjacent chips sitting nearly flush with only a
       thin border between them made a busy cell read as one solid block rather than distinct tasks. */
    gap: 0.4rem !important;
}

.planner-task-chip {
    min-width: 54px;
    box-sizing: border-box;
    line-height: 1.15;
    align-items: center !important;
    /* A soft shadow gives each chip a little lift/separation from the cell background and its
       neighbours, beyond what the existing 1px border alone provides. */
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    /* Bootstrap's default .border colour (--bs-border-color, a very light grey) all but disappeared
       against the grid's own background, especially for a chip with no custom colour set - reported
       as tasks with a white/default background "blending in" to the planner. A darker, still-subtle
       border colour (not a heavier width) keeps every chip's edge legible without reading as bold. */
    border-color: rgba(0, 0, 0, 0.22) !important;
}

/* The complete/incomplete checkbox icon uses fixed Bootstrap colors (green check, gray circle),
   which can blend into a custom task background of a similar shade. A small neutral backdrop keeps
   it visible against any background color rather than trying to compute a contrast color for it.

   The shared .btn/.btn-sm rules (above) set a min-height (38px/34px) but no matching min-width -
   fine for a labelled button that's wider than that anyway, but this is an icon-only toggle that
   hugs its ~15px icon horizontally via p-0. Without an explicit square size it renders as a tall,
   narrow rectangle instead of a compact square hit target. Sized per grid-scale like every other
   density-aware control in this file (Compact/Spacious overrides below); Normal's baseline size
   also covers the Timeline view, which shares the same grid-scale-@GridScale class on its table. */
.planner-task-checkbox-btn {
    background-color: rgba(255, 255, 255, 0.85) !important;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
    width: 22px;
    height: 22px;
    min-width: 22px !important;
    min-height: 22px !important;
}

.grid-scale-Compact .planner-task-checkbox-btn {
    width: 18px;
    height: 18px;
    min-width: 18px !important;
    min-height: 18px !important;
}

.grid-scale-Spacious .planner-task-checkbox-btn {
    width: 26px;
    height: 26px;
    min-width: 26px !important;
    min-height: 26px !important;
}

/* A small grip glyph makes drag-and-drop between slots/beds discoverable (Grid view chips only -
   only those are draggable="true"). Done via ::before rather than markup so it doesn't need to be
   threaded through every chip variant individually - uses the bootstrap-icons glyph for
   bi-grip-vertical (\f3fe) since the chip itself doesn't have an <i> element free for the purpose. */
.planner-task-chip[draggable="true"] {
    padding-left: 14px !important;
}
.planner-task-chip[draggable="true"]::before {
    content: "\f3fe";
    font-family: "bootstrap-icons" !important;
    position: absolute;
    left: 2px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.6rem;
    color: rgba(0, 0, 0, 0.3);
    pointer-events: none;
    cursor: grab;
}

.planner-task-chip .planner-task-text {
    white-space: nowrap !important;
    overflow: hidden;
    text-overflow: ellipsis;
    display: inline-block;
    flex: 1 1 auto;
    min-width: 0;
}

/* The task-text span carries Bootstrap's .text-body (and .text-muted when completed) classes, which
   in Bootstrap 5 declare `color` with !important directly on the element - that beats the chip's own
   inline `color: X !important` set by GetTaskStyle for auto-contrast, since CSS inheritance is always
   the weakest source of a value and any direct declaration (even from an external stylesheet) wins
   over an inherited one, regardless of importance. This selector is more specific than a single class
   selector, so it reclaims the inherited (contrast-correct) color instead. Completed tasks lose the
   text-muted "grayed out" look as a result, so opacity below stands in for that instead. */
.planner-task-chip .planner-task-text,
.planner-timeline-task-chip .planner-timeline-task-text {
    color: inherit !important;
}

.planner-task-chip .planner-task-text.text-decoration-line-through,
.planner-timeline-task-chip .planner-timeline-task-text.text-decoration-line-through {
    opacity: 0.7;
}

/* Priority reflected as a colored top-edge stripe on the whole chip (not just the small flag icon)
   so high/critical items are scannable at a glance without reading each chip - kept on the top edge
   specifically so it stays visually distinct from a custom task color's own border, which still
   shows in full on all four sides. Computed inline in GetTaskStyle (TimePlanner.razor.cs) rather
   than via this CSS attribute selector, since it needs to win a cascade tie-break against that
   custom-color border, which a plain stylesheet rule (even !important) can't do against inline
   styles. */

/* Overdue signal: the markup applies "overdue-pulsate" (see GetTaskStyle/IsTaskOverdue call sites
   in TimePlanner.razor). The animated pulse (box-shadow keyframe) was removed after user feedback
   that it was too distracting - the static border-danger/bg-danger classes plus this small corner
   badge are enough to make an overdue task unmistakable without a constant animation. */
.overdue-pulsate {
    position: relative;
}

.overdue-pulsate::after {
    content: "!";
    position: absolute;
    top: -6px;
    right: -6px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--nt-danger);
    color: #fff;
    font-size: 0.65rem;
    font-weight: 700;
    line-height: 15px;
    text-align: center;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    pointer-events: none;
}

/* Completion feedback: a quick "pop" on the checkmark icon and a brief fade as the strike-through
   text settles in, so marking a task done gives clearer tactile feedback (especially on touch
   devices where there's no hover state to rely on). Pure CSS - replays automatically whenever the
   icon/class changes because Blazor swaps bi-circle -> bi-check-circle-fill and adds the
   line-through class on the same render that flips IsCompleted. */
@keyframes taskCheckPop {
    0% { transform: scale(0.4); opacity: 0.3; }
    60% { transform: scale(1.3); }
    100% { transform: scale(1); opacity: 1; }
}
.planner-task-chip .bi-check-circle-fill,
.planner-timeline-task-chip .bi-check-circle-fill {
    display: inline-block;
    animation: taskCheckPop 0.35s ease-out;
}

/* Moved-task feedback (drag-and-drop or Bed Swap): a task chip previously just reappeared in its
   new cell with no transition at all, which could read as "did that actually register?". A brief
   highlight fade ties this into the same completion-feedback interaction language as
   taskCheckPop above, instead of introducing an unrelated new motion style. Backed by
   _recentlyMovedTaskIds (TimePlanner.razor.cs) - added on drop/swap, removed automatically after
   this animation's own duration. */
@keyframes taskMovedHighlight {
    0% { box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), 0.5); }
    100% { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
}

.planner-task-chip.just-moved {
    animation: taskMovedHighlight 0.7s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .planner-task-chip.just-moved {
        animation: none !important;
    }
}

@keyframes taskCompleteFade {
    0% { opacity: 1; }
    50% { opacity: 0.35; }
    100% { opacity: 0.75; }
}
.planner-task-chip .planner-task-text.text-decoration-line-through,
.planner-timeline-task-chip .planner-timeline-task-text.text-decoration-line-through {
    animation: taskCompleteFade 0.5s ease-out;
}

/* Chip text was inheriting the Compact grid's blanket 0.72rem cell font-size (.grid-scale-Compact
   td above - tuned for fitting headers/hour labels into a dense grid, not for reading task
   descriptions) since that rule's !important cascades over the chip's own inline font-size. Bumped
   specifically for the chip so Compact stays information-dense everywhere else while task text
   remains legible. */
.grid-scale-Compact .planner-task-chip {
    padding: 0.15rem 0.3rem !important;
    font-size: 0.78rem !important;
}

/* table-layout:fixed is applied to the grid table UNCONDITIONALLY (not just under Fit to Screen):
   in the default auto layout, a declared column width is only a hint - the browser will still
   widen a column beyond it if that column's cell content (e.g. the Hour column's checkbox + time
   text) needs more room, which is exactly why the Hour column used to render wider outside Fit to
   Screen. Fixed layout makes column widths strictly authoritative regardless of content, so the
   Hour column's 70px (see .planner-hour-column) now holds steady in both modes. This does not
   affect the "scroll to see more beds" behavior - the table can still be wider than its container
   and overflow into table-responsive's horizontal scrollbar either way. */
.planner-grid-table {
    table-layout: fixed;
}

/* Handover Notes panel: a soft info-tinted surface (not just the shared border-info accent, which
   any panel could equally use) so the most clinically important carry-over information between
   shifts is unmistakably distinct from every other collapsible panel. */
.planner-handover-panel {
    background-color: rgba(var(--bs-info-rgb), 0.06);
}

/* Hour column typography: this is the busiest column in the grid (every row repeats it), so the
   time itself should read as the dominant element at a glance, with the "mark hour complete"
   checkbox as a secondary, lower-contrast control until it's actually interacted with or checked. */
.planner-hour-column {
    line-height: 1.1;
}

.planner-hour-column .form-check-input {
    opacity: 0.7;
    transition: opacity 0.15s ease;
}

.planner-hour-column .form-check-input:hover,
.planner-hour-column .form-check-input:checked,
.planner-hour-column .form-check-input:focus {
    opacity: 1;
}


/* Stronger column separation for the bed-by-bed grid - table-bordered's shared 1px cell border is
   easy to lose track of when scanning straight down a tall column, especially once several bed
   columns require horizontal scrolling. Hour column excluded since it already has its own sticky
   left-edge treatment. */
.planner-grid-table > thead > tr > th.planner-bed-header,
.planner-grid-table > tbody > tr > td:not(.planner-hour-column) {
    border-left-width: 2px !important;
}

/* "Fit to Screen" grid mode: squeeze all bed columns into the viewport width instead of letting
   the table overflow horizontally (the default, which requires scrolling on small/mobile screens). */
.planner-fit-to-screen {
    width: 100%;
}

.planner-fit-to-screen th,
.planner-fit-to-screen td {
    /* overflow-wrap alone is enough to make text wrap within the narrower Fit to Screen columns -
       it doesn't need overflow:hidden to work. overflow:hidden here was clipping the audit-hover
       tooltip popup (an absolutely-positioned ::after that intentionally escapes the cell's box to
       render above the chip), hiding it whenever the tooltip needed to overlap the row/header above. */
    overflow-wrap: break-word;
}

/* Bed column headers carry a fixed predefined width (.planner-bed-header) so that, by default,
   columns keep a consistent size regardless of viewport and the user scrolls horizontally to see
   them all. In "Fit to Screen" mode that fixed width is overridden so table-layout:fixed can
   distribute all bed columns evenly across the available width instead. */
.planner-fit-to-screen .planner-bed-header {
    width: auto !important;
    /* A floor, not zero - below this, task text has no room to wrap at word boundaries and
       degrades into splitting individual characters onto their own line. If there are enough
       bed columns that even this minimum doesn't fit the viewport, the table-responsive wrapper
       still provides a horizontal scrollbar for the overflow rather than squeezing further. */
    min-width: 120px !important;
}

.planner-fit-to-screen .planner-task-chip {
    min-width: 0;
}

/* The Hour column used to only get squeezed narrow under Fit to Screen (table-layout:fixed forces
   its declared width exactly; the default auto layout let it stretch wider). The user liked that
   narrow width and wants it permanently, in every mode - so it's now a fixed width regardless of
   table-layout. */
.planner-hour-column {
    width: 70px !important;
    min-width: 70px !important;
    max-width: 70px !important;
}

/* On a narrow/phone-width viewport, the DEFAULT (non-Fit-to-Screen) grid shows ONE bed's worth of
   screen at a time instead of a fixed 180px per column - each bed column sized to fill the visible
   width, so the user reaches the next bed with a normal horizontal scroll/swipe rather than seeing
   several columns squeezed too narrow to read (the old fixed-180px default) or needing to switch
   into "Single" bed mode just to get a comfortably-sized column. 100vw minus .planner-hour-column's
   own 70px, minus a small safety margin for the grid's own padding/border so a column doesn't
   overshoot the viewport by those few extra pixels and force a sliver of unwanted scroll of its
   own. Explicitly scoped to NOT apply once .planner-fit-to-screen is active on the same table - that
   mode intentionally does the opposite (shrink every column to fit them all on screen at once). */
@media (max-width: 767.98px) {
    .planner-grid-table:not(.planner-fit-to-screen) .planner-bed-header {
        width: calc(100vw - 70px - 20px) !important;
        min-width: calc(100vw - 70px - 20px) !important;
    }
}

/* A phone rotated to landscape roughly doubles its portrait width (e.g. ~915px on a Galaxy S24
   Ultra) - past the 767.98px cutoff above, columns fell all the way back to the small fixed
   180px default, fitting 4+ beds on screen at once instead of the intended "about one bed's worth
   of screen" design this whole file is built around. Halving the same one-bed-per-screen formula
   instead targets roughly two beds per screen for this specific width band, which is what a
   landscape phone's actual doubled width comfortably fits without over-cramming. Genuinely
   larger devices (1024px+, ~10" tablets and up) fall through to the normal fixed 180px width. */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .planner-grid-table:not(.planner-fit-to-screen) .planner-bed-header {
        width: calc((100vw - 70px - 20px) / 2) !important;
        min-width: calc((100vw - 70px - 20px) / 2) !important;
    }
}


/* ========================================================
   STICKY GRID HEADER ROW + FIRST COLUMN
   Pins the bed-header row to the top and the Hour column to the left, Excel-freeze-pane style, so
   neither is lost while scrolling a long shift. This used to rely on .main-content being the
   nearest scrolling ancestor, but Bootstrap's .table-responsive wrapper (needed for the grid's
   horizontal scroll) sets overflow-x:auto - which per the CSS spec silently forces overflow-y to
   compute as 'auto' too, making .planner-grid-scroll itself the nearest scroll container and
   stealing the sticky positioning context away from .main-content. Since .planner-grid-scroll had
   no bounded height, it never actually scrolled internally, so top/bottom stickiness silently did
   nothing (most visible on mobile, where the page needs to scroll vertically far more often).
   nursingToolsPinPlannerGridHeight (print-helper.js) now gives it a JS-computed max-height sized to
   exactly fill the remaining viewport, making it a genuine self-contained scroll container without
   leaving .main-content anything left to scroll - avoiding the redundant double-scrollbar an
   earlier fixed-height attempt produced.
   ======================================================== */
.planner-grid-scroll thead th {
    position: sticky;
    top: 0;
    z-index: 10;
}

.planner-grid-scroll tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 5;
}

.planner-grid-scroll thead th:first-child {
    /* Top-left corner cell is sticky in both directions at once - needs to sit above both the
       plain top-sticky header cells and the plain left-sticky first-column cells. */
    z-index: 15;
}

/* The bed-header cog dropdown pops its menu out of a sticky <th> (position:sticky + z-index above
   establishes its own stacking context), so the dropdown-menu's own z-index alone can't lift it
   above a SIBLING sticky <th> with a higher baseline z-index - the Hour column (z-index:15 via
   :first-child above) otherwise always renders on top of it regardless of the popped-out menu's own
   z-index, since stacking is compared at the parent <th> level once a context is established.
   Lifting the specific <th> only while its own dropdown is actually open (:has the open menu) keeps
   the normal sticky layering untouched every other time. */
.planner-grid-scroll thead th:has(.dropdown-menu.show) {
    z-index: 20;
}

/* Sticky "Add Task" footer row - pins to the bottom of the scrolling viewport (same technique as
   the sticky header, just anchored to the opposite edge) so there's always a one-tap way to add a
   task without scrolling back up to find an empty cell. */
.planner-grid-add-row td {
    position: sticky;
    bottom: 0;
    z-index: 9;
    background-color: var(--bs-body-bg);
}

/* ========================================================
   TIMELINE VIEW: RESPONSIVE MULTI-COLUMN TASK CHIPS
   Tasks used to stack one-per-row in a single wide column. Wrapping them lets more fit on screen
   at once, scaled by viewport width - roughly 3 per row on mobile up to 5-7 on large desktops -
   without any JS/server-side breakpoint detection, so it reflows immediately on window resize.
   ======================================================== */
.planner-timeline-task-list {
    align-content: flex-start;
}

.planner-timeline-task-chip {
    /* Mobile-first: one per row by default (see item 3 - narrow phones can't fit 3 truncated
       chips legibly). The min-width: 768px query below restores the wider multi-column layout. */
    flex: 1 1 100%;
    max-width: 100%;
    box-sizing: border-box;
    /* Same reasoning as .planner-task-chip's border-color override above - kept in sync so a
       default-background task chip reads the same way in both Grid and Timeline. */
    border-color: rgba(0, 0, 0, 0.22) !important;
}

/* A dark-tinted border (the default above) all but vanishes against a dark theme's own dark
   backgrounds - the opposite problem it was fixing in light mode. Light tint instead, same idea. */
[data-bs-theme="dark"] .planner-task-chip,
[data-bs-theme="dark"] .planner-timeline-task-chip {
    border-color: rgba(255, 255, 255, 0.28) !important;
}

.planner-timeline-task-text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
}

@media (min-width: 768px) {
    .planner-timeline-task-chip {
        flex-basis: calc(25% - 0.75rem);
        max-width: calc(25% - 0.75rem);
    }
}

/* Grid view's per-cell task chips get their flex-basis computed inline (server-side, up to
   ~33% for 3-per-row - see GetTaskChipFlexBasis in TimePlanner.razor.cs). !important is required
   to win over that inline style; overriding it here rather than in C# keeps the "1 per row on
   mobile" rule purely presentational and viewport-reactive (no server round-trip on resize/rotate). */
@media (max-width: 767px) {
    .planner-task-chip {
        flex: 0 0 100% !important;
        max-width: 100% !important;
    }
}

/* Sticky <th>/<td> positioning silently breaks under Bootstrap's default border-collapse:collapse
   on <table> - a well-known cross-browser quirk, most visible on narrow/mobile viewports where
   scrolling the grid (both directions) is unavoidable. border-spacing:0 keeps the collapsed-border
   visual look while switching to the separate border model sticky positioning actually needs. */
.planner-grid-scroll table {
    border-collapse: separate;
    border-spacing: 0;
}

/* Under border-collapse:separate, a <tr>'s own background-color (e.g. the current-hour amber tint
   or the all-tasks-completed green tint - both applied as a class on the <tr> itself) no longer
   shows through its <td> children, which each paint their own background box once cells are
   separate rather than collapsed. inherit pulls the row's computed background back onto every
   cell, restoring the row-level tint - also incidentally fixes the sticky first column (Hour) not
   having an opaque background, which would otherwise let horizontally-scrolled content show
   through it. */
.planner-grid-scroll tbody td {
    background-color: inherit;
}

/* The rule above loses to the global alternating-row stripe (.table > tbody > tr:nth-of-type(even) >
   *, in the shared table rules) on every EVEN row - that selector's specificity (2 classes/pseudo-
   classes, 2 type selectors) beats this file's plain ".planner-grid-scroll tbody td" (1 class, 2
   types), so an even-numbered "fully completed" or "current hour" row rendered the neutral stripe
   color instead of its green/amber tint while the very next (odd) row correctly showed it - visible
   as seemingly random "different shades" between rows that are actually in the exact same state.
   Matching the semantic row classes directly here (2 classes + 3 types) is specific enough to win
   regardless of row position, without needing !important. */
.planner-grid-scroll tbody tr.bg-success td,
.planner-grid-scroll tbody tr.bg-warning td {
    background-color: inherit;
}

/* The framework's default #components-reconnect-modal is a full-screen, input-blocking dark
   overlay - jarring on mobile web, where a backgrounded tab losing/regaining its WebSocket is
   routine (app-switch, screen lock, brief signal loss) and shouldn't interrupt whatever the user
   is doing. Overridden here (with !important, since the SDK ships its own inline/companion styles)
   to a small fixed corner badge instead, matching the MAUI app's existing unobtrusive
   "Offline"/reconnecting header badge (see MainLayout.razor's IsOffline badge) - the actual
   content (spinner + "Reconnecting...") is set in App.razor's <ReconnectModal> child content. */
#components-reconnect-modal {
    /* Hidden by default - unlike the framework's own dynamically-created fallback UI (which ships
       its own show/hide plumbing), a hand-authored static div has no built-in "hidden until
       Blazor says otherwise" behavior, so without this it was visible from the very first paint
       and stayed that way, since nothing ever told it to disappear in the steady-state "connected,
       nothing wrong" case (only the show/failed/rejected states below were ever styled). */
    display: none !important;
    position: fixed !important;
    top: 10px !important;
    right: 10px !important;
    left: auto !important;
    bottom: auto !important;
    width: auto !important;
    height: auto !important;
    max-width: none !important;
    background: transparent !important;
    padding: 0 !important;
    margin: 0 !important;
    z-index: 1300 !important;
    pointer-events: none !important;
}

/* Delays the reconnect badge from actually becoming visible for ~700ms after Blazor Server
   detects the circuit dropped. Mobile browsers routinely suspend/kill a backgrounded tab's
   WebSocket the instant it loses focus (app-switch, screen lock), so returning to the tab almost
   always triggers a brief reconnect that resolves in well under a second - showing anything for
   that isn't useful, it's just flicker. A genuinely slow/failed reconnect (bad network, server
   restart) still shows the badge once the delay elapses. */
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: block !important;
    opacity: 0;
    animation: reconnectModalDelayedShow 0s 700ms forwards;
}

/* Blazor removes the show/failed/rejected class once reconnected but doesn't necessarily add a
   distinct "hide" class back - explicitly forcing display:none here (redundant with the base rule
   above under normal cascade rules, but cheap insurance against specificity surprises) guarantees
   the badge actually disappears once reconnection succeeds. */
#components-reconnect-modal.components-reconnect-hide {
    display: none !important;
}

@keyframes reconnectModalDelayedShow {
    to {
        opacity: 1;
    }
}

@media (min-width: 992px) {
    .planner-timeline-task-chip {
        flex-basis: calc(20% - 0.8rem);
        max-width: calc(20% - 0.8rem);
    }
}

@media (min-width: 1400px) {
    .planner-timeline-task-chip {
        flex-basis: calc(14.2857% - 0.86rem);
        max-width: calc(14.2857% - 0.86rem);
    }
}

/* ========================================================
   EMPTY-CELL "ADD" HINT
   A faint "+" that only appears while hovering a genuinely empty grid cell on mouse/trackpad
   devices (razor only renders this element when the cell has no tasks) - replaces a permanently-
   visible "+ Add Task" label that cluttered every cell regardless of how full it already was.
   Touch devices get the opposite treatment below: no hover state to reveal it, so it's always
   visible there instead, with an explicit "Add" label.
   ======================================================== */
.planner-empty-cell-hint {
    text-align: center;
    color: var(--bs-secondary-color, #6c757d);
    opacity: 0;
    transition: opacity 0.15s ease-in-out;
    pointer-events: none;
    font-size: 0.85rem;
}

td:hover .planner-empty-cell-hint {
    opacity: 0.6;
}

.planner-empty-cell-hint-label {
    display: none;
}

/* Touch devices have no hover state to reveal the hint above, and a bare ~14px "+" glyph is too
   small a target to reliably notice or tap - so on touch/coarse-pointer viewports the hint stays
   permanently visible with an explicit "Add" label and a taller tap area instead of relying on
   :hover. Mirrors the (hover: hover) and (pointer: fine) touch/mouse split already used for the
   task chip audit tooltip below. */
@media (hover: none) and (pointer: coarse) {
    .planner-empty-cell-hint {
        opacity: 0.6;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.25rem;
        min-height: 32px;
        font-size: 0.75rem;
    }

    .planner-empty-cell-hint-label {
        display: inline;
        font-weight: 600;
    }
}

/* A persistent small "+" strip that renders BELOW existing task chip(s) in a non-empty cell - the
   hint above only ever exists in a genuinely EMPTY cell, but a cell with exactly one task (or any
   exact multiple of the per-row count - see GetTaskChipFlexBasis) has its chip(s) tile the row with
   no leftover space, leaving nothing to tap through to the cell's own add-task click handler. This
   has no click handler of its own (no @onclick, no stopPropagation) - a tap bubbles straight up to
   the cell, exactly like clicking blank cell space would. Always at least faintly visible (not
   hover-only) since touch devices have no hover state to reveal it. */
.planner-add-more-hint {
    width: 100%;
    text-align: center;
    color: var(--bs-secondary-color, #6c757d);
    opacity: 0.45;
    font-size: 0.7rem;
    padding: 3px 0;
    margin-top: 2px;
    pointer-events: none;
    transition: opacity 0.15s ease-in-out;
}

td:hover .planner-add-more-hint {
    opacity: 0.85;
}

@media (hover: none) and (pointer: coarse) {
    .planner-add-more-hint {
        opacity: 0.7;
        min-height: 22px;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.25rem;
    }
}

/* Still needed for .planner-task-chip[draggable="true"]::before's grip icon below (position:absolute
   relative to the chip's own box) - the audit tooltip that used to also depend on this no longer
   does (see TASK CHIP AUDIT HOVER TOOLTIP below), but this rule stays for that reason. */
.planner-task-chip,
.planner-timeline-task-chip {
    position: relative;
}

/* ========================================================
   TASK CHIP AUDIT HOVER TOOLTIP
   See data-audit-tooltip / GetTaskAuditTooltip in TimePlanner.razor(.cs). Previously a pure-CSS
   ::after popup anchored to the chip itself - simple, but genuinely got clipped/hidden by
   .planner-grid-scroll's own overflow:auto (forced alongside overflow-x:auto for the horizontal
   scrollbar - see that rule's own comment above) whenever the popup's box extended outside the
   scrolled viewport, which is common for a chip in the grid's topmost visible row, right under the
   sticky header. installAuditTooltip (nursingtools.js) now renders a single reusable element
   appended to <body> and positioned with position:fixed from the hovered chip's real screen
   coordinates instead, which escapes that clipping entirely. This class only styles that element;
   the show/hide/position logic all lives in JS. */
.nt-audit-tooltip {
    display: none;
    position: fixed;
    white-space: pre-line;
    background: #212529;
    color: #fff;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 0.68rem;
    line-height: 1.4;
    width: max-content;
    max-width: 220px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.25);
    pointer-events: none;
    /* Above every sticky grid element (max 20 - see the sticky header/first-column rules above) and
       comfortably below real UI chrome (modals/offcanvas start at 1030+) that should never coexist
       with a hover tooltip anyway. */
    z-index: 200;
}

/* ========================================================
   PANEL TOGGLE BUTTON GROUP (Progress/Handover/Templates)
   Used to hold up to 5 icon+label buttons (this trio plus Deferred/Metrics, both since moved into
   their own on-demand modals opened from the Options dropdown instead - see TimePlanner.razor's
   Options menu). That row used to need real shrinking (smaller padding/gap/font-size) just to fit
   all 5 on a narrow MAUI/mobile viewport without hiding any off-screen. With only 3 buttons left
   there's comfortable room to go the other way instead: bigger touch targets on mobile/MAUI, which
   is exactly the screen size that benefits most from that.
   ======================================================== */
.planner-panel-toggle-btn {
    height: 31px;
    border: none !important;
    font-size: 0.72rem;
    padding-left: 0.65rem;
    padding-right: 0.65rem;
}

@media (max-width: 767px) {
    .planner-panel-toggle-btn {
        height: 38px;
        font-size: 0.82rem;
        padding-left: 0.75rem;
        padding-right: 0.75rem;
        gap: 0.3rem;
    }
}

/* ========================================================
   MOBILE FLOATING "ADD TASK" BUTTON
   ======================================================== */
.planner-mobile-fab {
    position: fixed;
    bottom: 20px;
    right: 20px;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    z-index: 1090;
}

/* A subtle nudge toward the Add Task button on a genuinely empty shift (see the
   planner-mobile-fab-pulse class only being rendered when TotalTasksCount == 0 in
   TimePlanner.razor) - stops the moment the first task exists, so it never distracts once the
   planner is actually in use. */
@keyframes plannerFabPulse {
    0% { box-shadow: 0 0 0 0 rgba(var(--bs-primary-rgb), 0.55); }
    70% { box-shadow: 0 0 0 12px rgba(var(--bs-primary-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--bs-primary-rgb), 0); }
}

.planner-mobile-fab-pulse {
    animation: plannerFabPulse 2s ease-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .planner-mobile-fab-pulse {
        animation: none !important;
    }
}

/* ========================================================
   DRAG-AND-DROP GHOST PREVIEW
   Purely client-side (see print-helper.js / the dragenter listener it registers) - toggled by
   plain DOM class add/remove on dragenter, NOT routed through a Blazor event handler. Binding a
   live .NET handler to a high-frequency drag event once caused a severe perf regression (see
   OnDropCell's history), so this stays 100% JS with zero server round-trips.
   ======================================================== */
.planner-drop-ghost {
    position: relative;
}

.planner-drop-ghost::after {
    content: "";
    position: absolute;
    inset: 3px;
    border: 2px dashed var(--bs-primary, #0d6efd);
    background-color: rgba(13, 110, 253, 0.12);
    border-radius: 6px;
    pointer-events: none;
    z-index: 6;
}

/* Touch/pen drag-and-drop (print-helper.js) - source chip while being carried, and the floating
   clone that follows the finger. */
.planner-touch-dragging {
    opacity: 0.35;
}

.planner-touch-ghost {
    opacity: 0.9;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
    transform: rotate(-2deg);
}

/* Flags a task chip that a SharedPlanner guest (no account) just added/edited, live - cleared
   automatically on the next reload of any kind (see RefreshGuestChangedTaskIds in TimePlanner.razor.cs). */
.guest-change-badge {
    flex-shrink: 0;
    font-size: 0.65rem;
    font-weight: bold;
    width: 15px;
    height: 15px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    margin-right: 4px;
    animation: guest-change-pulse 1.2s ease-in-out infinite;
}

@keyframes guest-change-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.6; transform: scale(1.15); }
}

/* Generic browser-print reset, kept even though TimePlanner's own Grid/Timeline print views now
   generate a server-side PDF instead of using window.print() (see PlannerPdfRenderer) - a user can
   still trigger a native Ctrl+P/browser print on ANY page, and MainLayout.razor's chrome
   (hamburger/dark-mode toggles) still marks itself .no-print for exactly that case. */
@media print {
    html, body, .main-content {
        background-color: #fff !important;
    }

    .no-print {
        display: none !important;
    }
}

/* ========================================================
   DYNAMIC SIDEBAR COLLAPSIBLE ACCORDION ANIMATIONS
   ======================================================== */
.collapse.show {
    display: flex !important;
}

.collapse:not(.show) {
    display: none !important;
}

/* Offsets the left padding alignment of the nested accordion links */
.sidebar .collapse .nav-link {
    padding-left: 2rem !important;
    font-size: 0.82rem !important;
    opacity: 0.75;
}

    .sidebar .collapse .nav-link:hover {
        opacity: 1;
    }

    /* High-fidelity custom focus ring and active indicator */
    .sidebar .collapse .nav-link.active {
        font-weight: 600 !important;
        background-color: rgba(13, 110, 253, 0.15) !important;
        color: var(--bs-primary) !important;
        box-shadow: none !important;
    }

/* ========================================================
   HIGH-FIDELITY MOBILE ENVIRONMENT MEDIA QUERIES (MAX 768px)
   Also applies on any touch device up to 1023.98px wide (not just a real desktop browser narrowed
   that far) - a phone rotated to landscape (a Galaxy S24 Ultra is still only ~915px wide there, not
   remotely tablet-sized) could otherwise cross 768px and flip the sidebar from its mobile hamburger-
   triggered overlay into the permanent desktop collapse-toggle column, which then visually overlaps
   the fixed bottom action bar/scroll-top/Add Task buttons (those stay in their mobile layout past
   768px on a touch device too - see the "(pointer: coarse)" bottom-bar override further down - so the
   sidebar needs to agree with them on where "mobile" ends, not switch independently at a different
   width). A real desktop browser resized this narrow (pointer: fine) is untouched by the OR clause
   and still only gets this treatment below 768px, same as before. 1024px roughly matches a 10"
   tablet in landscape - genuinely larger touch devices still get the desktop sidebar as intended. */
@media (max-width: 768px), (pointer: coarse) and (max-width: 1023.98px) {
    /* Forces the sidebar to occupy 100% screen width as the home screen on startup */
    .sidebar {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 100% !important; /* Overrides desktop min-width restrictions */
        position: fixed !important;
        top: 0;
        left: 0;
        height: 100vh !important;
        display: flex !important;
        z-index: 1050 !important;
        transition: transform 0.25s ease-in-out;
    }

        .sidebar:not(.show) {
            transform: translateX(-100%);
            display: none !important;
        }

        .sidebar.show {
            overflow-y: auto !important;
            -webkit-overflow-scrolling: touch !important;
            padding-bottom: 80px !important; /* Extra space at bottom to ensure last items are scrollable */
        }

        /* When the mobile sidebar is active, hide the clinical dashboard entirely */
        .sidebar.show ~ .main-content {
            display: none !important;
        }

    /* Allow main content to occupy full screen when sidebar is dismissed */
    .main-content {
        width: 100% !important;
        /* Deliberately NOT setting height here (used to be 100vh !important) - that silently
           re-overrode the base .main-content rule's 100svh fix (see the comment above it) the moment
           the viewport narrowed to this breakpoint, which is exactly MAUI/narrow-browser width. The
           resulting inflated .main-content.clientHeight fed straight into
           nursingToolsPinPlannerGridHeight's available-space calculation (print-helper.js), which sized
           .planner-grid-scroll taller than what was actually visible - overflowing .main-content and
           giving TimePlanner's grid view a second, competing vertical scrollbar on top of its own
           (plus the horizontal one), three scrollbars total. Falling through to the base rule's 100svh
           fixes this without needing a separate value here. */
        padding-top: 0px !important; /* Removes duplicate top padding to snap content cleanly beneath the header */
    }

    /* Tighter content padding optimized specifically for mobile screen dimensions */
    main.p-4 {
        padding: 0.5rem !important;
    }

    /* CRITICAL COLLAPSE: Forcibly collapses any spacer wrappers or container divs inside the main viewport */
    main,
    main > div,
    main > section,
    main > form {
        padding-top: 0px !important;
        margin-top: 0px !important;
    }

    .main-content .card,
    .main-content .container,
    .main-content .container-fluid {
        margin-top: 0.25rem !important; /* Snaps the card cleanly beneath the header */
        margin-bottom: 0.5rem !important;
    }

    .main-content h1,
    .main-content h2,
    .main-content h3,
    .main-content h4,
    .main-content h5 {
        margin-top: 0.25rem !important;
        margin-bottom: 0.5rem !important;
    }
}

/* Bootstrap's d-md-none/d-md-inline-flex utilities on the sidebar's own three trigger controls
   (mobile hamburger in the top navbar, mobile "Close Menu" X inside the sidebar, desktop collapse-
   toggle chevron) still key purely off the 768px width breakpoint, unaffected by the widened
   mobile-overlay query above - without this, a touch device between 768-1023.98px would get the
   mobile-overlay .sidebar layout back but with the DESKTOP's collapse-toggle/hidden-hamburger
   button set, leaving no working way to actually open it. Combined-class selectors
   (.mobile-nav-toggle-btn.d-md-none etc.) give this higher specificity than Bootstrap's own bare
   .d-md-none/.d-md-inline-flex rules, so source order doesn't matter. */
@media (pointer: coarse) and (max-width: 1023.98px) {
    .mobile-nav-toggle-btn.d-md-none {
        display: inline-flex !important;
    }

    .sidebar .btn-close.d-md-none {
        display: inline-block !important;
    }

    .sidebar-collapse-toggle.d-md-inline-flex {
        display: none !important;
    }
}

/* ========================================================
   NATIVE PLATFORM PHONE BOUNDARIES (iOS & Android Safe Areas)
   ======================================================== */
.status-bar-safe-area {
    display: none;
}

@supports (-webkit-touch-callout: none) {
    .status-bar-safe-area {
        display: flex;
        position: sticky;
        top: 0;
        height: env(safe-area-inset-top);
        background-color: var(--nt-page-bg);
        width: 100%;
        z-index: 1;
    }

    .flex-column, .navbar-brand {
        padding-left: env(safe-area-inset-left);
    }
}

/* ==========================================================================
   GLOBAL PAGE-LEVEL SPACING COMPRESSION (Kills Empty Top Gaps)
   ========================================================================== */
main .container-fluid,
main .container {
    padding-top: 0px !important; /* Overrides and flattens 'py-4' top padding globally */
    margin-top: 0px !important;
}

/* Snaps content cards directly beneath your top navigation header */
main .card {
    margin-top: 0px !important;
}

/* Reduces standard page-level top padding inside the main viewport container */
main.p-4 {
    padding-top: 0.5rem !important;
}

/* ==========================================================================
   SCROLLBAR STYLING: Thin auto-hiding scrollbars for all platforms
   ========================================================================== */

/* WebKit/Chromium: Thin, auto-hiding scrollbar */
::-webkit-scrollbar {
    width: 8px !important;
    height: 8px !important;
}

::-webkit-scrollbar-track {
    background: transparent !important;
}

::-webkit-scrollbar-thumb {
    background-color: rgba(128, 128, 128, 0.4) !important;
    border-radius: 4px !important;
}

::-webkit-scrollbar-thumb:hover {
    background-color: rgba(128, 128, 128, 0.6) !important;
}

/* Firefox: Thin scrollbar */
html, body, .main-content, .sidebar, .table-responsive, div {
    scrollbar-width: thin !important;
    scrollbar-color: rgba(128, 128, 128, 0.4) transparent !important;
}

/* ==========================================================================
   COOKIE / STORAGE CONSENT BANNER (see CookieConsentBanner.razor)
   ========================================================================== */
.cookie-consent-banner {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9980;
    padding: 0.75rem;
    background-color: var(--nt-card-bg);
    border-top: 1px solid var(--nt-card-border);
    box-shadow: var(--nt-card-shadow-hover);
}

.cookie-consent-banner-body {
    max-width: 960px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.cookie-consent-icon {
    font-size: 1.5rem;
    color: var(--nt-primary);
    flex-shrink: 0;
}

.cookie-consent-text {
    flex: 1 1 320px;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    font-size: 0.85rem;
    color: var(--bs-body-color);
}

.cookie-consent-text span {
    color: var(--bs-secondary-color);
}

.cookie-consent-actions {
    display: flex;
    gap: 0.5rem;
    flex-shrink: 0;
    margin-left: auto;
}

@media (max-width: 575px) {
    .cookie-consent-banner-body {
        flex-direction: column;
        align-items: stretch;
        text-align: center;
    }

    .cookie-consent-actions {
        margin-left: 0;
        justify-content: center;
    }
}

/* ==========================================================================
   TOAST NOTIFICATION SYSTEM
   ========================================================================== */
.toast-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}

.toast-notification {
    pointer-events: auto;
    min-width: 280px;
    max-width: 400px;
    padding: 12px 16px;
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.15);
    display: flex;
    align-items: center;
    gap: 10px;
    animation: toastSlideIn 0.3s ease-out;
    font-size: 0.85rem;
    font-weight: 500;
    border-left: 4px solid;
    position: relative;
    overflow: hidden;
}

/* Shrinks over the same 6s the Undo toast stays on screen (see showUndoToast in print-helper.js),
   so the user can see at a glance how much longer they have before the action can't be undone. */
.toast-undo-progress {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 100%;
    background-color: currentColor;
    opacity: 0.45;
    animation: toastUndoShrink 6s linear forwards;
}

@keyframes toastUndoShrink {
    from { width: 100%; }
    to { width: 0%; }
}

.toast-notification.toast-success {
    background-color: #d4edda;
    color: #155724;
    border-left-color: #28a745;
}

.toast-notification.toast-error {
    background-color: #f8d7da;
    color: #721c24;
    border-left-color: #dc3545;
}

.toast-notification.toast-warning {
    background-color: #fff3cd;
    color: #856404;
    border-left-color: #ffc107;
}

.toast-notification.toast-info {
    background-color: #d1ecf1;
    color: #0c5460;
    border-left-color: #17a2b8;
}

/* Dark-mode toasts: the light-mode pastels above (#d4edda etc.) are unreadably washed-out against
   the dark page background, so dark mode gets its own deeper, desaturated tint per status instead
   of reusing the light-mode colors. */
[data-bs-theme="dark"] .toast-notification.toast-success {
    background-color: #1c3a2e;
    color: #8fe3b8;
    border-left-color: var(--nt-success);
}

[data-bs-theme="dark"] .toast-notification.toast-error {
    background-color: #3a1f21;
    color: #f3a6a9;
    border-left-color: var(--nt-danger);
}

[data-bs-theme="dark"] .toast-notification.toast-warning {
    background-color: #3a301a;
    color: #f0cd8a;
    border-left-color: var(--nt-warning);
}

[data-bs-theme="dark"] .toast-notification.toast-info {
    background-color: #1c333a;
    color: #9bd6e8;
    border-left-color: var(--nt-info);
}

.toast-notification .toast-icon {
    font-size: 1.2rem;
    flex-shrink: 0;
}

.toast-notification .toast-close {
    margin-left: auto;
    cursor: pointer;
    background: none;
    border: none;
    font-size: 1.1rem;
    opacity: 0.6;
    padding: 0;
    line-height: 1;
    color: inherit;
}

.toast-notification .toast-close:hover {
    opacity: 1;
}

@keyframes toastSlideIn {
    from {
        transform: translateX(100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

@keyframes toastFadeOut {
    from {
        transform: translateX(0);
        opacity: 1;
    }
    to {
        transform: translateX(100%);
        opacity: 0;
    }
}
/* The user editor's tenant mapping grid needs five usable columns. Keep account
   details narrower on desktop and stack both panes before controls become cramped. */
.user-editor-modal {
    --bs-modal-width: min(1180px, calc(100vw - 2rem));
}

.tenant-mappings-table-wrap {
    max-height: 250px;
    overflow: auto;
}

.tenant-mappings-table {
    min-width: 620px;
    table-layout: fixed;
}

.tenant-mappings-table th:nth-child(1) { width: 21%; }
.tenant-mappings-table th:nth-child(2) { width: 21%; }
.tenant-mappings-table th:nth-child(3) { width: 23%; }
.tenant-mappings-table th:nth-child(4) { width: 14%; }
.tenant-mappings-table th:nth-child(5) { width: 21%; }

.tenant-mappings-table .form-select {
    width: 100%;
    min-width: 0;
}

.tenant-mapping-remove {
    white-space: nowrap;
}

/* The warning alert uses different foreground/background values in each theme, so Bootstrap's
   generic outline-dark button can become dark-on-dark. Keep this action readable in both modes. */
.scheduling-conflict-more-info {
    color: #3b300d;
    border: 1px solid #66531a;
    background-color: rgba(255, 255, 255, 0.38);
}

.scheduling-conflict-more-info:hover,
.scheduling-conflict-more-info:focus-visible {
    color: #211a05;
    border-color: #3b300d;
    background-color: rgba(255, 255, 255, 0.72);
}

[data-bs-theme="dark"] .scheduling-conflict-more-info {
    color: #fff3cd;
    border-color: #f3d98a;
    background-color: rgba(255, 243, 205, 0.08);
}

[data-bs-theme="dark"] .scheduling-conflict-more-info:hover,
[data-bs-theme="dark"] .scheduling-conflict-more-info:focus-visible {
    color: #1d1706;
    border-color: #ffe69c;
    background-color: #ffe69c;
}

@media (max-width: 991.98px) {
    .user-editor-access-column {
        border-left: 0 !important;
        border-top: 1px solid var(--bs-border-color);
        padding-top: 1rem;
    }
}

/* ==========================================================================
   SKELETON LOADING PLACEHOLDERS
   Replaces the plain spinner + "Loading..." text pattern on the app's heaviest
   pages (Home, Bed Board, Time Planner) with shimmering blocks shaped like the
   content that's about to appear - conveys "here's roughly what's coming and
   how much of it" instead of a generic spinner giving no sense of progress.
   ========================================================================== */
.skeleton-block {
    background: linear-gradient(90deg,
        var(--bs-tertiary-bg) 25%,
        var(--bs-secondary-bg) 37%,
        var(--bs-tertiary-bg) 63%);
    background-size: 400% 100%;
    animation: skeletonShimmer 1.4s ease infinite;
    border-radius: 0.5rem;
}

@keyframes skeletonShimmer {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
    .skeleton-block {
        animation: none;
        background: var(--bs-tertiary-bg);
    }
}

.skeleton-line {
    height: 0.85rem;
    margin-bottom: 0.5rem;
}

.skeleton-line.w-25 { width: 25%; }
.skeleton-line.w-40 { width: 40%; }
.skeleton-line.w-60 { width: 60%; }
.skeleton-line.w-75 { width: 75%; }

.skeleton-row {
    height: 2.5rem;
    margin-bottom: 0.6rem;
}

.skeleton-card {
    height: 100%;
    min-height: 160px;
}

/* ==========================================================================
   COLOR SWATCH PICKER (see ColorSwatchPicker.razor)
   ========================================================================== */
.color-swatch-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    max-width: 230px;
}

.color-swatch {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    margin: 0;
    position: relative;
    flex-shrink: 0;
    transition: transform 150ms ease, border-color 150ms ease;
}

.color-swatch:hover {
    transform: scale(1.15);
}

.color-swatch.selected {
    border-color: var(--bs-body-color);
    box-shadow: 0 0 0 2px var(--bs-body-bg);
}

.color-swatch.selected::after {
    content: "\f26e"; /* bi-check-lg */
    font-family: "bootstrap-icons" !important;
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 0.75rem;
    text-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
}

.color-swatch-custom {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background-color: #fff;
    background-image:
        linear-gradient(45deg, #ccc 25%, transparent 25%),
        linear-gradient(-45deg, #ccc 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, #ccc 75%),
        linear-gradient(-45deg, transparent 75%, #ccc 75%);
    background-size: 10px 10px;
    background-position: 0 0, 0 5px, 5px -5px, -5px 0;
    color: var(--bs-secondary-color);
    font-size: 0.75rem;
}

.color-swatch-custom.selected {
    color: transparent;
}

.color-swatch-custom input[type="color"] {
    opacity: 0;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
    padding: 0;
    border: 0;
}

/* ==========================================================================
   COLLAPSED (ICON-ONLY) SIDEBAR MODE - opt-in, persisted (see MainLayout.
   razor's ToggleSidebarCollapsed). Deliberately CSS-only: every nav link's
   label is a plain text node (no wrapping <span>), so collapsing is done by
   zeroing the .nav-link's own font-size (collapsing all direct text to zero
   width) while every icon re-declares its own absolute rem size (fs-4/fs-5
   etc.), which is unaffected by the parent's font-size and stays fully
   visible - no markup changes needed across the whole sidebar.
   Breakpoint matches the collapse toggle button's own d-md-inline-flex
   (MainLayout.razor) - was min-width:992px, which left the toggle rendering
   its class with no visual effect between 768-991px (the sidebar's own
   768px mobile-slide-out cutoff and this collapse mode's old 992px cutoff
   didn't line up), most commonly hit by a MAUI phone in landscape.
   Every selector below is also guarded with :not(.show) - .show is the class
   the mobile hamburger toggle (nursingToolsToggleSidebar) adds/removes, and
   only ever means "currently rendering as the mobile overlay" regardless of
   viewport width or pointer type. Without the guard, a touch device/MAUI
   session with SidebarCollapsed=true persisted from an earlier desktop-style
   session opened the mobile overlay already icon-only-collapsed - and with
   the collapse-toggle chevron button ALSO hidden in that same mobile-overlay
   range (see the "(pointer: coarse) and (max-width: 1023.98px)" sidebar
   trigger-button rules further up), there was no control left to expand it
   again at all. Decided the mobile overlay should just always open expanded,
   full stop, regardless of what SidebarCollapsed happens to be persisted as
   - simpler than trying to reintroduce a mobile-reachable way to toggle it. */
@media (min-width: 768px) {
    /* Widened from an earlier 72px, and the sidebar's own p-3 (1rem/16px each side) padding is cut
       to 0.4rem here too - at 72px + full padding, only ~40px of actual content width remained,
       and Bootstrap-icon glyphs plus their containing flex row had no margin for error, rendering
       as visibly clipped/off-center ("half cut off") against the sidebar's own overflow-x, which is
       implicitly clipped (not visible) any time overflow-y is anything other than visible. */
    .sidebar.sidebar-collapsed:not(.show) {
        width: 76px !important;
        min-width: 76px !important;
        padding-left: 0.4rem !important;
        padding-right: 0.4rem !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .nav-link,
    .sidebar.sidebar-collapsed:not(.show) .sidebar-org-name,
    .sidebar.sidebar-collapsed:not(.show) .sidebar-brand-text {
        font-size: 0 !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .nav-link {
        justify-content: center !important;
        padding-left: 0 !important;
        padding-right: 0 !important;
        margin-left: 0 !important;
        margin-right: 0 !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .nav-link i,
    .sidebar.sidebar-collapsed:not(.show) .nav-link .bi {
        margin: 0 !important;
        flex-shrink: 0 !important;
    }

    /* Group-header rows (Clinical Assessments, Calculations, dynamic organisation menu groups,
       etc.) pair a leading icon+label with a trailing .sidebar-chevron via justify-content-between
       - in a 76px icon rail there's no room for a second icon, and their nested, indented
       (.border-start) sub-item lists have nowhere to go at all. Hide both rather than let them
       overflow/clip; expanding a group again after leaving rail mode is unaffected, since this only
       hides rendering while collapsed; the underlying expand/collapse state is untouched. */
    .sidebar.sidebar-collapsed:not(.show) .sidebar-chevron,
    .sidebar.sidebar-collapsed:not(.show) .border-start {
        display: none !important;
    }

    /* "ORGANISATION LINKS" (and any future section-divider label) is a bare <span> with its own
       inline font-size, not a .nav-link - the generic font-size:0 rule above never reached it, so
       at full size (0.7rem) plus px-3 padding and no white-space:nowrap, it wrapped across several
       lines in a 76px rail and visibly broke the collapsed layout below it whenever an organisation
       had any Menu Builder items configured. Hide it outright rather than just shrinking its text -
       a lone section header with no room for its label serves no purpose in icon-only mode. */
    .sidebar.sidebar-collapsed:not(.show) .sidebar-section-label {
        display: none !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .sidebar-brand-row {
        justify-content: center !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .sidebar-brand-link {
        padding-left: 0 !important;
        padding-right: 0 !important;
    }

    .sidebar.sidebar-collapsed:not(.show) .sidebar-brand-link img,
    .sidebar.sidebar-collapsed:not(.show) .sidebar-brand-link i {
        margin: 0 !important;
    }

    /* Badges/counts on a nav item (e.g. a future unread indicator) would
       otherwise also collapse to font-size:0 via inheritance - pin them back
       to a small legible size instead of disappearing. */
    .sidebar.sidebar-collapsed:not(.show) .nav-link .badge {
        font-size: 0.55rem !important;
    }

    /* The "you are here" accent bar's expanded-mode offset (-0.5rem) assumes the sidebar's full
       1rem padding gutter to sit in - that gutter is only 0.4rem wide here, so the bar needs to sit
       flush against the link's own edge instead or it gets clipped by the sidebar's overflow-x. */
    .sidebar.sidebar-collapsed:not(.show) .nav-link.active::before {
        left: 0;
    }
}

/* Positioned fully inside the sidebar's own bounds (not straddling its right edge) - the sidebar
   has overflow-y-auto, which per the CSS overflow spec silently forces overflow-x to behave as
   auto (clipping) too whenever it isn't explicitly "visible". A button hanging half outside that
   boundary was being clipped down to a barely-visible sliver rather than truly rendering "on the
   edge" as intended. Solid primary-color fill (rather than a subtle outline) also keeps it clearly
   visible against the sidebar's own background in both themes without depending on hover state. */
.sidebar-collapse-toggle {
    position: absolute;
    top: 0.85rem;
    right: 6px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--nt-primary);
    border: 2px solid var(--bs-body-bg);
    color: #fff;
    box-shadow: var(--nt-card-shadow);
    z-index: 1041;
    font-size: 0.75rem;
}

.sidebar-collapse-toggle:hover {
    background: var(--nt-primary-hover);
}
