/* 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: var(--phx-scrim-background);
    /* Fades on its own rather than relying on the #base-modal shell fade
       below: edge drawers zero out that shell animation (see
       #base-modal:has(.phoxtail-modal-drawer)) so backdrop-filter on the
       drawer itself isn't flattened by an ancestor opacity animation. The
       scrim has no backdrop-filter, so it can safely keep fading on its
       own regardless of which content is in the placeholder. */
    animation: fadeIn var(--phx-scrim-duration) ease-out;
}

#base-modal.closing .modal-overlay,
#base-modal-level-1.closing .modal-overlay {
    animation: fadeOut var(--phx-scrim-duration) ease-out forwards;
}

.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: var(--phoxtail-drawer-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; }

/* A drawer set to the center (modal_drawer.html's side block) is a dialog:
   in the middle of the screen, as tall as its content up to most of it,
   fading in (modal.js). Its body scrolls past that height, as a drawer's. */
[data-modal-side="center"] .phoxtail-modal-drawer {
    inset: 0;
    width: min(var(--phoxtail-drawer-width, 27.5rem), calc(100vw - 2rem));
    height: fit-content;
    max-height: calc(100dvh - 4rem);
    margin: auto;
    border-radius: 1rem;
}

/* Glass drawer skin: an opt-in surface + chrome layer for .phoxtail-modal-drawer,
   pulled out of the various hand-rolled per-app copies (rga-*, evta-*, tka-*, ...)
   that were all reproducing the phoxtail-chatbot-drawer glassmorphism look. Add
   this class alongside .phoxtail-modal-drawer to get it; width stays a per-consumer
   override (set --phoxtail-drawer-width on the element) since content drives it. */
.phoxtail-modal-overlay {
    height: 100vh;
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
}

.phoxtail-drawer--glass {
    z-index: 50;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background-color: rgb(var(--color-surface-50, 255 255 255));
    backdrop-filter: blur(28px) saturate(1.6);
    -webkit-backdrop-filter: blur(28px) saturate(1.6);
    border-left: 1px solid rgb(0 0 0 / 0.07);
    box-shadow: -8px 0 48px rgba(0, 0, 0, 0.1), inset 0.5px 0 0 rgb(255 255 255 / 0.6);
    font-family: var(--font-ui, system-ui, -apple-system, sans-serif);
}

/* Left-anchored drawers: flip the edge the border/shadow read from, same
   attribute this element's own position (left: 0 vs right: 0) already reads. */
[data-animation-type="slide-left"] .phoxtail-drawer--glass {
    border-left: none;
    border-right: 1px solid rgb(0 0 0 / 0.07);
    box-shadow: 8px 0 48px rgba(0, 0, 0, 0.1), inset -0.5px 0 0 rgb(255 255 255 / 0.6);
}

.phoxtail-drawer__scroll {
    display: flex;
    flex-direction: column;
    flex: 1;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: rgb(0 0 0 / 0.12) transparent;
}

.phoxtail-drawer__scroll::-webkit-scrollbar {
    width: 4px;
}

.phoxtail-drawer__scroll::-webkit-scrollbar-track {
    background: transparent;
}

.phoxtail-drawer__scroll::-webkit-scrollbar-thumb {
    background-color: rgb(0 0 0 / 0.12);
    border-radius: 100px;
}

.phoxtail-drawer__scroll::-webkit-scrollbar-thumb:hover {
    background-color: rgb(0 0 0 / 0.2);
}

.phoxtail-drawer__header {
    flex-shrink: 0;
    padding: 1rem;
}

.phoxtail-drawer__title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

/* A title wraps, a long unbroken word too (a URL), beside a close button
   that keeps its size: the drawer never scrolls sideways. */
.phoxtail-drawer__title {
    min-width: 0;
    margin: 0;
    font-size: 1.125rem;
    font-weight: 700;
    color: rgb(var(--color-surface-900));
    line-height: 1.3;
    overflow-wrap: break-word;
}

.phoxtail-drawer__title-row > .phoxtail-close-btn {
    flex-shrink: 0;
}

.phoxtail-drawer__body {
    flex: 1;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.phoxtail-drawer__footer {
    flex-shrink: 0;
    padding: 1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
}

.phoxtail-close-btn {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--phx-size-small);
    height: var(--phx-size-small);
    min-width: var(--phx-size-small);
    border-radius: 50%;
    border: none;
    background-color: var(--phx-control-background);
    box-shadow: var(--phx-control-shadow);
    color: rgb(var(--color-surface-600, 82 82 91));
    cursor: pointer;
    padding: 0;
    font-family: inherit;
    line-height: 1;
    transition: background-color 150ms ease, color 150ms ease, box-shadow 200ms ease;
}

.phoxtail-close-btn:hover {
    background-color: var(--phx-control-background-hover);
    box-shadow: var(--phx-control-shadow-hover);
}

.phoxtail-close-btn:focus-visible {
    outline: 2px solid rgb(var(--color-surface-400) / 0.5);
    outline-offset: 2px;
}

.phoxtail-close-btn svg {
    width: var(--phx-size-small-icon);
    height: var(--phx-size-small-icon);
    display: block;
}

/* Thickens the icon's strokes. */
.phoxtail-close-btn svg path {
    stroke: currentColor;
    stroke-width: 16;
}

/* --xsmall: beside small text (a message), like phx-button--xsmall. The
   circle is smaller; what takes the tap stays the small size. */
