﻿/* Multiselect dropdown (performance-first) */

.ms {
    position: relative;
}

/* Button */
.ms-btn {
    width: 100%;
    height: 32px;
    padding: 0 10px;
    border-radius: 10px;
    border: 1px solid rgba(0,0,0,.14);
    background: rgba(255,255,255,.95);
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    cursor: pointer;
}

.ms-caret {
    opacity: .7;
    flex-shrink: 0;
    transition: transform 160ms cubic-bezier(.16, 1, .3, 1);
}

.ms-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Instead of :has(), add/remove .is-open on .ms from your component */
.ms.is-open .ms-caret {
    transform: rotate(180deg);
}

/* Backdrop (click-catcher) */
.ms-backdrop {
    position: fixed;
    inset: 0;
    background: transparent;
    z-index: 20000; /* higher than your sticky thead */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 120ms ease-out, visibility 0s linear 120ms;
}

    .ms-backdrop.is-in {
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
        transition: opacity 120ms ease-out;
    }

    .ms-backdrop.is-out {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
    }




/* Popover base (no keyframe filters, no blur) */
.ms-pop {
    --ms-s: var(--rh-pop-scale, 1);
    /* Render as fixed so the popover does not affect document flow (prevents layout gaps)
       JS will set precise left/top when opening. */
    position: fixed;
    top: -9999px;
    left: -9999px;
    width: min(calc(340px * var(--ms-s)), calc(100vw - 16px));
    max-width: calc(100vw - 16px);
    /* Cap to viewport so the popover never grows taller than the screen.
       Combined with the flex layout below, this keeps the All/None row and
       the search field always visible and lets the options list shrink to
       fill the remaining space. */
    max-height: calc(100vh - 16px);
    display: flex;
    flex-direction: column;
    /* Self-contained typography so the popover looks identical regardless
       of the host's cascade (table cell vs. toolbar button context). */
    font-size: max(10px, calc(12px * var(--ms-s)));
    font-weight: 400;
    color: #1f2937;
    background: rgba(255,255,255,.98);
    border: 1px solid rgba(0,0,0,.12);
    border-radius: calc(12px * var(--ms-s));
    box-shadow: 0 14px 30px rgba(0,0,0,.12);
    z-index: 200001;
    overflow: hidden;
    transform-origin: 12px 0px;
    /* Default hidden state (so we can transition) — ZZZ-style snappy with slight overshoot */
    opacity: 0;
    transform: translateY(-8px) scale(.92);
    pointer-events: none;
    transition:
        opacity 130ms ease-out,
        transform 210ms cubic-bezier(.34, 1.56, .64, 1);
    will-change: transform, opacity;
}

.rhwo-filterrow th .ms-pop {
    transform: translateY(-10px) scale(.9);
    transition:
        opacity 130ms ease-out,
        transform 220ms cubic-bezier(.34, 1.56, .64, 1);
    /* Defensive height cap.
       JS (`msDropdownAlign`) sets a precise inline max-height every open
       based on the nearest scroll-clipping ancestor (typically
       `.seq-card-scroll`). This CSS rule is a fallback ceiling in case the
       JS path is skipped or the inline style is dropped during a
       re-render — guarantees the filter popover never extends past the
       visible table area.
       Math: 100dvh - 180px ≈ appbar (56) + tabs (~40) + filter row (~30)
       + page footer (~30) + breathing room. Capped to 480px to match the
       JS preferredMax so the popover stays a comfortable size on tall
       viewports too. */
    max-height: min(480px, calc(100dvh - 180px));
}

    /* Open state */
    .ms-pop.is-in {
        opacity: 1;
        transform: translateY(0) scale(1);
        pointer-events: auto;
    }

    /* Closing state — faster, no overshoot */
    .ms-pop.is-out {
        opacity: 0;
        transform: translateY(-6px) scale(.96);
        pointer-events: none;
        transition:
            opacity 110ms ease-in,
            transform 140ms cubic-bezier(.4, 0, .2, 1);
    }

.rhwo-filterrow th .ms-pop.is-in {
    transform: translateY(0) scale(1);
}

.rhwo-filterrow th .ms-pop.is-out {
    transform: translateY(-6px) scale(.96);
}

    /* Optional: lightweight shine (transform + opacity only) */
    .ms-pop::before {
        content: "";
        position: absolute;
        left: -40%;
        top: 0;
        width: 60%;
        height: 48px;
        pointer-events: none;
        background: linear-gradient( 110deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.45) 35%, rgba(255,255,255,0) 70% );
        transform: translateX(0) skewX(-18deg);
        opacity: 0;
    }

    /* Trigger the sweep only when opening */
    .ms-pop.is-in::before {
        opacity: 1;
        animation: msShine 520ms cubic-bezier(.2, .9, .2, 1) both;
    }

@keyframes msShine {
    0% {
        transform: translateX(0) skewX(-18deg);
        opacity: 0;
    }

    15% {
        opacity: .85;
    }

    100% {
        transform: translateX(220%) skewX(-18deg);
        opacity: 0;
    }
}

/* Top row */
.ms-top {
    display: flex;
    gap: calc(8px * var(--ms-s));
    padding: calc(10px * var(--ms-s));
    border-bottom: 1px solid rgba(0,0,0,.08);
    flex: 0 0 auto;
}

.ms-mini {
    flex: 1;
    height: max(28px, calc(28px * var(--ms-s)));
    padding: 0 calc(10px * var(--ms-s));
    border-radius: calc(10px * var(--ms-s));
    border: 1px solid rgba(0,0,0,.12);
    background: rgba(255,255,255,.9);
    cursor: pointer;
    font-size: max(10px, calc(12px * var(--ms-s)));
    font-weight: 600;
}

