/*
 * Visoop — Offcanvas widget.
 *
 * Architecture:
 *   .visoop-oc        — fixed full-viewport wrapper, holds overlay + panel,
 *                        only `display: block` when `is-open`.
 *   .visoop-oc__overlay — dimmed backdrop, clicking dismisses.
 *   .visoop-oc__panel   — the actual drawer; transform-based slide/fade/scale.
 *   .visoop-oc__inner   — scrollable content area, receives the rendered
 *                        Elementor template.
 *
 * Animations use CSS custom properties so the widget controls can tune
 * duration / easing / stagger delay without rewriting selectors:
 *   --vo-duration   panel transition time
 *   --vo-easing     panel transition easing
 *   --vo-stagger    delay between direct children when stagger is on
 *
 * Child stagger: when --stagger is on, each direct child of .visoop-oc__inner
 * starts hidden + translated and animates in with a delay equal to its
 * index × --vo-stagger. Implemented with a small set of :nth-child rules
 * (covers the first 10 children; beyond that they share the last delay).
 */

/* ── Trigger ───────────────────────────────────────────────────────────── */
.visoop-oc-trigger-wrap {
	display: flex;
	width: 100%;
}
.visoop-oc-trigger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	border: 0;
	background: transparent;
	cursor: pointer;
	color: inherit;
	font: inherit;
	line-height: 1;
	padding: 10px 14px;
	transition: color .2s ease, background-color .2s ease;
	-webkit-tap-highlight-color: transparent;
}
.visoop-oc-trigger__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	line-height: 0;
}
.visoop-oc-trigger__icon svg {
	width: 1em;
	height: 1em;
	display: block;
}

/* ── Wrapper + overlay ─────────────────────────────────────────────────── */
.visoop-oc {
	--vo-duration: 450ms;
	--vo-easing: cubic-bezier(0.22, 1, 0.36, 1);
	--vo-stagger: 80ms;
	position: fixed;
	inset: 0;
	/* z-index 99998 sits just below WordPress's `#wpadminbar` (99999) so
	   the admin bar stays visible for logged-in users, and well above
	   every common page-chrome layer (sticky headers, banners, sticky
	   CTAs, etc. typically 100–10000). The portal-to-body in JS handles
	   any remaining stacking-context confinement. */
	z-index: 99998;
	pointer-events: none;
	visibility: hidden;
	/* Keep the wrapper visible for the full close animation, then hide.
	   Without this delayed hide, removing `is-open` flips visibility off
	   instantly and cuts the panel's slide-out short — so close looked like a
	   fade instead of a slide. On open, `.is-open` sets the delay back to 0 so
	   the panel appears immediately. */
	transition: visibility 0s linear var(--vo-duration);
}
.visoop-oc.is-open {
	pointer-events: auto;
	visibility: visible;
	transition-delay: 0s;
}
.visoop-oc__overlay {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, .55);
	opacity: 0;
	transition: opacity var(--vo-duration) var(--vo-easing);
	cursor: pointer;
}
.visoop-oc.is-open .visoop-oc__overlay {
	opacity: 1;
}

/* ── Panel base ────────────────────────────────────────────────────────── *
   Visual defaults (background colour, drop-shadow) live in :where() so the
   Panel Style → Background group control can override them at any
   specificity — including gradients, which the previous flat `background:
   #fff` declaration was beating in the cascade when Elementor's per-post
   CSS file hadn't picked up the user's change yet. Structural rules
   (position, transitions, will-change) stay outside :where() because the
   widget needs them no matter what styling the user layers on top. */
