UI/Foundation/OMRUIRuntimeStyle.cs
using Sandbox;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

/// <summary>
/// Applies GameResource values as typed Panel styles. SCSS remains responsible
/// for structure, transitions and responsive rules; this layer supplies the
/// values designers are expected to tune from the Inspector.
/// </summary>
public static class OMRUIRuntimeStyle
{
	public static void ApplyMenu(
		Panel root,
		OMRUITheme theme,
		OMRMainMenuLayout layout
	)
	{
		if ( root is null || theme is null || layout is null )
			return;

		root.Style.FontFamily = theme.BodyFont;
		root.Style.FontColor = theme.PrimaryText;

		foreach ( Panel panel in Panels( root ) )
		{
			ApplyTextFormatting( panel, theme );
			ApplyDisplayTextEffect( panel, theme );
			if ( panel.HasClass( "menu-screen" ) )
				panel.Style.BackgroundColor = theme.MenuCanvasColor;

			if ( panel.HasClass( "menu-background" ) )
			{
				if ( panel is Image background )
					background.Texture = theme.GetBackgroundImage();

				panel.Style.ObjectFit = Fit( layout.BackgroundFit );
				panel.Style.Opacity = theme.MenuBackgroundOpacity;

				// Keep the image box locked to the viewport and zoom it using a real
				// transform. Growing Width/Height caused the engine to effectively
				// zoom from the top-left corner and made pan/focus hard to reason about.
				panel.Style.Width = Length.Percent( 100f );
				panel.Style.Height = Length.Percent( 100f );
				panel.Style.Left = Length.Pixels( layout.BackgroundOffsetX );
				panel.Style.Top = Length.Pixels( layout.BackgroundOffsetY );

				float originX = Math.Clamp( layout.BackgroundPositionXPercent, 0f, 100f );
				float originY = Math.Clamp( layout.BackgroundPositionYPercent, 0f, 100f );
				panel.Style.TransformOriginX = Length.Percent( originX );
				panel.Style.TransformOriginY = Length.Percent( originY );

				float zoomScale = Math.Clamp( layout.BackgroundZoomPercent, 100f, 200f ) / 100f;
				PanelTransform backgroundTransform = new();
				backgroundTransform.AddScale( zoomScale );
				panel.Style.Transform = backgroundTransform;
			}

			if ( panel.HasClass( "menu-overlay" ) )
			{
				panel.Style.BackgroundColor = theme.ScreenOverlay;
				panel.Style.Opacity = layout.ShowScreenOverlay ? layout.ScreenOverlayOpacity : 0f;
			}

			if ( panel.HasClass( "menu-shell" ) )
			{
				if ( layout.ShellMode == OMRMenuShellMode.FullScreenCanvas )
				{
					// In canvas mode the shell stops being a layout constraint. It remains
					// only as a structural parent so brand/navigation offsets are relative
					// to the full screen rather than a centered panel.
					panel.Style.Width = Length.Percent( 100f );
					panel.Style.Height = Length.Percent( 100f );
					panel.Style.MaxHeight = Length.Percent( 100f );
					SetPadding( panel, 0f );
					panel.Style.Left = Length.Pixels( 0f );
					panel.Style.Top = Length.Pixels( 0f );
				}
				else
				{
					panel.Style.Width = Length.Pixels( layout.ShellWidth );
					panel.Style.Height = Length.Auto;
					panel.Style.MaxHeight = Length.Percent( layout.ShellMaxHeightPercent );
					SetPadding( panel, layout.ShellPadding );
					panel.Style.Left = Length.Pixels( layout.ShellOffsetX );
					panel.Style.Top = Length.Pixels( layout.ShellOffsetY );
				}
			}

			if ( panel.HasClass( "menu-shell-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.MenuShellBackground;
				panel.Style.BorderColor = theme.StrongBorder;
				panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );
				bool shellChromeVisible =
					layout.ShellMode == OMRMenuShellMode.Panel &&
					layout.ShowShellChrome;
				panel.Style.Opacity = shellChromeVisible ? layout.ShellChromeOpacity : 0f;
				SetCornerRadius( panel, theme.MenuShellCornerRadius );
				ApplyBackdropBlur( panel, theme.PrimaryPanelBackdropBlur );
				ApplyPrimaryPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "session-notice" ) )
			{
				panel.Style.BackgroundColor = theme.NoticeBackground;
				panel.Style.BorderColor = theme.SoftBorder;
				panel.Style.FontColor = theme.SecondaryText;
				SetCornerRadius( panel, theme.TagCornerRadius );
			}

			if ( panel.HasClass( "menu-brand" ) )
			{
				panel.Style.Display = layout.ShowBrand ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Opacity = layout.BrandOpacity;
				panel.Style.MarginBottom = Length.Pixels( layout.BrandBottomSpacing );
				panel.Style.Left = Length.Pixels( layout.BrandOffsetX );
				panel.Style.Top = Length.Pixels( layout.BrandOffsetY );
			}

			if ( panel.HasClass( "menu-logo" ) )
			{
				panel.Style.Display = layout.ShowLogo ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.ObjectFit = Fit( layout.LogoFit );
				panel.Style.Width = Length.Pixels( layout.LogoWidth );
				panel.Style.Height = Length.Pixels( layout.LogoHeight );
				panel.Style.Left = Length.Pixels( layout.LogoOffsetX );
				panel.Style.Top = Length.Pixels( layout.LogoOffsetY );
				panel.Style.MarginBottom = Length.Pixels( layout.LogoBottomSpacing );
				panel.Style.Opacity = layout.LogoOpacity;
				if ( panel is Image logo ) logo.Texture = theme.GetLogoImage();
			}

			if ( panel.HasClass( "menu-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontColor = theme.OverrideMenuTitleColor ? theme.MenuTitleColor : theme.PrimaryText;
				panel.Style.FontSize = Length.Pixels( layout.TitleSize );
				panel.Style.LetterSpacing = Length.Pixels( layout.TitleLetterSpacing );
				panel.Style.Width = Length.Percent( 100f );
				panel.Style.Left = Length.Pixels( layout.TitleOffsetX );
				panel.Style.Top = Length.Pixels( layout.TitleOffsetY );
				panel.Style.Opacity = layout.TitleOpacity;
				ApplyTextLayout( panel, layout.TitleAlignment, layout.TitleIndent );
			}

			if ( panel.HasClass( "menu-subtitle" ) )
			{
				panel.Style.FontFamily = theme.BodyFont;
				panel.Style.FontColor = theme.OverrideSubtitleColor ? theme.SubtitleColor : theme.SecondaryText;
				panel.Style.FontSize = Length.Pixels( layout.SubtitleSize );
				panel.Style.LetterSpacing = Length.Pixels( layout.SubtitleLetterSpacing );
				panel.Style.Width = Length.Percent( 100f );
				panel.Style.Left = Length.Pixels( layout.SubtitleOffsetX );
				panel.Style.Top = Length.Pixels( layout.SubtitleOffsetY );
				panel.Style.Opacity = layout.SubtitleOpacity;
				ApplyTextLayout( panel, layout.SubtitleAlignment, layout.SubtitleIndent );
			}

			if ( panel.HasClass( "menu-navigation" ) )
			{
				panel.Style.Left = Length.Pixels( layout.NavigationOffsetX );
				panel.Style.Top = Length.Pixels( layout.NavigationOffsetY );
				panel.Style.Display = layout.ShowNavigation ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Opacity = layout.NavigationOpacity;
			}

			if ( panel.HasClass( "menu-page" ) )
			{
				panel.Style.RowGap = Length.Pixels( layout.ButtonGap );
				panel.Style.AlignItems = layout.NavigationContentAlignment switch
				{
					OMRTextHorizontalAlignment.Left => Align.FlexStart,
					OMRTextHorizontalAlignment.Right => Align.FlexEnd,
					_ => Align.Center
				};
			}

			if ( panel.HasClass( "menu-button" ) )
			{
				panel.Style.Width = Length.Pixels( layout.ButtonWidth );
				panel.Style.MinHeight = Length.Pixels( layout.ButtonHeight );
				panel.Style.FontColor = theme.PrimaryText;
				panel.Style.TransformOriginX = Length.Percent( 50f );
				panel.Style.TransformOriginY = Length.Percent( 50f );

				float buttonScale = 1f;
				float hoverX = 0f;
				float hoverY = 0f;

				if ( panel.HasHovered )
				{
					buttonScale = Math.Clamp( layout.ButtonHoverScalePercent, 100f, 120f ) / 100f;
					hoverX = layout.ButtonHoverOffsetX;
					hoverY = layout.ButtonHoverOffsetY;
				}

				if ( panel.HasActive )
					buttonScale = Math.Clamp( layout.ButtonPressedScalePercent, 80f, 100f ) / 100f;

				PanelTransform buttonTransform = new();
				buttonTransform.AddTranslate( Length.Pixels( hoverX ), Length.Pixels( hoverY ), null );
				buttonTransform.AddScale( buttonScale );
				panel.Style.Transform = buttonTransform;

				bool accent =
					panel.HasClass( "primary" ) ||
					panel.HasClass( "matchmaking-button" );

				if ( layout.ShowButtonChrome )
				{
					panel.Style.BorderColor = accent ? theme.AccentBorder : theme.Border;
					panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );
					panel.Style.BackgroundColor = accent
						? InteractiveColor( panel, theme.Accent, theme.AccentHover, theme.AccentPressed )
						: InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
					SetCornerRadius( panel, theme.ButtonCornerRadius );
				}
				else
				{
					panel.Style.BorderWidth = Length.Pixels( 0f );
					panel.Style.BackgroundColor = Color.Transparent;
				}
			}

			if ( panel.HasClass( "button-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.ButtonTitleSize );
				panel.Style.FontColor = theme.OverrideButtonTitleColor ? theme.ButtonTitleColor : theme.PrimaryText;
				panel.Style.Width = Length.Percent( 100f );
				panel.Style.Left = Length.Pixels( layout.ButtonTitleOffsetX );
				panel.Style.Top = Length.Pixels( layout.ButtonTitleOffsetY );
				ApplyTextLayout( panel, layout.ButtonTitleAlignment, layout.ButtonTitleIndent );
			}

			if ( panel.HasClass( "button-description" ) )
			{
				panel.Style.Display = layout.ShowButtonDescriptions ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontFamily = theme.BodyFont;
				panel.Style.FontSize = Length.Pixels( layout.ButtonDescriptionSize );
				panel.Style.FontColor = theme.OverrideButtonDescriptionColor ? theme.ButtonDescriptionColor : theme.SecondaryText;
				panel.Style.Width = Length.Percent( 100f );
				panel.Style.Left = Length.Pixels( layout.ButtonDescriptionOffsetX );
				panel.Style.Top = Length.Pixels( layout.ButtonDescriptionOffsetY );
				ApplyTextLayout( panel, layout.ButtonDescriptionAlignment, layout.ButtonDescriptionIndent );
			}

			if ( panel.HasClass( "page-heading" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.PageHeadingSize );
				panel.Style.FontColor = theme.OverridePageHeadingColor ? theme.PageHeadingColor : theme.PrimaryText;
				panel.Style.Left = Length.Pixels( layout.PageHeadingOffsetX );
				panel.Style.Top = Length.Pixels( layout.PageHeadingOffsetY );
				ApplyTextLayout( panel, layout.PageHeadingAlignment, layout.PageHeadingIndent );
			}

			if (
				panel.HasClass( "page-copy" ) ||
				panel.HasClass( "setting-description" ) ||
				panel.HasClass( "lobby-note" )
			)
			{
				panel.Style.FontFamily = theme.BodyFont;
				panel.Style.FontColor = theme.SecondaryText;
			}

			if ( panel.HasClass( "page-copy" ) )
			{
				panel.Style.Display = layout.ShowPageCopy ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.PageCopySize );
				panel.Style.FontColor = theme.OverridePageCopyColor ? theme.PageCopyColor : theme.SecondaryText;
				panel.Style.Left = Length.Pixels( layout.PageCopyOffsetX );
				panel.Style.Top = Length.Pixels( layout.PageCopyOffsetY );
				ApplyTextLayout( panel, layout.PageCopyAlignment, layout.PageCopyIndent );
			}

			if ( panel.HasClass( "player-card" ) )
			{
				panel.Style.Width = Length.Pixels( layout.PlayerCardWidth );
				panel.Style.MinHeight = Length.Pixels( layout.PlayerCardHeight );

				if ( layout.ShowPlayerCardChrome )
				{
					panel.Style.BackgroundColor = theme.SoftPanelBackground;
					panel.Style.BorderColor = theme.SoftBorder;
					panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );

					if ( panel.HasClass( "ready" ) )
					{
						panel.Style.BackgroundColor = theme.PositiveSoft;
						panel.Style.BorderColor = theme.PositiveBorder;
					}
					else if ( panel.HasClass( "opponent-card" ) )
					{
						panel.Style.BackgroundColor = theme.DangerSoft;
						panel.Style.BorderColor = theme.DangerBorder;
					}
					else if ( panel.HasClass( "practicing" ) )
					{
						panel.Style.BackgroundColor = theme.AccentSoft;
						panel.Style.BorderColor = theme.AccentBorder;
					}

					SetCornerRadius( panel, theme.SoftPanelCornerRadius );
				}
				else
				{
					panel.Style.BackgroundColor = Color.Transparent;
					panel.Style.BorderWidth = Length.Pixels( 0f );
				}
			}

