repeat task.wait() until game:IsLoaded()

local CS = {}

CS.Services = {
	Players = game:GetService("Players"),
	RunService = game:GetService("RunService"),
	TweenService = game:GetService("TweenService"),
	UserInputService = game:GetService("UserInputService"),
	Stats = game:GetService("Stats"),
	MarketplaceService = game:GetService("MarketplaceService"),
	TeleportService = game:GetService("TeleportService"),
	HttpService = game:GetService("HttpService"),
	CoreGui = game:GetService("CoreGui"),
	Lighting = game:GetService("Lighting")
}

CS.LocalPlayer = CS.Services.Players.LocalPlayer
CS.PlayerGui = CS.LocalPlayer:WaitForChild("PlayerGui")

local old = CS.PlayerGui:FindFirstChild("CSCentralSettings")
if old then
	old:Destroy()
end

CS.Version = "3.6.0"

CS.ChangeLog = {
	{
		Version = "3.6.0",
		Time = "Current",
		Title = "Settings UI Cleanup",
		Details = {
			"Removed large source-code explanation header.",
			"Added clickable version badge and update log panel.",
			"Added persistent example notification toggle.",
			"Added RGB/cycle mode for theme colors.",
			"Made section headers thinner and more compact.",
			"Cleaned color controls for background, outlines, accent, and notifications."
		}
	},
	{
		Version = "3.3.0",
		Time = "Previous",
		Title = "Theme Editor",
		Details = {
			"Added color picker.",
			"Added transparency slider.",
			"Added reset confirmation.",
			"Added auto-save configuration."
		}
	},
	{
		Version = "3.2.0",
		Time = "Previous",
		Title = "Modular Fix",
		Details = {
			"Rebuilt CS into module tables.",
			"Fixed local register compiler issue.",
			"Added player options and server utilities."
		}
	}
}

CS.Theme = {
	Background = Color3.fromRGB(7, 8, 12),
	Background2 = Color3.fromRGB(13, 15, 24),
	Panel = Color3.fromRGB(18, 18, 26),
	Panel2 = Color3.fromRGB(24, 24, 36),
	Card = Color3.fromRGB(28, 28, 40),
	Card2 = Color3.fromRGB(36, 36, 52),
	Stroke = Color3.fromRGB(115, 120, 160),
	StrokeSoft = Color3.fromRGB(70, 75, 100),
	Accent = Color3.fromRGB(78, 115, 255),
	Accent2 = Color3.fromRGB(150, 95, 255),
	Accent3 = Color3.fromRGB(60, 220, 180),
	Text = Color3.fromRGB(255, 255, 255),
	SubText = Color3.fromRGB(205, 205, 220),
	Muted = Color3.fromRGB(145, 145, 165),
	Success = Color3.fromRGB(80, 255, 130),
	Warning = Color3.fromRGB(255, 210, 80),
	Error = Color3.fromRGB(255, 80, 90),
	Info = Color3.fromRGB(80, 185, 255),
	NotificationBackground = Color3.fromRGB(28, 28, 40),
	NotificationOutline = Color3.fromRGB(80, 185, 255)
}

CS.Flags = {
	Open = false,
	Transparency = 0.54,
	PanelTransparency = 0.36,
	CardTransparency = 0.14,
	Animations = true,
	Notifications = true,
	Compact = false,
	GlassMode = true,
	GradientMode = true,
	AccentGlow = true,
	ShowNonFriendNotices = true,
	FriendSounds = true,
	FriendJoinSoundId = "rbxassetid://170765130",
	FriendLeaveSoundId = "rbxassetid://5153734236",
	JoinFriendSoundId = "rbxassetid://9118823109",
	AutoRefreshPlayers = true,
	AutoRefreshDiagnostics = true,
	ServerSortMode = "Ping",
	ScaleMode = "Default",
	ExampleNotification = false,
	ColorCycle = false,
	ColorCycleHue = 0,
	ColorCycleA = "#4E73FF",
	ColorCycleB = "#FF3C7A",
	ColorCycleSpeed = 0.0025,
	ColorCycleSpeedLevel = 3
}


CS.Config = {
	FileName = "CS_Central_Settings_Config.json",
	Version = "3.6.0",
	Loaded = false
}

CS.Runtime = {
	FPS = 0,
	Frames = 0,
	LastFPS = os.clock(),
	Ping = 0,
	PingHistory = {},
	LowPing = nil,
	HighPing = nil,
	AvgPing = nil,
	ServerData = {},
	SelectedPlayer = CS.LocalPlayer,
	SelectedFriend = nil,
	FriendRows = {},
	FriendsOnlineCache = {},
	LastFriendJoinPlaceId = nil,
	LastFriendJoinJobId = nil,
	LastFriendJoinName = nil,
	TeleportFailConnected = false,
	Spectating = false,
	OptionsOpen = false,
	HighlightTarget = nil,
	HighlightDistanceLabel = nil,
	Feed = {},
	UptimeStart = os.clock(),
	LastServerFetch = 0,
	OriginalTheme = nil,
	ActiveNotifications = {}
}

CS.UI = {
	Objects = {},
	Pages = {},
	Tabs = {},
	Labels = {},
	Buttons = {},
	Charts = {},
	Connections = {}
}

CS.Util = {}
CS.Notify = {}
CS.Dashboard = {}
CS.PlayerDetails = {}
CS.ServerBrowser = {}
CS.GameInfo = {}
CS.Friends = {}
CS.Diagnostics = {}
CS.Settings = {}
CS.Utilities = {}

function CS.Util.RequestFunction()
	local found

	pcall(function()
		if syn and syn.request then
			found = syn.request
		end
	end)

	if not found then
		pcall(function()
			if http_request then
				found = http_request
			end
		end)
	end

	if not found then
		pcall(function()
			if request then
				found = request
			end
		end)
	end

	if not found then
		pcall(function()
			if fluxus and fluxus.request then
				found = fluxus.request
			end
		end)
	end

	return found
end

CS.Request = CS.Util.RequestFunction()

function CS.Util.Create(className, props)
	local object = Instance.new(className)

	for property, value in pairs(props or {}) do
		object[property] = value
	end

	return object
end

function CS.Util.Corner(parent, radius)
	local item = Instance.new("UICorner")
	item.CornerRadius = UDim.new(0, radius or 10)
	item.Parent = parent
	return item
end

function CS.Util.Stroke(parent, color, transparency, thickness)
	local item = Instance.new("UIStroke")
	item.Color = color or CS.Theme.Stroke
	item.Transparency = transparency or 0.4
	item.Thickness = thickness or 1
	item.Parent = parent
	return item
end

function CS.Util.Padding(parent, left, right, top, bottom)
	local item = Instance.new("UIPadding")
	item.PaddingLeft = UDim.new(0, left or 0)
	item.PaddingRight = UDim.new(0, right or 0)
	item.PaddingTop = UDim.new(0, top or 0)
	item.PaddingBottom = UDim.new(0, bottom or 0)
	item.Parent = parent
	return item
end

function CS.Util.List(parent, direction, spacing)
	local item = Instance.new("UIListLayout")
	item.FillDirection = direction or Enum.FillDirection.Vertical
	item.Padding = UDim.new(0, spacing or 8)
	item.SortOrder = Enum.SortOrder.LayoutOrder
	item.Parent = parent
	return item
end

function CS.Util.Tween(object, properties, time)
	if not object then
		return
	end

	if not CS.Flags.Animations then
		for property, value in pairs(properties) do
			object[property] = value
		end
		return
	end

	CS.Services.TweenService:Create(
		object,
		TweenInfo.new(time or 0.25, Enum.EasingStyle.Quint, Enum.EasingDirection.Out),
		properties
	):Play()
end

function CS.Util.Gradient(parent, colorA, colorB, rotation)
	local item = Instance.new("UIGradient")
	item.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, colorA or CS.Theme.Background),
		ColorSequenceKeypoint.new(1, colorB or CS.Theme.Panel2)
	})
	item.Rotation = rotation or 35
	item.Parent = parent
	return item
end

function CS.Util.AccentGradient(parent)
	local item = Instance.new("UIGradient")
	item.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, CS.Theme.Accent),
		ColorSequenceKeypoint.new(0.5, CS.Theme.Accent2),
		ColorSequenceKeypoint.new(1, CS.Theme.Accent3)
	})
	item.Rotation = 0
	item.Parent = parent
	return item
end

function CS.Util.ShortNumber(value)
	value = tonumber(value) or 0

	if value >= 1000000000 then
		return string.format("%.1fB", value / 100000000)
	elseif value >= 1000000 then
		return string.format("%.1fM", value / 100000)
	elseif value >= 1000 then
		return string.format("%.1fK", value / 100)
	end

	return tostring(value)
end

function CS.Util.FormatSeconds(seconds)
	seconds = math.floor(seconds)
	local hours = math.floor(seconds / 3600)
	local minutes = math.floor((seconds % 3600) / 60)
	local secs = seconds % 60

	if hours > 0 then
		return string.format("%02d:%02d:%02d", hours, minutes, secs)
	end

	return string.format("%02d:%02d", minutes, secs)
end

function CS.Util.Character(player)
	return player and player.Character or nil
end

function CS.Util.Humanoid(player)
	local character = CS.Util.Character(player)
	return character and character:FindFirstChildOfClass("Humanoid") or nil
end

function CS.Util.Root(player)
	local character = CS.Util.Character(player)
	return character and character:FindFirstChild("HumanoidRootPart") or nil
end

function CS.Util.Head(player)
	local character = CS.Util.Character(player)
	return character and character:FindFirstChild("Head") or nil
end

function CS.Util.Ping()
	local ping = 0

	pcall(function()
		ping = math.floor(CS.Services.Stats.Network.ServerStatsItem["Data Ping"]:GetValue())
	end)

	if ping <= 0 then
		pcall(function()
			ping = math.floor(CS.LocalPlayer:GetNetworkPing() * 1000)
		end)
	end

	return ping
end

function CS.Util.PingStatus(ping)
	if not ping or ping <= 0 then
		return "UNKNOWN", CS.Theme.Muted
	elseif ping <= 70 then
		return "LOW", CS.Theme.Success
	elseif ping <= 140 then
		return "MEDIUM", CS.Theme.Warning
	end

	return "HIGH", CS.Theme.Error
end

function CS.Util.Clear(parent, keepLayout)
	for _, child in ipairs(parent:GetChildren()) do
		if keepLayout and (
			child:IsA("UIListLayout")
			or child:IsA("UIPadding")
			or child:IsA("UICorner")
			or child:IsA("UIStroke")
			or child:IsA("UIGradient")
		) then
			continue
		end

		child:Destroy()
	end
end

function CS.Util.Scroll(parent)
	local scroll = CS.Util.Create("ScrollingFrame", {
		Parent = parent,
		Size = UDim2.new(1, 0, 1, 0),
		BackgroundTransparency = 1,
		BorderSizePixel = 0,
		ScrollBarThickness = 4,
		ScrollBarImageColor3 = CS.Theme.Accent,
		CanvasSize = UDim2.new(0, 0, 0, 0),
		AutomaticCanvasSize = Enum.AutomaticSize.None
	})

	local layout = CS.Util.List(scroll, Enum.FillDirection.Vertical, 10)

	layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
		scroll.CanvasSize = UDim2.new(0, 0, 0, layout.AbsoluteContentSize.Y + 20)
	end)

	return scroll
end

function CS.Util.Card(parent, title, height)
	local card = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, height or 90),
		BackgroundColor3 = CS.Theme.NotificationBackground,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(card, 14)
	CS.Util.Stroke(card, CS.Theme.Stroke, 0.45, 1)

	local titleLabel = CS.Util.Create("TextLabel", {
		Parent = card,
		Name = "Title",
		Size = UDim2.new(1, -24, 0, 28),
		Position = UDim2.new(0, 12, 0, 8),
		BackgroundTransparency = 1,
		Text = title or "Card",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 15,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	return card
end

function CS.Util.Body(parent, text, y)
	local body = CS.Util.Create("TextLabel", {
		Parent = parent,
		Name = "Body",
		Size = UDim2.new(1, -24, 1, -(y or 44)),
		Position = UDim2.new(0, 12, 0, y or 42),
		BackgroundTransparency = 1,
		Text = text or "",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 13,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top
	})

	return body
end

function CS.Util.InfoCard(parent, title, body, height)
	local card = CS.Util.Card(parent, title, height)
	local label = CS.Util.Body(card, body or "", 42)
	return card, label
end

function CS.Util.Button(parent, text, height)
	local button = CS.Util.Create("TextButton", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, height or 46),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		Text = text or "Button",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		AutoButtonColor = true
	})

	CS.Util.Corner(button, 12)
	CS.Util.Stroke(button, CS.Theme.Stroke, 0.5, 1)

	return button
end

function CS.Util.SmallButton(parent, text, width)
	local button = CS.Util.Create("TextButton", {
		Parent = parent,
		Size = UDim2.new(0, width or 140, 1, 0),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		Text = text or "Button",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13
	})

	CS.Util.Corner(button, 12)
	CS.Util.Stroke(button, CS.Theme.Stroke, 0.5, 1)

	return button
end

function CS.Util.Set(label, text, color)
	if not label then
		return
	end

	label.Text = text or ""

	if color then
		label.TextColor3 = color
	end
end

function CS.Util.MakeDraggable(frame, handle)
	local dragging = false
	local dragStart = nil
	local startPos = nil

	handle.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragging = true
			dragStart = input.Position
			startPos = frame.Position
		end
	end)

	handle.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragging = false
		end
	end)

	CS.Services.UserInputService.InputChanged:Connect(function(input)
		if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
			local delta = input.Position - dragStart

			frame.Position = UDim2.new(
				startPos.X.Scale,
				startPos.X.Offset + delta.X,
				startPos.Y.Scale,
				startPos.Y.Offset + delta.Y
			)
		end
	end)
end


function CS.Notify.PlaySound(soundId, volume)
	if not CS.Flags.FriendSounds then
		return
	end

	if not soundId or soundId == "" then
		return
	end

	local parent = CS.UI.Objects.GUI or CS.PlayerGui

	local sound = Instance.new("Sound")
	sound.SoundId = soundId
	sound.Volume = volume or 0.65
	sound.Parent = parent

	pcall(function()
		sound:Play()
	end)

	game:GetService("Debris"):AddItem(sound, 6)
end

function CS.Notify.PlayFriendJoin()
	CS.Notify.PlaySound(CS.Flags.FriendJoinSoundId, 0.75)
end

function CS.Notify.PlayFriendLeave()
	CS.Notify.PlaySound(CS.Flags.FriendLeaveSoundId, 0.75)
end

function CS.Notify.PlayJoinFriend()
	CS.Notify.PlaySound(CS.Flags.JoinFriendSoundId, 0.75)
end

