/* ============================================
   BLIPBLURB — FRONT END STYLES
   ============================================ */

.blip {
    position: relative;
    overflow: hidden;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    min-height: 40px;
}

.blip[data-sticky="1"] {
    position: fixed;
    left: 0;
    right: 0;
    z-index: 999999;
}

.blip[data-sticky="1"][data-position="top"] {
    top: 0;
}

.blip[data-sticky="1"][data-position="bottom"] {
    bottom: 0;
}

/* Floating: inset from the edges instead of flush against them. Works
   whether Sticky is on (margin still applies normally to a fixed
   element's offsets) or off (normal-flow spacing). Only the vertical
   margin matching where the bar is actually pinned applies — a
   bottom-positioned bar has no use for margin-top, and vice versa.
   The distance itself comes from the --blip-floating-distance custom
   property (set inline per the Distance setting); the fallback here
   only matters if that's somehow missing. */
.blip[data-floating="1"] {
    margin-left: var(--blip-floating-distance, 25px);
    margin-right: var(--blip-floating-distance, 25px);
}

.blip[data-floating="1"][data-position="top"] {
    margin-top: var(--blip-floating-distance, 25px);
}

.blip[data-floating="1"][data-position="bottom"] {
    margin-bottom: var(--blip-floating-distance, 25px);
}

@media screen and (max-width: 600px) {
    .blip[data-floating="1"] {
        margin-left: calc(var(--blip-floating-distance, 25px) / 2);
        margin-right: calc(var(--blip-floating-distance, 25px) / 2);
    }

    .blip[data-floating="1"][data-position="top"] {
        margin-top: calc(var(--blip-floating-distance, 25px) / 2);
    }

    .blip[data-floating="1"][data-position="bottom"] {
        margin-bottom: calc(var(--blip-floating-distance, 25px) / 2);
    }
}

/* The admin preview renders the same markup/attributes as the live bar —
   never let Sticky pin it to the viewport inside wp-admin. Higher
   specificity than the rules above so it always wins regardless of
   data-position. */
#blip-preview-wrapper .blip[data-sticky="1"] {
    position: relative;
    top: auto;
    bottom: auto;
    left: auto;
    right: auto;
    z-index: auto;
}

/* Floating's edge-inset spacing doesn't mean anything inside the small
   preview box — suppress it the same way Sticky's fixed positioning is
   suppressed above. Corner Radius (set inline, not here) still shows,
   since that's a purely visual effect that previews fine regardless. */
#blip-preview-wrapper .blip[data-floating="1"] {
    margin: 0;
}

/* --------------------------------------------
   MESSAGE LAYOUT
   -------------------------------------------- */

/*
 * All messages share the same grid cell (grid stacks them, like the old
 * position:absolute approach, but sizes the row to the TALLEST of them —
 * including ones not currently shown). That way a blip that wraps to two
 * lines (e.g. on mobile) grows the bar for every blip, instead of the bar
 * jumping in height each time rotation lands on a taller one.
 */
.blip-message {
    grid-column: 1;
    grid-row: 1;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    text-align: center;
    /* All messages stack in the same grid cell (for equal-height
       rotation) — border-box keeps a per-blip Border from growing
       that cell's size relative to messages without one. */
    box-sizing: border-box;
    /* ::before (the background fill) has no border-radius of its own —
       without this, it stays square-cornered and pokes past a rounded
       per-blip Border at the corner instead of matching its curve. */
    overflow: hidden;
}

.blip-message.is-active {
    z-index: 2;
}

/*
 * Both the incoming and outgoing message stay visible (opacity:1) during a
 * transition — otherwise the outgoing box disappears the instant it stops
 * being .is-active (the base rule's opacity:0 has no transition to animate
 * it), which exposes the page background instead of the outgoing blip's
 * own background/text mid-animation.
 */
.blip-message.is-active,
.blip-message.is-exiting {
    opacity: 1;
}

.blip-message::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: var(--blip-bg, #222222);
    z-index: 0;
    opacity: 1;
    /* Blur lives on the shared .blip::before base layer instead (see
       above) — this is just each blip's own color wash on top of that
       already-blurred base, so it needs no backdrop-filter of its own.
       Still needs its own radius: the parent's overflow:hidden clips a
       plain background fine on its own, but doesn't retroactively round
       this layer's own corners for anything checking its shape (e.g. a
       per-blip Border drawn on the same box). Matches whatever radius
       class-blip-render.php put on .blip-message (Floating's corner
       radius, or the Border's own radius). */
    border-radius: inherit;
}

