UI/Foundation/OMRUITheme.cs
using Sandbox;
using System;

/// <summary>
/// Shared visual language for every One More Round Razor UI surface.
///
/// Ownership rule:
/// - OMRUITheme owns visual identity: assets, fonts, text treatment, colors,
///   surfaces, borders, corner treatment and visual effects.
/// - OMRMainMenuLayout / OMRHUDLayout own geometry, placement, spacing,
///   visibility and interaction movement.
/// - Gameplay code owns values and state.
///
/// Keeping these responsibilities separate makes theme edits predictable:
/// changing a color should never move a panel, and moving a panel should never
/// silently recolor an unrelated component.
/// </summary>
[AssetType(
	Name = "OMR UI Theme",
	Extension = "omrtheme",
	Category = "One More Round/UI"
)]
public sealed partial class OMRUITheme : GameResource
{
	// ---------------------------------------------------------------------
	// BRAND IDENTITY
	// ---------------------------------------------------------------------
	[Group( "Brand Identity" ), Title( "Game Title" )]
	public string GameTitle { get; set; } = "ONE MORE ROUND";

	[Group( "Brand Identity" ), Title( "Subtitle" )]
	public string Subtitle { get; set; } = "Dev Build Alpha 1.6 · BOT 9";

	// Logo visibility belongs to OMRMENU. Keep this serialized field hidden so
	// older .omrtheme assets continue to load without losing data.
	[Hide]
	public bool ShowLogo { get; set; } = true;

	[Hide]
	public Texture LogoTexture { get; set; }

	[Hide]
	public Texture MenuBackgroundTexture { get; set; }

	[Group( "Brand Identity" ), ImageAssetPath, Title( "Logo Image" )]
	public string LogoImagePath { get; set; } = "";

	[Group( "Brand Identity" ), ImageAssetPath, Title( "Background Image" )]
	public string BackgroundImagePath { get; set; } = "";

	[Group( "Brand Identity" ), Range( 0f, 1f ), Title( "Background Image Opacity" )]
	public float MenuBackgroundOpacity { get; set; } = 1f;

	public bool HasLogo => !string.IsNullOrWhiteSpace( LogoImagePath ) || LogoTexture is not null;
	public Texture GetLogoImage() => string.IsNullOrWhiteSpace( LogoImagePath )
		? LogoTexture : Texture.Load( LogoImagePath );
	public Texture GetBackgroundImage() => string.IsNullOrWhiteSpace( BackgroundImagePath )
		? MenuBackgroundTexture : Texture.Load( BackgroundImagePath );

	// ---------------------------------------------------------------------
	// TYPOGRAPHY
	// ---------------------------------------------------------------------
	[Group( "Typography - Fonts" ), FontName, Title( "Display Font" )]
	public string DisplayFont { get; set; } = "Arial";

	[Group( "Typography - Fonts" ), FontName, Title( "Body Font" )]
	public string BodyFont { get; set; } = "Arial";

	[Group( "Typography - Fonts" ), FontName, Title( "Numeric Font" )]
	public string NumericFont { get; set; } = "Arial";

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Main Title" )]
	public OMRUITextFormat TitleFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Subtitle" )]
	public OMRUITextFormat SubtitleFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Headings" )]
	public OMRUITextFormat HeadingFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Buttons / Actions" )]
	public OMRUITextFormat ButtonFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Body / Descriptions" )]
	public OMRUITextFormat BodyFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Meta / Labels" )]
	public OMRUITextFormat MetaFormat { get; set; } = new();

	[Group( "Typography - Formatting" ), InlineEditor, Title( "Numbers" )]
	public OMRUITextFormat NumberFormat { get; set; } = new();

	// ---------------------------------------------------------------------
	// TEXT PALETTE
	// ---------------------------------------------------------------------
	[Group( "Text Palette - Global" ), Title( "Primary Text" )]
	[ColorUsage( true, false )]
	public Color PrimaryText { get; set; } = Color.White;

	[Group( "Text Palette - Global" ), Title( "Secondary Text" )]
	[ColorUsage( true, false )]
	public Color SecondaryText { get; set; } = new Color( 1f, 1f, 1f, 0.55f );