			if (
				panel.HasClass( "setup-section" ) ||
				panel.HasClass( "lobby-weapon-panel" ) ||
				panel.HasClass( "setting-row" )
			)
			{
				if ( layout.ShowSettingsChrome )
				{
					panel.Style.BackgroundColor = theme.SoftPanelBackground;
					panel.Style.BorderColor = theme.SoftBorder;
					panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );
					SetCornerRadius( panel, theme.SoftPanelCornerRadius );
				}
				else
				{
					panel.Style.BackgroundColor = Color.Transparent;
					panel.Style.BorderWidth = Length.Pixels( 0f );
				}
			}

			if ( panel.HasClass( "setting-row" ) )
				panel.Style.MinHeight = Length.Pixels( layout.SettingRowHeight );

			if (
				panel.HasClass( "setting-control" ) ||
				panel.HasClass( "setup-selector" )
			)
			{
				panel.Style.BackgroundColor = theme.ControlBackground;
				panel.Style.BorderColor = theme.Border;
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}

			if ( panel.HasClass( "menu-back" ) || panel.HasClass( "menu-primary" ) )
			{
				ApplyActionAlignment( panel, layout.ActionTextAlignment, layout.ActionTextIndent );
			}

			if (
				panel.HasClass( "menu-back" ) ||
				panel.HasClass( "setting-step" ) ||
				panel.HasClass( "setting-cycle" ) ||
				panel.HasClass( "settings-reset" ) ||
				panel.HasClass( "selector-arrow" )
			)
			{
				panel.Style.BackgroundColor = InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
				panel.Style.BorderColor = theme.Border;
				panel.Style.FontColor = theme.PrimaryText;
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}

			if ( panel.HasClass( "menu-primary" ) )
			{
				if ( panel.HasClass( "secondary-action" ) )
				{
					panel.Style.BackgroundColor = InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
					panel.Style.BorderColor = theme.Border;
				}
				else if ( panel.HasClass( "warmup-action" ) )
				{
					panel.Style.BackgroundColor = InteractiveColor( panel, theme.Accent, theme.AccentHover, theme.AccentPressed );
					panel.Style.BorderColor = theme.AccentBorder;
				}
				else
				{
					panel.Style.BackgroundColor = panel.HasClass( "active" )
						? theme.PositiveSelected
						: InteractiveColor( panel, theme.Positive, theme.PositiveHover, theme.PositivePressed );
					panel.Style.BorderColor = theme.PositiveBorder;
				}

				SetCornerRadius( panel, theme.ButtonCornerRadius );
			}

			if ( panel.HasClass( "online-subheading" ) )
				panel.Style.FontColor = theme.AccentText;

			if ( panel.HasClass( "lobby-ready-summary" ) )
				panel.Style.FontColor = theme.SecondaryText;

			if ( panel.HasClass( "setting-value" ) )
				panel.Style.FontColor = theme.PrimaryText;

			if (
				panel.HasClass( "error" ) ||
				panel.HasClass( "error-copy" )
			)
				panel.Style.FontColor = theme.ErrorText;
		}
	}


	/// <summary>
	/// Applies OMRTHEME to the developer reference board. Unlike menu/HUD layout
	/// styling this method intentionally contains no gameplay geometry; its only
	/// job is to expose the theme's semantic roles in one place.
	/// </summary>
	public static void ApplyThemePreview( Panel root, OMRUITheme theme )
	{
		if ( root is null || theme is null )
			return;

		root.Style.FontFamily = theme.BodyFont;
		root.Style.FontColor = theme.PrimaryText;

		foreach ( Panel panel in Panels( root ) )
		{
			ApplyTextFormatting( panel, theme );
			ApplyDisplayTextEffect( panel, theme );

			if ( panel.HasClass( "theme-background-sample" ) )
			{
				if ( panel is Image background )
					background.Texture = theme.GetBackgroundImage();
				panel.Style.Opacity = theme.MenuBackgroundOpacity;
			}

			if ( panel.HasClass( "theme-logo-sample" ) )
			{
				panel.Style.Display = theme.HasLogo ? DisplayMode.Flex : DisplayMode.None;
				if ( panel is Image logo )
					logo.Texture = theme.GetLogoImage();
			}

			if ( panel.HasClass( "theme-preview-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontColor = theme.OverrideMenuTitleColor ? theme.MenuTitleColor : theme.PrimaryText;
			}
			if ( panel.HasClass( "theme-preview-subtitle" ) )
				panel.Style.FontColor = theme.OverrideSubtitleColor ? theme.SubtitleColor : theme.SecondaryText;
			if ( panel.HasClass( "theme-preview-heading" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontColor = theme.OverridePageHeadingColor ? theme.PageHeadingColor : theme.PrimaryText;
			}
			if ( panel.HasClass( "theme-preview-body" ) )
				panel.Style.FontColor = theme.OverridePageCopyColor ? theme.PageCopyColor : theme.SecondaryText;
			if ( panel.HasClass( "theme-preview-meta" ) )
				panel.Style.FontColor = theme.MutedText;
			if ( panel.HasClass( "theme-preview-number" ) )
			{
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontColor = theme.PrimaryText;
			}

			if ( panel.HasClass( "theme-text-primary" ) ) panel.Style.FontColor = theme.PrimaryText;
			if ( panel.HasClass( "theme-text-secondary" ) ) panel.Style.FontColor = theme.SecondaryText;
			if ( panel.HasClass( "theme-text-muted" ) ) panel.Style.FontColor = theme.MutedText;
			if ( panel.HasClass( "theme-text-accent" ) ) panel.Style.FontColor = theme.AccentText;
			if ( panel.HasClass( "theme-text-positive" ) ) panel.Style.FontColor = theme.PositiveText;
			if ( panel.HasClass( "theme-text-danger" ) ) panel.Style.FontColor = theme.DangerText;
			if ( panel.HasClass( "theme-text-error" ) ) panel.Style.FontColor = theme.ErrorText;

			if ( panel.HasClass( "theme-surface-menu-shell" ) )
				StylePreviewSurface( panel, theme.MenuShellBackground, theme.StrongBorder, theme.MenuShellCornerRadius, theme.PrimaryPanelBackdropBlur );
			if ( panel.HasClass( "theme-surface-soft" ) )
				StylePreviewSurface( panel, theme.SoftPanelBackground, theme.SoftBorder, theme.SoftPanelCornerRadius );
			if ( panel.HasClass( "theme-surface-control" ) )
				StylePreviewSurface( panel, theme.ControlBackground, theme.Border, theme.ControlCornerRadius );
			if ( panel.HasClass( "theme-surface-notice" ) )
				StylePreviewSurface( panel, theme.NoticeBackground, theme.SoftBorder, theme.TagCornerRadius );
			if ( panel.HasClass( "theme-surface-pause" ) )
				StylePreviewSurface( panel, theme.PausePanelBackground, theme.StrongBorder, theme.PanelCornerRadius, theme.PrimaryPanelBackdropBlur );
			if ( panel.HasClass( "theme-surface-hud" ) )
				StylePreviewSurface( panel, theme.HudPanel, theme.SoftBorder, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-timer" ) )
				StylePreviewSurface( panel, theme.TimerPanel, theme.Border, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-pot" ) )
				StylePreviewSurface( panel, theme.PotPanel, theme.Border, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-ammo" ) )
				StylePreviewSurface( panel, theme.AmmoPanel, theme.Border, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-decision" ) )
				StylePreviewSurface( panel, theme.DecisionPanelBackground, theme.StrongBorder, theme.PanelCornerRadius, theme.PrimaryPanelBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-sandbox" ) )
				StylePreviewSurface( panel, theme.SandboxLabel, theme.Border, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-warmup" ) )
				StylePreviewSurface( panel, theme.WarmupLabel, theme.AccentBorder, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-track" ) )
				StylePreviewSurface( panel, theme.HealthTrack, theme.SoftBorder, theme.HealthBarCornerRadius, 0f, theme.HudBorderWidth );
			if ( panel.HasClass( "theme-surface-sudden" ) )
				StylePreviewSurface( panel, theme.SuddenDeath, theme.DangerBorder, theme.HudCornerRadius, theme.HudBackdropBlur, theme.HudBorderWidth );

			if ( panel.HasClass( "theme-surface-menu-shell" ) || panel.HasClass( "theme-surface-pause" ) || panel.HasClass( "theme-surface-decision" ) )
				ApplyPrimaryPanelShadow( panel, theme );
			else if ( panel.HasClass( "theme-surface-hud" ) || panel.HasClass( "theme-surface-timer" ) || panel.HasClass( "theme-surface-pot" ) || panel.HasClass( "theme-surface-ammo" ) || panel.HasClass( "theme-surface-sandbox" ) || panel.HasClass( "theme-surface-warmup" ) || panel.HasClass( "theme-surface-sudden" ) )
				ApplyHudPanelShadow( panel, theme );

			if ( panel.HasClass( "theme-overlay-menu-canvas" ) ) panel.Style.BackgroundColor = theme.MenuCanvasColor;
			if ( panel.HasClass( "theme-overlay-menu" ) ) panel.Style.BackgroundColor = theme.ScreenOverlay;
			if ( panel.HasClass( "theme-overlay-pause" ) ) panel.Style.BackgroundColor = theme.PauseOverlay;
			if ( panel.HasClass( "theme-overlay-decision" ) ) panel.Style.BackgroundColor = theme.DecisionOverlay;

			if ( panel.HasClass( "theme-health-local" ) )
			{
				panel.Style.BackgroundColor = theme.LocalHealth;
				SetCornerRadius( panel, theme.HealthBarCornerRadius );
			}
			if ( panel.HasClass( "theme-health-opponent" ) )
			{
				panel.Style.BackgroundColor = theme.OpponentHealth;
				SetCornerRadius( panel, theme.HealthBarCornerRadius );
			}

			if ( panel.HasClass( "theme-border-primary" ) ) panel.Style.BorderColor = theme.Border;
			if ( panel.HasClass( "theme-border-soft" ) ) panel.Style.BorderColor = theme.SoftBorder;
			if ( panel.HasClass( "theme-border-strong" ) ) panel.Style.BorderColor = theme.StrongBorder;
			if ( panel.HasClass( "theme-border-accent" ) ) panel.Style.BorderColor = theme.AccentBorder;
			if ( panel.HasClass( "theme-border-positive" ) ) panel.Style.BorderColor = theme.PositiveBorder;
			if ( panel.HasClass( "theme-border-danger" ) ) panel.Style.BorderColor = theme.DangerBorder;
			if ( panel.HasClass( "theme-border-sample" ) )
			{
				panel.Style.BorderWidth = Length.Pixels( Math.Max( 1f, theme.HudBorderWidth ) );
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}

			if ( panel.HasClass( "theme-interactive-neutral-normal" ) ) panel.Style.BackgroundColor = theme.Button;
			if ( panel.HasClass( "theme-interactive-neutral-hover" ) ) panel.Style.BackgroundColor = theme.ButtonHover;
			if ( panel.HasClass( "theme-interactive-neutral-pressed" ) ) panel.Style.BackgroundColor = theme.ButtonPressed;
			if ( panel.HasClass( "theme-interactive-accent-normal" ) ) panel.Style.BackgroundColor = theme.Accent;
			if ( panel.HasClass( "theme-interactive-accent-hover" ) ) panel.Style.BackgroundColor = theme.AccentHover;
			if ( panel.HasClass( "theme-interactive-accent-pressed" ) ) panel.Style.BackgroundColor = theme.AccentPressed;
			if ( panel.HasClass( "theme-interactive-accent-selected" ) ) panel.Style.BackgroundColor = theme.AccentSelected;
			if ( panel.HasClass( "theme-interactive-accent-soft" ) ) panel.Style.BackgroundColor = theme.AccentSoft;
			if ( panel.HasClass( "theme-interactive-positive-normal" ) ) panel.Style.BackgroundColor = theme.Positive;
			if ( panel.HasClass( "theme-interactive-positive-hover" ) ) panel.Style.BackgroundColor = theme.PositiveHover;
			if ( panel.HasClass( "theme-interactive-positive-pressed" ) ) panel.Style.BackgroundColor = theme.PositivePressed;
			if ( panel.HasClass( "theme-interactive-positive-selected" ) ) panel.Style.BackgroundColor = theme.PositiveSelected;
			if ( panel.HasClass( "theme-interactive-positive-soft" ) ) panel.Style.BackgroundColor = theme.PositiveSoft;
			if ( panel.HasClass( "theme-interactive-danger-normal" ) ) panel.Style.BackgroundColor = theme.Danger;
			if ( panel.HasClass( "theme-interactive-danger-hover" ) ) panel.Style.BackgroundColor = theme.DangerHover;
			if ( panel.HasClass( "theme-interactive-danger-pressed" ) ) panel.Style.BackgroundColor = theme.DangerPressed;
			if ( panel.HasClass( "theme-interactive-danger-soft" ) ) panel.Style.BackgroundColor = theme.DangerSoft;

			if ( panel.HasClass( "theme-interactive-chip" ) )
			{
				panel.Style.BorderColor = theme.SoftBorder;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				SetCornerRadius( panel, theme.ButtonCornerRadius );
			}
		}
	}

	public static void ApplyPause(
		Panel root,
		OMRUITheme theme,
		OMRMainMenuLayout layout
	)
	{
		if ( root is null || theme is null || layout is null )
			return;

		root.Style.FontFamily = theme.BodyFont;
		root.Style.FontColor = theme.PrimaryText;

		foreach ( Panel panel in Panels( root ) )
		{
			ApplyTextFormatting( panel, theme );
			ApplyDisplayTextEffect( panel, theme );
			if ( panel.HasClass( "pause-screen-overlay" ) )
			{
				panel.Style.BackgroundColor = theme.PauseOverlay;
				panel.Style.Opacity = layout.ShowPauseOverlay ? layout.PauseOverlayOpacity : 0f;
			}

			if ( panel.HasClass( "pause-panel" ) )
			{
				panel.Style.Width = Length.Pixels( layout.PauseWidth );
				SetPadding( panel, layout.PausePadding );
				panel.Style.Left = Length.Pixels( layout.PauseOffsetX );
				panel.Style.Top = Length.Pixels( layout.PauseOffsetY );
			}

			if ( panel.HasClass( "pause-panel-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.PausePanelBackground;
				panel.Style.BorderColor = theme.StrongBorder;
				panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );
				panel.Style.Opacity = layout.ShowPauseChrome ? layout.PauseChromeOpacity : 0f;
				SetCornerRadius( panel, theme.PanelCornerRadius );
				ApplyBackdropBlur( panel, theme.PrimaryPanelBackdropBlur );
				ApplyPrimaryPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "pause-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.PauseTitleSize );
			}

			if (
				panel.HasClass( "pause-note" ) ||
				panel.HasClass( "pause-button-copy" ) ||
				panel.HasClass( "pause-summary" ) ||
				panel.HasClass( "setting-description" )
			)
				panel.Style.FontColor = theme.SecondaryText;

			if ( panel.HasClass( "pause-eyebrow" ) )
				panel.Style.FontColor = theme.AccentText;

			if ( panel.HasClass( "setting-value" ) )
				panel.Style.FontColor = theme.PrimaryText;

			if ( panel.HasClass( "pause-button" ) )
			{
				panel.Style.MinHeight = Length.Pixels( layout.PauseButtonHeight );
				panel.Style.BorderColor = theme.Border;
				panel.Style.BorderWidth = Length.Pixels( layout.BorderWidth );

				if ( panel.HasClass( "primary" ) )
				{
					panel.Style.BackgroundColor = InteractiveColor( panel, theme.Positive, theme.PositiveHover, theme.PositivePressed );
					panel.Style.BorderColor = theme.PositiveBorder;
				}
				else if ( panel.HasClass( "danger" ) )
				{
					panel.Style.BackgroundColor = InteractiveColor( panel, theme.Danger, theme.DangerHover, theme.DangerPressed );
					panel.Style.BorderColor = theme.DangerBorder;
				}
				else
				{
					panel.Style.BackgroundColor = InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
				}

				SetCornerRadius( panel, theme.ButtonCornerRadius );
			}

			if (
				panel.HasClass( "pause-settings-back" ) ||
				panel.HasClass( "setting-step" ) ||
				panel.HasClass( "setting-cycle" ) ||
				panel.HasClass( "settings-reset" )
			)
			{
				panel.Style.BackgroundColor = InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
				panel.Style.BorderColor = theme.Border;
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}

			if ( panel.HasClass( "setting-row" ) )
			{
				panel.Style.MinHeight = Length.Pixels( layout.SettingRowHeight );
				panel.Style.BackgroundColor = theme.SoftPanelBackground;
				panel.Style.BorderColor = theme.SoftBorder;
				SetCornerRadius( panel, theme.SoftPanelCornerRadius );
			}

			if ( panel.HasClass( "setting-control" ) )
			{
				panel.Style.BackgroundColor = theme.ControlBackground;
				panel.Style.BorderColor = theme.Border;
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}
		}
	}

	public static void ApplyHud(
		Panel root,
		OMRUITheme theme,
		OMRHUDLayout layout
	)
	{
		if ( root is null || theme is null || layout is null )
			return;

		root.Style.FontFamily = theme.BodyFont;
		root.Style.FontColor = theme.PrimaryText;

		foreach ( Panel panel in Panels( root ) )
		{
			ApplyTextFormatting( panel, theme );
			ApplyDisplayTextEffect( panel, theme );

			// ------------------------------------------------------------
			// HEALTH
			// ------------------------------------------------------------
			if ( panel.HasClass( "combat-health" ) )
			{
				panel.Style.Top = Length.Pixels( layout.HealthTop );
				panel.Style.Left = Length.Pixels( layout.HealthHorizontalInset );
				panel.Style.Right = Length.Pixels( layout.HealthHorizontalInset );
				panel.Style.Display = layout.ShowLocalHealth || layout.ShowOpponentHealth
					? DisplayMode.Flex : DisplayMode.None;
			}

			if ( panel.HasClass( "health-side" ) )
			{
				bool opponent = panel.HasClass( "opponent" );
				panel.Style.Width = Length.Percent( layout.HealthSideWidthPercent );
				panel.Style.MaxWidth = Length.Pixels( layout.HealthMaximumWidth );
				panel.Style.Display = opponent
					? (layout.ShowOpponentHealth ? DisplayMode.Flex : DisplayMode.None)
					: (layout.ShowLocalHealth ? DisplayMode.Flex : DisplayMode.None);
				panel.Style.Left = Length.Pixels( opponent ? layout.OpponentHealthOffsetX : layout.LocalHealthOffsetX );
				panel.Style.Top = Length.Pixels( opponent ? layout.OpponentHealthOffsetY : layout.LocalHealthOffsetY );
				panel.Style.Opacity = opponent ? layout.OpponentHealthOpacity : layout.LocalHealthOpacity;
			}

			if ( panel.HasClass( "health-heading" ) )
			{
				bool opponent = panel.Ancestors.Any( x => x.HasClass( "health-side" ) && x.HasClass( "opponent" ) );
				OMRHUDPairAlignment headingAlignment = opponent
					? layout.OpponentHealthHeadingAlignment
					: layout.LocalHealthHeadingAlignment;
				float headingGap = opponent
					? layout.OpponentHealthHeadingGap
					: layout.LocalHealthHeadingGap;

				panel.Style.MarginBottom = Length.Pixels( layout.HealthHeadingBottomSpacing );
				// SpaceBetween owns spacing in Split mode. A manual gap only has
				// meaning when both items are grouped to one side or the center.
				panel.Style.ColumnGap = Length.Pixels(
					headingAlignment == OMRHUDPairAlignment.Split ? 0f : headingGap
				);
				panel.Style.FlexDirection = opponent ? FlexDirection.RowReverse : FlexDirection.Row;
				ApplyPairAlignment( panel, headingAlignment );
			}

			if ( panel.HasClass( "health-label" ) )
			{
				panel.Style.Display = layout.ShowHealthLabels ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.HealthLabelSize );
				panel.Style.FontColor = theme.SecondaryText;
			}

			if ( panel.HasClass( "health-number" ) )
			{
				bool opponent = panel.Ancestors.Any( x => x.HasClass( "health-side" ) && x.HasClass( "opponent" ) );
				panel.Style.Display = layout.ShowHealthNumbers ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontSize = Length.Pixels( layout.HealthNumberSize );
				panel.Style.FontColor = opponent
					? (theme.OverrideOpponentHealthTextColor ? theme.OpponentHealthTextColor : theme.PrimaryText)
					: (theme.OverrideLocalHealthTextColor ? theme.LocalHealthTextColor : theme.PrimaryText);
			}

			if ( panel.HasClass( "health-track" ) )
			{
				panel.Style.Display = layout.ShowHealthBars ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Height = Length.Pixels( layout.HealthBarHeight );
				panel.Style.BackgroundColor = Color.Transparent;
				panel.Style.BorderWidth = Length.Pixels( 0f );
				SetCornerRadius( panel, theme.HealthBarCornerRadius );
			}

			if ( panel.HasClass( "health-track-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.HealthTrack;
				panel.Style.BorderColor = theme.SoftBorder;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowHealthTrackChrome ? layout.HealthTrackChromeOpacity : 0f;
				SetCornerRadius( panel, theme.HealthBarCornerRadius );
			}

			if ( panel.HasClass( "health-fill" ) )
			{
				bool opponent = panel.Ancestors.Any( x => x.HasClass( "health-side" ) && x.HasClass( "opponent" ) );
				OMRHUDFillDirection direction = opponent
					? layout.OpponentHealthFillDirection
					: layout.LocalHealthFillDirection;

				panel.Style.BackgroundColor = opponent ? theme.OpponentHealth : theme.LocalHealth;
				panel.Style.Opacity = layout.HealthBarOpacity;
				panel.Style.Left = direction == OMRHUDFillDirection.LeftToRight ? Length.Pixels( 0f ) : null;
				panel.Style.Right = direction == OMRHUDFillDirection.RightToLeft ? Length.Pixels( 0f ) : null;
				SetCornerRadius( panel, theme.HealthBarCornerRadius );
			}

			// ------------------------------------------------------------
			// MATCH HEADER
			// ------------------------------------------------------------
			if ( panel.HasClass( "match-header" ) )
			{
				panel.Style.Display = layout.ShowMatchHeader ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Top = Length.Pixels( layout.MatchHeaderTop + layout.MatchHeaderOffsetY );
				panel.Style.Left = Length.Pixels( layout.MatchHeaderHorizontalInset + layout.MatchHeaderOffsetX );
				panel.Style.Right = Length.Pixels( layout.MatchHeaderHorizontalInset - layout.MatchHeaderOffsetX );
				panel.Style.Width = Length.Auto;
				panel.Style.Opacity = layout.MatchHeaderOpacity;
				panel.Style.RowGap = Length.Pixels( layout.MatchHeaderElementGap );
				ApplyHorizontalContainerAlignment( panel, layout.MatchHeaderAlignment );
			}

			if ( panel.HasClass( "round" ) )
			{
				panel.Style.Display = layout.ShowRoundTitle ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Top = Length.Pixels( layout.RoundTitleOffsetY );
				panel.Style.MarginBottom = Length.Pixels( 0f );
				panel.Style.Opacity = layout.RoundTitleOpacity;
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.RoundTitleSize );
				panel.Style.FontColor = theme.OverrideRoundTextColor ? theme.RoundTextColor : theme.PrimaryText;
				ApplyTextLayout( panel, layout.MatchHeaderAlignment, 0f );
			}

			if ( panel.HasClass( "round-timer" ) )
			{
				panel.Style.Display = layout.ShowTimer ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Top = Length.Pixels( layout.TimerOffsetY );
				panel.Style.MarginBottom = Length.Pixels( 0f );
				panel.Style.MinWidth = Length.Pixels( layout.TimerMinimumWidth );
				panel.Style.PaddingLeft = Length.Pixels( layout.TimerPaddingX );
				panel.Style.PaddingRight = Length.Pixels( layout.TimerPaddingX );
				panel.Style.PaddingTop = Length.Pixels( layout.TimerPaddingY );
				panel.Style.PaddingBottom = Length.Pixels( layout.TimerPaddingY );
			}

			if ( panel.HasClass( "round-timer-chrome" ) )
			{
				bool sudden = panel.Ancestors.Any( x => x.HasClass( "round-timer" ) && x.HasClass( "sudden" ) );
				panel.Style.BackgroundColor = sudden ? theme.SuddenDeath : theme.TimerPanel;
				panel.Style.BorderColor = sudden ? theme.DangerBorder : theme.Border;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowTimerChrome ? layout.TimerChromeOpacity : 0f;
				SetCornerRadius( panel, theme.HudCornerRadius );
				ApplyBackdropBlur( panel, theme.HudBackdropBlur );
				ApplyHudPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "timer-number" ) )
			{
				bool sudden = panel.Ancestors.Any( x => x.HasClass( "round-timer" ) && x.HasClass( "sudden" ) );
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontColor = sudden
					? (theme.OverrideSuddenDeathTextColor ? theme.SuddenDeathTextColor : theme.PrimaryText)
					: (theme.OverrideTimerTextColor ? theme.TimerTextColor : theme.PrimaryText);
				panel.Style.FontSize = Length.Pixels( sudden ? layout.SuddenDeathTimerSize : layout.TimerSize );
			}

			if ( panel.HasClass( "timer-mode" ) )
			{
				panel.Style.Display = layout.ShowTimerMode ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.TimerMetaSize );
			}

			if ( panel.HasClass( "timer-note" ) )
			{
				panel.Style.Display = layout.ShowTimerNote ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.TimerMetaSize );
			}

			if ( panel.HasClass( "score-label" ) )
			{
				panel.Style.Display = layout.ShowScore && layout.ShowScoreHeading ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.ScoreHeadingSize );
				panel.Style.MarginBottom = Length.Pixels( 0f );
				ApplyTextLayout( panel, layout.MatchHeaderAlignment, 0f );
			}

			if ( panel.HasClass( "score" ) )
			{
				panel.Style.Display = layout.ShowScore ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Top = Length.Pixels( layout.ScoreOffsetY );
				panel.Style.ColumnGap = Length.Pixels( layout.ScoreColumnGap );
			}

			if ( panel.HasClass( "score-number" ) )
			{
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontColor = theme.OverrideScoreTextColor ? theme.ScoreTextColor : theme.PrimaryText;
				panel.Style.FontSize = Length.Pixels( layout.ScoreNumberSize );
			}

			if ( panel.HasClass( "score-player-label" ) )
			{
				panel.Style.Display = layout.ShowScorePlayerLabels ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.ScoreLabelSize );
				panel.Style.FontColor = theme.SecondaryText;
			}

			if ( panel.HasClass( "separator" ) )
			{
				panel.Style.Display = layout.ShowScoreSeparator ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.ScoreSeparatorSize );
				panel.Style.FontColor = theme.MutedText;
			}

			if ( panel.HasClass( "pot-display" ) )
			{
				panel.Style.Display = layout.ShowPot ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.MarginTop = Length.Pixels( 0f );
				panel.Style.PaddingLeft = Length.Pixels( layout.PotPaddingX );
				panel.Style.PaddingRight = Length.Pixels( layout.PotPaddingX );
				panel.Style.PaddingTop = Length.Pixels( layout.PotPaddingY );
				panel.Style.PaddingBottom = Length.Pixels( layout.PotPaddingY );
				panel.Style.ColumnGap = Length.Pixels( layout.PotGap );
				panel.Style.BackgroundColor = Color.Transparent;
				panel.Style.BorderWidth = Length.Pixels( 0f );
			}

			if ( panel.HasClass( "pot-display-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.PotPanel;
				panel.Style.BorderColor = theme.Border;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowPotChrome ? layout.PotChromeOpacity : 0f;
				SetCornerRadius( panel, theme.HudCornerRadius );
				ApplyBackdropBlur( panel, theme.HudBackdropBlur );
				ApplyHudPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "pot-owner" ) )
			{
				panel.Style.FontSize = Length.Pixels( layout.PotOwnerSize );
				panel.Style.Opacity = layout.PotOpacity;
				panel.Style.FontColor = theme.SecondaryText;
			}

			if ( panel.HasClass( "pot-value" ) )
			{
				panel.Style.FontSize = Length.Pixels( layout.PotValueSize );
				panel.Style.Opacity = layout.PotOpacity;
				panel.Style.FontColor = theme.OverridePotTextColor ? theme.PotTextColor : theme.PrimaryText;
			}

			if ( panel.HasClass( "modifier-label" ) )
			{
				panel.Style.Display = layout.ShowModifierLabel ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.MarginTop = Length.Pixels( 0f );
				panel.Style.FontSize = Length.Pixels( layout.ModifierLabelSize );
				panel.Style.Opacity = layout.ModifierLabelOpacity;
				panel.Style.FontColor = theme.OverrideModifierTextColor ? theme.ModifierTextColor : theme.AccentText;
				ApplyTextLayout( panel, layout.MatchHeaderAlignment, 0f );
			}

			// ------------------------------------------------------------
			// CENTER MESSAGE
			// ------------------------------------------------------------
			if ( panel.HasClass( "center-message" ) )
			{
				panel.Style.Display = layout.ShowCenterMessage ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.Top = Length.Percent( layout.CenterMessageTopPercent );
				panel.Style.Left = Length.Pixels( 0f );
				panel.Style.MarginTop = Length.Pixels( layout.CenterMessageOffsetY );
				panel.Style.Opacity = layout.CenterMessageOpacity;
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.CenterMessageSize );
				panel.Style.FontColor = theme.OverrideCenterMessageColor ? theme.CenterMessageColor : theme.PrimaryText;
				PanelTransform messageTransform = new();
				messageTransform.AddTranslate( Length.Pixels( layout.CenterMessageOffsetX ), Length.Pixels( 0f ), null );
				panel.Style.Transform = messageTransform;
				ApplyTextLayout( panel, layout.CenterMessageAlignment, layout.CenterMessageIndent );
			}

			// ------------------------------------------------------------
			// DECISIONS
			// ------------------------------------------------------------
			if ( panel.HasClass( "decision-screen-overlay" ) )
			{
				panel.Style.BackgroundColor = theme.DecisionOverlay;
				panel.Style.Opacity = layout.ShowDecisionOverlay ? layout.DecisionScreenOpacity : 0f;
			}

			if ( panel.HasClass( "decision-panel" ) )
			{
				panel.Style.Width = Length.Pixels( layout.DecisionPanelWidth );
				SetPadding( panel, layout.DecisionPanelPadding );
				panel.Style.MarginLeft = Length.Pixels( 0f );
				panel.Style.MarginTop = Length.Pixels( 0f );
				panel.Style.Left = Length.Pixels( layout.DecisionOffsetX );
				panel.Style.Top = Length.Pixels( layout.DecisionOffsetY );
				ApplyHorizontalContainerAlignment( panel, layout.DecisionContentAlignment );
			}

			if ( panel.HasClass( "decision-panel-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.DecisionPanelBackground;
				panel.Style.BorderColor = theme.StrongBorder;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowDecisionChrome ? layout.DecisionChromeOpacity : 0f;
				SetCornerRadius( panel, theme.PanelCornerRadius );
				ApplyBackdropBlur( panel, theme.PrimaryPanelBackdropBlur );
				ApplyPrimaryPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "decision-eyebrow" ) )
			{
				panel.Style.Display = layout.ShowDecisionEyebrow ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.DecisionEyebrowSize );
				panel.Style.FontColor = theme.AccentText;
				ApplyTextLayout( panel, layout.DecisionContentAlignment, layout.DecisionTextIndent );
			}

			if ( panel.HasClass( "decision-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.DecisionTitleSize );
				panel.Style.FontColor = theme.OverrideDecisionTitleColor ? theme.DecisionTitleColor : theme.PrimaryText;
				ApplyTextLayout( panel, layout.DecisionContentAlignment, layout.DecisionTextIndent );
			}

			if ( panel.HasClass( "decision-pot" ) )
			{
				panel.Style.Display = layout.ShowDecisionPot ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.DecisionPotSize );
				ApplyTextLayout( panel, layout.DecisionContentAlignment, layout.DecisionTextIndent );
			}

			if ( panel.HasClass( "decision-timer" ) )
			{
				panel.Style.Display = layout.ShowDecisionTimer ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontSize = Length.Pixels( layout.DecisionTimerSize );
				ApplyTextLayout( panel, layout.DecisionContentAlignment, layout.DecisionTextIndent );
			}

			if ( panel.HasClass( "final-score" ) )
			{
				panel.Style.Display = layout.ShowDecisionFinalScore ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontSize = Length.Pixels( layout.DecisionFinalScoreSize );
				ApplyTextLayout( panel, layout.DecisionContentAlignment, layout.DecisionTextIndent );
			}

			if ( panel.HasClass( "decision-buttons" ) )
				panel.Style.ColumnGap = Length.Pixels( layout.DecisionButtonGap );

			if ( panel.HasClass( "decision-button" ) )
			{
				panel.Style.MinHeight = Length.Pixels( layout.DecisionButtonHeight );
				SetPadding( panel, layout.DecisionButtonPadding );
				panel.Style.TransformOriginX = Length.Percent( 50f );
				panel.Style.TransformOriginY = Length.Percent( 50f );

				float scale = 1f;
				float hoverX = 0f;
				float hoverY = 0f;
				if ( panel.HasHovered )
				{
					scale = Math.Clamp( layout.DecisionButtonHoverScalePercent, 100f, 120f ) / 100f;
					hoverX = layout.DecisionButtonHoverOffsetX;
					hoverY = layout.DecisionButtonHoverOffsetY;
				}
				if ( panel.HasActive )
					scale = Math.Clamp( layout.DecisionButtonPressedScalePercent, 80f, 100f ) / 100f;

				PanelTransform buttonTransform = new();
				buttonTransform.AddTranslate( Length.Pixels( hoverX ), Length.Pixels( hoverY ), null );
				buttonTransform.AddScale( scale );
				panel.Style.Transform = buttonTransform;

				if ( layout.ShowDecisionButtonChrome )
				{
					panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
					if ( panel.HasClass( "safe" ) )
					{
						panel.Style.BorderColor = theme.PositiveBorder;
						panel.Style.BackgroundColor = InteractiveColor( panel, theme.Positive, theme.PositiveHover, theme.PositivePressed );
					}
					else if ( panel.HasClass( "risk" ) )
					{
						panel.Style.BorderColor = theme.DangerBorder;
						panel.Style.BackgroundColor = InteractiveColor( panel, theme.Danger, theme.DangerHover, theme.DangerPressed );
					}
					else
					{
						panel.Style.BorderColor = theme.AccentBorder;
						panel.Style.BackgroundColor = InteractiveColor( panel, theme.Accent, theme.AccentHover, theme.AccentPressed );
					}
					SetCornerRadius( panel, theme.ButtonCornerRadius );
				}
				else
				{
					panel.Style.BorderWidth = Length.Pixels( 0f );
					panel.Style.BackgroundColor = Color.Transparent;
				}
			}

			if ( panel.HasClass( "decision-button-title" ) )
			{
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.DecisionButtonTitleSize );

				Panel action = panel.Ancestors.FirstOrDefault( x => x.HasClass( "decision-button" ) );
				panel.Style.FontColor = action?.HasClass( "safe" ) == true
					? theme.PositiveText
					: action?.HasClass( "risk" ) == true
						? theme.DangerText
						: theme.AccentText;

				ApplyTextLayout( panel, layout.DecisionButtonTitleAlignment, layout.DecisionButtonTitleIndent );
			}

			if ( panel.HasClass( "decision-button-description" ) )
			{
				panel.Style.Display = layout.ShowDecisionButtonDescriptions ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.DecisionButtonDescriptionSize );
				ApplyTextLayout( panel, layout.DecisionButtonDescriptionAlignment, layout.DecisionButtonDescriptionIndent );
			}

			// ------------------------------------------------------------
			// AMMO
			// ------------------------------------------------------------
			if ( panel.HasClass( "ammo-hud" ) )
			{
				panel.Style.Display = layout.ShowAmmo ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.MinWidth = Length.Pixels( layout.AmmoMinimumWidth );
				panel.Style.RowGap = Length.Pixels( layout.AmmoElementGap );
				SetPadding( panel, layout.AmmoPadding );
				ApplyScreenAnchor( panel, layout.AmmoAnchor, layout.AmmoRight, layout.AmmoBottom, layout.AmmoOffsetX, layout.AmmoOffsetY );
				ApplyHorizontalContainerAlignment( panel, layout.AmmoAlignment );
			}

			if ( panel.HasClass( "ammo-hud-chrome" ) )
			{
				panel.Style.BackgroundColor = theme.AmmoPanel;
				panel.Style.BorderColor = theme.Border;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowAmmoChrome ? layout.AmmoChromeOpacity : 0f;
				SetCornerRadius( panel, theme.HudCornerRadius );
				ApplyBackdropBlur( panel, theme.HudBackdropBlur );
				ApplyHudPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "ammo-weapon" ) )
			{
				panel.Style.Display = layout.ShowAmmoWeaponName ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.AmmoWeaponSize );
				panel.Style.FontColor = theme.SecondaryText;
				panel.Style.Opacity = layout.AmmoOpacity;
				ApplyTextLayout( panel, layout.AmmoAlignment, 0f );
			}

			if ( panel.HasClass( "ammo-state" ) )
			{
				panel.Style.Display = layout.ShowAmmoReloadState ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.AmmoReloadSize );
				panel.Style.Opacity = layout.AmmoOpacity;
				ApplyTextLayout( panel, layout.AmmoAlignment, 0f );
			}

			if ( panel.HasClass( "ammo-count" ) )
			{
				panel.Style.ColumnGap = Length.Pixels( layout.AmmoCountGap );
				panel.Style.Opacity = layout.AmmoOpacity;
			}

			if ( panel.HasClass( "ammo-current" ) )
			{
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontSize = Length.Pixels( layout.AmmoCurrentSize );
				panel.Style.FontColor = theme.OverrideAmmoCurrentColor ? theme.AmmoCurrentColor : theme.PrimaryText;
			}

			if ( panel.HasClass( "ammo-divider" ) )
			{
				panel.Style.Display = layout.ShowAmmoDivider ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.AmmoDividerSize );
				panel.Style.FontColor = theme.MutedText;
			}

			if ( panel.HasClass( "ammo-capacity" ) )
			{
				panel.Style.FontFamily = theme.NumericFont;
				panel.Style.FontSize = Length.Pixels( layout.AmmoCapacitySize );
				panel.Style.FontColor = theme.SecondaryText;
			}

			// ------------------------------------------------------------
			// SANDBOX / WARMUP
			// ------------------------------------------------------------
			if ( panel.HasClass( "sandbox-label" ) )
			{
				panel.Style.Display = layout.ShowSandboxLabel ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.MinWidth = layout.SandboxLabelMinimumWidth > 0f ? Length.Pixels( layout.SandboxLabelMinimumWidth ) : null;
				SetPadding( panel, layout.SandboxLabelPadding );
				panel.Style.RowGap = Length.Pixels( layout.SandboxElementGap );
				panel.Style.BackgroundColor = Color.Transparent;
				ApplyScreenAnchor( panel, layout.SandboxLabelAnchor, layout.SandboxLabelLeft, layout.SandboxLabelTop, layout.SandboxLabelOffsetX, layout.SandboxLabelOffsetY );
				ApplyHorizontalContainerAlignment( panel, layout.SandboxLabelAlignment );
			}

			if ( panel.HasClass( "sandbox-label-chrome" ) )
			{
				bool warmup = panel.Ancestors.Any( x => x.HasClass( "sandbox-label" ) && x.HasClass( "warmup-label" ) );
				panel.Style.BackgroundColor = warmup ? theme.WarmupLabel : theme.SandboxLabel;
				panel.Style.BorderColor = warmup ? theme.AccentBorder : theme.Border;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				panel.Style.Opacity = layout.ShowSandboxLabelChrome ? layout.SandboxLabelChromeOpacity : 0f;
				SetCornerRadius( panel, theme.HudCornerRadius );
				ApplyBackdropBlur( panel, theme.HudBackdropBlur );
				ApplyHudPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "sandbox-title" ) )
			{
				bool warmup = panel.Ancestors.Any( x => x.HasClass( "sandbox-label" ) && x.HasClass( "warmup-label" ) );
				panel.Style.FontFamily = theme.DisplayFont;
				panel.Style.FontSize = Length.Pixels( layout.SandboxTitleSize );
				panel.Style.FontColor = warmup
					? (theme.OverrideWarmupTitleColor ? theme.WarmupTitleColor : theme.AccentText)
					: (theme.OverrideSandboxTitleColor ? theme.SandboxTitleColor : theme.PrimaryText);
				panel.Style.Opacity = layout.SandboxLabelOpacity;
				ApplyTextLayout( panel, layout.SandboxLabelAlignment, 0f );
			}

			if ( panel.HasClass( "sandbox-status" ) )
			{
				panel.Style.Display = layout.ShowSandboxStatus ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.SandboxStatusSize );
				panel.Style.Opacity = layout.SandboxLabelOpacity;
				ApplyTextLayout( panel, layout.SandboxLabelAlignment, 0f );
			}

			if ( panel.HasClass( "sandbox-hint" ) )
			{
				panel.Style.Display = layout.ShowSandboxHint ? DisplayMode.Flex : DisplayMode.None;
				panel.Style.FontSize = Length.Pixels( layout.SandboxHintSize );
				panel.Style.Opacity = layout.SandboxLabelOpacity;
				ApplyTextLayout( panel, layout.SandboxLabelAlignment, 0f );
			}

			// The developer preview toolbar is intentionally themed too, making the
			// generic HUD surface and selected accent state visible while tuning.
			if ( panel.HasClass( "hud-preview-toolbar" ) )
			{
				panel.Style.BackgroundColor = theme.HudPanel;
				panel.Style.BorderColor = theme.SoftBorder;
				panel.Style.BorderWidth = Length.Pixels( theme.HudBorderWidth );
				SetCornerRadius( panel, theme.HudCornerRadius );
				ApplyBackdropBlur( panel, theme.HudBackdropBlur );
				ApplyHudPanelShadow( panel, theme );
			}

			if ( panel.HasClass( "hud-preview-state" ) )
			{
				bool selected = panel.HasClass( "active" );
				panel.Style.BackgroundColor = selected
					? theme.AccentSelected
					: InteractiveColor( panel, theme.Button, theme.ButtonHover, theme.ButtonPressed );
				panel.Style.BorderColor = selected ? theme.AccentBorder : theme.SoftBorder;
				SetCornerRadius( panel, theme.ControlCornerRadius );
			}

			// Semantic text roles. Keep this explicit rather than using generic class
			// names so one theme control cannot accidentally recolor the entire HUD.
			if ( panel.HasClass( "decision-eyebrow" ) )
				panel.Style.FontColor = theme.AccentText;
			else if ( (panel.HasClass( "timer-mode" ) || panel.HasClass( "timer-note" )) && panel.Ancestors.Any( x => x.HasClass( "round-timer" ) && x.HasClass( "sudden" ) ) )
				panel.Style.FontColor = theme.DangerText;
			else if ( panel.HasClass( "ammo-state" ) )
				panel.Style.FontColor = theme.AccentText;
			else if ( panel.HasClass( "sandbox-hint" ) )
				panel.Style.FontColor = theme.MutedText;
			else if (
				panel.HasClass( "decision-pot" ) ||
				panel.HasClass( "decision-button-description" ) ||
				panel.HasClass( "timer-mode" ) ||
				panel.HasClass( "timer-note" ) ||
				panel.HasClass( "score-label" ) ||
				panel.HasClass( "score-player-label" ) ||
				panel.HasClass( "pot-owner" ) ||
				panel.HasClass( "sandbox-status" ) ||
				panel.HasClass( "ammo-weapon" )
			)
				panel.Style.FontColor = theme.SecondaryText;
		}
	}

	private static void ApplyTextFormatting( Panel panel, OMRUITheme theme )
	{
		if ( panel.HasClass( "menu-title" ) )
			OMRUITextFormat.Apply( panel, theme.TitleFormat );
		else if ( panel.HasClass( "menu-subtitle" ) )
			OMRUITextFormat.Apply( panel, theme.SubtitleFormat );
		else if (
			panel.HasClass( "health-number" ) ||
			panel.HasClass( "timer-number" ) ||
			panel.HasClass( "score-number" ) ||
			panel.HasClass( "ammo-current" ) ||
			panel.HasClass( "ammo-capacity" ) ||
			panel.HasClass( "decision-timer" ) ||
			panel.HasClass( "final-score" ) ||
			panel.HasClass( "pot-value" ) ||
			panel.HasClass( "theme-preview-number" )
		)
			OMRUITextFormat.Apply( panel, theme.NumberFormat );
		else if (
			panel.HasClass( "page-heading" ) ||
			panel.HasClass( "pause-title" ) ||
			panel.HasClass( "round" ) ||
			panel.HasClass( "center-message" ) ||
			panel.HasClass( "decision-title" ) ||
			panel.HasClass( "sandbox-title" ) ||
			panel.HasClass( "modifier-label" ) ||
			panel.HasClass( "online-status-label" ) ||
			panel.HasClass( "setting-name" )
		)
			OMRUITextFormat.Apply( panel, theme.HeadingFormat );
		else if (
			panel.HasClass( "button-title" ) ||
			panel.HasClass( "menu-primary" ) ||
			panel.HasClass( "menu-back" ) ||
			panel.HasClass( "pause-button-title" ) ||
			panel.HasClass( "decision-button-title" ) ||
			panel.HasClass( "theme-interactive-chip" )
		)
			OMRUITextFormat.Apply( panel, theme.ButtonFormat );
		else if (
			panel.HasClass( "health-label" ) ||
			panel.HasClass( "score-label" ) ||
			panel.HasClass( "score-player-label" ) ||
			panel.HasClass( "timer-mode" ) ||
			panel.HasClass( "timer-note" ) ||
			panel.HasClass( "decision-eyebrow" ) ||
			panel.HasClass( "decision-pot" ) ||
			panel.HasClass( "pot-owner" ) ||
			panel.HasClass( "sandbox-status" ) ||
			panel.HasClass( "sandbox-hint" ) ||
			panel.HasClass( "ammo-weapon" ) ||
			panel.HasClass( "ammo-state" ) ||
			panel.HasClass( "session-notice" ) ||
			panel.HasClass( "pause-eyebrow" ) ||
			panel.HasClass( "online-subheading" ) ||
			panel.HasClass( "lobby-ready-summary" ) ||
			panel.HasClass( "setting-value" ) ||
			panel.HasClass( "hud-preview-toolbar-title" ) ||
			panel.HasClass( "theme-preview-meta" )
		)
			OMRUITextFormat.Apply( panel, theme.MetaFormat );
		else if (
			panel.HasClass( "page-copy" ) ||
			panel.HasClass( "button-description" ) ||
			panel.HasClass( "lobby-note" ) ||
			panel.HasClass( "setting-description" ) ||
			panel.HasClass( "pause-note" ) ||
			panel.HasClass( "pause-summary" ) ||
			panel.HasClass( "pause-button-copy" ) ||
			panel.HasClass( "decision-button-description" ) ||
			panel.HasClass( "theme-preview-body" )
		)
			OMRUITextFormat.Apply( panel, theme.BodyFormat );
	}

	private static IEnumerable<Panel> Panels( Panel root )
	{
		yield return root;

		foreach ( Panel panel in root.Descendants )
			yield return panel;
	}

	private static ObjectFit Fit( OMRImageFit fit ) => fit switch
	{
		OMRImageFit.Contain => ObjectFit.Contain,
		OMRImageFit.Fill => ObjectFit.Fill,
		_ => ObjectFit.Cover
	};

	private static void ApplyTextLayout(
		Panel panel,
		OMRTextHorizontalAlignment alignment,
		float indent
	)
	{
		// Configurable UI text is authored as Label panels. Give every text
		// element a real horizontal layout area instead of letting it collapse
		// to the intrinsic width of its glyphs. Without this, text-align can be
		// technically applied while producing almost no visible movement.
		panel.Style.Width = Length.Percent( 100f );
		panel.Style.AlignSelf = Align.Stretch;

		panel.Style.TextAlign = alignment switch
		{
			OMRTextHorizontalAlignment.Left => TextAlign.Left,
			OMRTextHorizontalAlignment.Right => TextAlign.Right,
			_ => TextAlign.Center
		};

		float safeIndent = Math.Max( 0f, indent );
		panel.Style.PaddingLeft = Length.Pixels(
			alignment == OMRTextHorizontalAlignment.Right ? 0f : safeIndent
		);
		panel.Style.PaddingRight = Length.Pixels(
			alignment == OMRTextHorizontalAlignment.Left ? 0f : safeIndent
		);
	}

	private static void ApplyActionAlignment(
		Panel panel,
		OMRTextHorizontalAlignment alignment,
		float indent
	)
	{
		panel.Style.JustifyContent = alignment switch
		{
			OMRTextHorizontalAlignment.Left => Justify.FlexStart,
			OMRTextHorizontalAlignment.Right => Justify.FlexEnd,
			_ => Justify.Center
		};

		const float baseHorizontalPadding = 20f;
		float safeIndent = Math.Max( 0f, indent );
		panel.Style.PaddingLeft = Length.Pixels(
			baseHorizontalPadding +
			(alignment == OMRTextHorizontalAlignment.Left ? safeIndent : 0f)
		);
		panel.Style.PaddingRight = Length.Pixels(
			baseHorizontalPadding +
			(alignment == OMRTextHorizontalAlignment.Right ? safeIndent : 0f)
		);
	}


	private static void ApplyHorizontalContainerAlignment(
		Panel panel,
		OMRTextHorizontalAlignment alignment
	)
	{
		panel.Style.AlignItems = alignment switch
		{
			OMRTextHorizontalAlignment.Left => Align.FlexStart,
			OMRTextHorizontalAlignment.Right => Align.FlexEnd,
			_ => Align.Center
		};
	}

	private static void ApplyPairAlignment(
		Panel panel,
		OMRHUDPairAlignment alignment
	)
	{
		panel.Style.JustifyContent = alignment switch
		{
			OMRHUDPairAlignment.Left => Justify.FlexStart,
			OMRHUDPairAlignment.Center => Justify.Center,
			OMRHUDPairAlignment.Right => Justify.FlexEnd,
			_ => Justify.SpaceBetween
		};
	}

	private static void ApplyScreenAnchor(
		Panel panel,
		OMRHUDScreenAnchor anchor,
		float horizontalInset,
		float verticalInset,
		float offsetX,
		float offsetY
	)
	{
		panel.Style.Left = null;
		panel.Style.Right = null;
		panel.Style.Top = null;
		panel.Style.Bottom = null;

		bool centerX = anchor is OMRHUDScreenAnchor.TopCenter or OMRHUDScreenAnchor.Center or OMRHUDScreenAnchor.BottomCenter;
		bool right = anchor is OMRHUDScreenAnchor.TopRight or OMRHUDScreenAnchor.MiddleRight or OMRHUDScreenAnchor.BottomRight;
		bool centerY = anchor is OMRHUDScreenAnchor.MiddleLeft or OMRHUDScreenAnchor.Center or OMRHUDScreenAnchor.MiddleRight;
		bool bottom = anchor is OMRHUDScreenAnchor.BottomLeft or OMRHUDScreenAnchor.BottomCenter or OMRHUDScreenAnchor.BottomRight;

		if ( centerX )
			panel.Style.Left = Length.Percent( 50f );
		else if ( right )
			panel.Style.Right = Length.Pixels( horizontalInset );
		else
			panel.Style.Left = Length.Pixels( horizontalInset );

		if ( centerY )
			panel.Style.Top = Length.Percent( 50f );
		else if ( bottom )
			panel.Style.Bottom = Length.Pixels( verticalInset );
		else
			panel.Style.Top = Length.Pixels( verticalInset );

		PanelTransform transform = new();
		if ( centerX )
			transform.AddTranslate( Length.Percent( -50f ), Length.Pixels( 0f ), null );
		if ( centerY )
			transform.AddTranslate( Length.Pixels( 0f ), Length.Percent( -50f ), null );
		transform.AddTranslate( Length.Pixels( offsetX ), Length.Pixels( offsetY ), null );
		panel.Style.Transform = transform;
	}

	private static void ApplyPrimaryPanelShadow( Panel panel, OMRUITheme theme )
	{
		ApplyBoxShadow(
			panel,
			theme.EnablePrimaryPanelShadow,
			theme.PrimaryPanelShadowOffsetX,
			theme.PrimaryPanelShadowOffsetY,
			theme.PrimaryPanelShadowBlur,
			theme.PrimaryPanelShadowSpread,
			theme.PrimaryPanelShadowColor
		);
	}

	private static void ApplyHudPanelShadow( Panel panel, OMRUITheme theme )
	{
		ApplyBoxShadow(
			panel,
			theme.EnableHudPanelShadow,
			theme.HudPanelShadowOffsetX,
			theme.HudPanelShadowOffsetY,
			theme.HudPanelShadowBlur,
			theme.HudPanelShadowSpread,
			theme.HudPanelShadowColor
		);
	}

	private static void ApplyBoxShadow( Panel panel, bool enabled, float x, float y, float blur, float spread, Color color )
	{
		if ( !enabled )
		{
			panel.Style.Set( "box-shadow", "none" );
			return;
		}

		string value = string.Format(
			CultureInfo.InvariantCulture,
			"{0:0.###}px {1:0.###}px {2:0.###}px {3:0.###}px {4}",
			x, y, Math.Max( 0f, blur ), spread, color.Rgba
		);
		panel.Style.Set( "box-shadow", value );
	}

	private static void ApplyTextShadow( Panel panel, bool enabled, float x, float y, float blur, Color color )
	{
		if ( !enabled )
		{
			panel.Style.Set( "text-shadow", "none" );
			return;
		}

		string value = string.Format(
			CultureInfo.InvariantCulture,
			"{0:0.###}px {1:0.###}px {2:0.###}px {3}",
			x, y, Math.Max( 0f, blur ), color.Rgba
		);
		panel.Style.Set( "text-shadow", value );
	}

	private static void StylePreviewSurface(
		Panel panel,
		Color background,
		Color border,
		float radius,
		float blur = 0f,
		float borderWidth = 1f
	)
	{
		panel.Style.BackgroundColor = background;
		panel.Style.BorderColor = border;
		panel.Style.BorderWidth = Length.Pixels( Math.Max( 0f, borderWidth ) );
		SetCornerRadius( panel, radius );
		ApplyBackdropBlur( panel, blur );
	}

	private static Color InteractiveColor( Panel panel, Color normal, Color hover, Color pressed )
	{
		if ( panel.HasActive )
			return pressed;
		if ( panel.HasHovered )
			return hover;
		return normal;
	}

	private static void ApplyBackdropBlur( Panel panel, float pixels )
	{
		panel.Style.BackdropFilterBlur = Length.Pixels( Math.Max( 0f, pixels ) );
	}

	private static void ApplyDisplayTextEffect( Panel panel, OMRUITheme theme )
	{
		bool displayText =
			panel.HasClass( "menu-title" ) ||
			panel.HasClass( "page-heading" ) ||
			panel.HasClass( "pause-title" ) ||
			panel.HasClass( "round" ) ||
			panel.HasClass( "center-message" ) ||
			panel.HasClass( "decision-title" ) ||
			panel.HasClass( "sandbox-title" );

		if ( !displayText )
			return;

		ApplyTextShadow(
			panel,
			theme.EnableDisplayTextShadow,
			theme.DisplayTextShadowOffsetX,
			theme.DisplayTextShadowOffsetY,
			theme.DisplayTextShadowBlur,
			theme.DisplayTextShadowColor
		);

		bool enabled = theme.EnableDisplayTextStroke && theme.DisplayTextStrokeWidth > 0f;
		panel.Style.TextStrokeColor = enabled ? theme.DisplayTextStrokeColor : Color.Transparent;
		panel.Style.TextStrokeWidth = Length.Pixels( enabled
			? Math.Max( 0f, theme.DisplayTextStrokeWidth )
			: 0f );
	}

	private static void SetCornerRadius( Panel panel, float pixels )
	{
		Length? radius = Length.Pixels( Math.Max( 0f, pixels ) );
		panel.Style.BorderTopLeftRadius = radius;
		panel.Style.BorderTopRightRadius = radius;
		panel.Style.BorderBottomLeftRadius = radius;
		panel.Style.BorderBottomRightRadius = radius;
	}

	private static void SetPadding( Panel panel, float pixels )
	{
		Length? value = Length.Pixels( pixels );

		panel.Style.PaddingLeft = value;
		panel.Style.PaddingRight = value;
		panel.Style.PaddingTop = value;
		panel.Style.PaddingBottom = value;
	}
}