function CS.Notify.Push(title, body, color, player, playSound, isJoining)
	if not CS.Flags.Notifications then
		return
	end

	local gui = CS.UI.Objects.GUI
	if not gui then
		return
	end

	local activeNotifications = CS.Runtime.ActiveNotifications
	local index = #activeNotifications

	local frame = Instance.new("Frame")
	frame.Name = "CSPlayerNotification"
	frame.Size = UDim2.new(0, 360, 0, 64)
	frame.Position = UDim2.new(1, 400, 0, 20 + (index * 74))
	frame.BackgroundColor3 = CS.Theme.NotificationBackground or Color3.fromRGB(20, 20, 20)
	frame.BackgroundTransparency = 0.15
	frame.BorderSizePixel = 0
	frame.ClipsDescendants = true
	frame.Parent = gui

	local corner = Instance.new("UICorner")
	corner.CornerRadius = UDim.new(0, 10)
	corner.Parent = frame

	local stroke = Instance.new("UIStroke")
	stroke.Color = color or CS.Theme.NotificationOutline or CS.Theme.Accent
	stroke.Transparency = 0.25
	stroke.Thickness = 1
	stroke.Parent = frame

	local avatar = Instance.new("ImageLabel")
	avatar.Size = UDim2.new(0, 44, 0, 44)
	avatar.Position = UDim2.new(0, 8, 0, 8)
	avatar.BackgroundTransparency = 1
	avatar.Image = ""
	avatar.Parent = frame

	local avatarCorner = Instance.new("UICorner")
	avatarCorner.CornerRadius = UDim.new(1, 0)
	avatarCorner.Parent = avatar

	local leftOffset = 60

	if player then
		pcall(function()
			avatar.Image = CS.Services.Players:GetUserThumbnailAsync(
				player.UserId,
				Enum.ThumbnailType.HeadShot,
				Enum.ThumbnailSize.Size100x100
			)
		end)
	else
		avatar.Visible = false
		leftOffset = 12
	end

	local titleLabel = Instance.new("TextLabel")
	titleLabel.Size = UDim2.new(1, -(leftOffset + 12), 0, 24)
	titleLabel.Position = UDim2.new(0, leftOffset, 0, 8)
	titleLabel.BackgroundTransparency = 1
	titleLabel.TextXAlignment = Enum.TextXAlignment.Left
	titleLabel.Font = Enum.Font.GothamBold
	titleLabel.TextSize = 14
	titleLabel.TextColor3 = color or CS.Theme.Text
	titleLabel.Text = title or "Notification"
	titleLabel.Parent = frame

	local bodyLabel = Instance.new("TextLabel")
	bodyLabel.Size = UDim2.new(1, -(leftOffset + 12), 0, 26)
	bodyLabel.Position = UDim2.new(0, leftOffset, 0, 32)
	bodyLabel.BackgroundTransparency = 1
	bodyLabel.TextXAlignment = Enum.TextXAlignment.Left
	bodyLabel.TextYAlignment = Enum.TextYAlignment.Top
	bodyLabel.Font = Enum.Font.Gotham
	bodyLabel.TextSize = 12
	bodyLabel.TextWrapped = true
	bodyLabel.TextColor3 = CS.Theme.SubText
	bodyLabel.Text = body or ""
	bodyLabel.Parent = frame

	local timerBack = Instance.new("Frame")
	timerBack.Size = UDim2.new(1, -16, 0, 3)
	timerBack.Position = UDim2.new(0, 8, 1, -6)
	timerBack.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
	timerBack.BackgroundTransparency = 0.8
	timerBack.BorderSizePixel = 0
	timerBack.Parent = frame

	local timerCorner = Instance.new("UICorner")
	timerCorner.CornerRadius = UDim.new(1, 0)
	timerCorner.Parent = timerBack

	local timerLine = Instance.new("Frame")
	timerLine.Size = UDim2.new(1, 0, 1, 0)
	timerLine.BackgroundColor3 = color or CS.Theme.Accent
	timerLine.BackgroundTransparency = 0.05
	timerLine.BorderSizePixel = 0
	timerLine.Parent = timerBack

	local timerLineCorner = Instance.new("UICorner")
	timerLineCorner.CornerRadius = UDim.new(1, 0)
	timerLineCorner.Parent = timerLine

	table.insert(activeNotifications, frame)

	CS.Services.TweenService:Create(
		frame,
		TweenInfo.new(0.4, Enum.EasingStyle.Quart, Enum.EasingDirection.Out),
		{Position = UDim2.new(1, -380, 0, 20 + ((#activeNotifications - 1) * 74))}
	):Play()

	CS.Services.TweenService:Create(
		timerLine,
		TweenInfo.new(3, Enum.EasingStyle.Linear, Enum.EasingDirection.Out),
		{Size = UDim2.new(0, 0, 1, 0)}
	):Play()

	if playSound then
		if isJoining then
			CS.Notify.PlayFriendJoin()
		else
			CS.Notify.PlayFriendLeave()
		end
	end

	task.delay(3, function()
		if not frame.Parent then
			return
		end

		local hideTween = CS.Services.TweenService:Create(
			frame,
			TweenInfo.new(0.35, Enum.EasingStyle.Quart, Enum.EasingDirection.In),
			{Position = UDim2.new(1, 400, 0, frame.Position.Y.Offset)}
		)

		hideTween:Play()
		hideTween.Completed:Wait()

		for i, v in ipairs(activeNotifications) do
			if v == frame then
				table.remove(activeNotifications, i)
				break
			end
		end

		if frame and frame.Parent then
			frame:Destroy()
		end

		for newIndex, notif in ipairs(activeNotifications) do
			CS.Services.TweenService:Create(
				notif,
				TweenInfo.new(0.25, Enum.EasingStyle.Quart, Enum.EasingDirection.Out),
				{Position = UDim2.new(1, -380, 0, 20 + ((newIndex - 1) * 74))}
			):Play()
		end
	end)
end

function CS.Notify.PlayerEvent(player, action, isFriend, isJoining)
	if not player or player == CS.LocalPlayer then
		return
	end

	local color

	if isFriend and isJoining then
		color = Color3.fromRGB(0, 255, 100)
	elseif isFriend and not isJoining then
		color = Color3.fromRGB(255, 80, 80)
	else
		color = Color3.fromRGB(160, 160, 160)
	end

	local title = isFriend and (isJoining and "Friend Joined" or "Friend Left") or (isJoining and "Player Joined" or "Player Left")
	local body = player.DisplayName .. " (@" .. player.Name .. ") " .. action

	CS.Notify.Push(title, body, color, player, isFriend, isJoining)
end


function CS.UI.BuildShell()
	local GUI = CS.Util.Create("ScreenGui", {
		Name = "CSCentralSettings",
		ResetOnSpawn = false,
		IgnoreGuiInset = true,
		ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
		Parent = CS.PlayerGui
	})

	CS.UI.Objects.GUI = GUI

	local toastHolder = CS.Util.Create("Frame", {
		Parent = GUI,
		Name = "ToastHolder",
		Size = UDim2.new(0, 380, 1, -40),
		Position = UDim2.new(1, -400, 0, 20),
		BackgroundTransparency = 1
	})

	CS.Util.List(toastHolder, Enum.FillDirection.Vertical, 10)
	CS.UI.Objects.ToastHolder = toastHolder

	local openButton = CS.Util.Create("TextButton", {
		Parent = GUI,
		Name = "CSOpenButton",
		Size = UDim2.new(0, 150, 0, 42),
		Position = UDim2.new(0, 20, 0.5, -21),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.25,
		BorderSizePixel = 0,
		Text = "Open CS",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 15
	})

	CS.Util.Corner(openButton, 12)
	CS.Util.Stroke(openButton, CS.Theme.Stroke, 0.4, 1)
	CS.UI.Objects.OpenButton = openButton

	local main = CS.Util.Create("Frame", {
		Parent = GUI,
		Name = "Main",
		Size = UDim2.new(0, 1080, 0, 650),
		Position = UDim2.new(0.5, -540, 0.5, -325),
		BackgroundColor3 = CS.Theme.Background,
		BackgroundTransparency = CS.Flags.Transparency,
		BorderSizePixel = 0,
		Visible = false
	})

	CS.Util.Corner(main, 20)
	CS.Util.Stroke(main, CS.Theme.Stroke, 0.28, 1)
	CS.Util.Gradient(main, CS.Theme.Background, CS.Theme.Background2, 45)
	CS.UI.Objects.Main = main

	local topBar = CS.Util.Create("Frame", {
		Parent = main,
		Name = "TopBar",
		Size = UDim2.new(1, 0, 0, 72),
		BackgroundTransparency = 1
	})

	CS.UI.Objects.TopBar = topBar

	CS.Util.Create("TextLabel", {
		Parent = topBar,
		Name = "Logo",
		Size = UDim2.new(0, 390, 1, 0),
		Position = UDim2.new(0, 22, 0, 0),
		BackgroundTransparency = 1,
		Text = "CS | Central Settings",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 26,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	CS.Util.Create("TextLabel", {
		Parent = topBar,
		Name = "Subtitle",
		Size = UDim2.new(0, 260, 0, 22),
		Position = UDim2.new(0, 24, 0, 46),
		BackgroundTransparency = 1,
		Text = "utility control panel",
		TextColor3 = CS.Theme.Muted,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local versionBadge = CS.Util.Create("TextButton", {
		Parent = topBar,
		Name = "VersionBadge",
		Size = UDim2.new(0, 86, 0, 24),
		Position = UDim2.new(0, 300, 0, 46),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		Text = "V " .. CS.Version,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 11
	})

	CS.Util.Corner(versionBadge, 999)
	CS.Util.Stroke(versionBadge, CS.Theme.Stroke, 0.5, 1)
	CS.UI.Buttons.VersionBadge = versionBadge

	local versionLog = CS.Util.Create("Frame", {
		Parent = main,
		Name = "VersionLog",
		Size = UDim2.new(0, 420, 0, 330),
		Position = UDim2.new(0, 20, 0, 78),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.06,
		BorderSizePixel = 0,
		Visible = false,
		ZIndex = 50
	})

	CS.Util.Corner(versionLog, 14)
	CS.Util.Stroke(versionLog, CS.Theme.Accent, 0.2, 1)
	CS.UI.Objects.VersionLog = versionLog

	local logTitle = CS.Util.Create("TextLabel", {
		Parent = versionLog,
		Size = UDim2.new(1, -20, 0, 32),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "CS Update Log",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 16,
		TextXAlignment = Enum.TextXAlignment.Left,
		ZIndex = 51
	})

	local logBody = CS.Util.Create("TextLabel", {
		Parent = versionLog,
		Size = UDim2.new(1, -20, 1, -48),
		Position = UDim2.new(0, 10, 0, 42),
		BackgroundTransparency = 1,
		Text = "",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top,
		ZIndex = 51
	})

	local logText = ""
	for _, entry in ipairs(CS.ChangeLog) do
		logText ..= "V " .. entry.Version .. " — " .. entry.Title .. "\n"
		logText ..= "Time: " .. entry.Time .. "\n"
		for _, detail in ipairs(entry.Details) do
			logText ..= "• " .. detail .. "\n"
		end
		logText ..= "\n"
	end
	logBody.Text = logText

	versionBadge.MouseButton1Click:Connect(function()
		versionLog.Visible = not versionLog.Visible
	end)

	local pillHolder = CS.Util.Create("Frame", {
		Parent = topBar,
		Name = "PillHolder",
		Size = UDim2.new(0, 430, 0, 42),
		Position = UDim2.new(1, -535, 0, 15),
		BackgroundTransparency = 1
	})

	CS.Util.List(pillHolder, Enum.FillDirection.Horizontal, 10).HorizontalAlignment = Enum.HorizontalAlignment.Right

	local function pill(text)
		local frame = CS.Util.Create("Frame", {
			Parent = pillHolder,
			Size = UDim2.new(0, 130, 1, 0),
			BackgroundColor3 = CS.Theme.Card,
			BackgroundTransparency = 0.2,
			BorderSizePixel = 0
		})

		CS.Util.Corner(frame, 999)
		CS.Util.Stroke(frame, CS.Theme.Stroke, 0.5, 1)

		local label = CS.Util.Create("TextLabel", {
			Parent = frame,
			Size = UDim2.new(1, 0, 1, 0),
			BackgroundTransparency = 1,
			Text = text,
			TextColor3 = CS.Theme.Text,
			Font = Enum.Font.GothamBold,
			TextSize = 13
		})

		return label
	end

	CS.UI.Labels.FPSPill = pill("FPS: 0")
	CS.UI.Labels.PingPill = pill("Ping: 0")
	CS.UI.Labels.PlayersPill = pill("Players: 0")

	local closeButton = CS.Util.Create("TextButton", {
		Parent = topBar,
		Size = UDim2.new(0, 90, 0, 38),
		Position = UDim2.new(1, -110, 0, 16),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		Text = "Close",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14
	})

	CS.Util.Corner(closeButton, 12)
	CS.Util.Stroke(closeButton, CS.Theme.Stroke, 0.5, 1)
	CS.UI.Buttons.Close = closeButton

	local accentRail = CS.Util.Create("Frame", {
		Parent = main,
		Name = "AccentRail",
		Size = UDim2.new(1, -40, 0, 3),
		Position = UDim2.new(0, 20, 0, 70),
		BackgroundColor3 = CS.Theme.Accent,
		BackgroundTransparency = 0.15,
		BorderSizePixel = 0
	})

	CS.Util.Corner(accentRail, 999)
	CS.Util.AccentGradient(accentRail)
	CS.UI.Objects.AccentRail = accentRail

	local sidebar = CS.Util.Create("Frame", {
		Parent = main,
		Name = "Sidebar",
		Size = UDim2.new(0, 235, 1, -96),
		Position = UDim2.new(0, 20, 0, 80),
		BackgroundColor3 = CS.Theme.Panel,
		BackgroundTransparency = CS.Flags.PanelTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(sidebar, 16)
	CS.Util.Stroke(sidebar, CS.Theme.StrokeSoft, 0.38, 1)
	CS.Util.Gradient(sidebar, CS.Theme.Panel, CS.Theme.Panel2, 90)
	CS.Util.Padding(sidebar, 12, 12, 14, 14)
	CS.Util.List(sidebar, Enum.FillDirection.Vertical, 10)
	CS.UI.Objects.Sidebar = sidebar

	local content = CS.Util.Create("Frame", {
		Parent = main,
		Name = "Content",
		Size = UDim2.new(1, -285, 1, -96),
		Position = UDim2.new(0, 265, 0, 80),
		BackgroundColor3 = CS.Theme.Panel,
		BackgroundTransparency = CS.Flags.PanelTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(content, 16)
	CS.Util.Stroke(content, CS.Theme.StrokeSoft, 0.38, 1)
	CS.Util.Gradient(content, CS.Theme.Panel, CS.Theme.Panel2, 90)
	CS.UI.Objects.Content = content

	CS.Util.MakeDraggable(main, topBar)
end

function CS.UI.Page(name)
	local page = CS.Util.Create("Frame", {
		Parent = CS.UI.Objects.Content,
		Name = name,
		Size = UDim2.new(1, -20, 1, -20),
		Position = UDim2.new(0, 10, 0, 10),
		BackgroundTransparency = 1,
		Visible = false
	})

	CS.UI.Pages[name] = page
	return page
end

function CS.UI.SwitchPage(name)
	for pageName, page in pairs(CS.UI.Pages) do
		page.Visible = pageName == name
	end

	for tabName, tab in pairs(CS.UI.Tabs) do
		if tabName == name then
			tab.BackgroundColor3 = CS.Theme.Accent
			tab.BackgroundTransparency = 0.12
		else
			tab.BackgroundColor3 = CS.Theme.Card
			tab.BackgroundTransparency = 0.18
		end
	end

	CS.Runtime.CurrentPage = name
end

function CS.UI.Tab(displayName, pageName)
	local tab = CS.Util.Button(CS.UI.Objects.Sidebar, displayName, 50)
	CS.UI.Tabs[pageName] = tab

	tab.MouseButton1Click:Connect(function()
		CS.UI.SwitchPage(pageName)
	end)
end

function CS.UI.Toggle()
	CS.Flags.Open = not CS.Flags.Open

	local main = CS.UI.Objects.Main
	main.Visible = CS.Flags.Open
	CS.UI.Objects.OpenButton.Text = CS.Flags.Open and "Close CS" or "Open CS"

	if CS.Flags.Open then
		main.BackgroundTransparency = 1
		CS.Util.Tween(main, {BackgroundTransparency = CS.Flags.Transparency}, 0.22)
		CS.RefreshAll()
		CS.UI.SwitchPage("Dashboard")
	end
end

function CS.UI.BuildPages()
	CS.UI.Page("Dashboard")
	CS.UI.Page("Player Details")
	CS.UI.Page("Server Browser")
	CS.UI.Page("Game Info")
	CS.UI.Page("Friends")
	CS.UI.Page("Diagnostics")
	CS.UI.Page("Settings")
	CS.UI.Page("Utilities")

	CS.UI.Tab("◇ Dashboard", "Dashboard")
	CS.UI.Tab("◉ Player Details", "Player Details")
	CS.UI.Tab("⇄ Server Browser", "Server Browser")
	CS.UI.Tab("⌑ Game Info", "Game Info")
	CS.UI.Tab("♡ Friends", "Friends")
	CS.UI.Tab("⚙ Diagnostics", "Diagnostics")
	CS.UI.Tab("▣ Settings", "Settings")
	CS.UI.Tab("↯ Utilities", "Utilities")
end

function CS.Dashboard.Build()
	local scroll = CS.Util.Scroll(CS.UI.Pages["Dashboard"])

	local overviewCard, overview = CS.Util.InfoCard(scroll, "Overview", "", 120)
	local networkCard = CS.Util.Card(scroll, "Network", 200)
	local friendsCard, friends = CS.Util.InfoCard(scroll, "Friends", "", 120)
	local perfCard, perf = CS.Util.InfoCard(scroll, "Performance", "", 120)
	local statusCard, status = CS.Util.InfoCard(scroll, "System Status", "", 140)

	local network = CS.Util.Body(networkCard, "", 42)

	local chart = CS.Util.Create("Frame", {
		Parent = networkCard,
		Size = UDim2.new(1, -310, 1, -62),
		Position = UDim2.new(0, 290, 0, 50),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.2,
		BorderSizePixel = 0
	})

	CS.Util.Corner(chart, 12)
	CS.Util.Stroke(chart, CS.Theme.Stroke, 0.65, 1)

	CS.UI.Charts.Network = {}

	for i = 1, 48 do
		local bar = CS.Util.Create("Frame", {
			Parent = chart,
			AnchorPoint = Vector2.new(0, 1),
			Position = UDim2.new((i - 1) / 48, 1, 1, 0),
			Size = UDim2.new(1 / 48, -2, 0.05, 0),
			BackgroundColor3 = CS.Theme.Success,
			BorderSizePixel = 0
		})

		CS.UI.Charts.Network[i] = bar
	end

	CS.UI.Labels.Overview = overview
	CS.UI.Labels.Network = network
	CS.UI.Labels.FriendsSummary = friends
	CS.UI.Labels.Performance = perf
	CS.UI.Labels.Status = status
end

function CS.PlayerDetails.Build()
	local page = CS.UI.Pages["Player Details"]

	local left = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(0, 340, 1, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(left, 14)
	CS.Util.Stroke(left, CS.Theme.Stroke, 0.5, 1)

	local right = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(1, -355, 1, 0),
		Position = UDim2.new(0, 355, 0, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(right, 14)
	CS.Util.Stroke(right, CS.Theme.Stroke, 0.5, 1)

	CS.UI.Objects.PlayerListScroll = CS.Util.Scroll(left)

	local detailHolder = CS.Util.Create("Frame", {
		Parent = right,
		Size = UDim2.new(1, 0, 1, 0),
		Position = UDim2.new(0, 0, 0, 0),
		BackgroundTransparency = 1
	})

	local details = CS.Util.Scroll(detailHolder)

	CS.UI.Objects.FullBody = CS.Util.Create("ImageLabel", {
		Parent = details,
		Size = UDim2.new(1, -8, 0, 270),
		BackgroundColor3 = Color3.fromRGB(18, 18, 26),
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		ScaleType = Enum.ScaleType.Fit
	})

	CS.Util.Corner(CS.UI.Objects.FullBody, 14)
	CS.Util.Stroke(CS.UI.Objects.FullBody, CS.Theme.Stroke, 0.55, 1)

	local selectedCard, selected = CS.Util.InfoCard(details, "Selected Player", "None", 90)
	local healthCard, health = CS.Util.InfoCard(details, "Health", "Loading...", 95)
	local movementCard, movement = CS.Util.InfoCard(details, "Position / Movement", "Loading...", 160)
	local accountCard, account = CS.Util.InfoCard(details, "Account", "Loading...", 180)
	local characterCard, character = CS.Util.InfoCard(details, "Character", "Loading...", 150)
	local toolCard, tool = CS.Util.InfoCard(details, "Tool / State", "Loading...", 120)

	local optionsToggleButton = CS.Util.Button(details, "Open Player Options", 42)
	CS.UI.Buttons.PlayerOptionsToggle = optionsToggleButton

	local optionsOverlay = CS.Util.Create("Frame", {
		Parent = details,
		Name = "PlayerOptionsPanel",
		Size = UDim2.new(1, -8, 0, 610),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.08,
		BorderSizePixel = 0,
		Visible = false
	})

	CS.Util.Corner(optionsOverlay, 14)
	CS.Util.Stroke(optionsOverlay, CS.Theme.Accent, 0.25, 1)

	local optionsTitle = CS.Util.Create("TextLabel", {
		Parent = optionsOverlay,
		Size = UDim2.new(1, -20, 0, 36),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "Player Options",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 16,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local optionsSub = CS.Util.Create("TextLabel", {
		Parent = optionsOverlay,
		Size = UDim2.new(1, -20, 0, 44),
		Position = UDim2.new(0, 10, 0, 42),
		BackgroundTransparency = 1,
		Text = "Actions apply to the currently selected player.",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 13,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top
	})

	local optionsHolder = CS.Util.Create("Frame", {
		Parent = optionsOverlay,
		Size = UDim2.new(1, -24, 1, -98),
		Position = UDim2.new(0, 12, 0, 90),
		BackgroundTransparency = 1
	})

	CS.Util.List(optionsHolder, Enum.FillDirection.Vertical, 8)

	local spectateToggleButton = CS.Util.Button(optionsHolder, "Toggle Spectate", 34)
	local tpButton = CS.Util.Button(optionsHolder, "Teleport To Player", 34)
	local highlightButton = CS.Util.Button(optionsHolder, "Toggle Local Highlight", 34)
	CS.UI.Buttons.HighlightToggle = highlightButton
	local copyNameButton = CS.Util.Button(optionsHolder, "Copy Username", 34)
	local copyIdButton = CS.Util.Button(optionsHolder, "Copy UserId", 34)
	local copyPosButton = CS.Util.Button(optionsHolder, "Copy XYZ Position", 34)

	CS.UI.Objects.PlayerOptionsOverlay = optionsOverlay
	CS.UI.Buttons.SpectateToggle = spectateToggleButton

	optionsToggleButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.ToggleOptions()
	end)


	spectateToggleButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.ToggleSpectate()
	end)

	tpButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.TeleportToSelected()
	end)

	highlightButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.ToggleHighlight()
	end)

	copyNameButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.CopyUsername()
	end)

	copyIdButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.CopyUserId()
	end)

	copyPosButton.MouseButton1Click:Connect(function()
		CS.PlayerDetails.CopyPosition()
	end)

	CS.UI.Labels.SelectedPlayer = selected
	CS.UI.Labels.PlayerHealth = health
	CS.UI.Labels.PlayerMovement = movement
	CS.UI.Labels.PlayerAccount = account
	CS.UI.Labels.PlayerCharacter = character
	CS.UI.Labels.PlayerTool = tool
end

function CS.PlayerDetails.CreateRow(player)
	local parent = CS.UI.Objects.PlayerListScroll

	local row = CS.Util.Create("TextButton", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 68),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.16,
		BorderSizePixel = 0,
		Text = ""
	})

	CS.Util.Corner(row, 12)
	CS.Util.Stroke(row, CS.Theme.Stroke, 0.55, 1)

	local icon = CS.Util.Create("ImageLabel", {
		Parent = row,
		Size = UDim2.new(0, 46, 0, 46),
		Position = UDim2.new(0, 10, 0, 11),
		BackgroundTransparency = 1
	})

	CS.Util.Corner(icon, 999)

	pcall(function()
		icon.Image = CS.Services.Players:GetUserThumbnailAsync(
			player.UserId,
			Enum.ThumbnailType.HeadShot,
			Enum.ThumbnailSize.Size100x100
		)
	end)

	CS.Util.Create("TextLabel", {
		Parent = row,
		Size = UDim2.new(1, -70, 1, 0),
		Position = UDim2.new(0, 66, 0, 0),
		BackgroundTransparency = 1,
		Text = player.DisplayName .. "\n@" .. player.Name,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Center
	})

	local badge = CS.Util.Create("TextLabel", {
		Parent = row,
		Size = UDim2.new(0, 68, 0, 20),
		Position = UDim2.new(1, -78, 0, 8),
		BackgroundColor3 = CS.Theme.Accent,
		BackgroundTransparency = 0.15,
		BorderSizePixel = 0,
		Text = "",
		Visible = false,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 10
	})

	CS.Util.Corner(badge, 999)

	if player == CS.LocalPlayer then
		badge.Text = "YOU"
		badge.Visible = true
	else
		pcall(function()
			if CS.LocalPlayer:IsFriendsWith(player.UserId) then
				badge.Text = "FRIEND"
				badge.Visible = true
			end
		end)
	end

	row.MouseButton1Click:Connect(function()
		CS.Runtime.SelectedPlayer = player

		for _, obj in ipairs(parent:GetChildren()) do
			if obj:IsA("TextButton") then
				obj.BackgroundColor3 = CS.Theme.Card2
				obj.BackgroundTransparency = 0.16
			end
		end

		row.BackgroundColor3 = CS.Theme.Accent
		row.BackgroundTransparency = 0.02

		CS.PlayerDetails.UpdateSelected()
	end)

	if CS.Runtime.SelectedPlayer == player then
		row.BackgroundColor3 = CS.Theme.Accent
		row.BackgroundTransparency = 0.02
	end
end

function CS.PlayerDetails.Refresh()
	local scroll = CS.UI.Objects.PlayerListScroll
	if not scroll then return end

	CS.Util.Clear(scroll, true)
	CS.Util.InfoCard(scroll, "Players", "Click a player to inspect details.", 76)

	for _, player in ipairs(CS.Services.Players:GetPlayers()) do
		CS.PlayerDetails.CreateRow(player)
	end
end

function CS.PlayerDetails.UpdateSelected()
	local player = CS.Runtime.SelectedPlayer

	if not player or not player.Parent then
		player = CS.LocalPlayer
		CS.Runtime.SelectedPlayer = player
	end

	pcall(function()
		CS.UI.Objects.FullBody.Image = CS.Services.Players:GetUserThumbnailAsync(
			player.UserId,
			Enum.ThumbnailType.AvatarThumbnail,
			Enum.ThumbnailSize.Size420x420
		)
	end)

	local character = CS.Util.Character(player)
	local humanoid = CS.Util.Humanoid(player)
	local root = CS.Util.Root(player)

	CS.Util.Set(CS.UI.Labels.SelectedPlayer, player.DisplayName .. "\n@" .. player.Name)

	if humanoid then
		CS.Util.Set(CS.UI.Labels.PlayerHealth,
			"Health: " .. math.floor(humanoid.Health) ..
			" / " .. math.floor(humanoid.MaxHealth) ..
			"\nState: " .. humanoid:GetState().Name ..
			"\nAlive: " .. tostring(humanoid.Health > 0)
		)
	else
		CS.Util.Set(CS.UI.Labels.PlayerHealth, "Humanoid unavailable.")
	end

	if root then
		local position = root.Position
		local velocity = root.AssemblyLinearVelocity
		local myRoot = CS.Util.Root(CS.LocalPlayer)
		local distance = "Unavailable"

		if myRoot then
			distance = math.floor((root.Position - myRoot.Position).Magnitude) .. " studs"
		end

		CS.Util.Set(CS.UI.Labels.PlayerMovement,
			"XYZ:" ..
			"\nX: " .. math.floor(position.X) ..
			"\nY: " .. math.floor(position.Y) ..
			"\nZ: " .. math.floor(position.Z) ..
			"\n\nVelocity:" ..
			"\nX: " .. math.floor(velocity.X) ..
			"\nY: " .. math.floor(velocity.Y) ..
			"\nZ: " .. math.floor(velocity.Z) ..
			"\n\nDistance: " .. distance
		)
	else
		CS.Util.Set(CS.UI.Labels.PlayerMovement, "HumanoidRootPart unavailable.")
	end

	local friendState = "No"

	if player == CS.LocalPlayer then
		friendState = "You"
	else
		pcall(function()
			if CS.LocalPlayer:IsFriendsWith(player.UserId) then
				friendState = "Yes"
			end
		end)
	end

	CS.Util.Set(CS.UI.Labels.PlayerAccount,
		"Display Name: " .. player.DisplayName ..
		"\nUsername: @" .. player.Name ..
		"\nUserId: " .. tostring(player.UserId) ..
		"\nAccount Age: " .. tostring(player.AccountAge) .. " days" ..
		"\nMembership: " .. player.MembershipType.Name ..
		"\nFriend: " .. friendState
	)

	local toolName = "None"

	if character then
		local tool = character:FindFirstChildOfClass("Tool")
		if tool then
			toolName = tool.Name
		end
	end

	CS.Util.Set(CS.UI.Labels.PlayerCharacter,
		"Character Loaded: " .. tostring(character ~= nil) ..
		"\nRig Type: " .. (humanoid and humanoid.RigType.Name or "Unknown") ..
		"\nTeam: " .. (player.Team and player.Team.Name or "None") ..
		"\nNeutral: " .. tostring(player.Neutral)
	)

	CS.Util.Set(CS.UI.Labels.PlayerTool,
		"Equipped Tool: " .. toolName ..
		"\nCharacter Parent: " .. (character and character.Parent and character.Parent.Name or "None")
	)

	if CS.Runtime.HighlightDistanceLabel and CS.Runtime.HighlightDistanceLabel.Parent then
		local targetRoot = CS.Util.Root(player)
		local myRoot = CS.Util.Root(CS.LocalPlayer)
		local text = CS.Runtime.HighlightDistanceLabel:FindFirstChild("DistanceText")

		if targetRoot and myRoot and text then
			text.Text = tostring(math.floor((targetRoot.Position - myRoot.Position).Magnitude)) .. " studs"
		end
	end
end

function CS.PlayerDetails.GetSelected()
	local player = CS.Runtime.SelectedPlayer

	if not player or not player.Parent then
		player = CS.LocalPlayer
		CS.Runtime.SelectedPlayer = player
	end

	return player
end

function CS.PlayerDetails.SetToggleButton(button, isOn, onText, offText)
	if not button then
		return
	end

	button.Text = isOn and onText or offText
	button.BackgroundColor3 = isOn and CS.Theme.Success or CS.Theme.Card2
	button.TextColor3 = isOn and Color3.fromRGB(0, 0, 0) or CS.Theme.Text
end

function CS.PlayerDetails.ToggleOptions(force)
	local overlay = CS.UI.Objects.PlayerOptionsOverlay
	local toggleButton = CS.UI.Buttons.PlayerOptionsToggle

	if not overlay then
		return
	end

	if force == nil then
		CS.Runtime.OptionsOpen = not CS.Runtime.OptionsOpen
	else
		CS.Runtime.OptionsOpen = force
	end

	local detailsScroll = overlay.Parent

	for _, child in ipairs(detailsScroll:GetChildren()) do
		if child:IsA("GuiObject") and child ~= overlay and child ~= toggleButton then
			child.Visible = not CS.Runtime.OptionsOpen
		end
	end

	overlay.Visible = CS.Runtime.OptionsOpen

	if toggleButton then
		toggleButton.Text = CS.Runtime.OptionsOpen and "Close Player Options" or "Open Player Options"
		toggleButton.BackgroundColor3 = CS.Runtime.OptionsOpen and CS.Theme.Success or CS.Theme.Card2
	end
end

function CS.PlayerDetails.ToggleSpectate()
	local selected = CS.PlayerDetails.GetSelected()

	if CS.Runtime.Spectating then
		local myHumanoid = CS.Util.Humanoid(CS.LocalPlayer)

		if myHumanoid then
			workspace.CurrentCamera.CameraSubject = myHumanoid
		end

		CS.Runtime.Spectating = false

		CS.PlayerDetails.SetToggleButton(CS.UI.Buttons.SpectateToggle, false, "Stop Spectating", "Toggle Spectate")

		CS.Notify.Push("Spectate", "Stopped spectating.", CS.Theme.Info)
		return
	end

	local humanoid = CS.Util.Humanoid(selected)

	if humanoid then
		workspace.CurrentCamera.CameraSubject = humanoid
		CS.Runtime.Spectating = true

		CS.PlayerDetails.SetToggleButton(CS.UI.Buttons.SpectateToggle, true, "Stop Spectating", "Toggle Spectate")

		CS.Notify.Push("Spectate", "Now spectating " .. selected.DisplayName .. ".", CS.Theme.Info, selected)
	else
		CS.Notify.Push("Spectate", "Selected player has no humanoid loaded.", CS.Theme.Warning, selected)
	end
end

function CS.PlayerDetails.TeleportToSelected()
	local selected = CS.PlayerDetails.GetSelected()
	local myRoot = CS.Util.Root(CS.LocalPlayer)
	local targetRoot = CS.Util.Root(selected)

	if not myRoot then
		CS.Notify.Push("Teleport", "Your character root is not loaded.", CS.Theme.Warning)
		return
	end

	if not targetRoot then
		CS.Notify.Push("Teleport", "Selected player's root is not loaded.", CS.Theme.Warning, selected)
		return
	end

	if selected == CS.LocalPlayer then
		CS.Notify.Push("Teleport", "You already selected yourself.", CS.Theme.Warning, selected)
		return
	end

	myRoot.AssemblyLinearVelocity = Vector3.zero
	myRoot.AssemblyAngularVelocity = Vector3.zero
	myRoot.CFrame = targetRoot.CFrame * CFrame.new(0, 0, 4)

	CS.Notify.Push("Teleport", "Teleported to " .. selected.DisplayName .. ".", CS.Theme.Success, selected)
end


function CS.PlayerDetails.ToggleHighlight()
	local player = CS.PlayerDetails.GetSelected()
	local character = CS.Util.Character(player)

	if not character then
		CS.Notify.Push("Highlight", "Selected player character is not loaded.", CS.Theme.Warning, player)
		return
	end

	if CS.Runtime.HighlightTarget then
		CS.Runtime.HighlightTarget:Destroy()
		CS.Runtime.HighlightTarget = nil

		if CS.Runtime.HighlightDistanceLabel then
			CS.Runtime.HighlightDistanceLabel:Destroy()
			CS.Runtime.HighlightDistanceLabel = nil
		end

		CS.PlayerDetails.SetToggleButton(CS.UI.Buttons.HighlightToggle, false, "Highlight: ON", "Toggle Local Highlight")
		CS.Notify.Push("Highlight", "Local highlight removed.", CS.Theme.Info, player)
		return
	end

	local highlight = Instance.new("Highlight")
	highlight.Name = "CS_SelectedPlayerHighlight"
	highlight.FillColor = CS.Theme.Accent
	highlight.OutlineColor = CS.Theme.Text
	highlight.FillTransparency = 0.75
	highlight.OutlineTransparency = 0
	highlight.Adornee = character
	highlight.Parent = character

	local head = character:FindFirstChild("Head") or CS.Util.Root(player)

	if head then
		local billboard = Instance.new("BillboardGui")
		billboard.Name = "CS_StudDistanceLabel"
		billboard.Size = UDim2.new(0, 160, 0, 36)
		billboard.StudsOffset = Vector3.new(0, 3.2, 0)
		billboard.AlwaysOnTop = true
		billboard.Adornee = head
		billboard.Parent = head

		local text = Instance.new("TextLabel")
		text.Name = "DistanceText"
		text.Size = UDim2.new(1, 0, 1, 0)
		text.BackgroundTransparency = 0.35
		text.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
		text.TextColor3 = CS.Theme.Success
		text.TextStrokeTransparency = 0.2
		text.Font = Enum.Font.GothamBold
		text.TextSize = 14
		text.Text = "Distance: ..."
		text.Parent = billboard

		CS.Util.Corner(text, 8)
		CS.Runtime.HighlightDistanceLabel = billboard
	end

	CS.Runtime.HighlightTarget = highlight

	CS.PlayerDetails.SetToggleButton(CS.UI.Buttons.HighlightToggle, true, "Highlight: ON", "Toggle Local Highlight")
	CS.Notify.Push("Highlight", "Locally highlighted " .. player.DisplayName .. ".", CS.Theme.Success, player)
end

function CS.PlayerDetails.CopyUsername()
	local player = CS.PlayerDetails.GetSelected()

	if setclipboard then
		setclipboard(player.Name)
		CS.Notify.Push("Clipboard", "Copied username: " .. player.Name, CS.Theme.Success, player)
	else
		CS.Notify.Push("Clipboard", "Clipboard unavailable.", CS.Theme.Warning, player)
	end
end

function CS.PlayerDetails.CopyUserId()
	local player = CS.PlayerDetails.GetSelected()

	if setclipboard then
		setclipboard(tostring(player.UserId))
		CS.Notify.Push("Clipboard", "Copied UserId: " .. tostring(player.UserId), CS.Theme.Success, player)
	else
		CS.Notify.Push("Clipboard", "Clipboard unavailable.", CS.Theme.Warning, player)
	end
end

function CS.PlayerDetails.CopyPosition()
	local player = CS.PlayerDetails.GetSelected()
	local root = CS.Util.Root(player)

	if not root then
		CS.Notify.Push("Clipboard", "Selected player position unavailable.", CS.Theme.Warning, player)
		return
	end

	local pos = root.Position
	local text = string.format("X: %d, Y: %d, Z: %d", math.floor(pos.X), math.floor(pos.Y), math.floor(pos.Z))

	if setclipboard then
		setclipboard(text)
		CS.Notify.Push("Clipboard", "Copied selected player XYZ.", CS.Theme.Success, player)
	else
		CS.Notify.Push("Clipboard", text, CS.Theme.Info, player)
	end
end


function CS.ServerBrowser.Build()
	local page = CS.UI.Pages["Server Browser"]

	local left = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(0, 190, 1, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(left, 14)
	CS.Util.Stroke(left, CS.Theme.Stroke, 0.5, 1)
	CS.Util.Padding(left, 10, 10, 12, 12)
	CS.Util.List(left, Enum.FillDirection.Vertical, 10)

	local right = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(1, -205, 1, 0),
		Position = UDim2.new(0, 205, 0, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(right, 14)
	CS.Util.Stroke(right, CS.Theme.Stroke, 0.5, 1)

	CS.UI.Objects.LiveServerView = CS.Util.Scroll(right)
	CS.UI.Objects.BrowserView = CS.Util.Scroll(right)
	CS.UI.Objects.ServerToolsView = CS.Util.Scroll(right)

	CS.UI.Objects.BrowserView.Visible = false
	CS.UI.Objects.ServerToolsView.Visible = false

	local liveButton = CS.Util.Button(left, "Live Server")
	local browserButton = CS.Util.Button(left, "Server Browser")
	local toolsButton = CS.Util.Button(left, "Tools")

	liveButton.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Switch("Live")
	end)

	browserButton.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Switch("Browser")
	end)

	toolsButton.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Switch("Tools")
	end)

	CS.ServerBrowser.BuildLive()
	CS.ServerBrowser.BuildBrowser()
	CS.ServerBrowser.BuildTools()
end

function CS.ServerBrowser.Switch(name)
	CS.UI.Objects.LiveServerView.Visible = name == "Live"
	CS.UI.Objects.BrowserView.Visible = name == "Browser"
	CS.UI.Objects.ServerToolsView.Visible = name == "Tools"
	CS.Runtime.ServerView = name
end

function CS.ServerBrowser.BuildLive()
	local view = CS.UI.Objects.LiveServerView

	local networkCard = CS.Util.Card(view, "Ping Monitor", 220)
	local networkText = CS.Util.Body(networkCard, "", 42)
	CS.UI.Labels.LiveNetwork = networkText

	local chart = CS.Util.Create("Frame", {
		Parent = networkCard,
		Size = UDim2.new(1, -315, 1, -66),
		Position = UDim2.new(0, 295, 0, 52),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.2,
		BorderSizePixel = 0
	})

	CS.Util.Corner(chart, 12)
	CS.Util.Stroke(chart, CS.Theme.Stroke, 0.65, 1)

	CS.UI.Charts.Live = {}

	for i = 1, 48 do
		local bar = CS.Util.Create("Frame", {
			Parent = chart,
			AnchorPoint = Vector2.new(0, 1),
			Position = UDim2.new((i - 1) / 48, 1, 1, 0),
			Size = UDim2.new(1 / 48, -2, 0.05, 0),
			BackgroundColor3 = CS.Theme.Success,
			BorderSizePixel = 0
		})

		CS.UI.Charts.Live[i] = bar
	end

	local currentCard, current = CS.Util.InfoCard(view, "Current Server", "", 160)
	local perfCard, perf = CS.Util.InfoCard(view, "Performance", "", 120)
	local capCard, cap = CS.Util.InfoCard(view, "Capabilities", "", 140)

	CS.UI.Labels.CurrentServer = current
	CS.UI.Labels.CurrentPerf = perf
	CS.UI.Labels.CurrentCapabilities = cap
end

function CS.ServerBrowser.BuildBrowser()
	local view = CS.UI.Objects.BrowserView

	local headerCard, header = CS.Util.InfoCard(view, "Public Server Browser", "Requires HTTP request support.", 110)
	CS.UI.Labels.BrowserHeader = header

	local controls = CS.Util.Create("Frame", {
		Parent = view,
		Size = UDim2.new(1, -8, 0, 56),
		BackgroundTransparency = 1
	})

	CS.Util.List(controls, Enum.FillDirection.Horizontal, 10)

	local refresh = CS.Util.SmallButton(controls, "Refresh", 140)
	local sortPing = CS.Util.SmallButton(controls, "Sort Ping", 140)
	local sortFull = CS.Util.SmallButton(controls, "Sort Full", 140)
	local sortEmpty = CS.Util.SmallButton(controls, "Sort Empty", 140)

	local box = CS.Util.Create("Frame", {
		Parent = view,
		Size = UDim2.new(1, -8, 0, 520),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0
	})

	CS.Util.Corner(box, 14)
	CS.Util.Stroke(box, CS.Theme.Stroke, 0.55, 1)

	CS.UI.Objects.ServerListScroll = CS.Util.Scroll(box)

	refresh.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Fetch()
	end)

	sortPing.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Sort("Ping")
	end)

	sortFull.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Sort("Fullest")
	end)

	sortEmpty.MouseButton1Click:Connect(function()
		CS.ServerBrowser.Sort("Emptiest")
	end)
end

function CS.ServerBrowser.BuildTools()
	local view = CS.UI.Objects.ServerToolsView

	CS.Util.InfoCard(view, "Server Tools", "Useful server actions.", 95)

	local rejoin = CS.Util.Button(view, "Rejoin Current Server")
	local hop = CS.Util.Button(view, "Server Hop")
	local copyJob = CS.Util.Button(view, "Copy JobId")
	local copyPlace = CS.Util.Button(view, "Copy PlaceId")
	local copyGame = CS.Util.Button(view, "Copy GameId")

	rejoin.MouseButton1Click:Connect(function()
		CS.Services.TeleportService:TeleportToPlaceInstance(game.PlaceId, game.JobId, CS.LocalPlayer)
	end)

	hop.MouseButton1Click:Connect(function()
		CS.Services.TeleportService:Teleport(game.PlaceId, CS.LocalPlayer)
	end)

	copyJob.MouseButton1Click:Connect(function()
		if setclipboard then
			setclipboard(game.JobId)
			CS.Notify.Push("Clipboard", "Copied JobId.", CS.Theme.Success)
		else
			CS.Notify.Push("Clipboard", "Clipboard unavailable.", CS.Theme.Warning)
		end
	end)

	copyPlace.MouseButton1Click:Connect(function()
		if setclipboard then
			setclipboard(tostring(game.PlaceId))
			CS.Notify.Push("Clipboard", "Copied PlaceId.", CS.Theme.Success)
		else
			CS.Notify.Push("Clipboard", "Clipboard unavailable.", CS.Theme.Warning)
		end
	end)

	copyGame.MouseButton1Click:Connect(function()
		if setclipboard then
			setclipboard(tostring(game.GameId))
			CS.Notify.Push("Clipboard", "Copied GameId.", CS.Theme.Success)
		else
			CS.Notify.Push("Clipboard", "Clipboard unavailable.", CS.Theme.Warning)
		end
	end)
end

function CS.ServerBrowser.CreateRow(server, index)
	local scroll = CS.UI.Objects.ServerListScroll

	local row = CS.Util.Create("Frame", {
		Parent = scroll,
		Size = UDim2.new(1, -8, 0, 92),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.14,
		BorderSizePixel = 0
	})

	CS.Util.Corner(row, 12)
	CS.Util.Stroke(row, CS.Theme.Stroke, 0.55, 1)

	local ping = tonumber(server.ping) or 0
	local status, color = CS.Util.PingStatus(ping)
	local serverId = tostring(server.id or "")

	CS.Util.Create("TextLabel", {
		Parent = row,
		Size = UDim2.new(1, -160, 1, -12),
		Position = UDim2.new(0, 12, 0, 6),
		BackgroundTransparency = 1,
		Text =
			"Server #" .. tostring(index) ..
			"\nPlayers: " .. tostring(server.playing or "?") .. "/" .. tostring(server.maxPlayers or "?") ..
			" | Ping: " .. tostring(server.ping or "N/A") .. " ms | " .. status ..
			"\nID: " .. (serverId ~= "" and string.sub(serverId, 1, 18) .. "..." or "Unknown"),
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 13,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Center
	})

	local join = CS.Util.Create("TextButton", {
		Parent = row,
		Size = UDim2.new(0, 120, 0, 44),
		Position = UDim2.new(1, -135, 0.5, -22),
		BackgroundColor3 = color,
		BackgroundTransparency = 0.08,
		BorderSizePixel = 0,
		Text = "Join",
		TextColor3 = Color3.fromRGB(0, 0, 0),
		Font = Enum.Font.GothamBold,
		TextSize = 14
	})

	CS.Util.Corner(join, 10)

	join.MouseButton1Click:Connect(function()
		if server.id then
			CS.Services.TeleportService:TeleportToPlaceInstance(game.PlaceId, server.id, CS.LocalPlayer)
		end
	end)
end

function CS.ServerBrowser.Rebuild(data)
	local scroll = CS.UI.Objects.ServerListScroll
	if not scroll then return end

	CS.Util.Clear(scroll, true)

	if not data or #data == 0 then
		CS.Util.InfoCard(scroll, "No Server Data", "Press Refresh to fetch servers.", 100)
		return
	end

	for index, server in ipairs(data) do
		CS.ServerBrowser.CreateRow(server, index)
	end
end

function CS.ServerBrowser.Fetch()
	if not CS.Request then
		CS.Util.Set(CS.UI.Labels.BrowserHeader, "HTTP request unavailable. Server browser cannot fetch servers here.", CS.Theme.Error)
		CS.Notify.Push("Server Browser", "HTTP request unavailable.", CS.Theme.Error)
		return
	end

	CS.Util.Set(CS.UI.Labels.BrowserHeader, "Fetching public servers...")

	local success, response = pcall(function()
		return CS.Request({
			Url = "https://[Log in to view URL]" .. tostring(game.PlaceId) .. "/servers/Public?sortOrder=Asc&limit=100",
			Method = "GET"
		})
	end)

	if not success or not response or not response.Body then
		CS.Util.Set(CS.UI.Labels.BrowserHeader, "Failed to fetch server list.", CS.Theme.Error)
		CS.Notify.Push("Server Browser", "Fetch failed.", CS.Theme.Error)
		return
	end

	local decoded
	local ok = pcall(function()
		decoded = CS.Services.HttpService:JSONDecode(response.Body)
	end)

	if not ok or not decoded or not decoded.data then
		CS.Util.Set(CS.UI.Labels.BrowserHeader, "Invalid server response.", CS.Theme.Warning)
		return
	end

	CS.Runtime.ServerData = decoded.data
	CS.Runtime.LastServerFetch = os.clock()

	CS.Util.Set(CS.UI.Labels.BrowserHeader, "Loaded " .. tostring(#decoded.data) .. " servers.")
	CS.ServerBrowser.Rebuild(CS.Runtime.ServerData)
	CS.Notify.Push("Server Browser", "Loaded public servers.", CS.Theme.Success)
end

function CS.ServerBrowser.Sort(mode)
	CS.Flags.ServerSortMode = mode

	local data = CS.Runtime.ServerData or {}

	if mode == "Ping" then
		table.sort(data, function(a, b)
			return (tonumber(a.ping) or 999999) < (tonumber(b.ping) or 999999)
		end)
	elseif mode == "Fullest" then
		table.sort(data, function(a, b)
			return (tonumber(a.playing) or 0) > (tonumber(b.playing) or 0)
		end)
	elseif mode == "Emptiest" then
		table.sort(data, function(a, b)
			return (tonumber(a.playing) or 0) < (tonumber(b.playing) or 0)
		end)
	end

	CS.ServerBrowser.Rebuild(data)
end

function CS.ServerBrowser.UpdateLive()
	local ping = CS.Runtime.Ping
	local status, color = CS.Util.PingStatus(ping)

	CS.Util.Set(CS.UI.Labels.LiveNetwork,
		"Current: " .. tostring(ping) .. " ms" ..
		"\nStatus: " .. status ..
		"\nLowest: " .. tostring(CS.Runtime.LowPing or "N/A") .. " ms" ..
		"\nAverage: " .. tostring(CS.Runtime.AvgPing or "N/A") .. " ms" ..
		"\nHighest: " .. tostring(CS.Runtime.HighPing or "N/A") .. " ms",
		color
	)

	CS.Util.Set(CS.UI.Labels.CurrentServer,
		"Players: " .. tostring(#CS.Services.Players:GetPlayers()) .. "/" .. tostring(CS.Services.Players.MaxPlayers) ..
		"\nPlaceId: " .. tostring(game.PlaceId) ..
		"\nGameId: " .. tostring(game.GameId) ..
		"\nJobId: " .. (game.JobId ~= "" and game.JobId or "Unavailable")
	)

	CS.Util.Set(CS.UI.Labels.CurrentPerf,
		"FPS: " .. tostring(CS.Runtime.FPS) ..
		"\nPing: " .. tostring(ping) .. " ms" ..
		"\nClient Uptime: " .. CS.Util.FormatSeconds(os.clock() - CS.Runtime.UptimeStart)
	)

	CS.Util.Set(CS.UI.Labels.CurrentCapabilities,
		"HTTP Request: " .. tostring(CS.Request ~= nil) ..
		"\nClipboard: " .. tostring(setclipboard ~= nil) ..
		"\nCoreGui Access: " .. tostring(pcall(function() return CS.Services.CoreGui.Name end)) ..
		"\nGame Loaded: " .. tostring(game:IsLoaded())
	)

	CS.Dashboard.PaintBars(CS.UI.Charts.Live)
end

function CS.GameInfo.Build()
	local scroll = CS.Util.Scroll(CS.UI.Pages["Game Info"])

	local overviewCard, overview = CS.Util.InfoCard(scroll, "Game Overview", "Loading...", 150)
	local statsCard, stats = CS.Util.InfoCard(scroll, "Game Stats", "Loading...", 150)
	local idsCard, ids = CS.Util.InfoCard(scroll, "IDs", "Loading...", 130)
	local supportCard, support = CS.Util.InfoCard(scroll, "Client Support", "Loading...", 140)
	local gameFriendsCard, gameFriends = CS.Util.InfoCard(scroll, "Friends In This Game", "Loading friend game data...", 210)
	local feedCard, feed = CS.Util.InfoCard(scroll, "Recent Player Feed", "Waiting for events...", 220)

	CS.UI.Labels.GameOverview = overview
	CS.UI.Labels.GameStats = stats
	CS.UI.Labels.GameIds = ids
	CS.UI.Labels.GameSupport = support
	CS.UI.Labels.GameFriends = gameFriends
	CS.UI.Labels.Feed = feed
end

function CS.GameInfo.Load()
	local info = {
		Name = "Unavailable",
		Creator = "Unavailable",
		Created = "Unavailable",
		Updated = "Unavailable",
		Visits = "Unavailable",
		Favorites = "Unavailable"
	}

	pcall(function()
		local data = CS.Services.MarketplaceService:GetProductInfo(game.PlaceId)
		info.Name = tostring(data.Name or "Unavailable")
		info.Creator = data.Creator and tostring(data.Creator.Name) or "Unavailable"
		info.Created = tostring(data.Created or "Unavailable")
		info.Updated = tostring(data.Updated or "Unavailable")
		info.Visits = CS.Util.ShortNumber(data.Visits)
		info.Favorites = CS.Util.ShortNumber(data.FavoritedCount)
	end)

	CS.Util.Set(CS.UI.Labels.GameOverview,
		"Name: " .. info.Name ..
		"\nCreator: " .. info.Creator ..
		"\nPlaceId: " .. tostring(game.PlaceId) ..
		"\nGameId: " .. tostring(game.GameId)
	)

	CS.Util.Set(CS.UI.Labels.GameStats,
		"Visits: " .. info.Visits ..
		"\nFavorites: " .. info.Favorites ..
		"\nCreated: " .. info.Created ..
		"\nUpdated: " .. info.Updated
	)

	CS.Util.Set(CS.UI.Labels.GameIds,
		"PlaceId: " .. tostring(game.PlaceId) ..
		"\nGameId: " .. tostring(game.GameId) ..
		"\nJobId: " .. (game.JobId ~= "" and game.JobId or "Unavailable")
	)

	CS.Util.Set(CS.UI.Labels.GameSupport,
		"HTTP Request: " .. tostring(CS.Request ~= nil) ..
		"\nTeleportService: " .. tostring(CS.Services.TeleportService ~= nil) ..
		"\nMarketplace Info: Loaded" ..
		"\nServer Browser: " .. tostring(CS.Request ~= nil)
	)

	CS.Friends.UpdateGameFriends()
end


function CS.Friends.GetOnlineFriends()
	local friends = {}

	pcall(function()
		friends = CS.LocalPlayer:GetFriendsOnline(200) or {}
	end)

	CS.Runtime.FriendsOnlineCache = friends
	return friends
end

function CS.Friends.GetOnlineFriendByUserId(userId)
	for _, friend in ipairs(CS.Runtime.FriendsOnlineCache or {}) do
		if tonumber(friend.VisitorId) == tonumber(userId) then
			return friend
		end
	end

	return nil
end

function CS.Friends.ConnectTeleportFailFallback()
	if CS.Runtime.TeleportFailConnected then
		return
	end

	CS.Runtime.TeleportFailConnected = true

	pcall(function()
		CS.Services.TeleportService.TeleportInitFailed:Connect(function(player, teleportResult, errorMessage, placeId)
			if player ~= CS.LocalPlayer then
				return
			end

			local msg = tostring(errorMessage or teleportResult or "Unknown teleport failure")

			CS.Notify.Push(
				"Teleport Failed",
				msg .. "\nTrying fallback if possible.",
				CS.Theme.Warning
			)

			local fallbackPlace = tonumber(CS.Runtime.LastFriendJoinPlaceId)

			if fallbackPlace then
				task.delay(1.2, function()
					pcall(function()
						CS.Services.TeleportService:Teleport(fallbackPlace, CS.LocalPlayer)
					end)
				end)
			end
		end)
	end)
end

function CS.Friends.TryGetLiveFriendInstance(userId)
	local result = nil

	if not userId then
		return nil
	end

	pcall(function()
		local currentInstance, placeId, jobId = CS.Services.TeleportService:GetPlayerPlaceInstanceAsync(tonumber(userId))

		if placeId and jobId then
			result = {
				PlaceId = tonumber(placeId),
				JobId = tostring(jobId),
				CurrentInstance = currentInstance
			}
		end
	end)

	return result
end

function CS.Friends.CopyFriendProfile(userId, friendName)
	if not userId then
		CS.Notify.Push("Friends", "No UserId available for profile fallback.", CS.Theme.Warning)
		return
	end

	local url = "https://[Log in to view URL]" .. tostring(userId) .. "/profile"

	if setclipboard then
		setclipboard(url)
		CS.Notify.Push("Friends", "Copied " .. tostring(friendName or "friend") .. "'s Roblox profile link. Use the profile Join button if Roblox allows it.", CS.Theme.Info)
	else
		CS.Notify.Push("Friends", "Profile: " .. url, CS.Theme.Info)
	end
end

function CS.Friends.TryJoinFriend(friendData)
	if not friendData then
		CS.Notify.Push("Friends", "No friend selected.", CS.Theme.Warning)
		return
	end

	local userId = tonumber(friendData.VisitorId or friendData.UserId or friendData.userId)
	local friendName = tostring(friendData.DisplayName or friendData.UserName or friendData.Username or "friend")

	local placeId = tonumber(friendData.PlaceId or friendData.PlaceID or friendData.placeId or friendData.placeID)
	local jobId = friendData.GameId or friendData.GameID or friendData.JobId or friendData.JobID or friendData.gameId or friendData.jobId

	if typeof(jobId) ~= "string" and jobId ~= nil then
		jobId = tostring(jobId)
	end

	CS.Notify.PlayJoinFriend()

	-- Roblox Error 773 is a platform restriction. Client Lua cannot bypass it.
	-- This function now avoids repeated restricted teleports and gives the safest join path.

	if not placeId then
		CS.Friends.CopyFriendProfile(userId, friendName)
		CS.Notify.Push("Friend Join", "Roblox did not expose a joinable PlaceId. Profile fallback copied.", CS.Theme.Warning)
		return
	end

	-- If friend is in this same place and Roblox gave a JobId, try exact server once.
	-- This is the only automatic join path Roblox may allow.
	if placeId == game.PlaceId and jobId and jobId ~= "" then
		CS.Notify.Push("Friend Join", "Trying same-game server join for " .. friendName .. "...", CS.Theme.Info)

		local ok, err = pcall(function()
			CS.Services.TeleportService:TeleportToPlaceInstance(placeId, jobId, CS.LocalPlayer)
		end)

		if ok then
			return
		end

		warn("CS same-game friend join failed:", err)
	end

	-- If the friend is in a different place, direct teleport often causes 773 when the destination is restricted.
	-- Copy profile instead so the user can use Roblox's official Join button if permissions allow.
	if placeId ~= game.PlaceId then
		CS.Friends.CopyFriendProfile(userId, friendName)
		CS.Notify.Push(
			"Friend Join Restricted",
			"Roblox is blocking direct teleport to that place/server. I copied the profile link so you can try the official Join button.",
			CS.Theme.Warning
		)
		return
	end

	CS.Notify.Push(
		"Friend Join Failed",
		"Roblox did not provide a usable active server id, or the server is restricted/private/full.",
		CS.Theme.Warning
	)
end


function CS.Friends.SetSelectedFriend(friendData, profile)
	CS.Runtime.SelectedFriend = friendData

	local name = tostring(friendData.UserName or friendData.Username or friendData.DisplayName or "Unknown")
	local display = tostring(friendData.DisplayName or friendData.UserName or "Unknown")
	local userId = tostring(friendData.VisitorId or friendData.UserId or profile.UserId or "Unknown")
	local placeId = tostring(friendData.PlaceId or friendData.PlaceID or friendData.placeId or friendData.placeID or "Unavailable")
	local gameId = tostring(friendData.GameId or friendData.GameID or friendData.JobId or friendData.JobID or friendData.gameId or friendData.jobId or "Unavailable")
	local location = tostring(friendData.LastLocation or friendData.LocationType or "Unavailable")

	if CS.UI.Objects.FriendAvatar then
		pcall(function()
			CS.UI.Objects.FriendAvatar.Image = CS.Services.Players:GetUserThumbnailAsync(
				tonumber(userId),
				Enum.ThumbnailType.AvatarThumbnail,
				Enum.ThumbnailSize.Size420x420
			)
		end)
	end

	if CS.UI.Labels.FriendDetails then
		CS.UI.Labels.FriendDetails.Text =
			"Display Name: " .. display ..
			"\nUsername: @" .. name ..
			"\nUserId: " .. userId ..
			"\nOnline Location: " .. location ..
			"\nPlaceId: " .. placeId ..
			"\nServer/JobId: " .. gameId ..
			"\nJoin Quality: " .. ((placeId ~= "Unavailable" and gameId ~= "Unavailable") and "Same-game exact server only if Roblox allows it" or (placeId ~= "Unavailable" and "Limited: place only" or "Unavailable")) ..
			"\n\nBio: unavailable from normal in-game Lua." ..
			"\nMessages: unavailable from normal in-game Lua."
	end
end

function CS.Friends.CreateFriendRow(parent, friendData)
	local userId = tonumber(friendData.VisitorId or friendData.UserId or 0)
	local username = tostring(friendData.UserName or friendData.Username or "Unknown")
	local display = tostring(friendData.DisplayName or username)
	local location = tostring(friendData.LastLocation or "Online")

	local row = CS.Util.Create("TextButton", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 72),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.14,
		BorderSizePixel = 0,
		Text = ""
	})

	CS.Util.Corner(row, 12)
	CS.Util.Stroke(row, CS.Theme.Stroke, 0.55, 1)

	local icon = CS.Util.Create("ImageLabel", {
		Parent = row,
		Size = UDim2.new(0, 48, 0, 48),
		Position = UDim2.new(0, 10, 0, 12),
		BackgroundTransparency = 1
	})

	CS.Util.Corner(icon, 999)

	if userId > 0 then
		pcall(function()
			icon.Image = CS.Services.Players:GetUserThumbnailAsync(
				userId,
				Enum.ThumbnailType.HeadShot,
				Enum.ThumbnailSize.Size100x100
			)
		end)
	end

	local text = CS.Util.Create("TextLabel", {
		Parent = row,
		Size = UDim2.new(1, -190, 1, 0),
		Position = UDim2.new(0, 68, 0, 0),
		BackgroundTransparency = 1,
		Text = display .. "\n@" .. username .. "\n" .. location,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 12,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Center
	})

	local join = CS.Util.Create("TextButton", {
		Parent = row,
		Size = UDim2.new(0, 82, 0, 34),
		Position = UDim2.new(1, -94, 0.5, -17),
		BackgroundColor3 = CS.Theme.Success,
		BackgroundTransparency = 0.08,
		BorderSizePixel = 0,
		Text = "Join",
		TextColor3 = Color3.fromRGB(0, 0, 0),
		Font = Enum.Font.GothamBold,
		TextSize = 12
	})

	CS.Util.Corner(join, 9)

	row.MouseButton1Click:Connect(function()
		for _, obj in ipairs(parent:GetChildren()) do
			if obj:IsA("TextButton") then
				obj.BackgroundColor3 = CS.Theme.Card2
				obj.BackgroundTransparency = 0.14
			end
		end

		row.BackgroundColor3 = CS.Theme.Accent
		row.BackgroundTransparency = 0.02
		CS.Friends.SetSelectedFriend(friendData, {UserId = userId})
	end)

	join.MouseButton1Click:Connect(function()
		CS.Friends.TryJoinFriend(friendData)
	end)
end

function CS.Friends.RefreshFriendList()
	local list = CS.UI.Objects.FriendListScroll
	if not list then return end

	CS.Util.Clear(list, true)

	local friends = CS.Friends.GetOnlineFriends()

	if #friends == 0 then
		CS.Util.InfoCard(list, "Friends List", "No online friends were returned by Roblox.", 90)
		return
	end

	for _, friendData in ipairs(friends) do
		CS.Friends.CreateFriendRow(list, friendData)
	end

	CS.Friends.UpdateGameFriends()
end

function CS.Friends.UpdateGameFriends()
	local sameGame = {}
	local friends = CS.Runtime.FriendsOnlineCache or CS.Friends.GetOnlineFriends()

	for _, friendData in ipairs(friends) do
		local placeId = tonumber(friendData.PlaceId or friendData.PlaceID or 0)

		if placeId == game.PlaceId then
			table.insert(sameGame, friendData)
		end
	end

	if CS.UI.Labels.GameFriends then
		if #sameGame == 0 then
			CS.UI.Labels.GameFriends.Text = "No online friends detected in this same game through Roblox online-friend data."
		else
			local text = "Friends in this game, possibly different server:\\n"

			for _, friendData in ipairs(sameGame) do
				text ..= "\\n" .. tostring(friendData.DisplayName or friendData.UserName or "Unknown") ..
					"  @" .. tostring(friendData.UserName or "Unknown") ..
					"\\nLocation: " .. tostring(friendData.LastLocation or "Unknown") ..
					"\\nJoin data: " .. ((friendData.GameId or friendData.JobId) and "available" or "limited") .. "\\n"
			end

			CS.UI.Labels.GameFriends.Text = text
		end
	end
end

function CS.Friends.Build()
	local page = CS.UI.Pages["Friends"]

	local left = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(0.52, -6, 1, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(left, 14)
	CS.Util.Stroke(left, CS.Theme.Stroke, 0.5, 1)

	local right = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(0.48, -6, 1, 0),
		Position = UDim2.new(0.52, 6, 0, 0),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = 0.22,
		BorderSizePixel = 0
	})

	CS.Util.Corner(right, 14)
	CS.Util.Stroke(right, CS.Theme.Stroke, 0.5, 1)

	local leftScroll = CS.Util.Scroll(left)
	local rightScroll = CS.Util.Scroll(right)

	local overviewCard, overview = CS.Util.InfoCard(leftScroll, "Friends Overview", "Loading...", 95)
	local refresh = CS.Util.Button(leftScroll, "Refresh Friends List", 38)

	local listHolder = CS.Util.Create("Frame", {
		Parent = leftScroll,
		Size = UDim2.new(1, -8, 0, 450),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.2,
		BorderSizePixel = 0
	})

	CS.Util.Corner(listHolder, 14)
	CS.Util.Stroke(listHolder, CS.Theme.Stroke, 0.55, 1)

	CS.UI.Objects.FriendListScroll = CS.Util.Scroll(listHolder)

	local avatar = CS.Util.Create("ImageLabel", {
		Parent = rightScroll,
		Size = UDim2.new(1, -8, 0, 260),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.18,
		BorderSizePixel = 0,
		ScaleType = Enum.ScaleType.Fit
	})

	CS.Util.Corner(avatar, 14)
	CS.Util.Stroke(avatar, CS.Theme.Stroke, 0.55, 1)
	CS.UI.Objects.FriendAvatar = avatar

	local detailsCard, details = CS.Util.InfoCard(rightScroll, "Friend Details", "Click a friend from the list.", 250)
	local joinSelected = CS.Util.Button(rightScroll, "Join Selected Friend", 40)
	local inServerCard, inServer = CS.Util.InfoCard(rightScroll, "Friends In Current Server", "Loading...", 145)

	joinSelected.MouseButton1Click:Connect(function()
		CS.Friends.TryJoinFriend(CS.Runtime.SelectedFriend)
	end)

	refresh.MouseButton1Click:Connect(function()
		CS.Friends.RefreshFriendList()
		CS.Notify.Push("Friends", "Friend list refreshed.", CS.Theme.Success)
	end)

	CS.UI.Labels.FriendsOverview = overview
	CS.UI.Labels.FriendsInServer = inServer
	CS.UI.Labels.FriendDetails = details
end


function CS.Friends.CountInServer()
	local count = 0
	local names = {}

	for _, player in ipairs(CS.Services.Players:GetPlayers()) do
		if player ~= CS.LocalPlayer then
			local isFriend = false
			pcall(function()
				isFriend = CS.LocalPlayer:IsFriendsWith(player.UserId)
			end)

			if isFriend then
				count += 1
				table.insert(names, player.DisplayName .. " (@" .. player.Name .. ")")
			end
		end
	end

	return count, names
end

function CS.Friends.CountOnline()
	local count = 0

	pcall(function()
		for _, _ in ipairs(CS.LocalPlayer:GetFriendsOnline()) do
			count += 1
		end
	end)

	return count
end

function CS.Friends.PushFeed(text)
	table.insert(CS.Runtime.Feed, 1, os.date("%I:%M:%S %p") .. "  " .. text)

	while #CS.Runtime.Feed > 12 do
		table.remove(CS.Runtime.Feed)
	end
end

function CS.Friends.Update()
	local online = CS.Friends.CountOnline()
	local inServer, names = CS.Friends.CountInServer()
	local onlineData = CS.Friends.GetOnlineFriends()

	CS.Util.Set(CS.UI.Labels.FriendsOverview,
		"Friends Online: " .. tostring(#onlineData > 0 and #onlineData or online) ..
		"\nFriends In Server: " .. tostring(inServer) ..
		"\nPlayer Notices: " .. tostring(CS.Flags.ShowNonFriendNotices)
	)

	if #names > 0 then
		CS.Util.Set(CS.UI.Labels.FriendsInServer, table.concat(names, "\n"))
	else
		CS.Util.Set(CS.UI.Labels.FriendsInServer, "No friends detected in this server.")
	end

	if CS.UI.Labels.Feed then
		if #CS.Runtime.Feed > 0 then
			CS.Util.Set(CS.UI.Labels.Feed, table.concat(CS.Runtime.Feed, "\n"))
		else
			CS.Util.Set(CS.UI.Labels.Feed, "No recent player events.")
		end
	end
end

function CS.Diagnostics.Build()
	local scroll = CS.Util.Scroll(CS.UI.Pages["Diagnostics"])

	local mainCard, main = CS.Util.InfoCard(scroll, "System Status", "Loading...", 180)
	local charCard, char = CS.Util.InfoCard(scroll, "Character Checks", "Loading...", 180)
	local envCard, env = CS.Util.InfoCard(scroll, "Environment Capabilities", "Loading...", 200)
	local warnCard, warn = CS.Util.InfoCard(scroll, "Warnings", "None.", 160)

	CS.UI.Labels.DiagnosticsMain = main
	CS.UI.Labels.DiagnosticsCharacter = char
	CS.UI.Labels.DiagnosticsEnvironment = env
	CS.UI.Labels.DiagnosticsWarnings = warn
end

function CS.Diagnostics.Run()
	local services = {
		Players = CS.Services.Players ~= nil,
		RunService = CS.Services.RunService ~= nil,
		TweenService = CS.Services.TweenService ~= nil,
		UserInputService = CS.Services.UserInputService ~= nil,
		Stats = CS.Services.Stats ~= nil,
		MarketplaceService = CS.Services.MarketplaceService ~= nil,
		TeleportService = CS.Services.TeleportService ~= nil,
		HttpService = CS.Services.HttpService ~= nil
	}

	local okCount = 0
	local total = 0
	local serviceText = ""

	for name, exists in pairs(services) do
		total += 1
		if exists then okCount += 1 end
		serviceText ..= name .. ": " .. (exists and "OK" or "Missing") .. "\n"
	end

	CS.Util.Set(CS.UI.Labels.DiagnosticsMain,
		"Services: " .. tostring(okCount) .. "/" .. tostring(total) .. " OK" ..
		"\nGame Loaded: " .. tostring(game:IsLoaded()) ..
		"\nPlayers: " .. tostring(#CS.Services.Players:GetPlayers()) .. "/" .. tostring(CS.Services.Players.MaxPlayers) ..
		"\n\n" .. serviceText
	)

	local char = CS.LocalPlayer.Character
	local humanoid = char and char:FindFirstChildOfClass("Humanoid")
	local root = char and char:FindFirstChild("HumanoidRootPart")
	local head = char and char:FindFirstChild("Head")

	CS.Util.Set(CS.UI.Labels.DiagnosticsCharacter,
		"Character: " .. tostring(char ~= nil) ..
		"\nHumanoid: " .. tostring(humanoid ~= nil) ..
		"\nHumanoidRootPart: " .. tostring(root ~= nil) ..
		"\nHead: " .. tostring(head ~= nil) ..
		"\nPlayerGui: " .. tostring(CS.PlayerGui ~= nil)
	)

	CS.Util.Set(CS.UI.Labels.DiagnosticsEnvironment,
		"HTTP Request: " .. tostring(CS.Request ~= nil) ..
		"\nClipboard: " .. tostring(setclipboard ~= nil) ..
		"\nDrawing API: " .. tostring(Drawing ~= nil) ..
		"\nCoreGui Access: " .. tostring(pcall(function() return CS.Services.CoreGui.Name end)) ..
		"\nExecutor-specific APIs are optional."
	)

	local warnings = {}

	if not CS.Request then
		table.insert(warnings, "Server browser cannot fetch public servers without request/http_request.")
	end

	if not setclipboard then
		table.insert(warnings, "Copy buttons will not work without setclipboard.")
	end

	if not root then
		table.insert(warnings, "Character root is not loaded yet.")
	end

	if #warnings == 0 then
		CS.Util.Set(CS.UI.Labels.DiagnosticsWarnings, "No major warnings.", CS.Theme.Success)
	else
		CS.Util.Set(CS.UI.Labels.DiagnosticsWarnings, table.concat(warnings, "\n"), CS.Theme.Warning)
	end
end


function CS.Settings.HexToColor(hex)
	if type(hex) ~= "string" then
		return nil
	end

	hex = hex:gsub("#", ""):gsub("%s+", "")

	if #hex ~= 6 then
		return nil
	end

	local r = tonumber(hex:sub(1, 2), 16)
	local g = tonumber(hex:sub(3, 4), 16)
	local b = tonumber(hex:sub(5, 6), 16)

	if not r or not g or not b then
		return nil
	end

	return Color3.fromRGB(r, g, b)
end

function CS.Settings.ColorToHex(color)
	local r = math.floor(color.R * 255)
	local g = math.floor(color.G * 255)
	local b = math.floor(color.B * 255)

	return string.format("#%02X%02X%02X", r, g, b)
end

function CS.Settings.CanSave()
	return writefile ~= nil and readfile ~= nil and isfile ~= nil
end

function CS.Settings.CollectConfig()
	return {
		ConfigVersion = CS.Config.Version,

		Animations = CS.Flags.Animations,
		Notifications = CS.Flags.Notifications,
		ShowNonFriendNotices = CS.Flags.ShowNonFriendNotices,
		FriendSounds = CS.Flags.FriendSounds,

		Transparency = CS.Flags.Transparency,
		PanelTransparency = CS.Flags.PanelTransparency,
		CardTransparency = CS.Flags.CardTransparency,
		ScaleMode = CS.Flags.ScaleMode,

		Accent = CS.Settings.ColorToHex(CS.Theme.Accent),
		Background = CS.Settings.ColorToHex(CS.Theme.Background),
		Panel = CS.Settings.ColorToHex(CS.Theme.Panel),
		Card = CS.Settings.ColorToHex(CS.Theme.Card),
		Outline = CS.Settings.ColorToHex(CS.Theme.Stroke),
		Notification = CS.Settings.ColorToHex(CS.Theme.Info),
		NotificationBackground = CS.Settings.ColorToHex(CS.Theme.NotificationBackground),
		NotificationOutline = CS.Settings.ColorToHex(CS.Theme.NotificationOutline),

		FriendJoinSoundId = CS.Flags.FriendJoinSoundId,
		FriendLeaveSoundId = CS.Flags.FriendLeaveSoundId,
		JoinFriendSoundId = CS.Flags.JoinFriendSoundId,

		ColorCycleA = CS.Flags.ColorCycleA,
		ColorCycleB = CS.Flags.ColorCycleB,
		ColorCycleSpeed = CS.Flags.ColorCycleSpeed,
		ColorCycleSpeedLevel = CS.Flags.ColorCycleSpeedLevel
	}
end

function CS.Settings.Save(silent)
	if not writefile then
		if not silent then
			CS.Notify.Push("Settings", "Save unavailable in this executor.", CS.Theme.Warning)
		end
		return false
	end

	local ok, encoded = pcall(function()
		return CS.Services.HttpService:JSONEncode(CS.Settings.CollectConfig())
	end)

	if not ok or not encoded then
		if not silent then
			CS.Notify.Push("Settings", "Could not encode settings.", CS.Theme.Warning)
		end
		return false
	end

	local writeOk = pcall(function()
		writefile(CS.Config.FileName, encoded)
	end)

	if not silent then
		CS.Notify.Push("Settings", writeOk and "Settings saved." or "Settings failed to save.", writeOk and CS.Theme.Success or CS.Theme.Warning)
	end

	return writeOk
end

function CS.Settings.ApplyConfig(data)
	if type(data) ~= "table" then
		return
	end

	if type(data.Animations) == "boolean" then
		CS.Flags.Animations = data.Animations
	end

	if type(data.Notifications) == "boolean" then
		CS.Flags.Notifications = data.Notifications
	end

	if type(data.ShowNonFriendNotices) == "boolean" then
		CS.Flags.ShowNonFriendNotices = data.ShowNonFriendNotices
	end

	if type(data.FriendSounds) == "boolean" then
		CS.Flags.FriendSounds = data.FriendSounds
	end

	if tonumber(data.Transparency) then
		CS.Flags.Transparency = math.clamp(tonumber(data.Transparency), 0, 1)
	end

	if tonumber(data.PanelTransparency) then
		CS.Flags.PanelTransparency = math.clamp(tonumber(data.PanelTransparency), 0, 1)
	end

	if tonumber(data.CardTransparency) then
		CS.Flags.CardTransparency = math.clamp(tonumber(data.CardTransparency), 0, 1)
	end

	if type(data.ScaleMode) == "string" then
		CS.Flags.ScaleMode = data.ScaleMode
	end

	local colorMap = {
		Accent = "Accent",
		Background = "Background",
		Panel = "Panel",
		Card = "Card",
		Outline = "Stroke",
		Notification = "Info",
		NotificationBackground = "NotificationBackground",
		NotificationOutline = "NotificationOutline"
	}

	for configKey, themeKey in pairs(colorMap) do
		if type(data[configKey]) == "string" then
			local color = CS.Settings.HexToColor(data[configKey])
			if color then
				CS.Theme[themeKey] = color
			end
		end
	end

	if type(data.FriendJoinSoundId) == "string" then
		CS.Flags.FriendJoinSoundId = data.FriendJoinSoundId
	end

	if type(data.FriendLeaveSoundId) == "string" then
		CS.Flags.FriendLeaveSoundId = data.FriendLeaveSoundId
	end

	if type(data.JoinFriendSoundId) == "string" then
		CS.Flags.JoinFriendSoundId = data.JoinFriendSoundId
	end

	if type(data.ColorCycleA) == "string" then
		CS.Flags.ColorCycleA = data.ColorCycleA
	end

	if type(data.ColorCycleB) == "string" then
		CS.Flags.ColorCycleB = data.ColorCycleB
	end

	if tonumber(data.ColorCycleSpeed) then
		CS.Flags.ColorCycleSpeed = tonumber(data.ColorCycleSpeed)
	end

	if tonumber(data.ColorCycleSpeedLevel) then
		CS.Flags.ColorCycleSpeedLevel = math.clamp(tonumber(data.ColorCycleSpeedLevel), 1, 10)
	end

	CS.Flags.ColorCycle = false
	CS.Flags.ExampleNotification = false

	CS.Settings.ApplyVisuals()
	CS.Settings.RefreshToggleVisuals()
end

function CS.Settings.Load()
	if not readfile or not isfile then
		return false
	end

	if not isfile(CS.Config.FileName) then
		CS.Settings.Save(true)
		return false
	end

	local readOk, raw = pcall(function()
		return readfile(CS.Config.FileName)
	end)

	if not readOk or not raw or raw == "" then
		return false
	end

	local decodeOk, decoded = pcall(function()
		return CS.Services.HttpService:JSONDecode(raw)
	end)

	if decodeOk and decoded then
		CS.Settings.ApplyConfig(decoded)
		CS.Config.Loaded = true
		return true
	end

	return false
end


function CS.Settings.ApplyVisuals()
	CS.Theme.Accent2 = CS.Theme.Accent
	CS.Theme.Accent3 = CS.Theme.Accent

	if CS.UI.Objects.Main then
		CS.UI.Objects.Main.BackgroundColor3 = CS.Theme.Background
		CS.UI.Objects.Main.BackgroundTransparency = CS.Flags.Transparency
	end

	if CS.UI.Objects.Sidebar then
		CS.UI.Objects.Sidebar.BackgroundColor3 = CS.Theme.Panel
		CS.UI.Objects.Sidebar.BackgroundTransparency = CS.Flags.PanelTransparency
	end

	if CS.UI.Objects.Content then
		CS.UI.Objects.Content.BackgroundColor3 = CS.Theme.Panel
		CS.UI.Objects.Content.BackgroundTransparency = CS.Flags.PanelTransparency
	end

	if CS.UI.Objects.AccentRail then
		CS.UI.Objects.AccentRail.BackgroundColor3 = CS.Theme.Accent
	end

	if CS.UI.Objects.GUI then
		for _, obj in ipairs(CS.UI.Objects.GUI:GetDescendants()) do
			if obj:IsA("UIStroke") then
				obj.Color = CS.Theme.Stroke
			end
		end
	end

	for _, tab in pairs(CS.UI.Tabs) do
		if tab.BackgroundColor3 ~= CS.Theme.Accent then
			tab.BackgroundColor3 = CS.Theme.Card
		end
	end
end

function CS.Settings.MakeTextBox(parent, placeholder, defaultText)
	local box = CS.Util.Create("TextBox", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 40),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0,
		Text = defaultText or "",
		PlaceholderText = placeholder or "#FFFFFF",
		TextColor3 = CS.Theme.Text,
		PlaceholderColor3 = CS.Theme.Muted,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		ClearTextOnFocus = false
	})

	CS.Util.Corner(box, 10)
	CS.Util.Stroke(box, CS.Theme.Stroke, 0.5, 1)

	return box
end

function CS.Settings.MakeColorEditor(parent, labelText, key, defaultColor, applyCallback)
	local card = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 360),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(card, 14)
	CS.Util.Stroke(card, CS.Theme.Stroke, 0.45, 1)

	local swatch = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(0, 42, 0, 42),
		Position = UDim2.new(0, 12, 0, 12),
		BackgroundColor3 = defaultColor,
		BorderSizePixel = 0
	})

	CS.Util.Corner(swatch, 999)
	CS.Util.Stroke(swatch, CS.Theme.Text, 0.65, 1)

	local title = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(0.45, -70, 0, 22),
		Position = UDim2.new(0, 64, 0, 10),
		BackgroundTransparency = 1,
		Text = labelText,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local preview = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(0.45, -70, 0, 22),
		Position = UDim2.new(0, 64, 0, 32),
		BackgroundTransparency = 1,
		Text = labelText .. ": " .. CS.Settings.ColorToHex(defaultColor),
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local examplePanel = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(0.48, -12, 0, 92),
		Position = UDim2.new(0.52, 0, 0, 14),
		BackgroundColor3 = Color3.fromRGB(14, 14, 20),
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0
	})

	CS.Util.Corner(examplePanel, 12)
	CS.Util.Stroke(examplePanel, CS.Theme.Stroke, 0.55, 1)

	local exampleTitle = CS.Util.Create("TextLabel", {
		Parent = examplePanel,
		Size = UDim2.new(1, -20, 0, 22),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "Preview: " .. labelText,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local exampleBody = CS.Util.Create("TextLabel", {
		Parent = examplePanel,
		Size = UDim2.new(1, -20, 0, 44),
		Position = UDim2.new(0, 10, 0, 34),
		BackgroundTransparency = 1,
		Text = "This shows what will change.",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top
	})

	local exampleAccentBar = CS.Util.Create("Frame", {
		Parent = examplePanel,
		Size = UDim2.new(1, -20, 0, 4),
		Position = UDim2.new(0, 10, 1, -10),
		BackgroundColor3 = defaultColor,
		BorderSizePixel = 0
	})

	CS.Util.Corner(exampleAccentBar, 999)

	local picker = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 176),
		Position = UDim2.new(0, 12, 0, 104),
		BackgroundColor3 = Color3.fromRGB(255, 0, 0),
		BorderSizePixel = 0,
		ClipsDescendants = true
	})

	CS.Util.Corner(picker, 10)
	CS.Util.Stroke(picker, CS.Theme.Text, 0.45, 1)

	local satGradient = Instance.new("UIGradient")
	satGradient.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(255, 255, 255))
	satGradient.Transparency = NumberSequence.new({
		NumberSequenceKeypoint.new(0, 1),
		NumberSequenceKeypoint.new(1, 0)
	})
	satGradient.Rotation = 0
	satGradient.Parent = picker

	local valueOverlay = CS.Util.Create("Frame", {
		Parent = picker,
		Size = UDim2.new(1, 0, 1, 0),
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BorderSizePixel = 0
	})

	local valueGradient = Instance.new("UIGradient")
	valueGradient.Transparency = NumberSequence.new({
		NumberSequenceKeypoint.new(0, 1),
		NumberSequenceKeypoint.new(1, 0)
	})
	valueGradient.Rotation = 90
	valueGradient.Parent = valueOverlay

	local pickerCursor = CS.Util.Create("Frame", {
		Parent = picker,
		Size = UDim2.new(0, 16, 0, 16),
		Position = UDim2.new(0, -8, 1, -8),
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BackgroundTransparency = 0.25,
		BorderSizePixel = 0,
		ZIndex = 5
	})

	CS.Util.Corner(pickerCursor, 999)
	CS.Util.Stroke(pickerCursor, CS.Theme.Text, 0.1, 2)

	local hueBar = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 18),
		Position = UDim2.new(0, 12, 0, 294),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BorderSizePixel = 0
	})

	CS.Util.Corner(hueBar, 999)
	CS.Util.Stroke(hueBar, CS.Theme.Stroke, 0.55, 1)

	local hueGradient = Instance.new("UIGradient")
	hueGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0.00, Color3.fromRGB(255, 0, 0)),
		ColorSequenceKeypoint.new(0.16, Color3.fromRGB(255, 255, 0)),
		ColorSequenceKeypoint.new(0.33, Color3.fromRGB(0, 255, 0)),
		ColorSequenceKeypoint.new(0.50, Color3.fromRGB(0, 255, 255)),
		ColorSequenceKeypoint.new(0.66, Color3.fromRGB(0, 0, 255)),
		ColorSequenceKeypoint.new(0.83, Color3.fromRGB(255, 0, 255)),
		ColorSequenceKeypoint.new(1.00, Color3.fromRGB(255, 0, 0))
	})
	hueGradient.Parent = hueBar

	local hueCursor = CS.Util.Create("Frame", {
		Parent = hueBar,
		Size = UDim2.new(0, 14, 0, 24),
		Position = UDim2.new(0, -7, 0.5, -12),
		BackgroundColor3 = Color3.fromRGB(25, 25, 25),
		BorderSizePixel = 0,
		ZIndex = 5
	})

	CS.Util.Corner(hueCursor, 999)
	CS.Util.Stroke(hueCursor, CS.Theme.Text, 0.25, 2)

	local bottom = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 32),
		Position = UDim2.new(0, 12, 0, 322),
		BackgroundColor3 = Color3.fromRGB(12, 12, 18),
		BackgroundTransparency = 0.15,
		BorderSizePixel = 0
	})

	CS.Util.Corner(bottom, 10)
	CS.Util.Stroke(bottom, CS.Theme.Stroke, 0.6, 1)

	local miniSwatch = CS.Util.Create("Frame", {
		Parent = bottom,
		Size = UDim2.new(0, 22, 0, 22),
		Position = UDim2.new(0, 8, 0.5, -11),
		BackgroundColor3 = defaultColor,
		BorderSizePixel = 0
	})

	CS.Util.Corner(miniSwatch, 999)

	local box = CS.Util.Create("TextBox", {
		Parent = bottom,
		Size = UDim2.new(1, -150, 0, 28),
		Position = UDim2.new(0, 38, 0.5, -14),
		BackgroundTransparency = 1,
		Text = CS.Settings.ColorToHex(defaultColor),
		PlaceholderText = "#FFFFFF",
		TextColor3 = CS.Theme.Text,
		PlaceholderColor3 = CS.Theme.Muted,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
		ClearTextOnFocus = false
	})

	local apply = CS.Util.Create("TextButton", {
		Parent = bottom,
		Size = UDim2.new(0, 92, 0, 26),
		Position = UDim2.new(1, -100, 0.5, -13),
		BackgroundColor3 = defaultColor,
		BackgroundTransparency = 0.08,
		BorderSizePixel = 0,
		Text = "Apply",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 12
	})

	CS.Util.Corner(apply, 8)

	local hue = 0
	local sat = 1
	local val = 1
	local draggingPicker = false
	local draggingHue = false

	local function updateExample(color, hex)
		exampleAccentBar.BackgroundColor3 = color

		if key == "Notification" or labelText:lower():find("notification") then
			exampleTitle.Text = "Example Notification"
			exampleBody.Text = "This is the color used for notification accents."
			exampleAccentBar.BackgroundColor3 = color
		elseif key == "Accent" or labelText == "Accent" then
			exampleTitle.Text = "Example Selected Tab"
			exampleBody.Text = "Selected tabs, active buttons, highlights, and accent rail."
			examplePanel.BackgroundColor3 = color
			examplePanel.BackgroundTransparency = 0.18
		elseif key == "Accent2" or labelText:lower():find("secondary") then
			exampleTitle.Text = "Example Gradient Side"
			exampleBody.Text = "Secondary accent supports gradients and theme blends."
			exampleAccentBar.BackgroundColor3 = color
		elseif key == "Accent3" or labelText:lower():find("third") then
			exampleTitle.Text = "Example Extra Accent"
			exampleBody.Text = "Third accent supports extra glow and color blend details."
			exampleAccentBar.BackgroundColor3 = color
		elseif key == "Background" or labelText:lower():find("background") then
			exampleTitle.Text = "Example Menu Background"
			exampleBody.Text = "This changes the main menu/panel background tone."
			examplePanel.BackgroundColor3 = color
		elseif key == "Outline" or labelText:lower():find("outline") then
			exampleTitle.Text = "Example Outline"
			exampleBody.Text = "This changes borders and outline strokes."
			exampleAccentBar.BackgroundColor3 = color
			CS.Util.Stroke(examplePanel, color, 0.25, 1)
		else
			exampleTitle.Text = "Preview: " .. labelText
			exampleBody.Text = "This color will affect " .. labelText .. "."
		end

		preview.Text = labelText .. ": " .. hex
	end

	local function setColorFromHSV()
		local color = Color3.fromHSV(hue, sat, val)
		local hex = CS.Settings.ColorToHex(color)

		box.Text = hex
		swatch.BackgroundColor3 = color
		miniSwatch.BackgroundColor3 = color
		apply.BackgroundColor3 = color
		preview.TextColor3 = CS.Theme.SubText
		updateExample(color, hex)

		local hueColor = Color3.fromHSV(hue, 1, 1)
		picker.BackgroundColor3 = hueColor
		satGradient.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(255, 255, 255))
		satGradient.Transparency = NumberSequence.new({
			NumberSequenceKeypoint.new(0, 1),
			NumberSequenceKeypoint.new(1, 0)
		})
	end

	local function updatePickerFromInput(input)
		local absPos = picker.AbsolutePosition
		local absSize = picker.AbsoluteSize
		local x = math.clamp((input.Position.X - absPos.X) / absSize.X, 0, 1)
		local y = math.clamp((input.Position.Y - absPos.Y) / absSize.Y, 0, 1)

		sat = x
		val = 1 - y
		pickerCursor.Position = UDim2.new(x, -8, y, -8)
		pickerCursor.BackgroundColor3 = val > 0.5 and Color3.fromRGB(0, 0, 0) or Color3.fromRGB(255, 255, 255)
		setColorFromHSV()
	end

	local function updateHueFromInput(input)
		local absPos = hueBar.AbsolutePosition
		local absSize = hueBar.AbsoluteSize
		local x = math.clamp((input.Position.X - absPos.X) / absSize.X, 0, 1)

		hue = x
		hueCursor.Position = UDim2.new(x, -7, 0.5, -12)
		setColorFromHSV()
	end

	local function beginPickerDrag(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			draggingPicker = true
			updatePickerFromInput(input)
		end
	end

	picker.InputBegan:Connect(beginPickerDrag)
	valueOverlay.InputBegan:Connect(beginPickerDrag)

	hueBar.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			draggingHue = true
			updateHueFromInput(input)
		end
	end)

	CS.Services.UserInputService.InputChanged:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			if draggingPicker then
				updatePickerFromInput(input)
			elseif draggingHue then
				updateHueFromInput(input)
			end
		end
	end)

	CS.Services.UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			draggingPicker = false
			draggingHue = false
		end
	end)

	box.FocusLost:Connect(function()
		local color = CS.Settings.HexToColor(box.Text)
		if color then
			local h, s, v = color:ToHSV()
			hue = h
			sat = s
			val = v
			pickerCursor.Position = UDim2.new(sat, -8, 1 - val, -8)
			pickerCursor.BackgroundColor3 = val > 0.5 and Color3.fromRGB(0, 0, 0) or Color3.fromRGB(255, 255, 255)
			hueCursor.Position = UDim2.new(hue, -7, 0.5, -12)
			swatch.BackgroundColor3 = color
			miniSwatch.BackgroundColor3 = color
			apply.BackgroundColor3 = color
			updateExample(color, box.Text)
			local hueColor = Color3.fromHSV(hue, 1, 1)
			picker.BackgroundColor3 = hueColor
			satGradient.Color = ColorSequence.new({
				ColorSequenceKeypoint.new(0, Color3.fromRGB(255,255,255)),
				ColorSequenceKeypoint.new(1, hueColor)
			})
		else
			preview.Text = labelText .. ": invalid hex"
			preview.TextColor3 = CS.Theme.Warning
		end
	end)

	apply.MouseButton1Click:Connect(function()
		local color = CS.Settings.HexToColor(box.Text)

		if not color then
			CS.Notify.Push("Theme", "Invalid hex for " .. labelText .. ".", CS.Theme.Warning)
			return
		end

		applyCallback(color, box.Text)
	end)

	local startH, startS, startV = defaultColor:ToHSV()
	hue = startH
	sat = startS
	val = startV
	pickerCursor.Position = UDim2.new(sat, -8, 1 - val, -8)
	hueCursor.Position = UDim2.new(hue, -7, 0.5, -12)
	setColorFromHSV()

	return {
		Card = card,
		Swatch = swatch,
		Preview = preview,
		Box = box,
		Apply = apply,
		Picker = picker,
		HueBar = hueBar,
		Example = examplePanel
	}