.blip-message-inner {
    position: relative;
    z-index: 1;
    padding: 5px 10px;
    color: var(--blip-text, #ffffff);
    transition:
        opacity 0.6s ease,
        transform 0.6s ease,
        filter 0.6s ease;
    will-change: opacity, transform, filter;
}

.blip-message.is-exiting {
    z-index: 1;
}

.blip-message a {
    text-decoration: none;
}

/* ============================================
   BACKGROUND ANIMATIONS
   ============================================ */

/* None */
.blip[data-background="none"] .blip-message::before {
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
}
.blip[data-background="none"] .blip-message.is-exiting::before {
    opacity: 1 !important;
    transform: none !important;
}

/*
 * Fade / Slide backgrounds: the outgoing message stays static and fully
 * opaque underneath — only the incoming message animates, on top of it
 * (see z-index above). This guarantees a blip color is visible for the
 * entire transition; nothing ever dips through to the page background.
 */

/* Fade */
.blip[data-background="fade"] .blip-message::before {
    opacity: 0;
    transition: opacity 1s ease-out;
}
.blip[data-background="fade"] .blip-message.is-active::before {
    opacity: 1;
}
.blip[data-background="fade"] .blip-message.is-exiting::before {
    opacity: 1;
}

/*
 * Slides reveal ::before (the background layer) via an animated clip-path
 * — never the whole .blip-message box, so Text Animation stays fully
 * independent of whatever Background Animation is doing, and never a
 * transform on ::before itself.
 *
 * This used to animate ::before with `transform: translate*()` instead —
 * sliding the actual filled/blurred layer across the screen. That's a
 * known cross-browser bug trigger: a `backdrop-filter` element clipped by
 * an ANCESTOR's overflow:hidden can leak blur past its own rounded corner
 * while the filtered element itself is mid-transform (the compositor
 * resamples the backdrop at a shifting position out of sync with the
 * clip), showing a brief dark seam during every crossfade. Revealing a
 * layer that never moves — its full-size box stays put; only how much of
 * it is clipped changes — produces the same left/right/up/down sweep
 * visually without ever putting a transform on the filtered element, so
 * there's nothing for that bug to trigger on.
 */

/* Slide Up — incoming background rises up and covers the outgoing one */
.blip[data-background="slide-up"] .blip-message::before {
    clip-path: inset(100% 0 0 0);
    transition: clip-path 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.blip[data-background="slide-up"] .blip-message.is-active::before,
.blip[data-background="slide-up"] .blip-message.is-exiting::before {
    clip-path: inset(0 0 0 0);
}

/* Slide Down — incoming background drops down and covers the outgoing one */
.blip[data-background="slide-down"] .blip-message::before {
    clip-path: inset(0 0 100% 0);
    transition: clip-path 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.blip[data-background="slide-down"] .blip-message.is-active::before,
.blip[data-background="slide-down"] .blip-message.is-exiting::before {
    clip-path: inset(0 0 0 0);
}

/* Slide Left — incoming background enters from the right, covers the outgoing one */
.blip[data-background="slide-left"] .blip-message::before {
    clip-path: inset(0 0 0 100%);
    transition: clip-path 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.blip[data-background="slide-left"] .blip-message.is-active::before,
.blip[data-background="slide-left"] .blip-message.is-exiting::before {
    clip-path: inset(0 0 0 0);
}

/* Slide Right — incoming background enters from the left, covers the outgoing one */
.blip[data-background="slide-right"] .blip-message::before {
    clip-path: inset(0 100% 0 0);
    transition: clip-path 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.blip[data-background="slide-right"] .blip-message.is-active::before,
.blip[data-background="slide-right"] .blip-message.is-exiting::before {
    clip-path: inset(0 0 0 0);
}

/* ============================================
   TEXT ANIMATIONS
   ============================================ */

/* None */
.blip[data-transition="none"] .blip-message-inner {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
}
.blip[data-transition="none"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
}

/* Fade In — pure opacity crossfade, no movement */
.blip[data-transition="fade-in"] .blip-message-inner {
    opacity: 0;
}
.blip[data-transition="fade-in"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
}
.blip[data-transition="fade-in"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
}

/* Slide + Fade (Up) */
.blip[data-transition="slide-fade-up"] .blip-message-inner {
    transform: translateY(20px);
    opacity: 0;
}
.blip[data-transition="slide-fade-up"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    transform: translateY(0);
}
.blip[data-transition="slide-fade-up"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    transform: translateY(-20px);
}

/* Slide + Fade (Down) */
.blip[data-transition="slide-fade-down"] .blip-message-inner {
    transform: translateY(-20px);
    opacity: 0;
}
.blip[data-transition="slide-fade-down"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    transform: translateY(0);
}
.blip[data-transition="slide-fade-down"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    transform: translateY(20px);
}

/* Slide + Fade (Left) */
.blip[data-transition="slide-fade-left"] .blip-message-inner {
    transform: translateX(20px);
    opacity: 0;
}
.blip[data-transition="slide-fade-left"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    transform: translateX(0);
}
.blip[data-transition="slide-fade-left"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    transform: translateX(-20px);
}

/* Slide + Fade (Right) */
.blip[data-transition="slide-fade-right"] .blip-message-inner {
    transform: translateX(-20px);
    opacity: 0;
}
.blip[data-transition="slide-fade-right"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    transform: translateX(0);
}
.blip[data-transition="slide-fade-right"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    transform: translateX(20px);
}

/* Scale Up */
.blip[data-transition="scale-up"] .blip-message-inner {
    transform: scale(0.97);
    opacity: 0;
}
.blip[data-transition="scale-up"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    transform: scale(1);
}
.blip[data-transition="scale-up"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    transform: scale(0.97);
}

/* Blur In */
.blip[data-transition="blur-in"] .blip-message-inner {
    filter: blur(4px);
    opacity: 0;
}
.blip[data-transition="blur-in"]
    .blip-message.is-active
    .blip-message-inner {
    opacity: 1;
    filter: blur(0);
}
.blip[data-transition="blur-in"]
    .blip-message.is-exiting
    .blip-message-inner {
    opacity: 0;
    filter: blur(4px);
}