.visoop-oc__panel {
	position: absolute;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	transition: transform var(--vo-duration) var(--vo-easing),
	            opacity var(--vo-duration) var(--vo-easing);
	will-change: transform, opacity;
}
:where(.visoop-oc__panel) {
	/* Transparent by default so the rendered template's OWN background defines
	   the drawer's look. A hard-coded colour here (previously #fff) flashes
	   behind a dark template while it slides in / staggers, before the content
	   paints — the "white glitch" on open. Users who want a solid drawer fill
	   set Panel Style → Background, which overrides this. */
	background: transparent;
}
.visoop-oc__inner {
	flex: 1 1 auto;
	overflow-y: auto;
	overflow-x: hidden;
	/* Zero default — the rendered Elementor template owns its own
	   spacing. The Panel Style → Padding control overrides this when
	   the user wants the offcanvas to add chrome of its own. */
	padding: 0;
	-webkit-overflow-scrolling: touch;
	/* Hide the visual scrollbar without losing scroll ability. The child
	   stagger animation briefly translates each direct child by 14px,
	   which can push the content past the panel by exactly that amount
	   for a few hundred milliseconds — long enough for a scrollbar to
	   flash in and out. Hiding the bar itself is the lightest fix: tall
	   content can still be reached with the wheel / touch / keyboard,
	   and "fit-to-screen" templates (the common case) never reveal a
	   stray scrollbar on every open. */
	scrollbar-width: none; /* Firefox */
}
.visoop-oc__inner::-webkit-scrollbar {
	display: none; /* WebKit / Blink */
}

/* ── Position × size ───────────────────────────────────────────────────── *
   Wrapped in :where() so they carry zero specificity — the per-instance
   Elementor inline CSS (and the size-mode override rules below) always
   win without specificity gymnastics.

   Custom-mode width / height are read from CSS variables that the Width
   and Height controls set on `.visoop-oc`. If the inline CSS for a
   particular widget instance hasn't been regenerated yet, the variables
   fall back to the original 420px / 60vh defaults — no broken layout. */
:where(.visoop-oc[data-position="left"] .visoop-oc__panel) {
	top: 0; bottom: 0; left: 0;
	width: var(--vo-panel-w, 420px);
	max-width: 100%;
}
:where(.visoop-oc[data-position="right"] .visoop-oc__panel) {
	top: 0; bottom: 0; right: 0;
	width: var(--vo-panel-w, 420px);
	max-width: 100%;
}
:where(.visoop-oc[data-position="top"] .visoop-oc__panel) {
	left: 0; right: 0; top: 0;
	height: var(--vo-panel-h, 60vh);
	max-height: 100%;
}
:where(.visoop-oc[data-position="bottom"] .visoop-oc__panel) {
	left: 0; right: 0; bottom: 0;
	height: var(--vo-panel-h, 60vh);
	max-height: 100%;
}

/* Size mode overrides — :where() too so they don't fight the user's
   custom-mode variable when they later switch back. */
:where(.visoop-oc--full[data-position="left"] .visoop-oc__panel,
       .visoop-oc--full[data-position="right"] .visoop-oc__panel) {
	width: 100%;
}
:where(.visoop-oc--full[data-position="top"] .visoop-oc__panel,
       .visoop-oc--full[data-position="bottom"] .visoop-oc__panel) {
	height: 100%;
}
:where(.visoop-oc--half[data-position="left"] .visoop-oc__panel,
       .visoop-oc--half[data-position="right"] .visoop-oc__panel) {
	width: 50%;
}
:where(.visoop-oc--half[data-position="top"] .visoop-oc__panel,
       .visoop-oc--half[data-position="bottom"] .visoop-oc__panel) {
	height: 50%;
}

/* ── Animation: SLIDE (default) ────────────────────────────────────────── */
.visoop-oc--slide[data-position="left"]   .visoop-oc__panel { transform: translateX(-100%); }
.visoop-oc--slide[data-position="right"]  .visoop-oc__panel { transform: translateX(100%); }
.visoop-oc--slide[data-position="top"]    .visoop-oc__panel { transform: translateY(-100%); }
.visoop-oc--slide[data-position="bottom"] .visoop-oc__panel { transform: translateY(100%); }
.visoop-oc--slide.is-open .visoop-oc__panel {
	transform: translate(0, 0);
}

/* ── Animation: FADE ───────────────────────────────────────────────────── */
.visoop-oc--fade .visoop-oc__panel {
	opacity: 0;
}
.visoop-oc--fade.is-open .visoop-oc__panel {
	opacity: 1;
}

/* ── Animation: SCALE ──────────────────────────────────────────────────── */
.visoop-oc--scale .visoop-oc__panel {
	opacity: 0;
	transform: scale(0.96);
	transform-origin: center;
}
.visoop-oc--scale.is-open .visoop-oc__panel {
	opacity: 1;
	transform: scale(1);
}

/* ── Close button ──────────────────────────────────────────────────────── */
.visoop-oc__close {
	position: absolute;
	top: 16px;
	width: 32px;
	height: 32px;
	border: 0;
	background: transparent;
	color: inherit;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	transition: transform .25s ease, color .2s ease, background-color .2s ease;
	z-index: 2;
	-webkit-tap-highlight-color: transparent;
}
.visoop-oc__panel--top-right .visoop-oc__close { right: 16px; }
.visoop-oc__panel--top-left  .visoop-oc__close { left: 16px; }
.visoop-oc__close:hover {
	transform: rotate(90deg);
}
.visoop-oc__close svg {
	width: 60%;
	height: 60%;
}

/* ── Child stagger ─────────────────────────────────────────────────────── *
   When the wrapper opens, fade & translate every direct child of __inner
   in turn. We seed the hidden state on the children unconditionally so
   there's no FOUC flash before the open class lands; the keyframes fire
   only when .is-open is added.
   ────────────────────────────────────────────────────────────────────────*/
.visoop-oc--stagger .visoop-oc__inner > * {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity .55s var(--vo-easing), transform .55s var(--vo-easing);
}
.visoop-oc--stagger.is-open .visoop-oc__inner > * {
	opacity: 1;
	transform: translateY(0);
}
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(1)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 0); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(2)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 1); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(3)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 2); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(4)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 3); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(5)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 4); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(6)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 5); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(7)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 6); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(8)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 7); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(9)  { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 8); }
.visoop-oc--stagger.is-open .visoop-oc__inner > *:nth-child(n+10) { transition-delay: calc(var(--vo-duration) * 0.4 + var(--vo-stagger) * 9); }

/* ── Notice (editor placeholder when no template chosen) ───────────────── */
.visoop-oc__notice {
	padding: 24px;
	border: 1px dashed rgba(0, 0, 0, .15);
	border-radius: 8px;
	color: #6b7280;
	font-style: italic;
	text-align: center;
}

/* ── Respect reduced-motion preference ─────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
	.visoop-oc__panel,
	.visoop-oc__overlay,
	.visoop-oc--stagger .visoop-oc__inner > * {
		transition-duration: 0.01ms !important;
	}
}

/* ── Body-scroll lock helper added by JS when any offcanvas opens ──────── */
body.visoop-oc-open {
	overflow: hidden;
}

/* ── Mobile (≤ 960px) — hide the offcanvas trigger ─────────────────────── *
   Below the same breakpoint where the Nav Menu widget collapses into a
   hamburger (nav-menu.css), the offcanvas trigger steps aside so the
   header doesn't carry two "menu" controls — the hamburger now does the
   primary navigation, and any offcanvas would just be redundant clutter.
   Sites that genuinely want an offcanvas on mobile (cart drawer, filter
   panel, etc.) can re-enable it by overriding this with their own CSS. */
@media (max-width: 960px) {
	.visoop-oc-trigger-wrap {
		display: none;
	}
}

/* ── Portal wrapper added by JS when the panel is moved to <body> ─────── *
   Carries the original `elementor-element-XXX` class so per-instance
   inline CSS keeps matching. We force layout-neutral defaults here so
   Elementor's generic widget rules (margins, paddings, max-widths) don't
   spill onto the portal and accidentally constrain a full-viewport
   panel. The panel inside is position:fixed so the portal's own box
   never participates in layout. */
.visoop-oc-portal {
	position: static;
	margin: 0;
	padding: 0;
	width: auto;
	max-width: none;
	display: block;
}