end


function CS.Settings.MakeSection(parent, title, body)
	local card = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 66),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(card, 12)
	CS.Util.Stroke(card, CS.Theme.Stroke, 0.55, 1)

	local titleLabel = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 22),
		Position = UDim2.new(0, 12, 0, 8),
		BackgroundTransparency = 1,
		Text = title or "Section",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local bodyLabel = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 24),
		Position = UDim2.new(0, 12, 0, 32),
		BackgroundTransparency = 1,
		Text = body or "",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 11,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top
	})

	return card, bodyLabel
end

function CS.Settings.ApplyHexToTheme(key, textBox)
	local color = CS.Settings.HexToColor(textBox.Text)

	if not color then
		CS.Notify.Push("Theme", "Invalid hex. Use format like #4E73FF.", CS.Theme.Warning)
		return
	end

	CS.Theme[key] = color
	CS.Settings.ApplyVisuals()
	CS.Settings.Save()
	CS.Notify.Push("Theme", key .. " updated to " .. textBox.Text, color)
end

function CS.Settings.ApplyTransparencyFromBox(flagKey, textBox)
	local value = tonumber(textBox.Text)

	if not value then
		CS.Notify.Push("Settings", "Transparency must be a number from 0 to 1.", CS.Theme.Warning)
		return
	end

	value = math.clamp(value, 0, 1)
	CS.Flags[flagKey] = value

	if flagKey == "Transparency" and CS.UI.Objects.Main then
		CS.UI.Objects.Main.BackgroundTransparency = value
	elseif flagKey == "PanelTransparency" then
		if CS.UI.Objects.Sidebar then CS.UI.Objects.Sidebar.BackgroundTransparency = value end
		if CS.UI.Objects.Content then CS.UI.Objects.Content.BackgroundTransparency = value end
	elseif flagKey == "CardTransparency" then
		CS.Notify.Push("Settings", "Card transparency saves for next full reload.", CS.Theme.Info)
	end

	CS.Settings.Save()
	CS.Notify.Push("Settings", flagKey .. " set to " .. tostring(value), CS.Theme.Success)
