/**
 * Roxy UI design tokens. 47 CSS custom properties that every component reads. Light + dark defaults. Host page overrides at :root or per element. No Tailwind, no class-name overrides, no JS theme switcher.
 *
 * Delivery model: this file ships as a global light-DOM stylesheet (linked, or auto-injected by the CDN bundle). Custom properties inherit through the shadow boundary, so tokens set on a light-DOM ancestor reach every component shadow. The explicit theme blocks therefore target light-DOM selectors (:root, .dark, [data-theme="dark"], a wrapper element), NOT :host (a :host rule in a global sheet matches nothing). The :host([data-theme]) selectors are kept only for the case where this CSS is ever adopted directly into a shadow root; they are inert in global delivery.
 *
 * Light is the default. Dark applies under any of:
 *   - prefers-color-scheme: dark
 *   - [data-theme="dark"] on the element itself or any ancestor (typically :root)
 *   - .dark class on the element itself or any ancestor (typically :root)
 *
 * Explicit choice wins over the OS: the [data-theme] / .dark blocks come after the @media (prefers-color-scheme: dark) block in source order, and a closer ancestor overrides a farther one through normal custom-property inheritance.
 *
 * Motion gating:
 *   - --roxy-motion-duration defaults to 200ms. Set to 0ms to disable.
 *   - prefers-reduced-motion: reduce forces 0ms regardless of host overrides.
 */

/*
 * Every selector below is wrapped in :where(), which has ZERO specificity.
 *
 * Source order still decides among these rules, so the explicit dark block still beats the
 * light defaults when it matches. But ANY declaration you write outranks all of them, whatever
 * selector you use. That is what makes a plain `:root { --roxy-accent: #8b5cf6 }` actually
 * rebrand the library.
 *
 * Without it, `:root.dark` and `[data-theme="dark"]` carry class/attribute specificity (0,1,1)
 * and outrank a plain `:root` (0,1,0). A brand override would then hold under
 * prefers-color-scheme and be reverted under the other two dark signals: one override, three
 * different results.
 *
 * Do NOT reach for a cascade layer instead. Layers mean UNLAYERED rules win, so every theme
 * block becomes weaker than ANY unlayered rule on the page (a consumer's Tailwind reset, a demo
 * page's own CSS) and dark mode stops applying at all. :where() lowers only OUR specificity,
 * which is exactly the intent, and leaves the cascade otherwise untouched.
 */