.phoxtail-close-btn--xsmall {
    position: relative;
    width: 1.75rem;
    height: 1.75rem;
    min-width: 1.75rem;
}

.phoxtail-close-btn--xsmall::before {
    content: "";
    position: absolute;
    inset: 50% auto auto 50%;
    width: var(--phx-size-small);
    height: var(--phx-size-small);
    transform: translate(-50%, -50%);
}

.phoxtail-close-btn--xsmall svg {
    width: 0.875rem;
    height: 0.875rem;
}

/* --phx-field-gap: the space between fields, which fields made of parts (the
   phone number) keep between their parts too. */
.phoxtail-form-stack {
    --phx-field-gap: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: var(--phx-field-gap);
}

/* The --color-surface-* scale is a fixed light->dark ramp (50=near-white,
   950=near-black); it is NOT redefined per color-scheme. Each component
   using it for a themed surface must swap which end of the scale it reads
   here, same as every other dark-mode block in this codebase. */
@media (prefers-color-scheme: dark) {
    .phoxtail-drawer--glass {
        background-color: rgb(var(--color-surface-900, 24 24 27));
        border-left-color: rgb(255 255 255 / 0.08);
        box-shadow: -8px 0 48px rgba(0, 0, 0, 0.5), inset 0.5px 0 0 rgb(255 255 255 / 0.06);
    }

    [data-animation-type="slide-left"] .phoxtail-drawer--glass {
        border-right-color: rgb(255 255 255 / 0.08);
        box-shadow: 8px 0 48px rgba(0, 0, 0, 0.5), inset -0.5px 0 0 rgb(255 255 255 / 0.06);
    }

    .phoxtail-drawer__scroll {
        scrollbar-color: rgb(255 255 255 / 0.12) transparent;
    }

    .phoxtail-drawer__scroll::-webkit-scrollbar-thumb {
        background-color: rgb(255 255 255 / 0.12);
    }

    .phoxtail-drawer__scroll::-webkit-scrollbar-thumb:hover {
        background-color: rgb(255 255 255 / 0.2);
    }

    .phoxtail-drawer__title {
        color: rgb(var(--color-surface-100));
    }

    .phoxtail-close-btn {
        color: rgb(var(--color-surface-300, 212 212 216));
    }

    .phoxtail-close-btn:hover {
        color: rgb(var(--color-surface-200, 228 228 231));
    }
}

/* 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
   when the drawer's slide ends (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 var(--phx-drawer-duration) 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;
}

/* :not(:has(.phoxtail-modal-drawer)) excludes edge drawers using the
   primitive — those get their transform from the drawer-scoped rules below
   instead (see the comment there for why placeholder-level and drawer-level
   transforms can't both apply). */
#core-modal-placeholder[data-animation-type="slide-right"]:not(:has(.phoxtail-modal-drawer)),
#core-modal-level-1-placeholder[data-animation-type="slide-right"]:not(:has(.phoxtail-modal-drawer)) {
    animation: slideRightIn 0.3s ease-out;
}

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

#core-modal-placeholder[data-animation-type="fade"]:not(:has(.phoxtail-modal-drawer)),
#core-modal-level-1-placeholder[data-animation-type="fade"]:not(:has(.phoxtail-modal-drawer)) {
    animation: fadeIn 0.3s ease-out;
}

#base-modal.closing #core-modal-placeholder[data-animation-type="fade"]:not(:has(.phoxtail-modal-drawer)),
#base-modal-level-1.closing #core-modal-level-1-placeholder[data-animation-type="fade"]:not(:has(.phoxtail-modal-drawer)) {
    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 var(--phx-drawer-duration) var(--phx-drawer-easing);
}

#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 var(--phx-drawer-duration) var(--phx-drawer-easing) forwards;
}

[data-animation-type="slide-right"] .phoxtail-modal-drawer {
    animation: drawerSlideRightIn var(--phx-drawer-duration) var(--phx-drawer-easing);
}

#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 var(--phx-drawer-duration) var(--phx-drawer-easing) forwards;
}

/* A drawer in the center (a dialog) fades on its own box, on the drawers'
   timing, and stays faded until the modal is removed: the shell's hold
   above ends with it. */
[data-modal-side="center"] .phoxtail-modal-drawer {
    animation: fadeIn var(--phx-drawer-duration) var(--phx-drawer-easing);
}

#base-modal.closing [data-modal-side="center"] .phoxtail-modal-drawer,
#base-modal-level-1.closing [data-modal-side="center"] .phoxtail-modal-drawer {
    animation: fadeOut var(--phx-drawer-duration) var(--phx-drawer-easing) forwards;
}

/* Modal content replaced in place (phoxtail.core.htmx.replace_modal): while
   htmx holds the swap, what is there leaves with its side's exit; what comes
   in then enters with its own. */
[data-modal-content].htmx-swapping [data-modal-side="right"] .phoxtail-modal-drawer {
    animation: drawerSlideRightOut var(--phx-drawer-duration) var(--phx-drawer-easing) forwards;
}

[data-modal-content].htmx-swapping [data-modal-side="left"] .phoxtail-modal-drawer {
    animation: drawerSlideLeftOut var(--phx-drawer-duration) var(--phx-drawer-easing) forwards;
}

[data-modal-content].htmx-swapping [data-modal-side="center"] .phoxtail-modal-drawer {
    animation: fadeOut var(--phx-drawer-duration) var(--phx-drawer-easing) 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 when the slide ends. */
@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%);
    }
}