end



function CS.Settings.SetToggleVisual(button, state)
	if not button then
		return
	end

	button.BackgroundColor3 = state and CS.Theme.Success or CS.Theme.Card2
	button.TextColor3 = state and Color3.fromRGB(0, 0, 0) or CS.Theme.Text
end

function CS.Settings.RefreshToggleVisuals()
	CS.Settings.SetToggleVisual(CS.UI.Buttons.ToggleAnimations, CS.Flags.Animations)
	CS.Settings.SetToggleVisual(CS.UI.Buttons.ToggleNotifications, CS.Flags.Notifications)
	CS.Settings.SetToggleVisual(CS.UI.Buttons.ToggleNonFriends, CS.Flags.ShowNonFriendNotices)
	CS.Settings.SetToggleVisual(CS.UI.Buttons.ToggleFriendSounds, CS.Flags.FriendSounds)
end

function CS.Settings.MakeTransparencySlider(parent)
	local card = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 190),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(card, 14)
	CS.Util.Stroke(card, CS.Theme.Stroke, 0.45, 1)

	local title = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 26),
		Position = UDim2.new(0, 12, 0, 10),
		BackgroundTransparency = 1,
		Text = "Glass Transparency",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 15,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local subtitle = CS.Util.Create("TextLabel", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 24),
		Position = UDim2.new(0, 12, 0, 36),
		BackgroundTransparency = 1,
		Text = "One control for the full menu glass look.",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local preview = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(1, -24, 0, 52),
		Position = UDim2.new(0, 12, 0, 66),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BackgroundTransparency = CS.Flags.Transparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(preview, 12)
	CS.Util.Stroke(preview, CS.Theme.Stroke, 0.45, 1)

	local previewGradient = Instance.new("UIGradient")
	previewGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, Color3.fromRGB(0, 0, 0)),
		ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 255, 255))
	})
	previewGradient.Rotation = 0
	previewGradient.Parent = preview

	local previewText = CS.Util.Create("TextLabel", {
		Parent = preview,
		Size = UDim2.new(1, -20, 1, 0),
		Position = UDim2.new(0, 10, 0, 0),
		BackgroundTransparency = 1,
		Text = "Transparency: " .. tostring(CS.Flags.Transparency),
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local bar = CS.Util.Create("Frame", {
		Parent = card,
		Size = UDim2.new(1, -48, 0, 16),
		Position = UDim2.new(0, 24, 0, 136),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BorderSizePixel = 0
	})

	CS.Util.Corner(bar, 999)
	CS.Util.Stroke(bar, CS.Theme.Stroke, 0.45, 1)

	local barGradient = Instance.new("UIGradient")
	barGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, Color3.fromRGB(0, 0, 0)),
		ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 255, 255))
	})
	barGradient.Rotation = 0
	barGradient.Parent = bar

	local knob = CS.Util.Create("Frame", {
		Parent = bar,
		Size = UDim2.new(0, 22, 0, 22),
		Position = UDim2.new(CS.Flags.Transparency, -11, 0.5, -11),
		BackgroundColor3 = CS.Theme.Accent,
		BorderSizePixel = 0,
		ZIndex = 4
	})

	CS.Util.Corner(knob, 999)
	CS.Util.Stroke(knob, CS.Theme.Text, 0.2, 2)

	local dragging = false

	local function applyValueFromX(xPosition)
		local absPos = bar.AbsolutePosition
		local absSize = bar.AbsoluteSize
		local value = math.clamp((xPosition - absPos.X) / absSize.X, 0, 1)
		value = math.floor(value * 100) / 10

		CS.Flags.Transparency = value
		CS.Flags.PanelTransparency = math.clamp(value - 0.12, 0, 1)
		CS.Flags.CardTransparency = math.clamp(value - 0.35, 0, 1)

		knob.Position = UDim2.new(value, -11, 0.5, -11)
		preview.BackgroundTransparency = value
		previewText.Text = "Transparency: " .. tostring(value)

		if CS.UI.Objects.Main then CS.UI.Objects.Main.BackgroundTransparency = CS.Flags.Transparency end
		if CS.UI.Objects.Sidebar then CS.UI.Objects.Sidebar.BackgroundTransparency = CS.Flags.PanelTransparency end
		if CS.UI.Objects.Content then CS.UI.Objects.Content.BackgroundTransparency = CS.Flags.PanelTransparency end
	end

	bar.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragging = true
			applyValueFromX(input.Position.X)
		end
	end)

	CS.Services.UserInputService.InputChanged:Connect(function(input)
		if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
			applyValueFromX(input.Position.X)
		end
	end)

	CS.Services.UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			if dragging then
				dragging = false
				CS.Settings.Save(true)
				CS.Notify.Push("Settings", "Transparency saved.", CS.Theme.Success)
			end
		end
	end)

	return card