	[Group( "Text Palette - Global" ), Title( "Muted / Hint Text" )]
	[ColorUsage( true, false )]
	public Color MutedText { get; set; } = new Color( 1f, 1f, 1f, 0.34f );

	[Group( "Text Palette - Global" ), Title( "Accent Text" )]
	[ColorUsage( true, false )]
	public Color AccentText { get; set; } = new Color( 0.68f, 0.78f, 1f, 0.95f );

	[Group( "Text Palette - Global" ), Title( "Positive Text" )]
	[ColorUsage( true, false )]
	public Color PositiveText { get; set; } = new Color( 0.58f, 0.86f, 0.66f, 0.98f );

	[Group( "Text Palette - Global" ), Title( "Danger Text" )]
	[ColorUsage( true, false )]
	public Color DangerText { get; set; } = new Color( 1f, 0.49f, 0.45f, 0.98f );

	[Group( "Text Palette - Global" ), Title( "Error Text" )]
	[ColorUsage( true, false )]
	public Color ErrorText { get; set; } = new Color( 0.92f, 0.45f, 0.41f, 1f );

	// Menu-specific overrides are optional. Disabled means the semantic global
	// role above is used, so broad palette edits remain easy.
	[Group( "Text Overrides - Menu" ), Title( "Customize Menu Title" )]
	public bool OverrideMenuTitleColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverrideMenuTitleColor ), true ), Title( "Menu Title" )]
	[ColorUsage( true, false )]
	public Color MenuTitleColor { get; set; } = Color.White;

	[Group( "Text Overrides - Menu" ), Title( "Customize Subtitle" )]
	public bool OverrideSubtitleColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverrideSubtitleColor ), true ), Title( "Subtitle" )]
	[ColorUsage( true, false )]
	public Color SubtitleColor { get; set; } = Color.White;

	[Group( "Text Overrides - Menu" ), Title( "Customize Button Titles" )]
	public bool OverrideButtonTitleColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverrideButtonTitleColor ), true ), Title( "Button Titles" )]
	[ColorUsage( true, false )]
	public Color ButtonTitleColor { get; set; } = Color.White;

	[Group( "Text Overrides - Menu" ), Title( "Customize Button Descriptions" )]
	public bool OverrideButtonDescriptionColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverrideButtonDescriptionColor ), true ), Title( "Button Descriptions" )]
	[ColorUsage( true, false )]
	public Color ButtonDescriptionColor { get; set; } = Color.White;

	[Group( "Text Overrides - Menu" ), Title( "Customize Page Headings" )]
	public bool OverridePageHeadingColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverridePageHeadingColor ), true ), Title( "Page Headings" )]
	[ColorUsage( true, false )]
	public Color PageHeadingColor { get; set; } = Color.White;

	[Group( "Text Overrides - Menu" ), Title( "Customize Page Copy" )]
	public bool OverridePageCopyColor { get; set; }
	[Group( "Text Overrides - Menu" ), ShowIf( nameof( OverridePageCopyColor ), true ), Title( "Page Copy" )]
	[ColorUsage( true, false )]
	public Color PageCopyColor { get; set; } = new Color( 1f, 1f, 1f, 0.55f );

	[Group( "Text Overrides - HUD" ), Title( "Customize Local Health Number" )]
	public bool OverrideLocalHealthTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideLocalHealthTextColor ), true ), Title( "Local Health Number" )]
	[ColorUsage( true, false )]
	public Color LocalHealthTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Opponent Health Number" )]
	public bool OverrideOpponentHealthTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideOpponentHealthTextColor ), true ), Title( "Opponent Health Number" )]
	[ColorUsage( true, false )]
	public Color OpponentHealthTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Round Title" )]
	public bool OverrideRoundTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideRoundTextColor ), true ), Title( "Round Title" )]
	[ColorUsage( true, false )]
	public Color RoundTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Timer Number" )]
	public bool OverrideTimerTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideTimerTextColor ), true ), Title( "Timer Number" )]
	[ColorUsage( true, false )]
	public Color TimerTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Sudden Death Number" )]
	public bool OverrideSuddenDeathTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideSuddenDeathTextColor ), true ), Title( "Sudden Death Number" )]
	[ColorUsage( true, false )]
	public Color SuddenDeathTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Score Numbers" )]
	public bool OverrideScoreTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideScoreTextColor ), true ), Title( "Score Numbers" )]
	[ColorUsage( true, false )]
	public Color ScoreTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Pot Value" )]
	public bool OverridePotTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverridePotTextColor ), true ), Title( "Pot Value" )]
	[ColorUsage( true, false )]
	public Color PotTextColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Modifier Label" )]
	public bool OverrideModifierTextColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideModifierTextColor ), true ), Title( "Modifier Label" )]
	[ColorUsage( true, false )]
	public Color ModifierTextColor { get; set; } = new Color( 0.68f, 0.78f, 1f, 0.95f );

	[Group( "Text Overrides - HUD" ), Title( "Customize Center Message" )]
	public bool OverrideCenterMessageColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideCenterMessageColor ), true ), Title( "Center Message" )]
	[ColorUsage( true, false )]
	public Color CenterMessageColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Decision Title" )]
	public bool OverrideDecisionTitleColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideDecisionTitleColor ), true ), Title( "Decision Title" )]
	[ColorUsage( true, false )]
	public Color DecisionTitleColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Current Ammo" )]
	public bool OverrideAmmoCurrentColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideAmmoCurrentColor ), true ), Title( "Current Ammo" )]
	[ColorUsage( true, false )]
	public Color AmmoCurrentColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Sandbox Title" )]
	public bool OverrideSandboxTitleColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideSandboxTitleColor ), true ), Title( "Sandbox Title" )]
	[ColorUsage( true, false )]
	public Color SandboxTitleColor { get; set; } = Color.White;

	[Group( "Text Overrides - HUD" ), Title( "Customize Warmup Title" )]
	public bool OverrideWarmupTitleColor { get; set; }
	[Group( "Text Overrides - HUD" ), ShowIf( nameof( OverrideWarmupTitleColor ), true ), Title( "Warmup Title" )]
	[ColorUsage( true, false )]
	public Color WarmupTitleColor { get; set; } = new Color( 0.68f, 0.78f, 1f, 0.95f );

	// ---------------------------------------------------------------------
	// SURFACES
	// ---------------------------------------------------------------------
	[Group( "Surfaces - Menu" ), Title( "Menu Canvas Behind Image" )]
	[ColorUsage( true, false )]
	public Color MenuCanvasColor { get; set; } = new Color( 10f / 255f, 12f / 255f, 15f / 255f, 1f );

	[Group( "Surfaces - Menu" ), Title( "Menu Screen Overlay" )]
	[ColorUsage( true, false )]
	public Color ScreenOverlay { get; set; } = new Color( 0.04f, 0.05f, 0.065f, 0.84f );

	[Group( "Surfaces - Menu" ), Title( "Menu Shell" )]
	[ColorUsage( true, false )]
	public Color MenuShellBackground { get; set; } = new Color( 0.055f, 0.063f, 0.075f, 0.95f );

	[Group( "Surfaces - Menu" ), Title( "Soft Cards / Rows" )]
	[ColorUsage( true, false )]
	public Color SoftPanelBackground { get; set; } = new Color( 1f, 1f, 1f, 0.04f );

	[Group( "Surfaces - Menu" ), Title( "Controls / Selectors" )]
	[ColorUsage( true, false )]
	public Color ControlBackground { get; set; } = new Color( 0.04f, 0.05f, 0.06f, 0.58f );

	[Group( "Surfaces - Menu" ), Title( "Session Notice" )]
	[ColorUsage( true, false )]
	public Color NoticeBackground { get; set; } = new Color( 1f, 1f, 1f, 0.08f );

	[Group( "Surfaces - Pause" ), Title( "Pause Overlay" )]
	[ColorUsage( true, false )]
	public Color PauseOverlay { get; set; } = new Color( 0.02f, 0.025f, 0.035f, 0.64f );

	[Group( "Surfaces - Pause" ), Title( "Pause Panel" )]
	[ColorUsage( true, false )]
	public Color PausePanelBackground { get; set; } = new Color( 0.055f, 0.063f, 0.075f, 0.95f );

	[Group( "Surfaces - HUD" ), Title( "Default HUD Panel" )]
	[ColorUsage( true, false )]
	public Color HudPanel { get; set; } = new Color( 0.04f, 0.05f, 0.06f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Timer Panel" )]
	[ColorUsage( true, false )]
	public Color TimerPanel { get; set; } = new Color( 0.04f, 0.05f, 0.06f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Pot Panel" )]
	[ColorUsage( true, false )]
	public Color PotPanel { get; set; } = new Color( 0.04f, 0.05f, 0.06f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Ammo Panel" )]
	[ColorUsage( true, false )]
	public Color AmmoPanel { get; set; } = new Color( 0.04f, 0.05f, 0.06f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Health Track" )]
	[ColorUsage( true, false )]
	public Color HealthTrack { get; set; } = new Color( 0f, 0f, 0f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Sudden Death Panel" )]
	[ColorUsage( true, false )]
	public Color SuddenDeath { get; set; } = new Color( 0.62f, 0.10f, 0.09f, 0.58f );

	[Group( "Surfaces - HUD" ), Title( "Decision Screen Overlay" )]
	[ColorUsage( true, false )]
	public Color DecisionOverlay { get; set; } = new Color( 0.02f, 0.025f, 0.035f, 0.64f );

	[Group( "Surfaces - HUD" ), Title( "Decision Panel" )]
	[ColorUsage( true, false )]
	public Color DecisionPanelBackground { get; set; } = new Color( 0.055f, 0.063f, 0.075f, 0.96f );

	[Group( "Surfaces - HUD" ), Title( "Sandbox Label" )]
	[ColorUsage( true, false )]
	public Color SandboxLabel { get; set; } = new Color( 0f, 0f, 0f, 0.48f );

	[Group( "Surfaces - HUD" ), Title( "Warmup Label" )]
	[ColorUsage( true, false )]
	public Color WarmupLabel { get; set; } = new Color( 45f / 255f, 65f / 255f, 105f / 255f, 0.48f );

	// ---------------------------------------------------------------------
	// HEALTH
	// ---------------------------------------------------------------------
	[Group( "Health Colors" ), Title( "Local Health Fill" )]
	[ColorUsage( true, false )]
	public Color LocalHealth { get; set; } = new Color( 0.92f, 0.92f, 0.92f, 0.96f );

	[Group( "Health Colors" ), Title( "Opponent Health Fill" )]
	[ColorUsage( true, false )]
	public Color OpponentHealth { get; set; } = new Color( 0.82f, 0.29f, 0.27f, 0.96f );

	// ---------------------------------------------------------------------
	// BORDERS
	// ---------------------------------------------------------------------
	[Group( "Borders" ), Title( "Primary Border" )]
	[ColorUsage( true, false )]
	public Color Border { get; set; } = new Color( 1f, 1f, 1f, 0.16f );

	[Group( "Borders" ), Title( "Soft Border" )]
	[ColorUsage( true, false )]
	public Color SoftBorder { get; set; } = new Color( 1f, 1f, 1f, 0.10f );

	[Group( "Borders" ), Title( "Strong Border" )]
	[ColorUsage( true, false )]
	public Color StrongBorder { get; set; } = new Color( 1f, 1f, 1f, 0.22f );

	[Group( "Borders" ), Title( "Accent Border" )]
	[ColorUsage( true, false )]
	public Color AccentBorder { get; set; } = new Color( 0.49f, 0.61f, 0.88f, 0.30f );

	[Group( "Borders" ), Title( "Positive Border" )]
	[ColorUsage( true, false )]
	public Color PositiveBorder { get; set; } = new Color( 0.53f, 0.86f, 0.61f, 0.30f );

	[Group( "Borders" ), Title( "Danger Border" )]
	[ColorUsage( true, false )]
	public Color DangerBorder { get; set; } = new Color( 0.86f, 0.50f, 0.47f, 0.30f );

	[Group( "Borders" ), Range( 0f, 6f ), Title( "HUD Border Width" )]
	public float HudBorderWidth { get; set; } = 1f;

	// ---------------------------------------------------------------------
	// INTERACTIVE SURFACES
	// ---------------------------------------------------------------------
	[Group( "Interactive - Neutral" ), Title( "Normal" )]
	[ColorUsage( true, false )]
	public Color Button { get; set; } = new Color( 1f, 1f, 1f, 0.07f );

	[Group( "Interactive - Neutral" ), Title( "Hover" )]
	[ColorUsage( true, false )]
	public Color ButtonHover { get; set; } = new Color( 1f, 1f, 1f, 0.16f );

	[Group( "Interactive - Neutral" ), Title( "Pressed" )]
	[ColorUsage( true, false )]
	public Color ButtonPressed { get; set; } = new Color( 1f, 1f, 1f, 0.22f );

	[Group( "Interactive - Accent" ), Title( "Normal" )]
	[ColorUsage( true, false )]
	public Color Accent { get; set; } = new Color( 0.275f, 0.41f, 0.61f, 0.24f );

	[Group( "Interactive - Accent" ), Title( "Hover" )]
	[ColorUsage( true, false )]
	public Color AccentHover { get; set; } = new Color( 0.275f, 0.41f, 0.61f, 0.42f );

	[Group( "Interactive - Accent" ), Title( "Pressed" )]
	[ColorUsage( true, false )]
	public Color AccentPressed { get; set; } = new Color( 0.275f, 0.41f, 0.61f, 0.55f );

	[Group( "Interactive - Accent" ), Title( "Selected / Active" )]
	[ColorUsage( true, false )]
	public Color AccentSelected { get; set; } = new Color( 0.31f, 0.47f, 0.72f, 0.42f );

	[Group( "Interactive - Accent" ), Title( "Soft State Surface" )]
	[ColorUsage( true, false )]
	public Color AccentSoft { get; set; } = new Color( 0.33f, 0.41f, 0.65f, 0.13f );

	[Group( "Interactive - Positive" ), Title( "Normal" )]
	[ColorUsage( true, false )]
	public Color Positive { get; set; } = new Color( 0.29f, 0.53f, 0.37f, 0.24f );

	[Group( "Interactive - Positive" ), Title( "Hover" )]
	[ColorUsage( true, false )]
	public Color PositiveHover { get; set; } = new Color( 0.29f, 0.53f, 0.37f, 0.42f );

	[Group( "Interactive - Positive" ), Title( "Pressed" )]
	[ColorUsage( true, false )]
	public Color PositivePressed { get; set; } = new Color( 0.29f, 0.53f, 0.37f, 0.55f );

	[Group( "Interactive - Positive" ), Title( "Selected / Active" )]
	[ColorUsage( true, false )]
	public Color PositiveSelected { get; set; } = new Color( 0.31f, 0.65f, 0.41f, 0.34f );

	[Group( "Interactive - Positive" ), Title( "Soft State Surface" )]
	[ColorUsage( true, false )]
	public Color PositiveSoft { get; set; } = new Color( 0.29f, 0.53f, 0.37f, 0.10f );

	[Group( "Interactive - Danger" ), Title( "Normal" )]
	[ColorUsage( true, false )]
	public Color Danger { get; set; } = new Color( 0.67f, 0.29f, 0.25f, 0.22f );

	[Group( "Interactive - Danger" ), Title( "Hover" )]
	[ColorUsage( true, false )]
	public Color DangerHover { get; set; } = new Color( 0.67f, 0.29f, 0.25f, 0.40f );

	[Group( "Interactive - Danger" ), Title( "Pressed" )]
	[ColorUsage( true, false )]
	public Color DangerPressed { get; set; } = new Color( 0.67f, 0.29f, 0.25f, 0.52f );

	[Group( "Interactive - Danger" ), Title( "Soft State Surface" )]
	[ColorUsage( true, false )]
	public Color DangerSoft { get; set; } = new Color( 0.55f, 0.29f, 0.29f, 0.10f );

	// ---------------------------------------------------------------------
	// SHAPE / MATERIAL TREATMENT
	// ---------------------------------------------------------------------
	[Group( "Shape" ), Range( 0f, 80f ), Title( "Menu Shell Corner Radius" )]
	public float MenuShellCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 80f ), Title( "Primary Panel Corner Radius" )]
	public float PanelCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 60f ), Title( "Soft Panel / Card Corner Radius" )]
	public float SoftPanelCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 60f ), Title( "Button Corner Radius" )]
	public float ButtonCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 60f ), Title( "Control Corner Radius" )]
	public float ControlCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 60f ), Title( "HUD Panel Corner Radius" )]
	public float HudCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 30f ), Title( "Health Bar Corner Radius" )]
	public float HealthBarCornerRadius { get; set; }

	[Group( "Shape" ), Range( 0f, 60f ), Title( "Tag / Notice Corner Radius" )]
	public float TagCornerRadius { get; set; }

	[Group( "Material Effects" ), Range( 0f, 30f ), Title( "Primary Panel Backdrop Blur" )]
	public float PrimaryPanelBackdropBlur { get; set; }

	[Group( "Material Effects" ), Range( 0f, 24f ), Title( "HUD Backdrop Blur" )]
	public float HudBackdropBlur { get; set; }

	[Group( "Material Effects - Primary Shadow" ), Title( "Enable Primary Panel Shadow" )]
	public bool EnablePrimaryPanelShadow { get; set; } = true;

	[Group( "Material Effects - Primary Shadow" ), ShowIf( nameof( EnablePrimaryPanelShadow ), true ), Title( "Shadow Color" )]
	[ColorUsage( true, false )]
	public Color PrimaryPanelShadowColor { get; set; } = new Color( 0f, 0f, 0f, 0.50f );

	[Group( "Material Effects - Primary Shadow" ), ShowIf( nameof( EnablePrimaryPanelShadow ), true ), Range( -50f, 50f ), Title( "Offset X" )]
	public float PrimaryPanelShadowOffsetX { get; set; }

	[Group( "Material Effects - Primary Shadow" ), ShowIf( nameof( EnablePrimaryPanelShadow ), true ), Range( -50f, 80f ), Title( "Offset Y" )]
	public float PrimaryPanelShadowOffsetY { get; set; } = 16f;

	[Group( "Material Effects - Primary Shadow" ), ShowIf( nameof( EnablePrimaryPanelShadow ), true ), Range( 0f, 120f ), Title( "Blur" )]
	public float PrimaryPanelShadowBlur { get; set; } = 65f;

	[Group( "Material Effects - Primary Shadow" ), ShowIf( nameof( EnablePrimaryPanelShadow ), true ), Range( -30f, 60f ), Title( "Spread" )]
	public float PrimaryPanelShadowSpread { get; set; }

	[Group( "Material Effects - HUD Shadow" ), Title( "Enable HUD Panel Shadow" )]
	public bool EnableHudPanelShadow { get; set; }

	[Group( "Material Effects - HUD Shadow" ), ShowIf( nameof( EnableHudPanelShadow ), true ), Title( "Shadow Color" )]
	[ColorUsage( true, false )]
	public Color HudPanelShadowColor { get; set; } = new Color( 0f, 0f, 0f, 0.40f );

	[Group( "Material Effects - HUD Shadow" ), ShowIf( nameof( EnableHudPanelShadow ), true ), Range( -40f, 40f ), Title( "Offset X" )]
	public float HudPanelShadowOffsetX { get; set; }

	[Group( "Material Effects - HUD Shadow" ), ShowIf( nameof( EnableHudPanelShadow ), true ), Range( -40f, 60f ), Title( "Offset Y" )]
	public float HudPanelShadowOffsetY { get; set; } = 6f;

	[Group( "Material Effects - HUD Shadow" ), ShowIf( nameof( EnableHudPanelShadow ), true ), Range( 0f, 80f ), Title( "Blur" )]
	public float HudPanelShadowBlur { get; set; } = 24f;

	[Group( "Material Effects - HUD Shadow" ), ShowIf( nameof( EnableHudPanelShadow ), true ), Range( -20f, 40f ), Title( "Spread" )]
	public float HudPanelShadowSpread { get; set; }

	[Group( "Material Effects - Display Text Shadow" ), Title( "Enable Display Text Shadow" )]
	public bool EnableDisplayTextShadow { get; set; }

	[Group( "Material Effects - Display Text Shadow" ), ShowIf( nameof( EnableDisplayTextShadow ), true ), Title( "Shadow Color" )]
	[ColorUsage( true, false )]
	public Color DisplayTextShadowColor { get; set; } = new Color( 0f, 0f, 0f, 0.85f );

	[Group( "Material Effects - Display Text Shadow" ), ShowIf( nameof( EnableDisplayTextShadow ), true ), Range( -30f, 30f ), Title( "Offset X" )]
	public float DisplayTextShadowOffsetX { get; set; }

	[Group( "Material Effects - Display Text Shadow" ), ShowIf( nameof( EnableDisplayTextShadow ), true ), Range( -30f, 30f ), Title( "Offset Y" )]
	public float DisplayTextShadowOffsetY { get; set; } = 3f;

	[Group( "Material Effects - Display Text Shadow" ), ShowIf( nameof( EnableDisplayTextShadow ), true ), Range( 0f, 50f ), Title( "Blur" )]
	public float DisplayTextShadowBlur { get; set; } = 10f;

	[Group( "Material Effects" ), Title( "Display Text Stroke" )]
	public bool EnableDisplayTextStroke { get; set; }

	[Group( "Material Effects" ), ShowIf( nameof( EnableDisplayTextStroke ), true ), Title( "Display Text Stroke Color" )]
	[ColorUsage( true, false )]
	public Color DisplayTextStrokeColor { get; set; } = new Color( 0f, 0f, 0f, 0.85f );

	[Group( "Material Effects" ), ShowIf( nameof( EnableDisplayTextStroke ), true ), Range( 0f, 6f ), Title( "Display Text Stroke Width" )]
	public float DisplayTextStrokeWidth { get; set; } = 1f;

	/// <summary>
	/// Razor panels use this to notice edits made to the asset while the game is
	/// running. Every tunable visual property must participate in this hash.
	/// </summary>
	public int ContentHash()
	{
		HashCode hash = new();

		hash.Add( GameTitle );
		hash.Add( Subtitle );
		hash.Add( ShowLogo );
		hash.Add( LogoTexture );
		hash.Add( MenuBackgroundTexture );
		hash.Add( LogoImagePath );
		hash.Add( BackgroundImagePath );
		hash.Add( MenuBackgroundOpacity );

		hash.Add( DisplayFont );
		hash.Add( BodyFont );
		hash.Add( NumericFont );
		hash.Add( TitleFormat?.ContentHash() ?? 0 );
		hash.Add( SubtitleFormat?.ContentHash() ?? 0 );
		hash.Add( HeadingFormat?.ContentHash() ?? 0 );
		hash.Add( ButtonFormat?.ContentHash() ?? 0 );
		hash.Add( BodyFormat?.ContentHash() ?? 0 );
		hash.Add( MetaFormat?.ContentHash() ?? 0 );
		hash.Add( NumberFormat?.ContentHash() ?? 0 );

		hash.Add( PrimaryText );
		hash.Add( SecondaryText );
		hash.Add( MutedText );
		hash.Add( AccentText );
		hash.Add( PositiveText );
		hash.Add( DangerText );
		hash.Add( ErrorText );

		hash.Add( OverrideMenuTitleColor ); hash.Add( MenuTitleColor );
		hash.Add( OverrideSubtitleColor ); hash.Add( SubtitleColor );
		hash.Add( OverrideButtonTitleColor ); hash.Add( ButtonTitleColor );
		hash.Add( OverrideButtonDescriptionColor ); hash.Add( ButtonDescriptionColor );
		hash.Add( OverridePageHeadingColor ); hash.Add( PageHeadingColor );
		hash.Add( OverridePageCopyColor ); hash.Add( PageCopyColor );
		hash.Add( OverrideLocalHealthTextColor ); hash.Add( LocalHealthTextColor );
		hash.Add( OverrideOpponentHealthTextColor ); hash.Add( OpponentHealthTextColor );
		hash.Add( OverrideRoundTextColor ); hash.Add( RoundTextColor );
		hash.Add( OverrideTimerTextColor ); hash.Add( TimerTextColor );
		hash.Add( OverrideSuddenDeathTextColor ); hash.Add( SuddenDeathTextColor );
		hash.Add( OverrideScoreTextColor ); hash.Add( ScoreTextColor );
		hash.Add( OverridePotTextColor ); hash.Add( PotTextColor );
		hash.Add( OverrideModifierTextColor ); hash.Add( ModifierTextColor );
		hash.Add( OverrideCenterMessageColor ); hash.Add( CenterMessageColor );
		hash.Add( OverrideDecisionTitleColor ); hash.Add( DecisionTitleColor );
		hash.Add( OverrideAmmoCurrentColor ); hash.Add( AmmoCurrentColor );
		hash.Add( OverrideSandboxTitleColor ); hash.Add( SandboxTitleColor );
		hash.Add( OverrideWarmupTitleColor ); hash.Add( WarmupTitleColor );

		hash.Add( MenuCanvasColor );
		hash.Add( ScreenOverlay );
		hash.Add( MenuShellBackground );
		hash.Add( SoftPanelBackground );
		hash.Add( ControlBackground );
		hash.Add( NoticeBackground );
		hash.Add( PauseOverlay );
		hash.Add( PausePanelBackground );
		hash.Add( HudPanel );
		hash.Add( TimerPanel );
		hash.Add( PotPanel );
		hash.Add( AmmoPanel );
		hash.Add( HealthTrack );
		hash.Add( SuddenDeath );
		hash.Add( DecisionOverlay );
		hash.Add( DecisionPanelBackground );
		hash.Add( SandboxLabel );
		hash.Add( WarmupLabel );
		hash.Add( LocalHealth );
		hash.Add( OpponentHealth );

		hash.Add( Border );
		hash.Add( SoftBorder );
		hash.Add( StrongBorder );
		hash.Add( AccentBorder );
		hash.Add( PositiveBorder );
		hash.Add( DangerBorder );
		hash.Add( HudBorderWidth );

		hash.Add( Button );
		hash.Add( ButtonHover );
		hash.Add( ButtonPressed );
		hash.Add( Accent );
		hash.Add( AccentHover );
		hash.Add( AccentPressed );
		hash.Add( AccentSelected );
		hash.Add( AccentSoft );
		hash.Add( Positive );
		hash.Add( PositiveHover );
		hash.Add( PositivePressed );
		hash.Add( PositiveSelected );
		hash.Add( PositiveSoft );
		hash.Add( Danger );
		hash.Add( DangerHover );
		hash.Add( DangerPressed );
		hash.Add( DangerSoft );

		hash.Add( MenuShellCornerRadius );
		hash.Add( PanelCornerRadius );
		hash.Add( SoftPanelCornerRadius );
		hash.Add( ButtonCornerRadius );
		hash.Add( ControlCornerRadius );
		hash.Add( HudCornerRadius );
		hash.Add( HealthBarCornerRadius );
		hash.Add( TagCornerRadius );
		hash.Add( PrimaryPanelBackdropBlur );
		hash.Add( HudBackdropBlur );
		hash.Add( EnablePrimaryPanelShadow );
		hash.Add( PrimaryPanelShadowColor );
		hash.Add( PrimaryPanelShadowOffsetX );
		hash.Add( PrimaryPanelShadowOffsetY );
		hash.Add( PrimaryPanelShadowBlur );
		hash.Add( PrimaryPanelShadowSpread );
		hash.Add( EnableHudPanelShadow );
		hash.Add( HudPanelShadowColor );
		hash.Add( HudPanelShadowOffsetX );
		hash.Add( HudPanelShadowOffsetY );
		hash.Add( HudPanelShadowBlur );
		hash.Add( HudPanelShadowSpread );
		hash.Add( EnableDisplayTextShadow );
		hash.Add( DisplayTextShadowColor );
		hash.Add( DisplayTextShadowOffsetX );
		hash.Add( DisplayTextShadowOffsetY );
		hash.Add( DisplayTextShadowBlur );
		hash.Add( EnableDisplayTextStroke );
		hash.Add( DisplayTextStrokeColor );
		hash.Add( DisplayTextStrokeWidth );

		return hash.ToHashCode();
	}
}