/* ============================================================
   UFO Beam Nav — Pure CSS hover-expand trapezoid menu
   ============================================================ */

/* ── CSS custom properties (defaults) ───────────────────── */

.ubn-menu {
    /* Palette-bound defaults (never a grey un-themed frame; brand fallback if the
       global vars have not resolved yet). */
    --ubn-circle-bg: var(--e-global-color-a2cf421, #084147);
    --ubn-circle-icon: var(--e-global-color-76c389d, #f9d3e6);
    --ubn-circle-border: transparent;
}

/* ── Menu wrapper ───────────────────────────────────────── */

.ubn-menu {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;        /* 100% (not 100vw) so a scrollbar can't add width */
    height: 100vh;      /* fallback for browsers without dvh */
    height: 100dvh;     /* dynamic viewport height = the actually-visible screen on mobile */
    overflow-x: clip;   /* the fixed overlay never contributes a horizontal scroll,
                           even if a template's inner container has side margins */
    pointer-events: none;
    z-index: 100000; /* sits above the TOC rail (99999) */
}

/* Bump any positioned Elementor ancestor that contains the UFO menu above
   the TOC rail. Without this, the parent's stacking context (e.g. a sticky
   header at z-index 99) traps the menu below higher-z root-level elements. */
.elementor-element:has(.ubn-menu),
.elementor-section:has(.ubn-menu),
.e-con:has(.ubn-menu) {
    z-index: 100001 !important;
}

/* ── Circle trigger ─────────────────────────────────────── */

.ubn-circle {
    position: absolute;
    border-radius: 50%;
    /* Fill lives on ::after so the tinted blur halo (::before) can sit behind it;
       a background here would be captured and blurred by the halo. */
    background: transparent !important;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    pointer-events: auto;
    z-index: 2;
    border: 0 solid var(--ubn-circle-border);
    isolation: isolate;   /* own stacking context for the ::before/::after order */
    transition: border-color 0.35s ease, box-shadow 0.35s ease;
}

/* Opaque disc — the visible circle fill. Colour follows the scheme (the --dark/--light
   rules target ::after). Sits above the body-level .gv-glass panel behind the assembly. */
.ubn-circle::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    z-index: 1;
    pointer-events: none;
    background: var(--ubn-circle-bg);
    transition: background 0.35s ease;
}

/* Trigger content (icon / logo / link / close) sits above the disc. */
.ubn-circle > * {
    position: relative;
    z-index: 2;
}

/* ── Knockout logomark ─────────────────────────────────────
   The idle logo is stamped OUT of the disc (transparent through to whatever is
   behind the widget) instead of drawn in a contrasting colour. Two mask layers on
   the circle, composited with XOR/exclude: a full-coverage layer (the disc) minus
   the logo silhouette (--ubn-svg), leaving a logo-shaped hole. --ubn-svg is set
   inline on the circle by the render (knockout + SVG logo). */
.ubn-circle--knockout::after {
    -webkit-mask:
        linear-gradient(#000 0 0),
        var(--ubn-svg) center / 60% 60% no-repeat;
            mask:
        linear-gradient(#000 0 0),
        var(--ubn-svg) center / 60% 60% no-repeat;
    -webkit-mask-composite: xor;
            mask-composite: exclude;
}
/* Drop the knockout wherever a different icon takes over the trigger, so the idle
   logomark's hole doesn't bleed through it. Mirrors exactly the states that reveal
   those icons: the hover/open logo on desktop (only when a separate hover logo is
   set), and the mobile open overlay (close X). Otherwise the disc stays solid and
   the taking-over icon shows normally. */
.ubn-menu:not(.ubn-menu--mobile):hover      .ubn-circle--knockout:has(.ubn-logo-hover)::after,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-circle--knockout:has(.ubn-logo-hover)::after,
.ubn-menu:not(.ubn-menu--mobile).ubn-closing .ubn-circle--knockout:has(.ubn-logo-hover)::after,
.ubn-menu--mobile.ubn-menu--open             .ubn-circle--knockout::after {
    -webkit-mask: none;
            mask: none;
}
/* The idle filled icon is redundant in knockout mode — the hole is the logo. (The
   close icon and, on hover, a separate hover logo still show — their higher-
   specificity reveal rules win over this.) */
.ubn-circle--knockout .ubn-circle-icon:not(.ubn-circle-icon-close) {
    opacity: 0;
}

.ubn-circle-icon {
    font-size: 1.2rem;
    color: var(--ubn-circle-icon);
    line-height: 1;
    transition: color 0.35s ease;
}

/* Optional link behind the logomark — fills the circle so the whole trigger
   is clickable, and stays visually identical to the bare icon. */
.ubn-circle-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: inherit;
    text-decoration: none;
}
/* On the mobile overlay the circle is a click-toggle, not a link — neutralise
   the anchor so a tap never navigates (the JS also preventDefaults). */
.ubn-menu--mobile .ubn-circle-link {
    pointer-events: none;
}

.ubn-circle-icon--img {
    font-size: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* SVG icon — rendered as a mask so `icon_color` fills the shape */
.ubn-circle-icon--svg {
    width: 60%;
    height: 60%;
    background-color: currentColor;
    -webkit-mask-image: var(--ubn-svg);
            mask-image: var(--ubn-svg);
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    transition: background-color 0.35s ease;
}

.ubn-circle-icon--img img {
    width: 60%;
    height: 60%;
    object-fit: contain;
    transition: filter 0.35s ease;
}

/* ── Idle vs hover/open custom logo (desktop) ─────────────
   Idle + hover are stacked as identical full-size flex frames that centre a
   60% inner logo, so they overlap pixel-perfectly and crossfade. The idle
   image shows at rest; on desktop hover / open the hover image fades in.
   Mobile keeps the close-icon behaviour. */
.ubn-circle-link { position: relative; }
.ubn-logo-idle,
.ubn-logo-hover {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.35s ease;
}
.ubn-logo-hover { opacity: 0; }
.ubn-menu:not(.ubn-menu--mobile):hover   .ubn-logo-hover,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-logo-hover { opacity: 1; }
/* Hide the idle image ONLY when a hover image is present (:has guards against
   blanking the logo when no hover image is set; safely no-ops without :has). */
.ubn-menu:not(.ubn-menu--mobile):hover   .ubn-circle:has(.ubn-logo-hover) .ubn-logo-idle,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-circle:has(.ubn-logo-hover) .ubn-logo-idle { opacity: 0; }

/* ── Overlay (full-viewport backdrop behind beam) ────────── */

/* The blur lives on its own sibling so that the user can dial down the
   color overlay's opacity without killing backdrop-filter (parent opacity<1
   creates a flattened group, so filters lose their backdrop reference). */
.ubn-overlay,
.ubn-overlay-blur {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.5s ease;
}

.ubn-overlay {
    background: rgba(0, 0, 0, 0);
}

.ubn-menu:not(.ubn-menu--mobile):hover .ubn-overlay,
.ubn-menu:not(.ubn-menu--mobile):hover .ubn-overlay-blur {
    opacity: 1;
}

/* ── Beam / trapezoid ───────────────────────────────────── */

/* Two stacked elements sharing the same trapezoidal clip-path:
     .ubn-beam-blur — backdrop-filter only, opacity always reaches 1 so the
                      blur survives even when .ubn-beam's opacity slider is dialled down.
     .ubn-beam      — visible color + content (nav). Opacity slider applies here. */
.ubn-beam,
.ubn-beam-blur {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    clip-path: var(--ubn-clip-collapsed);
    transition: clip-path 0.6s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.5s ease;
}

.ubn-beam-blur {
    z-index: 1;
    opacity: 0;
}

.ubn-beam {
    z-index: 2;
    background: #f0ede6;
}

.ubn-menu:not(.ubn-menu--mobile):hover .ubn-beam {
    pointer-events: auto;
    clip-path: var(--ubn-clip-expanded);
}

.ubn-menu:not(.ubn-menu--mobile):hover .ubn-beam-blur {
    clip-path: var(--ubn-clip-expanded);
    opacity: 1;
}

/* ── Link positioning variants ──────────────────────────── */

.ubn-beam-nav {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    /* Promote the nav to its own compositing layer so hover repaints on the links
       (colour change) stay contained here and don't force the beam's backdrop-filter
       blur (painted behind) to re-render every frame — the source of the background
       flicker while moving over the menu options. */
    transform: translateZ(0);
}

.ubn-beam-nav--bottom-right {
    bottom: 8vh;
    right: 6vw;
    align-items: flex-end;
}

.ubn-beam-nav--bottom-left {
    bottom: 8vh;
    left: 6vw;
    align-items: flex-start;
}

.ubn-beam-nav--center {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    align-items: center;
}

/* Template content fills the whole beam — no shrink-to-fit, no corner anchor.
   The Elementor template inside handles its own internal alignment. */
.ubn-beam-nav--template {
    inset: 0;
    padding: 8dvh 6vw;   /* dvh (not vh) so the padding matches the dvh overlay and the
                            content fits the *visible* mobile screen — otherwise the vh
                            padding (large viewport) overflows and pushes the bottom off. */
    box-sizing: border-box;
    align-items: stretch;
    justify-content: center;
}

/* Mobile full-screen menu: if the content is ever taller than the visible screen,
   scroll it INSIDE the overlay and never chain the scroll to the page underneath.
   overscroll-behavior:contain kills the "sliver of the page + address bar hides"
   over-scroll; the touch is captured here instead of falling through to the window. */
.ubn-menu--mobile.ubn-menu--open .ubn-beam-nav--template {
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
.ubn-menu--mobile.ubn-menu--open {
    overscroll-behavior: contain;
}

/* Mobile: stack the template full-width and push it to the BOTTOM of the beam.
   Full-width (stretch) means the template's own left-alignment lands at the
   screen's left edge; the inset from the corner is the template padding above
   (8dvh 6vw) — or the widget's own padding control if set. */
.ubn-menu--mobile .ubn-beam-nav--template {
    flex-direction: column;
    justify-content: flex-end;   /* vertical: bottom */
    align-items: stretch;        /* full width → left edge is the screen's left */
}

/* ── Template nav rows: staggered fade-in-from-right on open ─────────
   Each list row starts faintly offset to the right and hidden; on every open it
   slides into place, one after another. The per-row delay lives ONLY on the open
   state (JS sets --ubn-row = row index), so opening staggers but closing snaps
   back at once. Re-runs on each open because the open class/hover toggles. */
.ubn-beam-nav--template li,
.ubn-beam-nav--template .gft-ds {
    opacity: 0;
    transform: translateX(44px);
    transition: opacity 0.4s ease, transform 0.4s ease;
}
/* The base delay holds the whole cascade until the beam has extended over the nav
   area, so the reveal is seen on top of the settled panel rather than washing out
   under the expanding beam. Then each row staggers from there. The settled state is
   `transform: none` (NOT translateX(0)) so it leaves no persistent compositing layer
   — an identity transform on each row makes the beam's backdrop-filter flicker on
   hover. Items sharing a row get the same --ubn-row, so the row slides as one. */
.ubn-menu:not(.ubn-menu--mobile):hover       .ubn-beam-nav--template li,
.ubn-menu:not(.ubn-menu--mobile):hover       .ubn-beam-nav--template .gft-ds,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam-nav--template li,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam-nav--template .gft-ds,
.ubn-menu--mobile.ubn-menu--open             .ubn-beam-nav--template li,
.ubn-menu--mobile.ubn-menu--open             .ubn-beam-nav--template .gft-ds {
    opacity: 1;
    transform: none;
    transition-delay: calc(0.28s + var(--ubn-row, 0) * 0.09s);
}
@media (prefers-reduced-motion: reduce) {
    .ubn-beam-nav--template li,
    .ubn-beam-nav--template .gft-ds {
        transition: none;
        opacity: 1;
        transform: none;
    }
}

/* ── Links ──────────────────────────────────────────────── */

.ubn-beam-link {
    color: #1a1a1a;
    text-decoration: none;
    opacity: 0;
    transform: translateY(20px);
    transition:
        opacity 0.35s ease calc(var(--i, 0) * 0.07s + 0.25s),
        transform 0.4s ease calc(var(--i, 0) * 0.07s + 0.25s),
        color 0.2s ease,
        background-size 0.3s ease;
    pointer-events: none;
    background-image: linear-gradient(color-mix(in srgb, currentColor 15%, transparent), color-mix(in srgb, currentColor 15%, transparent));
    background-repeat: no-repeat;
    background-size: 100% 0.35em;
    background-position: 0 bottom;
    padding-bottom: 0;
}

.ubn-menu:not(.ubn-menu--mobile):hover .ubn-beam-link {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.ubn-beam-link:hover {
    color: #e8432a;
    background-size: 100% 100%;
}

.ubn-beam-link[aria-current="page"] {
    color: #e8432a;
}

/* ── SVG border overlay ─────────────────────────────────── */

.ubn-beam-border-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 2;
    opacity: 0;
    transition: opacity 0.4s ease 0.1s;
}

.ubn-menu:not(.ubn-menu--mobile):hover .ubn-beam-border-svg {
    opacity: 1;
}

.ubn-beam-border {
    fill: none;
    stroke: rgba(255, 255, 255, 0.15);
    stroke-width: 1;
}

/* ============================================================
   Colour schemes — Dark / Light

   Dark  = light icon on dark circle (for use over light backgrounds)
   Light = dark icon on light circle (for use over dark backgrounds)

   In "auto" mode, JS toggles these classes based on the
   background luminance of the element behind the circle.
   ============================================================ */

/* ── Dark scheme (default — dark circle, light icon) ────── */

/* Scheme = which of the two palette colours the circle uses. Fixed via
   data-ubn-scheme="dark|light"; for "auto" the JS toggles these to contrast
   with the sampled background. !important overrides the widget's per-post
   global binding on the circle. --ubn-scheme-light/-dark are published by
   resolvePalette() (JS); the nested var() is the pre-JS palette fallback. */
.ubn-menu--dark .ubn-circle::after {
    background: var(--ubn-scheme-dark, var(--e-global-color-a2cf421, #084147)) !important;
    border-color: var(--ubn-scheme-light, var(--e-global-color-76c389d, #f9d3e6)) !important;
}
.ubn-menu--dark .ubn-circle-icon,
.ubn-menu--dark .ubn-circle-icon-close {
    color: var(--ubn-scheme-light, var(--e-global-color-76c389d, #f9d3e6)) !important;
}

/* ── Light scheme (light circle, dark icon) ─────────────── */

.ubn-menu--light .ubn-circle::after {
    background: var(--ubn-scheme-light, var(--e-global-color-76c389d, #f9d3e6)) !important;
    border-color: var(--ubn-scheme-dark, var(--e-global-color-a2cf421, #084147)) !important;
}
.ubn-menu--light .ubn-circle-icon,
.ubn-menu--light .ubn-circle-icon-close {
    color: var(--ubn-scheme-dark, var(--e-global-color-a2cf421, #084147)) !important;
}

.ubn-menu--light .ubn-circle-icon--img img {
    filter: invert(1);
}

/* When the mobile overlay is OPEN, the trigger sits on the beam panel, which is
   always bound to a2cf421 (not the luminance-sorted --ubn-scheme-*). So lock the
   circle to the contrasting palette colour (76c389d, same as the menu links)
   regardless of the page-behind scheme; otherwise on pageloads where the circle
   resolved to a2cf421 it vanishes into the same-coloured panel. The close X takes
   the panel colour (a2cf421), which reads against the 76c389d disc. Higher
   specificity than the --dark/--light rules, so it wins. */
.ubn-menu--mobile.ubn-menu--open .ubn-circle,
.ubn-menu--mobile.ubn-menu--open .ubn-circle::after {
    background: var(--e-global-color-76c389d, #f9d3e6) !important;
    border-color: var(--e-global-color-a2cf421, #084147) !important;
}
/* The disc colour is painted on ::after by the --dark/--light scheme rules above, so the
   lock must target ::after too (3 classes beat their 2); locking only .ubn-circle left the
   scheme colour on top and the button could vanish into the panel. Closing the overlay
   drops --open, so the scroll-contrast scheme applies again. */
.ubn-menu--mobile.ubn-menu--open .ubn-circle-icon,
.ubn-menu--mobile.ubn-menu--open .ubn-circle-icon-close {
    color: var(--e-global-color-a2cf421, #084147) !important;
}

/* ============================================================
   Mobile mode — full-screen overlay (set by JS via .ubn-menu--mobile)

   Below the configured Elementor breakpoint, the trapezoid behavior is
   replaced by an opacity-fade full-viewport overlay opened by clicking
   the circle. The logo swaps for the configured close icon.
   ============================================================ */

/* Drop the trapezoid clip; we'll fade in/out instead. */
.ubn-menu--mobile .ubn-beam,
.ubn-menu--mobile .ubn-beam-blur {
    clip-path: none;
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* Defensive: defeat any compiled per-post Elementor selectors of the form
   `.elementor-NNNN ... .ubn-menu:hover .ubn-overlay` that pre-date the
   :not(.ubn-menu--mobile) scoping. Without this, hovering at mobile width
   on a widget whose post-CSS hasn't been regenerated will still fire the
   overlay/beam to opacity 1. The override only applies while CLOSED. */
.ubn-menu--mobile:not(.ubn-menu--open) .ubn-overlay,
.ubn-menu--mobile:not(.ubn-menu--open) .ubn-overlay-blur,
.ubn-menu--mobile:not(.ubn-menu--open) .ubn-beam,
.ubn-menu--mobile:not(.ubn-menu--open) .ubn-beam-blur {
    opacity: 0 !important;
    pointer-events: none !important;
}

/* No diagonal beam edges in mobile mode. */
.ubn-menu--mobile .ubn-beam-border-svg {
    display: none;
}

/* Open state — full-screen panel + content. */
.ubn-menu--mobile.ubn-menu--open .ubn-overlay,
.ubn-menu--mobile.ubn-menu--open .ubn-overlay-blur {
    opacity: 1;
}

.ubn-menu--mobile.ubn-menu--open .ubn-beam {
    opacity: 1;
    pointer-events: auto;
}

.ubn-menu--mobile.ubn-menu--open .ubn-beam-blur {
    opacity: 1;
}

.ubn-menu--mobile.ubn-menu--open .ubn-beam-link {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Close icon — replaces the logo when the overlay is open. The icon
   element renders whatever the user picked (font icon or inline SVG). */
.ubn-circle-icon-close {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Falls back to the circle trigger icon colour when --ubn-close-icon
       is unset by the widget control. */
    color: var(--ubn-close-icon, var(--ubn-circle-icon));
    font-size: 1.4rem;
    line-height: 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
}

.ubn-circle-icon-close i,
.ubn-circle-icon-close svg {
    width: 50%;
    height: 50%;
    fill: currentColor;
    color: currentColor;
    display: block;
}

.ubn-circle-icon {
    transition: opacity 0.2s ease;
}

.ubn-menu--mobile.ubn-menu--open .ubn-circle-icon {
    opacity: 0;
}

.ubn-menu--mobile.ubn-menu--open .ubn-circle-icon-close {
    opacity: 1;
}

/* When in mobile mode, snap the menu nav layout for menu source so links
   land in a centered column instead of pinned to a corner of the (now
   full-viewport) panel. Template source already uses --template which
   stretches to fill, so it needs no change. */
.ubn-menu--mobile .ubn-beam-nav--bottom-right,
.ubn-menu--mobile .ubn-beam-nav--bottom-left,
.ubn-menu--mobile .ubn-beam-nav--center {
    inset: 0;
    bottom: auto;
    right: auto;
    left: auto;
    top: 50%;
    transform: translateY(-50%);
    align-items: center;
    justify-content: center;
    padding: 0 6vw;
    gap: 1.2rem;
    text-align: center;
}

/* ============================================================
   Hover-intent open state (.ubn-is-open) — set by JS with a short
   grace delay so the beam stays open while the pointer crosses the
   dead zone between the round circle and the clipped trapezoid.
   Mirrors the desktop :hover rules; scoped away from mobile (which
   uses .ubn-menu--open). See ufo-beam-nav.js.
   ============================================================ */
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-overlay,
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-overlay-blur {
    opacity: 1;
}
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam {
    pointer-events: auto;
    clip-path: var(--ubn-clip-expanded);
}
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam-blur {
    clip-path: var(--ubn-clip-expanded);
    opacity: 1;
}
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam-link {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}
.ubn-menu:not(.ubn-menu--mobile).ubn-is-open .ubn-beam-border-svg {
    opacity: 1;
}