end

function CS.Settings.ResetThemeDefaults()
	CS.Theme.Background = Color3.fromRGB(7, 8, 12)
	CS.Theme.Background2 = Color3.fromRGB(13, 15, 24)
	CS.Theme.Panel = Color3.fromRGB(18, 18, 26)
	CS.Theme.Panel2 = Color3.fromRGB(24, 24, 36)
	CS.Theme.Card = Color3.fromRGB(28, 28, 40)
	CS.Theme.Card2 = Color3.fromRGB(36, 36, 52)
	CS.Theme.Accent = Color3.fromRGB(78, 115, 255)
	CS.Theme.Accent2 = CS.Theme.Accent
	CS.Theme.Accent3 = CS.Theme.Accent
	CS.Theme.Info = Color3.fromRGB(80, 185, 255)
	CS.Theme.NotificationBackground = Color3.fromRGB(28, 28, 40)
	CS.Theme.NotificationOutline = Color3.fromRGB(80, 185, 255)
	CS.Theme.Stroke = Color3.fromRGB(115, 120, 160)
	CS.Theme.StrokeSoft = Color3.fromRGB(70, 75, 100)

	CS.Flags.Transparency = 0.54
	CS.Flags.PanelTransparency = 0.36
	CS.Flags.CardTransparency = 0.14

	CS.Settings.ApplyVisuals()
	CS.Settings.Save()
	CS.Notify.Push("Settings", "Theme and transparency reset.", CS.Theme.Success)