:where(:root, :host) {
	/* Color: brand */
	--roxy-primary: #0f172a;
	--roxy-secondary: #475569;
	--roxy-accent: #f59e0b;
	/* Text-safe accent variant, DERIVED from --roxy-accent so overriding the accent
	 * alone rebrands the whole library. Never hardcode a colour here: a consumer
	 * following the theming recipe sets only --roxy-accent, and a fixed value leaves
	 * the active tab label, the conjunction aspect lines and the focus ring on the
	 * old hue, giving a two-tone palette from a one-line override.
	 *
	 * The raw accent is too light for text (amber-500 is 2.14:1 on white, far under
	 * WCAG AA). Mixing 70% of it into black is measured, not guessed: rasterised to
	 * real pixels, the darkest of the twelve shipped domain accents still lands at
	 * 5.24:1 on white, above AA. At 75% amber falls to 4.46 and fails, so 70 is a
	 * ceiling, not a round number.
	 *
	 * Fills (planet glyphs, chart accents, non-text badges) keep --roxy-accent. */
	--roxy-accent-ink: color-mix(in oklab, var(--roxy-accent) 70%, black);

	/* Color: status. The base value (success, warning, danger, info) is for
	 * fills and tints. The -fg variant is darker so text on a light tint passes
	 * WCAG AA (>= 4.5:1 on white). Use -fg whenever status color hits text. */
	--roxy-success: #16a34a;
	--roxy-success-fg: #166534;
	--roxy-warning: #ea580c;
	--roxy-warning-fg: #9a3412;
	--roxy-danger: #dc2626;
	--roxy-danger-fg: #991b1b;
	--roxy-info: #0284c7;
	--roxy-info-fg: #075985;

	/* Color: surface */
	--roxy-bg: #ffffff;
	--roxy-surface: #ffffff;
	--roxy-fg: #0a0a0a;
	--roxy-muted: #71717a;
	--roxy-border: #e4e4e7;
	--roxy-ring: color-mix(in srgb, var(--roxy-accent) 40%, transparent);

	/* Typography */
	--roxy-font-sans:
		"Geist", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
		sans-serif;
	--roxy-font-mono:
		"Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono",
		monospace;
	/* Heading/display font. Defaults to the sans stack so nothing changes for an
	 * existing consumer; a preset (the practitioner theme ships a serif) overrides
	 * it to restyle every result heading and the self-fetch form title in one token.
	 * Fonts do not vary by theme, so it is declared once here and inherited by the
	 * dark/light blocks unchanged. */
	--roxy-font-display: var(--roxy-font-sans);

	--roxy-text-xs: 0.75rem;
	--roxy-text-sm: 0.875rem;
	--roxy-text-base: 1rem;
	--roxy-text-lg: 1.125rem;
	--roxy-text-xl: 1.5rem;

	--roxy-weight-normal: 400;
	--roxy-weight-bold: 600;

	--roxy-leading-tight: 1.2;
	--roxy-leading-normal: 1.5;

	--roxy-tracking-tight: -0.02em;
	--roxy-tracking-normal: 0em;

	/* Spacing */
	--roxy-space-xs: 0.25rem;
	--roxy-space-sm: 0.5rem;
	--roxy-space-md: 1rem;
	--roxy-space-lg: 1.5rem;
	--roxy-space-xl: 2.5rem;

	/* Radius */
	--roxy-radius-sm: 4px;
	--roxy-radius-md: 8px;
	--roxy-radius-lg: 12px;
	--roxy-radius-full: 9999px;

	/* Shadow */
	--roxy-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
	--roxy-shadow-md:
		0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.06);
	--roxy-shadow-lg: 0 12px 24px -8px rgba(0, 0, 0, 0.14);

	/* Motion */
	--roxy-motion-duration: 200ms;
	--roxy-motion-easing: cubic-bezier(0.4, 0, 0.2, 1);

	/* Heat scale: a single base hue. Consumers mix it with `transparent` at
	 * increasing percentages to paint cooler-to-hotter tiers. The mix-with-
	 * transparent approach lets the page bg show through and keeps text
	 * colour stable (always --roxy-fg), so the cells read in both themes
	 * without per-tier text colours. */
	--roxy-heat: var(--roxy-danger, #ef4444);
}

