/* ============================================================================
   AltSounds Tooltips — liquid-glass edition (April 2026 v2)
   ----------------------------------------------------------------------------
   Replaces the OS-default beige title="..." bubbles with a single sitewide
   custom popover system:
     - Liquid-glass surface: heavy blur + saturation lift, subtle inner-light
       highlight on the top edge instead of a hard border, soft luminous
       halo in the AltSounds neon-green.
     - IBM Plex Mono at 10.5px — refined, technical, AltSounds-flavored.
     - 350ms hover delay, scale-in reveal animation, dynamic flip-below.
     - CSS-only render: tooltip body and arrow are ::before/::after on the
       host element. No tooltip DOM is appended at runtime.
     - Companion altsounds-tooltips.js auto-converts [title] → [data-alts-tip]
       AND [aria-label] on interactive elements → [data-alts-tip], so a vast
       amount of existing chrome lights up with no markup churn.

   Activation:
     We render against [data-alts-tip] rather than [title] because the JS
     strips the native title to suppress the OS-default bubble.

   Position variants:
     Default = above. data-alts-tip-pos="below" flips it (set automatically
     by JS for elements near the viewport top).

   Text variants:
     Single-line by default with white-space: nowrap. Add data-alts-tip-wrap="1"
     for wrapped multi-line popovers.

   Suppress:
     data-alts-tip-skip="1" opts an element out entirely.
   ============================================================================ */

/* IBM Plex Mono — loaded inline so the tooltip is fully self-contained.
   The CSP at /header.php already permits fonts.googleapis.com and
   fonts.gstatic.com, and style-src includes 'unsafe-inline' so the
   @import below resolves cleanly. We pull only the weights we use
   (400 regular, 500 medium) to keep the request tiny — Plex Mono is
   ~30KB per weight, woff2-compressed. We pull only Latin + Latin Ext
   subsets via &subset= for the same reason.

   Why @import here instead of wp_enqueue: the tooltip system is the
   only consumer of Plex Mono on the site; bundling the load with the
   tooltip stylesheet keeps the surface area contained and means
   removing tooltips removes the font request as a side-effect. If
   another surface ever wants Plex Mono we can promote this to a real
   wp_enqueue_style at that point. */
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&display=swap');

/* The host element needs a stacking context so ::before/::after sit on
   top of siblings reliably. Most interactive elements already create
   one (button :focus, a outline) but defensive position: relative is
   safer than relying on it. */
[data-alts-tip] {
	position: relative;
}

/* v2.17.45 — Hide the diamond arrow on player + NP rail tooltips per
   . Native-looking caret feels heavy on the dense playbar / rail
   chrome; cleaner without it. Keep the arrow on Mothership panel +
   tile tooltips where the larger context handles it gracefully. */
.alts-playbar [data-alts-tip]::before,
.alts-guide__np-rail [data-alts-tip]::before {
	display: none !important;
}

/* THE BUBBLE.
   Liquid-glass surface — the layered approach:
     (1) base background = very dark, ~75% alpha so the backdrop-filter
         actually shows through. Solid 95% alpha would defeat the blur.
     (2) backdrop-filter combines blur + saturate. Saturation boost (1.6)
         is the iOS 26 / Vision Pro signature — the surface tints toward
         what's behind it instead of staying neutral.
     (3) inset highlight on the top edge via box-shadow inset
         0 1px 0 — gives the "glass catching light from above" look
         without a solid border.
     (4) outer glow halo in AltSounds neon-green at very low alpha — the
         brand accent without the chunkiness of a hard border.
     (5) neutral drop-shadow for depth. */
[data-alts-tip]::after {
	content: attr(data-alts-tip);
	position: absolute;
	left: 50%;
	bottom: calc(100% + 12px);   /* default = above */
	transform: translate(-50%, 4px) scale(0.96);
	transform-origin: 50% 100%;

	/* Layout */
	min-width: 0;
	max-width: 260px;
	padding: 7px 12px 6px;
	white-space: nowrap;

	/* Type — IBM Plex Mono. Uppercase + letter-spacing for a refined
	   technical feel that pairs with the AltSounds chrome typography. */
	font-family: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
	font-size: 10.5px;
	font-weight: 500;
	line-height: 1.4;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.92);
	text-align: center;

	/* SURFACE — v5 (April 2026): much more transparent.
	   Was ~0.78 alpha bg; v5 is 0.38 so the video / page behind shows
	   through clearly. The backdrop-filter blur+saturate does the heavy
	   visual lifting; the bg gradient just adds a subtle vertical
	   tonal depth. */
	
	background:
		linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01)),
		rgba(8, 10, 8, 0.88);
	border-radius: 12px;
	box-shadow:
		/* Inner top highlight for depth */
		inset 0 1px 0 rgba(255, 255, 255, 0.08),
		/* Single hairline outline — same as the arrow's border so the
		   two compose as one unbroken outline around the speech-bubble
		   shape (bubble + arrow). */
		0 0 0 1px rgba(255, 255, 255, 0.18),
		/* Soft drop shadow */
		0 8px 24px rgba(0, 0, 0, 0.45);

	backdrop-filter: blur(20px) saturate(1.7);
	-webkit-backdrop-filter: blur(20px) saturate(1.7);

	/* Hidden by default — pointer-events: none so the bubble itself never
	   blocks interaction with the host or with anything behind it. */
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	z-index: 2147483646;   /* near max — beats playbar (9400), PIP (9500) */

	transition:
		opacity 160ms ease,
		transform 200ms cubic-bezier(.34, 1.2, .54, 1),   /* slight overshoot */
		visibility 0s linear 160ms;
}

/* THE ARROW — v5 (April 2026).
   
   Approach changed from a 0×0 triangle (drawn via border-only) to an
   8×8 rotated square. Why: triangles can't have a border that wraps
   around them, so the bubble's outline used to stop at the bubble's
   bottom edge — the arrow looked detached. With a rotated square,
   we draw two of its CSS borders (left + top, which become the bottom
   two visible edges after a 45° rotation) at the SAME color as the
   bubble's outline. The diamond's top corner overlaps the bubble's
   bottom edge so the outline reads as one continuous speech-bubble
   shape around bubble + arrow.
   
   Backdrop-filter on the diamond means the same blur-saturate effect
   continues across the arrow, not just the bubble.
   
   We use ::before for the arrow because ::after is the bubble body. */
[data-alts-tip]::before {
	content: "";
	position: absolute;
	left: 50%;
	bottom: calc(100% + 7px);   /* overlap bubble's bottom by 1-2px so seam is hidden */
	width: 8px;
	height: 8px;
	border-left: 1px solid rgba(255, 255, 255, 0.18);
	border-top:  1px solid rgba(255, 255, 255, 0.18);
	background: rgba(8, 10, 8, 0.88);
	transform: translate(-50%, 4px) rotate(45deg);

	backdrop-filter: blur(20px) saturate(1.7);
	-webkit-backdrop-filter: blur(20px) saturate(1.7);

	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	z-index: 2147483646;

	transition:
		opacity 160ms ease,
		transform 200ms cubic-bezier(.34, 1.2, .54, 1),
		visibility 0s linear 160ms;
}

/* HOVER / FOCUS — 350ms delay before reveal.
   transition-delay (not duration) means the bubble waits 350ms then
   fades in over 160ms with a slight scale-up overshoot. On hover-out
   it returns to the no-delay base rule so it disappears immediately
   — quick mouse passes across a row of icons don't fire popovers. */
[data-alts-tip]:hover::after,
[data-alts-tip]:hover::before,
[data-alts-tip]:focus-visible::after,
[data-alts-tip]:focus-visible::before {
	opacity: 1;
	visibility: visible;
	transform: translate(-50%, 0) scale(1);
	transition:
		opacity 160ms ease 350ms,
		transform 240ms cubic-bezier(.34, 1.2, .54, 1) 350ms,
		visibility 0s linear 350ms;
}

/* The arrow keeps its 45° rotation while sliding to the hover position.
   Without this, the hover transform would un-rotate the arrow back into
   a square — the diamond shape only renders correctly when rotate(45deg)
   is applied. Same goes for the flip-below variant below. */
[data-alts-tip]:hover::before,
[data-alts-tip]:focus-visible::before {
	transform: translate(-50%, 0) rotate(45deg);
}

/* v2.17.60 — RIGHT-EDGE ALIGN VARIANT.
   The default centered tooltip extends ~50% of its width to each side
   of the host. For buttons that sit flush against an overflow:hidden
   container's right edge (history row action buttons, mostly), the
   right half of the bubble gets clipped. data-alts-tip-align="right"
   anchors the bubble's RIGHT edge to the host's right edge so it
   expands LEFT into open layout space — guaranteed to stay visible
   regardless of how wide the text is or how narrow the container is. */
[data-alts-tip][data-alts-tip-align="right"]::after {
    left: auto;
    right: -4px;
    transform: translate(0, 4px) scale(0.96);
    transform-origin: 100% 100%;
}
[data-alts-tip][data-alts-tip-align="right"]:hover::after,
[data-alts-tip][data-alts-tip-align="right"]:focus-visible::after {
    transform: translate(0, 0) scale(1);
}
[data-alts-tip][data-alts-tip-align="right"]::before {
    left: auto;
    right: 8px;
    transform: translate(0, 4px) rotate(45deg);
}
[data-alts-tip][data-alts-tip-align="right"]:hover::before,
[data-alts-tip][data-alts-tip-align="right"]:focus-visible::before {
    transform: translate(0, 0) rotate(45deg);
}

/* FLIP-BELOW VARIANT.
   Rendered when an element sits near the top of the viewport — companion
   JS sets data-alts-tip-pos="below" automatically. Mirrors above: bubble
   under the host, arrow flipped. */
[data-alts-tip][data-alts-tip-pos="below"]::after {
	bottom: auto;
	top: calc(100% + 12px);
	transform: translate(-50%, -4px) scale(0.96);
	transform-origin: 50% 0%;
}
[data-alts-tip][data-alts-tip-pos="below"]:hover::after,
[data-alts-tip][data-alts-tip-pos="below"]:focus-visible::after {
	transform: translate(-50%, 0) scale(1);
}
[data-alts-tip][data-alts-tip-pos="below"]::before {
	bottom: auto;
	top: calc(100% + 7px);
	/* Flipped variant: when arrow points UP, the visible diamond borders
	   are the right + bottom (which become top-right + top-left after
	   45° rotation — i.e. the V opening upward toward the bubble). */
	border-left: 0;
	border-top:  0;
	border-right:  1px solid rgba(255, 255, 255, 0.18);
	border-bottom: 1px solid rgba(255, 255, 255, 0.18);
	transform: translate(-50%, -4px) rotate(45deg);
}
[data-alts-tip][data-alts-tip-pos="below"]:hover::before,
[data-alts-tip][data-alts-tip-pos="below"]:focus-visible::before {
	transform: translate(-50%, 0) rotate(45deg);
}

/* WRAP VARIANT — multi-line tooltips for longer explanatory copy.
   Same 280px max-width but allows wrapping. The wider variant is good
   for "first-time here" educational tooltips on chrome that's not
   self-explanatory. */
[data-alts-tip][data-alts-tip-wrap="1"]::after {
	white-space: normal;
	max-width: 280px;
	text-align: left;
	letter-spacing: 0.01em;
	line-height: 1.5;
	padding: 9px 13px 8px;
}

/* SUPPRESSION.
   On touch devices we suppress entirely. iOS sticks tooltips after tap
   (no hover-out event), which is a known nuisance. (hover: none) catches
   touch primary input and stylus-only inputs. */
@media (hover: none) {
	[data-alts-tip]::after,
	[data-alts-tip]::before {
		display: none !important;
	}
}

/* Author opt-out — element-level suppression alongside the data-attr
   gate in JS. Defense in depth so the bubble can't render even if the
   attribute slips through the JS converter. */
[data-alts-tip][data-alts-tip-skip="1"]::after,
[data-alts-tip][data-alts-tip-skip="1"]::before {
	display: none !important;
}

/* CONTEXT-AWARE SUPPRESSION (April 2026 v3 cleanup)
   ----------------------------------------------------------------
   Some chrome surfaces have title="..." attributes that ARE useful
   in some viewport / state combinations but pure noise in others.
   Rather than adding/removing the title with JS based on context,
   we render the title attr always and let CSS decide visibility.

   Mothership nav items: title duplicates the visible label at wide
   viewports (>=1280px), so suppress there. At narrow viewports
   the nav goes icon-only and the title becomes the only affordance
   — let it render. Mirrors the .alts-guide__nav-item-label hide
   media query in guide.css. */
@media (min-width: 1280px) {
	.alts-guide__nav-item[data-alts-tip]::after,
	.alts-guide__nav-item[data-alts-tip]::before {
		display: none !important;
	}
}

/* Mothership close × and stage-shrink × — icon is universally
   understood, no hover hint needed. These rules suppress the
   tooltip without removing the title attr (so the (Esc) keyboard
   hint is still attached to the element for assistive tech). */
.alts-guide__xclose[data-alts-tip]::after,
.alts-guide__xclose[data-alts-tip]::before,
.alts-guide__stage-shrink[data-alts-tip]::after,
.alts-guide__stage-shrink[data-alts-tip]::before,
.alts-guide__np-expand[data-alts-tip]::after,
.alts-guide__np-expand[data-alts-tip]::before,
.alts-guide__nav-collapse[data-alts-tip]::after,
.alts-guide__nav-collapse[data-alts-tip]::before,
.alts-guide__search-clear[data-alts-tip]::after,
.alts-guide__search-clear[data-alts-tip]::before,
.alts-guide__np-artist-station[data-alts-tip]::after,
.alts-guide__np-artist-station[data-alts-tip]::before {
	display: none !important;
}
/* v2.18.122 — Let the ARTIST INFO tooltip escape NP RAIL's overflow.
   .alts-guide__np-rail has overflow-y: auto + overflow-x: hidden as a
   defensive clip. A bubble positioned with position: absolute does NOT
   escape that clip — only position: fixed does, and position: fixed
   would untie us from the host's coordinates (no anchor without JS).
   :has(:hover) flips the rail's overflow to visible only while the
   artist link is hovered, so the bubble can float past the rail's
   bottom edge. Hover-out restores the clip. The artist row is near the
   top of the rail so scroll-y is always 0 here — no scroll-state jump. */
.alts-guide__np-rail:has(.alts-guide__np-artist-info[data-alts-tip]:hover),
.alts-guide__np-rail:has(.alts-guide__np-artist-info[data-alts-tip]:focus-visible) {
    overflow: visible !important;
}

/* v2.18.121 — NOT in suppression list anymore:
   .alts-guide__np-artist-info[data-alts-tip] (the ARTIST INFO link).
   It was added to this suppression block long ago and was the ACTUAL
   cause of the "ARTIST MODE tooltip never shows" bug across ~ten
   release cycles. Every other "fix" was treating a symptom because
   this rule overrode them with !important. Pulling it from the list
   lets the standard tooltip rules apply. */

/* REDUCED MOTION.
   Honor prefers-reduced-motion: drop the scale animation and the
   overshoot easing, just fade. Important for vestibular-sensitive
   users. */
@media (prefers-reduced-motion: reduce) {
	[data-alts-tip]::after,
	[data-alts-tip]::before {
		transition:
			opacity 120ms linear,
			visibility 0s linear 120ms;
		transform: translate(-50%, 0);
	}
	[data-alts-tip]:hover::after,
	[data-alts-tip]:hover::before,
	[data-alts-tip]:focus-visible::after,
	[data-alts-tip]:focus-visible::before {
		transform: translate(-50%, 0);
		transition:
			opacity 120ms linear 350ms,
			visibility 0s linear 350ms;
	}
}

/* ============================================================================
   BUSY-STATE SPINNER RESTORE — v5 (April 2026)
   ----------------------------------------------------------------------------
   The transport buttons (prev / next / shuffle) use ::before for their
   loading spinner. The tooltip CSS also targets ::before for the diamond
   arrow. Pseudo-elements don't compose — only one rule "wins" per
   pseudo per element — and the tooltip's "hidden until hover" default
   was clobbering the spinner's visibility.
   
   This block runs only when an element has BOTH data-alts-tip AND
   data-busy="1". It restores the spinner's geometry + animation +
   visibility, and explicitly sets opacity:1 / visibility:visible so
   the spinner is always shown (no hover dependency). It also hides
   the bubble (::after) since you can't hover a button that's
   loading anyway.
   
   Direct fix for the "loading icons start working but then disappear"
   bug — the spinner pseudo was being made invisible by the tooltip
   CSS's hover-only visibility model.
   ============================================================================ */
[data-alts-tip][data-busy="1"]::before {
	/* Geometry — match the spinner CSS in altsounds-playbar.css */
	width: 16px;
	height: 16px;
	margin: -8px 0 0 -8px;
	top: 50%;
	left: 50%;
	bottom: auto;
	/* Spinner ring — 2px translucent border with neon-green top */
	border: 2px solid rgba(255, 255, 255, 0.25);
	border-top-color: var(--alts-color-accent, #bfff00);
	border-radius: 50%;
	/* Wipe the tooltip-arrow decorations */
	background: none;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
	box-shadow: none;
	filter: none;
	transform: none;
	/* Spinner animation */
	animation: alts-playbar-spin 0.7s linear infinite;
	/* CRITICAL: force visible regardless of hover. The tooltip's default
	   is opacity:0; visibility:hidden — those would suppress the spinner
	   when the user clicks Next and the cursor isn't over the button. */
	opacity: 1;
	visibility: visible;
}
/* Hide the bubble during busy state — no tooltip on a loading button. */
[data-alts-tip][data-busy="1"]::after {
	opacity: 0;
	visibility: hidden;
}