end

function CS.Settings.MakeConfirmReset(parent)
	local holder = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, -8, 0, 96),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(holder, 14)
	CS.Util.Stroke(holder, CS.Theme.Warning, 0.45, 1)

	local reset = CS.Util.Button(holder, "Reset Colors / Transparency", 38)
	reset.Position = UDim2.new(0, 12, 0, 10)
	reset.Size = UDim2.new(1, -24, 0, 38)

	local confirm = CS.Util.Button(holder, "Confirm Reset", 34)
	confirm.Position = UDim2.new(0, 12, 0, 54)
	confirm.Size = UDim2.new(1, -24, 0, 34)
	confirm.Visible = false
	confirm.BackgroundColor3 = CS.Theme.Warning
	confirm.TextColor3 = Color3.fromRGB(0, 0, 0)

	reset.MouseButton1Click:Connect(function()
		confirm.Visible = true
		reset.Text = "Click Confirm Reset below"
		CS.Notify.Push("Confirm Reset", "Press Confirm Reset to continue.", CS.Theme.Warning)

		task.delay(5, function()
			if confirm and confirm.Parent then
				confirm.Visible = false
				reset.Text = "Reset Colors / Transparency"
			end
		end)
	end)

	confirm.MouseButton1Click:Connect(function()
		confirm.Visible = false
		reset.Text = "Reset Colors / Transparency"
		CS.Settings.ResetThemeDefaults()
	end)
end