/* Dark theme via system preference */
@media (prefers-color-scheme: dark) {
	:where(:root, :host) {
		--roxy-primary: #f8fafc;
		--roxy-secondary: #94a3b8;
		--roxy-accent: #fbbf24;
		--roxy-accent-ink: var(--roxy-accent);

		/* Status -fg overrides must mirror the explicit [data-theme="dark"] block
		 * below. Without these, a user on system dark with no data-theme attribute
		 * inherits the light-mode -fg values (e.g. #166534 success) which renders
		 * as near-invisible dark green on a near-black surface and fails WCAG AA. */
		--roxy-success: #22c55e;
		--roxy-success-fg: #86efac;
		--roxy-warning: #fb923c;
		--roxy-warning-fg: #fdba74;
		--roxy-danger: #ef4444;
		--roxy-danger-fg: #fca5a5;
		--roxy-info: #38bdf8;
		--roxy-info-fg: #7dd3fc;

		--roxy-bg: #0a0a0a;
		--roxy-surface: #18181b;
		--roxy-fg: #fafafa;
		--roxy-muted: #a1a1aa;
		--roxy-border: #27272a;
		--roxy-ring: color-mix(in srgb, var(--roxy-accent) 45%, transparent);

		--roxy-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
		--roxy-shadow-md:
			0 4px 6px -1px rgba(0, 0, 0, 0.6), 0 2px 4px -2px rgba(0, 0, 0, 0.5);
		--roxy-shadow-lg: 0 12px 24px -8px rgba(0, 0, 0, 0.7);

		--roxy-heat: var(--roxy-danger, #ef4444);
	}
}

/* Light theme override. Beats the @media (prefers-color-scheme: dark) block
 * when the host explicitly opts back into light. Without this, a dark-mode OS
 * pins the tokens to dark even after data-theme="light" is set. The light-DOM
 * selectors (:root.light, .light, [data-theme="light"]) set the vars on the
 * ancestor or the element itself so they inherit into every component shadow;
 * :host([data-theme="light"]) is inert in global delivery and kept only for
 * shadow-adopted use. */
:where(
	:root[data-theme="light"],
	[data-theme="light"],
	:root.light,
	.light,
	:host([data-theme="light"])
) {
	--roxy-primary: #0f172a;
	--roxy-secondary: #475569;
	--roxy-accent: #f59e0b;
	--roxy-accent-ink: color-mix(in oklab, var(--roxy-accent) 70%, black);

	--roxy-success: #16a34a;
	--roxy-success-fg: #166534;
	--roxy-warning: #ea580c;
	--roxy-warning-fg: #9a3412;
	--roxy-danger: #dc2626;
	--roxy-danger-fg: #991b1b;
	--roxy-info: #0284c7;
	--roxy-info-fg: #075985;

	--roxy-bg: #ffffff;
	--roxy-surface: #ffffff;
	--roxy-fg: #0a0a0a;
	--roxy-muted: #71717a;
	--roxy-border: #e4e4e7;
	--roxy-ring: color-mix(in srgb, var(--roxy-accent) 40%, transparent);

	--roxy-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
	--roxy-shadow-md:
		0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.06);
	--roxy-shadow-lg: 0 12px 24px -8px rgba(0, 0, 0, 0.14);
}

/* Dark theme via explicit opt-in. The light-DOM selectors (:root.dark, .dark,
 * [data-theme="dark"]) set the vars on the ancestor or the element itself so
 * they inherit into every component shadow; this is what makes a .dark class on
 * any ancestor (Tailwind, shadcn) actually theme the components in global
 * delivery. :host([data-theme="dark"]) is inert in global delivery and kept
 * only for shadow-adopted use. */
:where(
	:root[data-theme="dark"],
	[data-theme="dark"],
	:root.dark,
	.dark,
	:host([data-theme="dark"])
) {
	--roxy-primary: #f8fafc;
	--roxy-secondary: #94a3b8;
	--roxy-accent: #fbbf24;
	--roxy-accent-ink: var(--roxy-accent);

	--roxy-success: #22c55e;
	--roxy-success-fg: #86efac;
	--roxy-warning: #fb923c;
	--roxy-warning-fg: #fdba74;
	--roxy-danger: #ef4444;
	--roxy-danger-fg: #fca5a5;
	--roxy-info: #38bdf8;
	--roxy-info-fg: #7dd3fc;

	--roxy-bg: #0a0a0a;
	--roxy-surface: #18181b;
	--roxy-fg: #fafafa;
	--roxy-muted: #a1a1aa;
	--roxy-border: #27272a;
	--roxy-ring: color-mix(in srgb, var(--roxy-accent) 45%, transparent);

	--roxy-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
	--roxy-shadow-md:
		0 4px 6px -1px rgba(0, 0, 0, 0.6), 0 2px 4px -2px rgba(0, 0, 0, 0.5);
	--roxy-shadow-lg: 0 12px 24px -8px rgba(0, 0, 0, 0.7);

	--roxy-heat: var(--roxy-danger, #ef4444);
}

/* Reduced motion override */
@media (prefers-reduced-motion: reduce) {
	:where(:root, :host) {
		--roxy-motion-duration: 0ms;
	}
}