.ms-list {
    /* In the flex-column popover, let the list fill remaining space and
       shrink on small viewports. The popup itself receives the active
       viewport-side height budget from JS; the list fills whatever remains. */
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow: auto;
    padding: calc(8px * var(--ms-s)) calc(10px * var(--ms-s));
}

/* Items */
/* IMPORTANT: height MUST exactly match `ItemSize` on the <Virtualize> in
   MultiSelectDropdown.razor. Even a 1-2px mismatch accumulates into hundreds
   of pixels of scroll-offset error on long lists (Parent Job, Case ID, End User,
   Product) during fast scroll, causing Virtualize's viewport math to desync
   and leaving blank/translucent rows in the visible window. */
.ms-item {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    gap: calc(10px * var(--ms-s));
    padding: 0 calc(6px * var(--ms-s));
    border-radius: calc(10px * var(--ms-s));
    cursor: pointer;
    user-select: none;
    height: max(28px, calc(32px * var(--ms-s)));
    box-sizing: border-box;
}

/* Single class for both open-stagger and search-restagger.
   Uses `both` so the keyframe `from` state (opacity:0, translate/skew) is
   applied THE MOMENT the class is added — through the pre-delay window and
   into the animation. Without `both` (i.e. with `forwards`), items would be
   briefly visible at full opacity before their delay expired, producing a
   "render → flash → animate in" stutter.
   The previous Virtualize-related hazard with `both` no longer applies:
   MultiSelectDropdown renders its options as a single MarkupString of raw
   HTML, so no node reuse happens. */
.ms-item-stagger-in {
    animation: ms-item-stagger-in 200ms cubic-bezier(.22, 1, .36, 1) both;
}

.ms-item-enter {
    animation: ms-item-in 160ms cubic-bezier(.22, 1, .36, 1) both;
}

.ms-item-open-enter {
    animation: ms-item-open-in 220ms cubic-bezier(.22, 1, .36, 1) both;
}

@keyframes ms-item-stagger-in {
    from { transform: translate3d(-10px, 0, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

@keyframes ms-item-in {
    from { transform: translate3d(-6px, 0, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

@keyframes ms-item-open-in {
    from {
        opacity: 0;
        transform: translate3d(-14px, 0, 0) skewX(-4deg);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0) skewX(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ms-item-stagger-in,
    .ms-item-enter,
    .ms-item-open-enter { animation: none; }
}

    .ms-item:hover {
        background: rgba(15,88,214,.08);
    }

    .ms-item input {
        margin-top: 0;
        transform: none;
        flex: 0 0 auto;
    }

    .ms-item span {
        line-height: 1.15;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        min-width: 0;
        flex: 1 1 auto;
    }



/* Search bar inside dropdown */
.ms-searchWrap {
    padding: calc(8px * var(--ms-s)) calc(10px * var(--ms-s));
    border-bottom: 1px solid rgba(0,0,0,.08);
    background: rgba(255,255,255,.98);
    flex: 0 0 auto;
}

/* Field wrapper keeps everything contained */
.ms-searchField {
    position: relative;
    width: 100%;
}


/* Input */
.ms-search {
    width: 100%;
    height: max(28px, calc(32px * var(--ms-s)));
    box-sizing: border-box;
    padding: 0 calc(34px * var(--ms-s)) 0 calc(10px * var(--ms-s)); /* room for clear btn */
    border-radius: calc(10px * var(--ms-s));
    border: 1px solid rgba(0,0,0,.14);
    background: rgba(255,255,255,.95);
    outline: none;
    font-size: max(10px, calc(12.5px * var(--ms-s)));
}


    /* Focus ring WITHOUT overflowing */
    .ms-search:focus {
        border-color: rgba(15,88,214,.45);
        box-shadow: 0 0 0 2px rgba(15,88,214,.14); /* smaller ring */
    }






.ms-empty {
    padding: calc(10px * var(--ms-s)) calc(6px * var(--ms-s));
    color: rgba(0,0,0,.55);
    font-size: max(10px, calc(12px * var(--ms-s)));
}

/* .ms-pop.align-right default styling is defined later; removed duplicate conflicting rule. */


.rhwo-filterrow th .ms-pop {
    /* intentionally empty - alignment is controlled by JS via .align-left / .align-right classes */
}
.ms-pop.align-right {
    /* When JS adds .align-right we open towards the right. Position is set inline by JS. */
    transform-origin: 12px 0px;
}
.ms-pop.align-left {
    /* When JS adds .align-left we open towards the left. Position is set inline by JS. */
    transform-origin: calc(100% - 12px) 0px;
}

/* Fallback: if JS isn't available or hasn't positioned the pop yet, position it absolutely
   relative to the button container so it is visible. JS will overwrite inline styles when ready. */
.ms.is-open .ms-pop.align-right {
    /* fallback: align left edge with button (open to the right) */
    position: absolute;
    left: 0;
    right: auto;
    top: calc(100% + 8px);
    transform-origin: 12px 0px;
    /* NOTE: opacity / visibility / pointer-events are intentionally NOT set here.
       They are driven entirely by the .is-in / .is-out state machine so the
       open transition actually plays. */
}
.ms.is-open .ms-pop.align-left {
    /* fallback: align right edge with button (open to the left) */
    position: absolute;
    right: 0;
    left: auto;
    top: calc(100% + 8px);
    transform-origin: calc(100% - 12px) 0px;
}