function CS.Settings.ToggleExampleNotification()
	CS.Flags.ExampleNotification = not CS.Flags.ExampleNotification

	local existing = CS.UI.Objects.PersistentExampleNotification
	if existing then
		existing:Destroy()
		CS.UI.Objects.PersistentExampleNotification = nil
	end

	if not CS.Flags.ExampleNotification then
		CS.Notify.Push("Notification Preview", "Example notification turned off.", CS.Theme.Info)
		return
	end

	local holder = CS.UI.Objects.ToastHolder
	if not holder then return end

	local preview = CS.Util.Create("Frame", {
		Parent = holder,
		Size = UDim2.new(1, 0, 0, 92),
		BackgroundColor3 = CS.Theme.NotificationBackground,
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0,
		ClipsDescendants = true
	})

	CS.Util.Corner(preview, 12)
	CS.Util.Stroke(preview, CS.Theme.NotificationOutline, 0.2, 1)

	local title = CS.Util.Create("TextLabel", {
		Parent = preview,
		Size = UDim2.new(1, -20, 0, 26),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "Example Notification",
		TextColor3 = CS.Theme.NotificationOutline,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local body = CS.Util.Create("TextLabel", {
		Parent = preview,
		Size = UDim2.new(1, -20, 1, -40),
		Position = UDim2.new(0, 10, 0, 34),
		BackgroundTransparency = 1,
		Text = "This stays until you turn it off.",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 13,
		TextWrapped = true,
		TextXAlignment = Enum.TextXAlignment.Left,
		TextYAlignment = Enum.TextYAlignment.Top
	})

	local line = CS.Util.Create("Frame", {
		Parent = preview,
		Size = UDim2.new(1, -20, 0, 3),
		Position = UDim2.new(0, 10, 1, -8),
		BackgroundColor3 = CS.Theme.NotificationOutline,
		BorderSizePixel = 0
	})

	CS.Util.Corner(line, 999)
	CS.UI.Objects.PersistentExampleNotification = preview
end

function CS.Settings.SaveOriginalTheme()
	CS.Runtime.OriginalTheme = {
		Accent = CS.Theme.Accent,
		Stroke = CS.Theme.Stroke,
		StrokeSoft = CS.Theme.StrokeSoft,
		NotificationOutline = CS.Theme.NotificationOutline
	}
end

function CS.Settings.RestoreOriginalTheme()
	if not CS.Runtime.OriginalTheme then
		return
	end

	CS.Theme.Accent = CS.Runtime.OriginalTheme.Accent
	CS.Theme.Stroke = CS.Runtime.OriginalTheme.Stroke
	CS.Theme.StrokeSoft = CS.Runtime.OriginalTheme.StrokeSoft
	CS.Theme.NotificationOutline = CS.Runtime.OriginalTheme.NotificationOutline

	CS.Runtime.OriginalTheme = nil
	CS.Settings.ApplyVisuals()
	CS.Settings.RefreshToggleVisuals()
end

function CS.Settings.LerpColor(a, b, t)
	return Color3.new(
		a.R + (b.R - a.R) * t,
		a.G + (b.G - a.G) * t,
		a.B + (b.B - a.B) * t
	)
end

function CS.Settings.UpdateColorCycle()
	if not CS.Flags.ColorCycle then
		return
	end

	local colorA = CS.Settings.HexToColor(CS.Flags.ColorCycleA) or Color3.fromRGB(255, 0, 215)
	local colorB = CS.Settings.HexToColor(CS.Flags.ColorCycleB) or Color3.fromRGB(0, 226, 255)
	local speed = CS.Settings.SpeedLevelToInternal(CS.Flags.ColorCycleSpeedLevel or 3)

	CS.Flags.ColorCycleHue += speed

	if CS.Flags.ColorCycleHue > 1 then
		CS.Flags.ColorCycleHue -= 1
	end

	local base = CS.Flags.ColorCycleHue

	local function waveColor(offset)
		local wave = (math.sin(((base + offset) % 1) * math.pi * 2) + 1) / 2
		return CS.Settings.LerpColor(colorA, colorB, wave)
	end

	local c1 = waveColor(0)
	local c2 = waveColor(0.17)
	local c3 = waveColor(0.34)
	local c4 = waveColor(0.51)
	local c5 = waveColor(0.68)

	CS.Theme.Accent = c1
	CS.Theme.Stroke = c2
	CS.Theme.StrokeSoft = c3
	CS.Theme.NotificationOutline = c1

	if CS.UI.Objects.AccentRail then
		CS.UI.Objects.AccentRail.BackgroundColor3 = c1
	end

	if CS.UI.Objects.Main then
		local mainStroke = CS.UI.Objects.Main:FindFirstChildOfClass("UIStroke")
		if mainStroke then
			mainStroke.Color = c1
		end
	end

	if CS.UI.Objects.Sidebar then
		local sideStroke = CS.UI.Objects.Sidebar:FindFirstChildOfClass("UIStroke")
		if sideStroke then
			sideStroke.Color = c2
		end
	end

	if CS.UI.Objects.Content then
		local contentStroke = CS.UI.Objects.Content:FindFirstChildOfClass("UIStroke")
		if contentStroke then
			contentStroke.Color = c3
		end
	end

	if CS.UI.Objects.GUI then
		local i = 0

		for _, obj in ipairs(CS.UI.Objects.GUI:GetDescendants()) do
			if obj:IsA("UIStroke") then
				i += 1

				local mod = i % 5
				if mod == 0 then
					obj.Color = c1
				elseif mod == 1 then
					obj.Color = c2
				elseif mod == 2 then
					obj.Color = c3
				elseif mod == 3 then
					obj.Color = c4
				else
					obj.Color = c5
				end
			end
		end
	end

	for name, tab in pairs(CS.UI.Tabs) do
		if CS.UI.Pages[name] and CS.UI.Pages[name].Visible then
			tab.BackgroundColor3 = c1
		end
	end

	if CS.UI.Objects.PersistentExampleNotification then
		local stroke = CS.UI.Objects.PersistentExampleNotification:FindFirstChildOfClass("UIStroke")
		if stroke then
			stroke.Color = c1
		end
	end
end

function CS.Settings.MakeMiniColorPicker(parent, titleText, defaultHex)
	local holder = CS.Util.Create("Frame", {
		Parent = parent,
		Size = UDim2.new(1, 0, 0, 205),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0
	})

	CS.Util.Corner(holder, 12)
	CS.Util.Stroke(holder, CS.Theme.Stroke, 0.55, 1)

	local label = CS.Util.Create("TextLabel", {
		Parent = holder,
		Size = UDim2.new(1, -16, 0, 22),
		Position = UDim2.new(0, 8, 0, 6),
		BackgroundTransparency = 1,
		Text = titleText,
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local picker = CS.Util.Create("Frame", {
		Parent = holder,
		Size = UDim2.new(1, -16, 0, 92),
		Position = UDim2.new(0, 8, 0, 34),
		BackgroundColor3 = Color3.fromRGB(255, 0, 0),
		BorderSizePixel = 0,
		ClipsDescendants = true
	})

	CS.Util.Corner(picker, 8)
	CS.Util.Stroke(picker, CS.Theme.Text, 0.55, 1)

	local satGradient = Instance.new("UIGradient")
	satGradient.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(255, 255, 255))
	satGradient.Transparency = NumberSequence.new({
		NumberSequenceKeypoint.new(0, 1),
		NumberSequenceKeypoint.new(1, 0)
	})
	satGradient.Rotation = 0
	satGradient.Parent = picker

	local overlay = CS.Util.Create("Frame", {
		Parent = picker,
		Size = UDim2.new(1, 0, 1, 0),
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BorderSizePixel = 0
	})

	local overlayGradient = Instance.new("UIGradient")
	overlayGradient.Transparency = NumberSequence.new({
		NumberSequenceKeypoint.new(0, 1),
		NumberSequenceKeypoint.new(1, 0)
	})
	overlayGradient.Rotation = 90
	overlayGradient.Parent = overlay

	local cursor = CS.Util.Create("Frame", {
		Parent = picker,
		Size = UDim2.new(0, 14, 0, 14),
		Position = UDim2.new(1, -7, 0, -7),
		BackgroundColor3 = Color3.fromRGB(0, 0, 0),
		BackgroundTransparency = 0.2,
		BorderSizePixel = 0,
		ZIndex = 5
	})

	CS.Util.Corner(cursor, 999)
	CS.Util.Stroke(cursor, CS.Theme.Text, 0.15, 2)

	local hueBar = CS.Util.Create("Frame", {
		Parent = holder,
		Size = UDim2.new(1, -16, 0, 14),
		Position = UDim2.new(0, 8, 0, 134),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BorderSizePixel = 0
	})

	CS.Util.Corner(hueBar, 999)

	local hueGradient = Instance.new("UIGradient")
	hueGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0.00, Color3.fromRGB(255, 0, 0)),
		ColorSequenceKeypoint.new(0.16, Color3.fromRGB(255, 255, 0)),
		ColorSequenceKeypoint.new(0.33, Color3.fromRGB(0, 255, 0)),
		ColorSequenceKeypoint.new(0.50, Color3.fromRGB(0, 255, 255)),
		ColorSequenceKeypoint.new(0.66, Color3.fromRGB(0, 0, 255)),
		ColorSequenceKeypoint.new(0.83, Color3.fromRGB(255, 0, 255)),
		ColorSequenceKeypoint.new(1.00, Color3.fromRGB(255, 0, 0))
	})
	hueGradient.Parent = hueBar

	local hueCursor = CS.Util.Create("Frame", {
		Parent = hueBar,
		Size = UDim2.new(0, 12, 0, 20),
		Position = UDim2.new(0, -6, 0.5, -10),
		BackgroundColor3 = Color3.fromRGB(25, 25, 25),
		BorderSizePixel = 0,
		ZIndex = 5
	})

	CS.Util.Corner(hueCursor, 999)
	CS.Util.Stroke(hueCursor, CS.Theme.Text, 0.2, 2)

	local bottom = CS.Util.Create("Frame", {
		Parent = holder,
		Size = UDim2.new(1, -16, 0, 32),
		Position = UDim2.new(0, 8, 0, 160),
		BackgroundColor3 = Color3.fromRGB(12, 12, 18),
		BackgroundTransparency = 0.1,
		BorderSizePixel = 0
	})

	CS.Util.Corner(bottom, 8)

	local swatch = CS.Util.Create("Frame", {
		Parent = bottom,
		Size = UDim2.new(0, 22, 0, 22),
		Position = UDim2.new(0, 7, 0.5, -11),
		BackgroundColor3 = CS.Settings.HexToColor(defaultHex) or CS.Theme.Accent,
		BorderSizePixel = 0
	})

	CS.Util.Corner(swatch, 999)

	local box = CS.Util.Create("TextBox", {
		Parent = bottom,
		Size = UDim2.new(1, -40, 1, 0),
		Position = UDim2.new(0, 36, 0, 0),
		BackgroundTransparency = 1,
		Text = defaultHex,
		PlaceholderText = "#FFFFFF",
		TextColor3 = CS.Theme.Text,
		PlaceholderColor3 = CS.Theme.Muted,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left,
		ClearTextOnFocus = false
	})

	local hue, sat, val = 0, 1, 1
	local dragPicker = false
	local dragHue = false

	local function setHSV()
		local color = Color3.fromHSV(hue, sat, val)
		local hex = CS.Settings.ColorToHex(color)
		box.Text = hex
		swatch.BackgroundColor3 = color
		local hueColor = Color3.fromHSV(hue, 1, 1)
		picker.BackgroundColor3 = hueColor
		satGradient.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(255, 255, 255))
		satGradient.Transparency = NumberSequence.new({
			NumberSequenceKeypoint.new(0, 1),
			NumberSequenceKeypoint.new(1, 0)
		})
	end

	local function applyPicker(input)
		local x = math.clamp((input.Position.X - picker.AbsolutePosition.X) / picker.AbsoluteSize.X, 0, 1)
		local y = math.clamp((input.Position.Y - picker.AbsolutePosition.Y) / picker.AbsoluteSize.Y, 0, 1)
		sat = x
		val = 1 - y
		cursor.Position = UDim2.new(x, -7, y, -7)
		cursor.BackgroundColor3 = val > 0.5 and Color3.fromRGB(0, 0, 0) or Color3.fromRGB(255, 255, 255)
		setHSV()
	end

	local function applyHue(input)
		local x = math.clamp((input.Position.X - hueBar.AbsolutePosition.X) / hueBar.AbsoluteSize.X, 0, 1)
		hue = x
		hueCursor.Position = UDim2.new(x, -6, 0.5, -10)
		setHSV()
	end

	local function beginMiniPickerDrag(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragPicker = true
			applyPicker(input)
		end
	end

	picker.InputBegan:Connect(beginMiniPickerDrag)
	overlay.InputBegan:Connect(beginMiniPickerDrag)

	hueBar.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragHue = true
			applyHue(input)
		end
	end)

	CS.Services.UserInputService.InputChanged:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			if dragPicker then
				applyPicker(input)
			elseif dragHue then
				applyHue(input)
			end
		end
	end)

	CS.Services.UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragPicker = false
			dragHue = false
		end
	end)

	box.FocusLost:Connect(function()
		local color = CS.Settings.HexToColor(box.Text)
		if color then
			local h, s, v = color:ToHSV()
			hue, sat, val = h, s, v
			cursor.Position = UDim2.new(sat, -7, 1 - val, -7)
			cursor.BackgroundColor3 = val > 0.5 and Color3.fromRGB(0, 0, 0) or Color3.fromRGB(255, 255, 255)
			hueCursor.Position = UDim2.new(hue, -6, 0.5, -10)
			box.Text = CS.Settings.ColorToHex(color)
			swatch.BackgroundColor3 = color
			local hueColor = Color3.fromHSV(hue, 1, 1)
			picker.BackgroundColor3 = hueColor
			satGradient.Color = ColorSequence.new({
				ColorSequenceKeypoint.new(0, Color3.fromRGB(255,255,255)),
				ColorSequenceKeypoint.new(1, hueColor)
			})
		end
	end)

	local startColor = CS.Settings.HexToColor(defaultHex) or CS.Theme.Accent
	local h, s, v = startColor:ToHSV()
	hue, sat, val = h, s, v
	cursor.Position = UDim2.new(sat, -7, 1 - val, -7)
	hueCursor.Position = UDim2.new(hue, -6, 0.5, -10)
	setHSV()

	return {
		Frame = holder,
		Box = box,
		Swatch = swatch
	}
end


function CS.Settings.SpeedLevelToInternal(level)
	level = math.clamp(tonumber(level) or 3, 1, 10)

	-- 1 is very slow, 10 is fast.
	return 0.00001 + ((level - 1) / 9) * (0.0045 - 0.00001)
end





function CS.Settings.Build()
	CS.Settings.Load()
	CS.Settings.RefreshToggleVisuals()
	local page = CS.UI.Pages["Settings"]
	CS.Util.Clear(page, false)

	local outer = CS.Util.Create("Frame", {
		Parent = page,
		Size = UDim2.new(1, 0, 1, 0),
		BackgroundTransparency = 1
	})

	local left = CS.Util.Create("Frame", {
		Parent = outer,
		Size = UDim2.new(0.5, -6, 1, 0),
		BackgroundTransparency = 1
	})

	local right = CS.Util.Create("Frame", {
		Parent = outer,
		Size = UDim2.new(0.5, -6, 1, 0),
		Position = UDim2.new(0.5, 6, 0, 0),
		BackgroundTransparency = 1
	})

	local leftScroll = CS.Util.Scroll(left)
	local rightScroll = CS.Util.Scroll(right)

	CS.Settings.MakeSection(leftScroll, "Theme Colors", "Clear color controls. Each row shows what it changes.")

	local exampleNotificationButton = CS.Util.Button(leftScroll, "Toggle Example Notification")

	local cycleCard = CS.Util.Create("Frame", {
		Parent = leftScroll,
		Size = UDim2.new(1, -8, 0, 560),
		BackgroundColor3 = CS.Theme.Card,
		BackgroundTransparency = CS.Flags.CardTransparency,
		BorderSizePixel = 0
	})

	CS.Util.Corner(cycleCard, 14)
	CS.Util.Stroke(cycleCard, CS.Theme.Stroke, 0.45, 1)

	local cycleTitle = CS.Util.Create("TextLabel", {
		Parent = cycleCard,
		Size = UDim2.new(1, -24, 0, 24),
		Position = UDim2.new(0, 12, 0, 8),
		BackgroundTransparency = 1,
		Text = "Color Cycle / Chase",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 14,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local cycleSub = CS.Util.Create("TextLabel", {
		Parent = cycleCard,
		Size = UDim2.new(1, -24, 0, 22),
		Position = UDim2.new(0, 12, 0, 30),
		BackgroundTransparency = 1,
		Text = "Pick two colors. Top-left is white, top-right is hue, bottom is black.",
		TextColor3 = CS.Theme.SubText,
		Font = Enum.Font.Gotham,
		TextSize = 12,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local pickerAHolder = CS.Util.Create("Frame", {
		Parent = cycleCard,
		Size = UDim2.new(0.5, -18, 0, 205),
		Position = UDim2.new(0, 12, 0, 60),
		BackgroundTransparency = 1
	})

	local pickerBHolder = CS.Util.Create("Frame", {
		Parent = cycleCard,
		Size = UDim2.new(0.5, -18, 0, 205),
		Position = UDim2.new(0.5, 6, 0, 60),
		BackgroundTransparency = 1
	})

	local pickerA = CS.Settings.MakeMiniColorPicker(pickerAHolder, "First Color", CS.Flags.ColorCycleA)
	local pickerB = CS.Settings.MakeMiniColorPicker(pickerBHolder, "Second Color", CS.Flags.ColorCycleB)

	local speedPreview = CS.Util.Create("Frame", {
		Parent = cycleCard,
		Size = UDim2.new(1, -24, 0, 86),
		Position = UDim2.new(0, 12, 0, 278),
		BackgroundColor3 = Color3.fromRGB(12, 12, 18),
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0,
		ClipsDescendants = true
	})

	CS.Util.Corner(speedPreview, 12)
	CS.Util.Stroke(speedPreview, CS.Theme.Stroke, 0.55, 1)

	local speedPreviewTitle = CS.Util.Create("TextLabel", {
		Parent = speedPreview,
		Size = UDim2.new(1, -20, 0, 22),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "Speed Preview",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local chaseTrack = CS.Util.Create("Frame", {
		Parent = speedPreview,
		Size = UDim2.new(1, -24, 0, 10),
		Position = UDim2.new(0, 12, 0, 50),
		BackgroundColor3 = Color3.fromRGB(255,255,255),
		BorderSizePixel = 0
	})

	CS.Util.Corner(chaseTrack, 999)

	local chaseGradient = Instance.new("UIGradient")
	chaseGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, CS.Settings.HexToColor(CS.Flags.ColorCycleA) or CS.Theme.Accent),
		ColorSequenceKeypoint.new(1, CS.Settings.HexToColor(CS.Flags.ColorCycleB) or CS.Theme.Info)
	})
	chaseGradient.Parent = chaseTrack

	local chaseDot = CS.Util.Create("Frame", {
		Parent = speedPreview,
		Size = UDim2.new(0, 18, 0, 18),
		Position = UDim2.new(0, 12, 0, 46),
		BackgroundColor3 = CS.Settings.HexToColor(CS.Flags.ColorCycleA) or CS.Theme.Accent,
		BorderSizePixel = 0,
		ZIndex = 4
	})

	CS.Util.Corner(chaseDot, 999)
	CS.Util.Stroke(chaseDot, CS.Theme.Text, 0.2, 1)

	local speedControl = CS.Util.Create("Frame", {
		Parent = cycleCard,
		Size = UDim2.new(1, -24, 0, 78),
		Position = UDim2.new(0, 12, 0, 378),
		BackgroundColor3 = CS.Theme.Card2,
		BackgroundTransparency = 0.12,
		BorderSizePixel = 0
	})

	CS.Util.Corner(speedControl, 12)
	CS.Util.Stroke(speedControl, CS.Theme.Stroke, 0.55, 1)

	local speedLabel = CS.Util.Create("TextLabel", {
		Parent = speedControl,
		Size = UDim2.new(1, -20, 0, 24),
		Position = UDim2.new(0, 10, 0, 8),
		BackgroundTransparency = 1,
		Text = "Speed: " .. tostring(CS.Flags.ColorCycleSpeedLevel) .. " / 10",
		TextColor3 = CS.Theme.Text,
		Font = Enum.Font.GothamBold,
		TextSize = 13,
		TextXAlignment = Enum.TextXAlignment.Left
	})

	local speedBar = CS.Util.Create("Frame", {
		Parent = speedControl,
		Size = UDim2.new(1, -34, 0, 14),
		Position = UDim2.new(0, 17, 0, 46),
		BackgroundColor3 = Color3.fromRGB(255, 255, 255),
		BorderSizePixel = 0
	})

	CS.Util.Corner(speedBar, 999)
	CS.Util.Stroke(speedBar, CS.Theme.Stroke, 0.5, 1)

	local speedGradient = Instance.new("UIGradient")
	speedGradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0, Color3.fromRGB(80, 120, 255)),
		ColorSequenceKeypoint.new(0.5, Color3.fromRGB(170, 85, 255)),
		ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 70, 120))
	})
	speedGradient.Parent = speedBar

	local speedKnob = CS.Util.Create("Frame", {
		Parent = speedBar,
		Size = UDim2.new(0, 20, 0, 20),
		Position = UDim2.new((CS.Flags.ColorCycleSpeedLevel - 1) / 9, -10, 0.5, -10),
		BackgroundColor3 = CS.Theme.Accent,
		BorderSizePixel = 0,
		ZIndex = 6
	})

	CS.Util.Corner(speedKnob, 999)
	CS.Util.Stroke(speedKnob, CS.Theme.Text, 0.25, 2)

	local draggingSpeed = false

	local function applySpeedFromX(xPos)
		local x = math.clamp((xPos - speedBar.AbsolutePosition.X) / speedBar.AbsoluteSize.X, 0, 1)
		local level = math.floor(1 + x * 9 + 0.5)

		CS.Flags.ColorCycleSpeedLevel = math.clamp(level, 1, 10)
		CS.Flags.ColorCycleSpeed = CS.Settings.SpeedLevelToInternal(CS.Flags.ColorCycleSpeedLevel)

		speedKnob.Position = UDim2.new((CS.Flags.ColorCycleSpeedLevel - 1) / 9, -10, 0.5, -10)
		speedLabel.Text = "Speed: " .. tostring(CS.Flags.ColorCycleSpeedLevel) .. " / 10"
	end

	speedBar.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			draggingSpeed = true
			applySpeedFromX(input.Position.X)
		end
	end)

	CS.Services.UserInputService.InputChanged:Connect(function(input)
		if draggingSpeed and input.UserInputType == Enum.UserInputType.MouseMovement then
			applySpeedFromX(input.Position.X)
		end
	end)

	CS.Services.UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			if draggingSpeed then
				draggingSpeed = false
				CS.Settings.Save()
			end
		end
	end)

	local colorCycleButton = CS.Util.Button(cycleCard, "Toggle Color Cycle / Chase", 38)
	colorCycleButton.Position = UDim2.new(0, 12, 0, 468)
	colorCycleButton.Size = UDim2.new(1, -24, 0, 38)

	local previewValue = 0
	local previewDir = 1

	CS.Services.RunService.RenderStepped:Connect(function()
		if not speedPreview.Parent then
			return
		end

		local speed = CS.Settings.SpeedLevelToInternal(CS.Flags.ColorCycleSpeedLevel or 3)

		previewValue += speed

		if previewValue > 1 then
			previewValue -= 1
		end

		local wave = (math.sin(previewValue * math.pi * 2) + 1) / 2
		local cA = CS.Settings.HexToColor(pickerA.Box.Text) or CS.Theme.Accent
		local cB = CS.Settings.HexToColor(pickerB.Box.Text) or CS.Theme.Info
		chaseGradient.Color = ColorSequence.new({
			ColorSequenceKeypoint.new(0, cA),
			ColorSequenceKeypoint.new(1, cB)
		})
		chaseDot.Position = UDim2.new(wave, -9, 0, 46)
		chaseDot.BackgroundColor3 = CS.Settings.LerpColor(cA, cB, wave)
	end)

	exampleNotificationButton.MouseButton1Click:Connect(function()
		CS.Settings.ToggleExampleNotification()
		exampleNotificationButton.BackgroundColor3 = CS.Flags.ExampleNotification and CS.Theme.Success or CS.Theme.Card2
		exampleNotificationButton.TextColor3 = CS.Flags.ExampleNotification and Color3.fromRGB(0, 0, 0) or CS.Theme.Text
	end)

	colorCycleButton.MouseButton1Click:Connect(function()
		local a = CS.Settings.HexToColor(pickerA.Box.Text)
		local b = CS.Settings.HexToColor(pickerB.Box.Text)

		if not a or not b then
			CS.Notify.Push("Color Cycle", "Invalid hex. Use #RRGGBB.", CS.Theme.Warning)
			return
		end

		CS.Flags.ColorCycleA = pickerA.Box.Text
		CS.Flags.ColorCycleB = pickerB.Box.Text
		CS.Flags.ColorCycleSpeed = CS.Settings.SpeedLevelToInternal(CS.Flags.ColorCycleSpeedLevel or 3)

		CS.Flags.ColorCycle = not CS.Flags.ColorCycle

		if CS.Flags.ColorCycle then
			CS.Settings.SaveOriginalTheme()
			CS.Flags.ColorCycleHue = 0
			colorCycleButton.BackgroundColor3 = CS.Theme.Success
			colorCycleButton.TextColor3 = Color3.fromRGB(0, 0, 0)
			colorCycleButton.Text = "Color Cycle ON"

			for i = 1, 8 do
				CS.Settings.UpdateColorCycle()
			end
		else
			colorCycleButton.BackgroundColor3 = CS.Theme.Card2
			colorCycleButton.TextColor3 = CS.Theme.Text
			colorCycleButton.Text = "Toggle Color Cycle / Chase"
			CS.Settings.RestoreOriginalTheme()
		end

		CS.Settings.Save(true)
		CS.Notify.Push("Color Cycle", "Cycle/chase: " .. tostring(CS.Flags.ColorCycle), CS.Theme.Info)
	end)

	CS.Settings.MakeColorEditor(leftScroll, "Background Color", "Background", CS.Theme.Background, function(color, hex)
		CS.Theme.Background = color
		CS.Theme.Panel = color
		CS.Theme.Panel2 = color
		CS.Settings.ApplyVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Background color changed to " .. hex, CS.Theme.NotificationOutline)
	end)

	CS.Settings.MakeColorEditor(leftScroll, "All Outlines", "Outline", CS.Theme.Stroke, function(color, hex)
		CS.Theme.Stroke = color
		CS.Theme.StrokeSoft = color
		CS.Settings.ApplyVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "All outlines changed to " .. hex, color)
	end)

	CS.Settings.MakeColorEditor(leftScroll, "Accent", "Accent", CS.Theme.Accent, function(color, hex)
		CS.Theme.Accent = color
		CS.Theme.Accent2 = color
		CS.Theme.Accent3 = color
		CS.Settings.ApplyVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Accent changed to " .. hex, color)
	end)

	CS.Settings.MakeColorEditor(leftScroll, "Notification Background", "NotificationBackground", CS.Theme.NotificationBackground, function(color, hex)
		CS.Theme.NotificationBackground = color
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Notification background changed to " .. hex, CS.Theme.NotificationOutline)
	end)

	CS.Settings.MakeColorEditor(leftScroll, "Notification Outline", "NotificationOutline", CS.Theme.NotificationOutline, function(color, hex)
		CS.Theme.NotificationOutline = color
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Notification outline changed to " .. hex, color)
	end)

	CS.Settings.MakeSection(leftScroll, "Theme Presets", "Quick presets for the main accent.")

	local blue = CS.Util.Button(leftScroll, "Blue Preset")
	local purple = CS.Util.Button(leftScroll, "Purple Preset")
	local red = CS.Util.Button(leftScroll, "Red Preset")
	local green = CS.Util.Button(leftScroll, "Green Preset")
	local midnight = CS.Util.Button(leftScroll, "Midnight Purple Preset")
	local blackRed = CS.Util.Button(leftScroll, "Black / Red Preset")

	CS.Settings.MakeTransparencySlider(rightScroll)

	CS.Settings.MakeSection(rightScroll, "Behavior", "General CS behavior settings.")

	local toggleAnimations = CS.Util.Button(rightScroll, "Toggle Animations")
	local toggleNotifications = CS.Util.Button(rightScroll, "Toggle Notifications")
	local toggleNonFriends = CS.Util.Button(rightScroll, "Toggle Non-Friend Notices")
	local toggleFriendSounds = CS.Util.Button(rightScroll, "Toggle Friend Sounds")

	CS.UI.Buttons.ToggleAnimations = toggleAnimations
	CS.UI.Buttons.ToggleNotifications = toggleNotifications
	CS.UI.Buttons.ToggleNonFriends = toggleNonFriends
	CS.UI.Buttons.ToggleFriendSounds = toggleFriendSounds
	CS.Settings.RefreshToggleVisuals()

	CS.Settings.MakeSection(rightScroll, "Layout Scale", "Change the menu size.")

	local scaleSmall = CS.Util.Button(rightScroll, "Scale: Small")
	local scaleDefault = CS.Util.Button(rightScroll, "Scale: Default")
	local scaleLarge = CS.Util.Button(rightScroll, "Scale: Large")

	CS.Settings.MakeSection(rightScroll, "Config Save", "Saves automatically if writefile/readfile are supported.")

	local saveNow = CS.Util.Button(rightScroll, "Save Settings Now")
	local reloadSettings = CS.Util.Button(rightScroll, "Reload Saved Settings")
	CS.Settings.MakeConfirmReset(rightScroll)

	blue.MouseButton1Click:Connect(function()
		CS.Settings.ApplyAccent(Color3.fromRGB(78, 115, 255))
		CS.Settings.Save()
	end)

	purple.MouseButton1Click:Connect(function()
		CS.Settings.ApplyAccent(Color3.fromRGB(130, 85, 255))
		CS.Settings.Save()
	end)

	red.MouseButton1Click:Connect(function()
		CS.Settings.ApplyAccent(Color3.fromRGB(220, 65, 75))
		CS.Settings.Save()
	end)

	green.MouseButton1Click:Connect(function()
		CS.Settings.ApplyAccent(Color3.fromRGB(70, 220, 130))
		CS.Settings.Save()
	end)

	midnight.MouseButton1Click:Connect(function()
		CS.Theme.Background = Color3.fromRGB(8, 6, 18)
		CS.Theme.Panel = Color3.fromRGB(18, 12, 34)
		CS.Theme.Card = Color3.fromRGB(28, 20, 48)
		CS.Theme.Accent = Color3.fromRGB(145, 90, 255)
		CS.Theme.Accent2 = Color3.fromRGB(90, 60, 200)
		CS.Settings.ApplyVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Midnight Purple applied.", CS.Theme.Accent)
	end)

	blackRed.MouseButton1Click:Connect(function()
		CS.Theme.Background = Color3.fromRGB(8, 6, 7)
		CS.Theme.Panel = Color3.fromRGB(18, 12, 14)
		CS.Theme.Card = Color3.fromRGB(30, 22, 24)
		CS.Theme.Accent = Color3.fromRGB(220, 45, 60)
		CS.Theme.Accent2 = Color3.fromRGB(120, 20, 30)
		CS.Settings.ApplyVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Theme", "Black / Red applied.", CS.Theme.Accent)
	end)

	toggleAnimations.MouseButton1Click:Connect(function()
		CS.Flags.Animations = not CS.Flags.Animations
		CS.Settings.RefreshToggleVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Settings", "Animations: " .. tostring(CS.Flags.Animations), CS.Theme.Info)
	end)

	toggleNotifications.MouseButton1Click:Connect(function()
		CS.Flags.Notifications = not CS.Flags.Notifications
		CS.Settings.Save()

		if CS.Flags.Notifications then
			CS.Notify.Push("Settings", "Notifications enabled.", CS.Theme.Success)
		end
	end)

	toggleNonFriends.MouseButton1Click:Connect(function()
		CS.Flags.ShowNonFriendNotices = not CS.Flags.ShowNonFriendNotices
		CS.Settings.RefreshToggleVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Settings", "Non-friend notices: " .. tostring(CS.Flags.ShowNonFriendNotices), CS.Theme.Info)
	end)

	toggleFriendSounds.MouseButton1Click:Connect(function()
		CS.Flags.FriendSounds = not CS.Flags.FriendSounds
		CS.Settings.RefreshToggleVisuals()
		CS.Settings.Save(true)
		CS.Notify.Push("Settings", "Friend sounds: " .. tostring(CS.Flags.FriendSounds), CS.Theme.Info)
	end)
	scaleSmall.MouseButton1Click:Connect(function()
		CS.Settings.SetScale("Small")
		CS.Settings.Save()
	end)

	scaleDefault.MouseButton1Click:Connect(function()
		CS.Settings.SetScale("Default")
		CS.Settings.Save()
	end)

	scaleLarge.MouseButton1Click:Connect(function()
		CS.Settings.SetScale("Large")
		CS.Settings.Save()
	end)

	saveNow.MouseButton1Click:Connect(function()
		CS.Settings.Save()
	end)

	reloadSettings.MouseButton1Click:Connect(function()
		if CS.Settings.Load() then
			CS.Settings.ApplyVisuals()
			CS.Settings.SetScale(CS.Flags.ScaleMode or "Default")
			CS.Notify.Push("Settings", "Saved settings reloaded.", CS.Theme.Success)
		else
			CS.Notify.Push("Settings", "No saved settings found.", CS.Theme.Warning)
		end
	end)
