/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
//#region ../css/dist/css/postcss.helpers.mjs
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
const COLOR_MODE_RE = /^(\.(uds-color-mode-(?:dark|light)))(\s+.+)?$/;
const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const SCOPED_SELF_OR_PARENT_RE = /^(\.[^\s>+~]+)(.*)$/;
const getScopedColorModeAlternativeSelector = (selector, scopeClass, variant = "descendant") => {
	const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
	if (!scopeMatch) return null;
	const colorModeMatch = scopeMatch[2].match(COLOR_MODE_RE);
	if (!colorModeMatch) return null;
	const colorModeClass = colorModeMatch[1];
	const rest = colorModeMatch[3] ?? "";
	return variant === "same-element" ? `${colorModeClass}${scopeClass}${rest}` : `${colorModeClass} ${scopeClass}${rest}`;
};
const buildScopedColorModeSelectorList = (ruleSelector, scopeClass) => {
	const selectors = ruleSelector.split(",").map((value) => value.trim());
	return selectors.reduce((acc, selector) => {
		const nextAltSelectors = [getScopedColorModeAlternativeSelector(selector, scopeClass), getScopedColorModeAlternativeSelector(selector, scopeClass, "same-element")].filter((value) => Boolean(value)).filter((altSelector) => !selectors.includes(altSelector) && !acc.includes(altSelector));
		return [
			...acc,
			selector,
			...nextAltSelectors
		];
	}, []).join(", ");
};
const getScopedSelfOrParentAlternativeSelector = (selector, scopeClass) => {
	const scopeMatch = selector.match(new RegExp(`^(${escapeForRegExp(scopeClass)})\\s+(.+)$`));
	if (!scopeMatch) return null;
	const targetMatch = scopeMatch[2].match(SCOPED_SELF_OR_PARENT_RE);
	if (!targetMatch) return null;
	const scopedTarget = targetMatch[1];
	if (/^\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) return null;
	return `${scopeClass}${scopedTarget}${targetMatch[2]}`;
};
/**
* Insert the isolation-exclusion suffix
* `:not(:where(:is(.A, .B, …) *))` into a single selector. The list is the
* set of scope classes belonging to OTHER scoped bundles loaded on the page;
* the suffix causes this bundle's rule to skip elements whose ancestor chain
* contains any of those scope classes, preventing rule bleed-through into
* another scoped library's subtree.
*
* For selectors ending in a pseudo-element (`.x::placeholder`, `.x::before`,
* etc.), the suffix is inserted *before* the pseudo-element marker, since
* pseudo-elements must remain at the very end of a selector and the
* exclusion semantically applies to the host element.
*
* PRECONDITION: a pseudo-element, when present, is the trailing token of the
* selector — true for every UDS/Tailwind-generated rule this plugin runs on.
* Detection is a simple `lastIndexOf('::')`, so a pseudo-element in a non-final
* compound (`.x::before .y`) or a literal `::` inside an attribute value would
* be mis-handled; neither shape occurs in the generated CSS this processes.
*
* Returns the selector unchanged when `otherBoundaryScopes` is empty —
* there's nothing to exclude.
*/
const injectIsolationExclusion = (selector, otherBoundaryScopes) => {
	if (otherBoundaryScopes.length === 0) return selector;
	const suffix = `:not(:where(:is(${otherBoundaryScopes.map((scope) => {
		const trimmed = scope.trim();
		return /^[.#[:]/.test(trimmed) ? trimmed : `.${trimmed}`;
	}).join(", ")}) *))`;
	const pseudoElementIdx = selector.lastIndexOf("::");
	if (pseudoElementIdx === -1) return `${selector}${suffix}`;
	return `${selector.slice(0, pseudoElementIdx)}${suffix}${selector.slice(pseudoElementIdx)}`;
};
/**
* State pseudo-classes UDS emits for non-rest states: interactive
* (`:hover`, `:active`, `:focus`, `:focus-within`, `:focus-visible`,
* `:visited`, legacy `:disabled`) and `:has(…)` — which UDS uses only for the
* `invalid`/`readonly`/`placeholder-shown`/`autofill`/`disabled` atoms. The
* rest baseline carries none of these. Pseudo-ELEMENTS (`::placeholder`,
* `::before`) use `::` so the single-colon matcher skips them; the structural
* `:where()`/`:is()`/`:not()` (hover blockers and the isolation suffix itself)
* are not states.
*
* MAINTENANCE: `readonly`/`placeholder-shown`/`autofill` (and `invalid`/
* `disabled`) are intentionally absent as direct terms — UDS emits them wrapped
* in `:has(…)`, so the `:has\(` branch already catches them. If a component ever
* emits one of those atoms as a DIRECT pseudo-class (e.g. `:read-only`), add it
* here: a missed state selector is a false negative (state bleed — the leak this
* fence prevents), whereas an over-match would only fence a value-safe rest rule.
*/
const STATE_PSEUDO_RE = /:(?:hover|active|visited|disabled|focus(?:-within|-visible)?)\b|:has\(/;
/**
* True when a selector targets a non-rest (state) rule. Only non-rest theme
* rules need the isolation fence: a rest theme rule leaking into a nested
* foreign scope resolves its `var(--uds-*)` to that scope's own tokens
* (postcss-scope rewrites `:root`→scope), so it is value-safe and fencing it
* would only add bytes to the wire.
*/
const isNonRestStateSelector = (selector) => STATE_PSEUDO_RE.test(selector);
//#endregion
export { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector, injectIsolationExclusion, isNonRestStateSelector };
