/* Modal positioning & layout */
#base-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
}

#base-modal-level-1 {
    position: fixed;
    inset: 0;
    z-index: 19999;
}

#base-modal > .modal-underlay {
    position: absolute;
    inset: 0;
    cursor: pointer;
    z-index: 9999;
}

#base-modal-level-1 > .modal-underlay {
    position: absolute;
    inset: 0;
    cursor: pointer;
    z-index: 19999;
}

.modal-overlay {
    position: absolute;
    inset: 0;
    background: rgb(0 0 0 / 0.5);
}

.modal-overlay-transparent {
    position: absolute;
    inset: 0;
    background: transparent;
}

#core-modal-placeholder {
    position: relative;
    z-index: 10000;
}

#core-modal-level-1-placeholder {
    position: relative;
    z-index: 20000;
}

/* Base modal overlay animations */
#base-modal,
#base-modal-level-1 {
    animation: fadeIn 0.3s ease-out;
}

#base-modal.closing,
#base-modal-level-1.closing {
    animation: fadeOut 0.3s ease-out forwards;
}

/* Edge-drawer primitive: any slide-left/slide-right modal whose visible box
   is a narrow fixed-width panel (not the full placeholder) opts in with
   .phoxtail-modal-drawer instead of hand-rolling position/width/height in
   its own stylesheet. Consumers may still override width/background/etc;
   this only owns geometry + the edge it anchors to. */
.phoxtail-modal-drawer {
    position: fixed;
    top: 0;
    height: 100vh;
    height: 100dvh;
    width: 27.5rem;
    max-width: 100vw;
}
[data-animation-type="slide-left"] .phoxtail-modal-drawer { left: 0; }
[data-animation-type="slide-right"] .phoxtail-modal-drawer { right: 0; }

/* Side-drawer modals using the primitive move like the chatbot drawer: an
   OPAQUE slide, no fade. The shell must not fade either — the placeholder
   lives inside it, so a shell fade would stack on the drawer. On close the
   shell runs a visually inert "hold" animation purely so animationend fires
   at the drawer's 0.5s mark (removal is keyed to the shell's own animation
   ending). Keyed off .phoxtail-modal-drawer itself (not the animation-type
   value) so it covers both edges uniformly. */
#base-modal:has(.phoxtail-modal-drawer),
#base-modal-level-1:has(.phoxtail-modal-drawer) {
    animation: none;
}

#base-modal.closing:has(.phoxtail-modal-drawer),
#base-modal-level-1.closing:has(.phoxtail-modal-drawer) {
    animation: modalHold 0.5s linear forwards;
}

/* Content animation types */
#core-modal-placeholder[data-animation-type="slide-down"],
#core-modal-level-1-placeholder[data-animation-type="slide-down"] {
    animation: slideDownIn 0.3s ease-out;
}

#base-modal.closing #core-modal-placeholder[data-animation-type="slide-down"],
#base-modal-level-1.closing #core-modal-level-1-placeholder[data-animation-type="slide-down"] {
    animation: slideDownOut 0.3s ease-out;
}

#core-modal-placeholder[data-animation-type="slide-up"],
#core-modal-level-1-placeholder[data-animation-type="slide-up"] {
    animation: slideUpIn 0.3s ease-out;
}

#base-modal.closing #core-modal-placeholder[data-animation-type="slide-up"],
#base-modal-level-1.closing #core-modal-level-1-placeholder[data-animation-type="slide-up"] {
    animation: slideUpOut 0.3s ease-out;
}

#core-modal-placeholder[data-animation-type="slide-right"],
#core-modal-level-1-placeholder[data-animation-type="slide-right"] {
    animation: slideRightIn 0.3s ease-out;
}

#base-modal.closing #core-modal-placeholder[data-animation-type="slide-right"],
#base-modal-level-1.closing #core-modal-level-1-placeholder[data-animation-type="slide-right"] {
    animation: slideRightOut 0.32s ease-in;
}

#core-modal-placeholder[data-animation-type="fade"],
#core-modal-level-1-placeholder[data-animation-type="fade"] {
    animation: fadeIn 0.3s ease-out;
}

#base-modal.closing #core-modal-placeholder[data-animation-type="fade"],
#base-modal-level-1.closing #core-modal-level-1-placeholder[data-animation-type="fade"] {
    animation: fadeOut 0.3s ease-out;
}

/* Edge drawers using the primitive: same motion signature as the chatbot
   drawer's slide — opaque, no fade. The transform runs on the drawer's own
   fixed-width box (.phoxtail-modal-drawer), not on the placeholder: the
   placeholder is a position:fixed; inset:0 (full-viewport) wrapper, so a
   transform applied there travels the viewport's width while the visible
   drawer is only 27.5rem — it would sit off-screen for most of the eased
   timeline and only enter/exit view in a sliver of it, same as the chatbot
   drawer, whose transform lives on its own 27.5rem element. */
[data-animation-type="slide-left"] .phoxtail-modal-drawer {
    animation: drawerSlideLeftIn 0.5s cubic-bezier(0.45, 0, 0.2, 1);
}

#base-modal.closing [data-animation-type="slide-left"] .phoxtail-modal-drawer,
#base-modal-level-1.closing [data-animation-type="slide-left"] .phoxtail-modal-drawer {
    animation: drawerSlideLeftOut 0.5s cubic-bezier(0.45, 0, 0.2, 1) forwards;
}

[data-animation-type="slide-right"] .phoxtail-modal-drawer {
    animation: drawerSlideRightIn 0.5s cubic-bezier(0.45, 0, 0.2, 1);
}

#base-modal.closing [data-animation-type="slide-right"] .phoxtail-modal-drawer,
#base-modal-level-1.closing [data-animation-type="slide-right"] .phoxtail-modal-drawer {
    animation: drawerSlideRightOut 0.5s cubic-bezier(0.45, 0, 0.2, 1) forwards;
}

#core-modal-placeholder[data-animation-type="none"],
#core-modal-level-1-placeholder[data-animation-type="none"] {
    animation: none;
}

/* Keyframes (shared by all modals) */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes fadeOut {
    from { opacity: 1; }
    to { opacity: 0; }
}

@keyframes slideDownIn {
    from {
        opacity: 0;
        transform: translateY(-100%);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideDownOut {
    from {
        opacity: 1;
        transform: translateY(0);
    }
    to {
        opacity: 0;
        transform: translateY(-100%);
    }
}

@keyframes slideUpIn {
    from {
        opacity: 0;
        transform: translateY(100%);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideUpOut {
    from {
        opacity: 1;
        transform: translateY(0);
    }
    to {
        opacity: 0;
        transform: translateY(100%);
    }
}

@keyframes slideRightIn {
    from {
        opacity: 0;
        transform: translateX(100%);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes slideRightOut {
    from {
        opacity: 1;
        transform: translateX(0);
    }
    to {
        opacity: 0;
        transform: translateX(100%);
    }
}

/* Opaque drawer slide (no opacity coupling) + a visually inert hold used to
   time the shell's animationend on drawer close. */
@keyframes drawerSlideLeftIn {
    from { transform: translateX(-100%); }
    to { transform: translateX(0); }
}

@keyframes drawerSlideLeftOut {
    from { transform: translateX(0); }
    to { transform: translateX(-100%); }
}

@keyframes drawerSlideRightIn {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
}

@keyframes drawerSlideRightOut {
    from { transform: translateX(0); }
    to { transform: translateX(100%); }
}

/* Deliberately NOT opacity: animating opacity (even a 1->1 no-op) promotes
   the shell to an isolated compositing layer for the animation's duration,
   which cuts off the drawer's backdrop-filter from sampling real page
   content behind it — the glassmorphism blur goes flat for the whole close
   slide. background-color is inert here (the shell has no visible fill)
   but still fires animationend at the same 0.5s mark. */
@keyframes modalHold {
    from { background-color: transparent; }
    to { background-color: transparent; }
}

@keyframes slideLeftIn {
    from {
        opacity: 0;
        transform: translateX(-100%);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes slideLeftOut {
    from {
        opacity: 1;
        transform: translateX(0);
    }
    to {
        opacity: 0;
        transform: translateX(-100%);
    }
}