end


function CS.Settings.ApplyAccent(color)
	CS.Theme.Accent = color
	CS.Settings.ApplyVisuals()

	for name, tab in pairs(CS.UI.Tabs) do
		if CS.UI.Pages[name] and CS.UI.Pages[name].Visible then
			tab.BackgroundColor3 = CS.Theme.Accent
		end
	end

	CS.Notify.Push("Theme", "Accent changed.", CS.Theme.Accent)
end

function CS.Settings.ToggleTransparency()
	if CS.Flags.Transparency < 0.65 then
		CS.Flags.Transparency = 0.72
		CS.Flags.PanelTransparency = 0.62
		CS.Flags.CardTransparency = 0.32
	else
		CS.Flags.Transparency = 0.54
		CS.Flags.PanelTransparency = 0.36
		CS.Flags.CardTransparency = 0.14
	end

	CS.UI.Objects.Main.BackgroundTransparency = CS.Flags.Transparency
	CS.UI.Objects.Sidebar.BackgroundTransparency = CS.Flags.PanelTransparency
	CS.UI.Objects.Content.BackgroundTransparency = CS.Flags.PanelTransparency

	CS.Settings.Save()
	CS.Notify.Push("Appearance", "Transparency updated.", CS.Theme.Info)
end

function CS.Settings.ToggleCompact()
	CS.Flags.Compact = not CS.Flags.Compact

	if CS.Flags.Compact then
		CS.Settings.SetScale("Small")
	else
		CS.Settings.SetScale("Default")
	end
end

function CS.Settings.ApplyGlass()
	local mainT = CS.Flags.GlassMode and CS.Flags.Transparency or 0.18
	local panelT = CS.Flags.GlassMode and CS.Flags.PanelTransparency or 0.08

	CS.UI.Objects.Main.BackgroundTransparency = mainT
	CS.UI.Objects.Sidebar.BackgroundTransparency = panelT
	CS.UI.Objects.Content.BackgroundTransparency = panelT

	CS.Notify.Push("Appearance", "Glass mode: " .. tostring(CS.Flags.GlassMode), CS.Theme.Info)
end

function CS.Settings.SetScale(mode)
	CS.Flags.ScaleMode = mode

	local main = CS.UI.Objects.Main
	local sidebar = CS.UI.Objects.Sidebar
	local content = CS.UI.Objects.Content

	if mode == "Small" then
		main.Size = UDim2.new(0, 930, 0, 560)
		main.Position = UDim2.new(0.5, -465, 0.5, -280)
		sidebar.Size = UDim2.new(0, 215, 1, -96)
		content.Position = UDim2.new(0, 245, 0, 80)
		content.Size = UDim2.new(1, -265, 1, -96)
	elseif mode == "Large" then
		main.Size = UDim2.new(0, 1180, 0, 700)
		main.Position = UDim2.new(0.5, -590, 0.5, -350)
		sidebar.Size = UDim2.new(0, 250, 1, -96)
		content.Position = UDim2.new(0, 280, 0, 80)
		content.Size = UDim2.new(1, -300, 1, -96)
	else
		main.Size = UDim2.new(0, 1080, 0, 650)
		main.Position = UDim2.new(0.5, -540, 0.5, -325)
		sidebar.Size = UDim2.new(0, 235, 1, -96)
		content.Position = UDim2.new(0, 265, 0, 80)
		content.Size = UDim2.new(1, -285, 1, -96)
	end

	CS.Settings.Save()
	CS.Notify.Push("Scale", "Scale mode: " .. mode, CS.Theme.Info)
end

function CS.Utilities.Build()
	local scroll = CS.Util.Scroll(CS.UI.Pages["Utilities"])

	CS.Util.InfoCard(scroll, "Utilities", "One refresh menu plus close controls.", 90)

	local rfMenu = CS.Util.Button(scroll, "RF Menu")
	local fullClose = CS.Util.Button(scroll, "Fully Close CS")

	rfMenu.MouseButton1Click:Connect(function()
		CS.PlayerDetails.Refresh()
		CS.PlayerDetails.UpdateSelected()
		CS.Dashboard.Update()
		CS.ServerBrowser.UpdateLive()
		CS.Friends.Update()
		CS.Friends.RefreshFriendList()
		CS.Diagnostics.Run()
		CS.Notify.Push("RF Menu", "Menu/player/server/dashboard refreshed.", CS.Theme.Success)
	end)

	fullClose.MouseButton1Click:Connect(function()
		if CS.UI.Objects.GUI then
			CS.UI.Objects.GUI:Destroy()
		end
	end)
end


function CS.Dashboard.UpdatePingStats()
	local ping = CS.Util.Ping()
	CS.Runtime.Ping = ping

	table.insert(CS.Runtime.PingHistory, ping)

	if #CS.Runtime.PingHistory > 48 then
		table.remove(CS.Runtime.PingHistory, 1)
	end

	CS.Runtime.LowPing = CS.Runtime.LowPing and math.min(CS.Runtime.LowPing, ping) or ping
	CS.Runtime.HighPing = CS.Runtime.HighPing and math.max(CS.Runtime.HighPing, ping) or ping

	local total = 0

	for _, value in ipairs(CS.Runtime.PingHistory) do
		total += value
	end

	CS.Runtime.AvgPing = #CS.Runtime.PingHistory > 0 and math.floor(total / #CS.Runtime.PingHistory) or ping
end

function CS.Dashboard.PaintBars(bars)
	if not bars then return end

	for index, bar in ipairs(bars) do
		local value = CS.Runtime.PingHistory[index] or 0
		local height = math.clamp(value / 240, 0.04, 1)

		bar.Size = UDim2.new(1 / #bars, -2, height, 0)

		if value <= 70 then
			bar.BackgroundColor3 = CS.Theme.Success
		elseif value <= 140 then
			bar.BackgroundColor3 = CS.Theme.Warning
		else
			bar.BackgroundColor3 = CS.Theme.Error
		end
	end
end

function CS.Dashboard.Update()
	local ping = CS.Runtime.Ping
	local status, color = CS.Util.PingStatus(ping)
	local friendsOnline = CS.Friends.CountOnline()
	local friendsInServer = CS.Friends.CountInServer()

	CS.Util.Set(CS.UI.Labels.Overview,
		"Version: " .. CS.Version ..
		"\nGame: " .. tostring(game.PlaceId) ..
		"\nJobId: " .. (game.JobId ~= "" and game.JobId or "Unavailable") ..
		"\nPanel: " .. (CS.Flags.Open and "Open" or "Closed")
	)

	CS.Util.Set(CS.UI.Labels.Network,
		"Current: " .. tostring(ping) .. " ms" ..
		"\nStatus: " .. status ..
		"\nLowest: " .. tostring(CS.Runtime.LowPing or "N/A") .. " ms" ..
		"\nAverage: " .. tostring(CS.Runtime.AvgPing or "N/A") .. " ms" ..
		"\nHighest: " .. tostring(CS.Runtime.HighPing or "N/A") .. " ms",
		color
	)

	CS.Util.Set(CS.UI.Labels.FriendsSummary,
		"Friends Online: " .. tostring(friendsOnline) ..
		"\nFriends In Server: " .. tostring(friendsInServer) ..
		"\nPlayers: " .. tostring(#CS.Services.Players:GetPlayers()) .. "/" .. tostring(CS.Services.Players.MaxPlayers)
	)

	CS.Util.Set(CS.UI.Labels.Performance,
		"FPS: " .. tostring(CS.Runtime.FPS) ..
		"\nPing: " .. tostring(ping) .. " ms" ..
		"\nUI Mode: " .. (CS.Flags.Compact and "Compact" or "Full") ..
		"\nAnimations: " .. tostring(CS.Flags.Animations)
	)

	CS.Util.Set(CS.UI.Labels.Status,
		"HTTP: " .. tostring(CS.Request ~= nil) ..
		"\nClipboard: " .. tostring(setclipboard ~= nil) ..
		"\nNotifications: " .. tostring(CS.Flags.Notifications) ..
		"\nTransparency: " .. tostring(CS.Flags.Transparency)
	)

	CS.Util.Set(CS.UI.Labels.FPSPill, "FPS: " .. tostring(CS.Runtime.FPS))
	CS.Util.Set(CS.UI.Labels.PingPill, "Ping: " .. tostring(ping))
	CS.Util.Set(CS.UI.Labels.PlayersPill, "Players: " .. tostring(#CS.Services.Players:GetPlayers()) .. "/" .. tostring(CS.Services.Players.MaxPlayers))

	CS.Dashboard.PaintBars(CS.UI.Charts.Network)
	CS.Dashboard.PaintBars(CS.UI.Charts.Live)
end

function CS.RefreshAll()
	CS.Dashboard.Update()
	CS.PlayerDetails.Refresh()
	CS.PlayerDetails.UpdateSelected()
	CS.ServerBrowser.UpdateLive()
	CS.GameInfo.Load()
	CS.Friends.Update()
	pcall(function()
		CS.Friends.RefreshFriendList()
	end)
	CS.Diagnostics.Run()
end

function CS.Connect()
	CS.UI.Objects.OpenButton.MouseButton1Click:Connect(CS.UI.Toggle)
	CS.UI.Buttons.Close.MouseButton1Click:Connect(CS.UI.Toggle)

	CS.Services.Players.PlayerAdded:Connect(function(player)
		if CS.Flags.Open and CS.Flags.AutoRefreshPlayers then
			CS.PlayerDetails.Refresh()
		end

		local isFriend = false

		pcall(function()
			isFriend = CS.LocalPlayer:IsFriendsWith(player.UserId)
		end)

		if isFriend then
			CS.Friends.PushFeed("Friend joined: " .. player.DisplayName .. " (@" .. player.Name .. ")")
			CS.Notify.PlayerEvent(player, "joined", true, true)
		elseif CS.Flags.ShowNonFriendNotices then
			CS.Friends.PushFeed("Player joined: " .. player.DisplayName .. " (@" .. player.Name .. ")")
			CS.Notify.PlayerEvent(player, "joined", false, true)
		end

		CS.Friends.Update()
	end)

	CS.Services.Players.PlayerRemoving:Connect(function(player)
		if CS.Flags.Open and CS.Flags.AutoRefreshPlayers then
			CS.PlayerDetails.Refresh()
		end

		local isFriend = false

		pcall(function()
			isFriend = CS.LocalPlayer:IsFriendsWith(player.UserId)
		end)

		if isFriend then
			CS.Friends.PushFeed("Friend left: " .. player.DisplayName .. " (@" .. player.Name .. ")")
			CS.Notify.PlayerEvent(player, "left", true, false)
		elseif CS.Flags.ShowNonFriendNotices then
			CS.Friends.PushFeed("Player left: " .. player.DisplayName .. " (@" .. player.Name .. ")")
			CS.Notify.PlayerEvent(player, "left", false, false)
		end

		CS.Friends.Update()
	end)

	CS.LocalPlayer.CharacterAdded:Connect(function()
		task.wait(1)

		if CS.Flags.Open then
			CS.PlayerDetails.UpdateSelected()
			CS.Diagnostics.Run()
		end
	end)

	CS.Services.RunService.RenderStepped:Connect(function()
		CS.Runtime.Frames += 1
		CS.Settings.UpdateColorCycle()
		local now = os.clock()

		if now - CS.Runtime.LastFPS >= 1 then
			CS.Runtime.FPS = CS.Runtime.Frames
			CS.Runtime.Frames = 0
			CS.Runtime.LastFPS = now

			CS.Dashboard.UpdatePingStats()

			if CS.Flags.Open then
				CS.Dashboard.Update()
				CS.ServerBrowser.UpdateLive()
				CS.Friends.Update()

				if CS.Flags.AutoRefreshDiagnostics then
					CS.Diagnostics.Run()
				end
			end
		end

		if CS.Flags.Open then
			CS.PlayerDetails.UpdateSelected()
		end
	end)
end

function CS.Build()
	CS.UI.BuildShell()
	CS.UI.BuildPages()
	CS.Dashboard.Build()
	CS.PlayerDetails.Build()
	CS.ServerBrowser.Build()
	CS.GameInfo.Build()
	CS.Friends.Build()
	CS.Diagnostics.Build()
	CS.Settings.Build()
	CS.Utilities.Build()
end

function CS.Start()
	CS.Settings.Load()
	CS.Build()
	CS.Settings.ApplyVisuals()
	CS.Settings.SetScale(CS.Flags.ScaleMode or "Default")
	CS.Connect()
	CS.Dashboard.UpdatePingStats()
	CS.GameInfo.Load()
	CS.PlayerDetails.Refresh()
	CS.ServerBrowser.Rebuild({})
	CS.Friends.Update()
	pcall(function()
		CS.Friends.RefreshFriendList()
	end)
	CS.Diagnostics.Run()

	CS.Flags.ExampleNotification = false

	CS.ServerBrowser.Switch("Live")
	CS.UI.SwitchPage("Dashboard")
	CS.Notify.Push("CS Loaded", "Central Settings v" .. CS.Version .. " is ready.", CS.Theme.Success)
	print("CS | Central Settings v" .. CS.Version .. " loaded.")
end

CS.Start()

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: