-- Cat Face Customizer Made By GZ

local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")

-- ScreenGui
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = "CatCustomizerUI"
ScreenGui.ResetOnSpawn = false
ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
ScreenGui.Parent = PlayerGui

-- ══════════════════════════════════════════
-- MAIN PANEL
-- ══════════════════════════════════════════
local MainFrame = Instance.new("Frame")
MainFrame.Name = "MainFrame"
MainFrame.Size = UDim2.new(0, 280, 0, 130)
MainFrame.Position = UDim2.new(1, -300, 0.5, -65)
MainFrame.BackgroundColor3 = Color3.fromRGB(18, 18, 24)
MainFrame.BorderSizePixel = 0
MainFrame.ClipsDescendants = true
MainFrame.Parent = ScreenGui

local MainCorner = Instance.new("UICorner")
MainCorner.CornerRadius = UDim.new(0, 16)
MainCorner.Parent = MainFrame

local MainStroke = Instance.new("UIStroke")
MainStroke.Color = Color3.fromRGB(80, 80, 110)
MainStroke.Thickness = 1.2
MainStroke.Parent = MainFrame

local MainGrad = Instance.new("UIGradient")
MainGrad.Color = ColorSequence.new({
	ColorSequenceKeypoint.new(0, Color3.fromRGB(28, 28, 38)),
	ColorSequenceKeypoint.new(1, Color3.fromRGB(14, 14, 20)),
})
MainGrad.Rotation = 135
MainGrad.Parent = MainFrame

local TitleLabel = Instance.new("TextLabel")
TitleLabel.Size = UDim2.new(1, 0, 0, 28)
TitleLabel.Position = UDim2.new(0, 0, 0, 0)
TitleLabel.BackgroundTransparency = 1
TitleLabel.Text = "🐾 Cat Customizer"
TitleLabel.TextColor3 = Color3.fromRGB(220, 210, 255)
TitleLabel.TextSize = 13
TitleLabel.Font = Enum.Font.GothamBold
TitleLabel.TextXAlignment = Enum.TextXAlignment.Center
TitleLabel.Parent = MainFrame

local Divider = Instance.new("Frame")
Divider.Size = UDim2.new(1, -24, 0, 1)
Divider.Position = UDim2.new(0, 12, 0, 28)
Divider.BackgroundColor3 = Color3.fromRGB(70, 70, 100)
Divider.BorderSizePixel = 0
Divider.Parent = MainFrame

-- Preview
local PreviewFrame = Instance.new("Frame")
PreviewFrame.Size = UDim2.new(0, 56, 0, 56)
PreviewFrame.Position = UDim2.new(0, 10, 0, 36)
PreviewFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 45)
PreviewFrame.BorderSizePixel = 0
PreviewFrame.Parent = MainFrame

local PreviewCorner = Instance.new("UICorner")
PreviewCorner.CornerRadius = UDim.new(0, 10)
PreviewCorner.Parent = PreviewFrame

local PreviewStroke = Instance.new("UIStroke")
PreviewStroke.Color = Color3.fromRGB(100, 90, 160)
PreviewStroke.Thickness = 1
PreviewStroke.Parent = PreviewFrame

local PreviewImage = Instance.new("ImageLabel")
PreviewImage.Size = UDim2.new(1, -6, 1, -6)
PreviewImage.Position = UDim2.new(0, 3, 0, 3)
PreviewImage.BackgroundTransparency = 1
PreviewImage.Image = "rbxthumb://type=Asset&id=0&w=150&h=150"
PreviewImage.ScaleType = Enum.ScaleType.Fit
PreviewImage.Parent = PreviewFrame

-- Decal TextBox
local DecalBox = Instance.new("TextBox")
DecalBox.Size = UDim2.new(0, 170, 0, 34)
DecalBox.Position = UDim2.new(0, 74, 0, 38)
DecalBox.BackgroundColor3 = Color3.fromRGB(28, 28, 42)
DecalBox.BorderSizePixel = 0
DecalBox.PlaceholderText = "Enter Decal ID..."
DecalBox.PlaceholderColor3 = Color3.fromRGB(100, 95, 130)
DecalBox.Text = ""
DecalBox.TextColor3 = Color3.fromRGB(230, 225, 255)
DecalBox.TextSize = 12
DecalBox.Font = Enum.Font.GothamMedium
DecalBox.ClearTextOnFocus = false
DecalBox.Parent = MainFrame

local DecalBoxCorner = Instance.new("UICorner")
DecalBoxCorner.CornerRadius = UDim.new(0, 8)
DecalBoxCorner.Parent = DecalBox

local DecalBoxStroke = Instance.new("UIStroke")
DecalBoxStroke.Color = Color3.fromRGB(80, 75, 120)
DecalBoxStroke.Thickness = 1
DecalBoxStroke.Parent = DecalBox

local DecalPadding = Instance.new("UIPadding")
DecalPadding.PaddingLeft = UDim.new(0, 8)
DecalPadding.Parent = DecalBox

-- APPLY Button
local ApplyBtn = Instance.new("TextButton")
ApplyBtn.Size = UDim2.new(0, 170, 0, 28)
ApplyBtn.Position = UDim2.new(0, 74, 0, 76)
ApplyBtn.BackgroundColor3 = Color3.fromRGB(52, 199, 89)
ApplyBtn.BorderSizePixel = 0
ApplyBtn.Text = "APPLY"
ApplyBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
ApplyBtn.TextSize = 12
ApplyBtn.Font = Enum.Font.GothamBold
ApplyBtn.AutoButtonColor = false
ApplyBtn.Parent = MainFrame

local ApplyCorner = Instance.new("UICorner")
ApplyCorner.CornerRadius = UDim.new(0, 8)
ApplyCorner.Parent = ApplyBtn

-- Decal Store Button
local StoreBtn = Instance.new("TextButton")
StoreBtn.Size = UDim2.new(0, 170, 0, 22)
StoreBtn.Position = UDim2.new(0, 74, 0, 108)
StoreBtn.BackgroundColor3 = Color3.fromRGB(45, 45, 70)
StoreBtn.BorderSizePixel = 0
StoreBtn.Text = "πŸ–Ό  Decal Store"
StoreBtn.TextColor3 = Color3.fromRGB(180, 170, 230)
StoreBtn.TextSize = 11
StoreBtn.Font = Enum.Font.GothamMedium
StoreBtn.AutoButtonColor = false
StoreBtn.Parent = MainFrame

local StoreBtnCorner = Instance.new("UICorner")
StoreBtnCorner.CornerRadius = UDim.new(0, 7)
StoreBtnCorner.Parent = StoreBtn

local StoreBtnStroke = Instance.new("UIStroke")
StoreBtnStroke.Color = Color3.fromRGB(90, 85, 140)
StoreBtnStroke.Thickness = 1
StoreBtnStroke.Parent = StoreBtn

-- ══════════════════════════════════════════
-- DECAL STORE PANEL
-- ══════════════════════════════════════════
local StoreFrame = Instance.new("Frame")
StoreFrame.Size = UDim2.new(0, 280, 0, 0)
StoreFrame.Position = UDim2.new(1, -300, 0.5, 70)
StoreFrame.BackgroundColor3 = Color3.fromRGB(15, 15, 22)
StoreFrame.BorderSizePixel = 0
StoreFrame.ClipsDescendants = true
StoreFrame.Visible = false
StoreFrame.Parent = ScreenGui

local StoreCorner = Instance.new("UICorner")
StoreCorner.CornerRadius = UDim.new(0, 16)
StoreCorner.Parent = StoreFrame

local StoreStroke = Instance.new("UIStroke")
StoreStroke.Color = Color3.fromRGB(80, 80, 110)
StoreStroke.Thickness = 1.2
StoreStroke.Parent = StoreFrame

local StoreHeader = Instance.new("Frame")
StoreHeader.Size = UDim2.new(1, 0, 0, 48)
StoreHeader.BackgroundTransparency = 1
StoreHeader.Parent = StoreFrame

local StoreTitle = Instance.new("TextLabel")
StoreTitle.Size = UDim2.new(1, -80, 1, 0)
StoreTitle.Position = UDim2.new(0, 12, 0, 0)
StoreTitle.BackgroundTransparency = 1
StoreTitle.Text = "πŸ–Ό  Decal Store"
StoreTitle.TextColor3 = Color3.fromRGB(220, 210, 255)
StoreTitle.TextSize = 13
StoreTitle.Font = Enum.Font.GothamBold
StoreTitle.TextXAlignment = Enum.TextXAlignment.Left
StoreTitle.Parent = StoreHeader

local PageLabel = Instance.new("TextLabel")
PageLabel.Size = UDim2.new(0, 60, 0, 20)
PageLabel.Position = UDim2.new(1, -70, 0.5, -10)
PageLabel.BackgroundTransparency = 1
PageLabel.Text = "Page 1"
PageLabel.TextColor3 = Color3.fromRGB(130, 120, 180)
PageLabel.TextSize = 11
PageLabel.Font = Enum.Font.Gotham
PageLabel.Parent = StoreHeader

local CloseStoreBtn = Instance.new("TextButton")
CloseStoreBtn.Size = UDim2.new(0, 22, 0, 22)
CloseStoreBtn.Position = UDim2.new(1, -32, 0.5, -11)
CloseStoreBtn.BackgroundColor3 = Color3.fromRGB(60, 40, 60)
CloseStoreBtn.BorderSizePixel = 0
CloseStoreBtn.Text = "βœ•"
CloseStoreBtn.TextColor3 = Color3.fromRGB(200, 180, 220)
CloseStoreBtn.TextSize = 11
CloseStoreBtn.Font = Enum.Font.GothamBold
CloseStoreBtn.AutoButtonColor = false
CloseStoreBtn.Parent = StoreHeader

local CloseStoreBtnCorner = Instance.new("UICorner")
CloseStoreBtnCorner.CornerRadius = UDim.new(0, 6)
CloseStoreBtnCorner.Parent = CloseStoreBtn

local StoreDivider = Instance.new("Frame")
StoreDivider.Size = UDim2.new(1, -24, 0, 1)
StoreDivider.Position = UDim2.new(0, 12, 0, 48)
StoreDivider.BackgroundColor3 = Color3.fromRGB(55, 55, 85)
StoreDivider.BorderSizePixel = 0
StoreDivider.Parent = StoreFrame

-- Search row
local SearchRow = Instance.new("Frame")
SearchRow.Size = UDim2.new(1, -20, 0, 30)
SearchRow.Position = UDim2.new(0, 10, 0, 56)
SearchRow.BackgroundTransparency = 1
SearchRow.Parent = StoreFrame

local SearchBox = Instance.new("TextBox")
SearchBox.Size = UDim2.new(0, 120, 1, 0)
SearchBox.BackgroundColor3 = Color3.fromRGB(28, 28, 42)
SearchBox.BorderSizePixel = 0
SearchBox.PlaceholderText = "Search decals..."
SearchBox.PlaceholderColor3 = Color3.fromRGB(100, 95, 130)
SearchBox.Text = ""
SearchBox.TextColor3 = Color3.fromRGB(230, 225, 255)
SearchBox.TextSize = 11
SearchBox.Font = Enum.Font.GothamMedium
SearchBox.ClearTextOnFocus = false
SearchBox.Parent = SearchRow

local SearchBoxCorner = Instance.new("UICorner")
SearchBoxCorner.CornerRadius = UDim.new(0, 7)
SearchBoxCorner.Parent = SearchBox

local SearchBoxStroke = Instance.new("UIStroke")
SearchBoxStroke.Color = Color3.fromRGB(70, 65, 110)
SearchBoxStroke.Thickness = 1
SearchBoxStroke.Parent = SearchBox

local SearchPadding = Instance.new("UIPadding")
SearchPadding.PaddingLeft = UDim.new(0, 7)
SearchPadding.Parent = SearchBox

local LimitLabel = Instance.new("TextLabel")
LimitLabel.Size = UDim2.new(0, 28, 1, 0)
LimitLabel.Position = UDim2.new(0, 128, 0, 0)
LimitLabel.BackgroundTransparency = 1
LimitLabel.Text = "Limit"
LimitLabel.TextColor3 = Color3.fromRGB(120, 110, 160)
LimitLabel.TextSize = 10
LimitLabel.Font = Enum.Font.Gotham
LimitLabel.Parent = SearchRow

local LimitBox = Instance.new("TextBox")
LimitBox.Size = UDim2.new(0, 34, 1, 0)
LimitBox.Position = UDim2.new(0, 156, 0, 0)
LimitBox.BackgroundColor3 = Color3.fromRGB(28, 28, 42)
LimitBox.BorderSizePixel = 0
LimitBox.Text = "50"
LimitBox.TextColor3 = Color3.fromRGB(230, 225, 255)
LimitBox.TextSize = 11
LimitBox.Font = Enum.Font.GothamMedium
LimitBox.ClearTextOnFocus = false
LimitBox.Parent = SearchRow

local LimitBoxCorner = Instance.new("UICorner")
LimitBoxCorner.CornerRadius = UDim.new(0, 7)
LimitBoxCorner.Parent = LimitBox

local LimitBoxStroke = Instance.new("UIStroke")
LimitBoxStroke.Color = Color3.fromRGB(70, 65, 110)
LimitBoxStroke.Thickness = 1
LimitBoxStroke.Parent = LimitBox

local LimitPad = Instance.new("UIPadding")
LimitPad.PaddingLeft = UDim.new(0, 5)
LimitPad.Parent = LimitBox

-- Verified checkbox
local VerifiedRow = Instance.new("Frame")
VerifiedRow.Size = UDim2.new(0, 80, 1, 0)
VerifiedRow.Position = UDim2.new(0, 196, 0, 0)
VerifiedRow.BackgroundTransparency = 1
VerifiedRow.Parent = SearchRow

local VerifiedCheck = Instance.new("TextButton")
VerifiedCheck.Size = UDim2.new(0, 16, 0, 16)
VerifiedCheck.Position = UDim2.new(0, 0, 0.5, -8)
VerifiedCheck.BackgroundColor3 = Color3.fromRGB(28, 28, 42)
VerifiedCheck.BorderSizePixel = 0
VerifiedCheck.Text = ""
VerifiedCheck.AutoButtonColor = false
VerifiedCheck.Parent = VerifiedRow

local VCCorner = Instance.new("UICorner")
VCCorner.CornerRadius = UDim.new(0, 4)
VCCorner.Parent = VerifiedCheck

local VCStroke = Instance.new("UIStroke")
VCStroke.Color = Color3.fromRGB(80, 75, 130)
VCStroke.Thickness = 1
VCStroke.Parent = VerifiedCheck

local CheckMark = Instance.new("TextLabel")
CheckMark.Size = UDim2.new(1, 0, 1, 0)
CheckMark.BackgroundTransparency = 1
CheckMark.Text = ""
CheckMark.TextColor3 = Color3.fromRGB(80, 220, 120)
CheckMark.TextSize = 11
CheckMark.Font = Enum.Font.GothamBold
CheckMark.Parent = VerifiedCheck

local VerifiedLabel = Instance.new("TextLabel")
VerifiedLabel.Size = UDim2.new(1, -20, 1, 0)
VerifiedLabel.Position = UDim2.new(0, 20, 0, 0)
VerifiedLabel.BackgroundTransparency = 1
VerifiedLabel.Text = "Verified"
VerifiedLabel.TextColor3 = Color3.fromRGB(140, 130, 185)
VerifiedLabel.TextSize = 10
VerifiedLabel.Font = Enum.Font.Gotham
VerifiedLabel.TextXAlignment = Enum.TextXAlignment.Left
VerifiedLabel.Parent = VerifiedRow

-- Search button
local SearchBtn = Instance.new("TextButton")
SearchBtn.Size = UDim2.new(1, -20, 0, 26)
SearchBtn.Position = UDim2.new(0, 10, 0, 94)
SearchBtn.BackgroundColor3 = Color3.fromRGB(80, 70, 150)
SearchBtn.BorderSizePixel = 0
SearchBtn.Text = "πŸ”  Search"
SearchBtn.TextColor3 = Color3.fromRGB(230, 225, 255)
SearchBtn.TextSize = 11
SearchBtn.Font = Enum.Font.GothamBold
SearchBtn.AutoButtonColor = false
SearchBtn.Parent = StoreFrame

local SearchBtnCorner = Instance.new("UICorner")
SearchBtnCorner.CornerRadius = UDim.new(0, 8)
SearchBtnCorner.Parent = SearchBtn

-- Pagination
local PaginationRow = Instance.new("Frame")
PaginationRow.Size = UDim2.new(1, -20, 0, 26)
PaginationRow.Position = UDim2.new(0, 10, 0, 126)
PaginationRow.BackgroundTransparency = 1
PaginationRow.Parent = StoreFrame

local PrevBtn = Instance.new("TextButton")
PrevBtn.Size = UDim2.new(0, 70, 1, 0)
PrevBtn.BackgroundColor3 = Color3.fromRGB(40, 40, 62)
PrevBtn.BorderSizePixel = 0
PrevBtn.Text = "β—€ Prev"
PrevBtn.TextColor3 = Color3.fromRGB(180, 170, 230)
PrevBtn.TextSize = 11
PrevBtn.Font = Enum.Font.GothamMedium
PrevBtn.AutoButtonColor = false
PrevBtn.Parent = PaginationRow

local PrevCorner = Instance.new("UICorner")
PrevCorner.CornerRadius = UDim.new(0, 7)
PrevCorner.Parent = PrevBtn

local NextBtn = Instance.new("TextButton")
NextBtn.Size = UDim2.new(0, 70, 1, 0)
NextBtn.Position = UDim2.new(1, -70, 0, 0)
NextBtn.BackgroundColor3 = Color3.fromRGB(40, 40, 62)
NextBtn.BorderSizePixel = 0
NextBtn.Text = "Next β–Ά"
NextBtn.TextColor3 = Color3.fromRGB(180, 170, 230)
NextBtn.TextSize = 11
NextBtn.Font = Enum.Font.GothamMedium
NextBtn.AutoButtonColor = false
NextBtn.Parent = PaginationRow

local NextCorner = Instance.new("UICorner")
NextCorner.CornerRadius = UDim.new(0, 7)
NextCorner.Parent = NextBtn

local TotalLabel = Instance.new("TextLabel")
TotalLabel.Size = UDim2.new(1, -160, 1, 0)
TotalLabel.Position = UDim2.new(0, 80, 0, 0)
TotalLabel.BackgroundTransparency = 1
TotalLabel.Text = ""
TotalLabel.TextColor3 = Color3.fromRGB(120, 110, 160)
TotalLabel.TextSize = 10
TotalLabel.Font = Enum.Font.Gotham
TotalLabel.Parent = PaginationRow

-- Scroll frame
local ScrollFrame = Instance.new("ScrollingFrame")
ScrollFrame.Size = UDim2.new(1, -16, 0, 320)
ScrollFrame.Position = UDim2.new(0, 8, 0, 158)
ScrollFrame.BackgroundTransparency = 1
ScrollFrame.BorderSizePixel = 0
ScrollFrame.ScrollBarThickness = 4
ScrollFrame.ScrollBarImageColor3 = Color3.fromRGB(90, 80, 140)
ScrollFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
ScrollFrame.ClipsDescendants = true
ScrollFrame.Parent = StoreFrame

local GridLayout = Instance.new("UIGridLayout")
GridLayout.CellSize = UDim2.new(0, 76, 0, 90)
GridLayout.CellPadding = UDim2.new(0, 6, 0, 6)
GridLayout.SortOrder = Enum.SortOrder.LayoutOrder
GridLayout.Parent = ScrollFrame

local GridPad = Instance.new("UIPadding")
GridPad.PaddingTop = UDim.new(0, 4)
GridPad.PaddingLeft = UDim.new(0, 4)
GridPad.Parent = ScrollFrame

local StatusLabel = Instance.new("TextLabel")
StatusLabel.Size = UDim2.new(1, 0, 0, 30)
StatusLabel.Position = UDim2.new(0, 0, 0, 158)
StatusLabel.BackgroundTransparency = 1
StatusLabel.Text = ""
StatusLabel.TextColor3 = Color3.fromRGB(160, 150, 200)
StatusLabel.TextSize = 11
StatusLabel.Font = Enum.Font.Gotham
StatusLabel.Visible = false
StatusLabel.Parent = StoreFrame

-- ══════════════════════════════════════════
-- DRAGGING
-- ══════════════════════════════════════════
local function makeDraggable(frame)
	local dragging, dragInput, dragStart, startPos = false, nil, nil, nil
	frame.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
			dragging = true
			dragStart = input.Position
			startPos = frame.Position
			input.Changed:Connect(function()
				if input.UserInputState == Enum.UserInputState.End then dragging = false end
			end)
		end
	end)
	frame.InputChanged:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
			dragInput = input
		end
	end)
	UserInputService.InputChanged:Connect(function(input)
		if input == dragInput and dragging then
			local delta = input.Position - dragStart
			local newPos = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
			TweenService:Create(frame, TweenInfo.new(0.07, Enum.EasingStyle.Linear), {Position = newPos}):Play()
			if frame == MainFrame and StoreFrame.Visible then
				StoreFrame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y + MainFrame.AbsoluteSize.Y + 6)
			end
		end
	end)
end

makeDraggable(MainFrame)

-- ══════════════════════════════════════════
-- APPLY BUTTON ANIMATION
-- ══════════════════════════════════════════
local applyNormalColor = Color3.fromRGB(52, 199, 89)
local applyPressColor  = Color3.fromRGB(30, 130, 55)
local applyNormalSize  = UDim2.new(0, 170, 0, 28)
local applyPressSize   = UDim2.new(0, 162, 0, 25)
local applyNormalPos   = UDim2.new(0, 74, 0, 76)
local applyPressPos    = UDim2.new(0, 78, 0, 77.5)

local function applyPress()
	TweenService:Create(ApplyBtn, TweenInfo.new(0.1, Enum.EasingStyle.Quad), {BackgroundColor3 = applyPressColor, Size = applyPressSize, Position = applyPressPos}):Play()
end
local function applyRelease()
	TweenService:Create(ApplyBtn, TweenInfo.new(0.15, Enum.EasingStyle.Back, Enum.EasingDirection.Out), {BackgroundColor3 = applyNormalColor, Size = applyNormalSize, Position = applyNormalPos}):Play()
end

ApplyBtn.MouseButton1Down:Connect(applyPress)
ApplyBtn.MouseButton1Up:Connect(applyRelease)
ApplyBtn.InputBegan:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch then applyPress() end end)
ApplyBtn.InputEnded:Connect(function(i) if i.UserInputType == Enum.UserInputType.Touch then applyRelease() end end)

-- ══════════════════════════════════════════
-- PREVIEW UPDATE
-- ══════════════════════════════════════════
local previewDebounce = nil
local function updatePreview(id)
	local cleaned = tostring(id):match("^%s*(.-)%s*$")
	if cleaned ~= "" and tonumber(cleaned) then
		PreviewImage.Image = "rbxthumb://type=Asset&id=" .. cleaned .. "&w=150&h=150"
	else
		PreviewImage.Image = "rbxthumb://type=Asset&id=0&w=150&h=150"
	end
end

DecalBox:GetPropertyChangedSignal("Text"):Connect(function()
	if previewDebounce then task.cancel(previewDebounce) end
	previewDebounce = task.delay(0.3, function()
		updatePreview(DecalBox.Text)
		previewDebounce = nil
	end)
end)

-- ══════════════════════════════════════════
-- GET CAT COLOR REALTIME ("RGB Format")
-- ══════════════════════════════════════════
local function getCatColor()
	-- CatColor.Value is a string like "255, 255, 255"
	local raw = LocalPlayer.Data.CatColor.Value
	local r, g, b = tostring(raw):match("^%s*(%d+)%s*,%s*(%d+)%s*,%s*(%d+)%s*$")
	if r and g and b then
		return Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))
	end
	-- Fallback: if it's already a Color3 stored as a Color3Value
	if typeof(raw) == "Color3" then
		return raw
	end
	return Color3.fromRGB(255, 255, 255)
end

-- ══════════════════════════════════════════
-- APPLY FIRE
-- ══════════════════════════════════════════
ApplyBtn.MouseButton1Click:Connect(function()
	local decalId = DecalBox.Text:match("^%s*(.-)%s*$")
	if decalId == "" or not tonumber(decalId) then return end

	local catColor = getCatColor()
	local hatName = LocalPlayer.Data.HatName.Value
	local textureUrl = "rbxthumb://type=Asset&id=" .. decalId .. "&w=150&h=150"

	ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("CatCustomizerRemote"):FireServer(catColor, hatName, textureUrl)
end)

-- ══════════════════════════════════════════
-- DECAL STORE
-- ══════════════════════════════════════════
local storeOpen      = false
local verifiedOnly   = false
local currentPage    = 0
local currentKeyword = ""
local currentLimit   = 50
local STORE_HEIGHT   = 490
local API_BASE       = "https://open-source-ten-eta.vercel.app/api/decal.js"

local function syncStorePos()
	StoreFrame.Position = UDim2.new(
		MainFrame.Position.X.Scale, MainFrame.Position.X.Offset,
		MainFrame.Position.Y.Scale, MainFrame.Position.Y.Offset + MainFrame.AbsoluteSize.Y + 6
	)
end

local function tweenStoreOpen(open)
	if open then
		syncStorePos()
		StoreFrame.Visible = true
		TweenService:Create(StoreFrame, TweenInfo.new(0.28, Enum.EasingStyle.Quint, Enum.EasingDirection.Out), {Size = UDim2.new(0, 280, 0, STORE_HEIGHT)}):Play()
	else
		TweenService:Create(StoreFrame, TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {Size = UDim2.new(0, 280, 0, 0)}):Play()
		task.delay(0.21, function() StoreFrame.Visible = false end)
	end
end

VerifiedCheck.MouseButton1Click:Connect(function()
	verifiedOnly = not verifiedOnly
	CheckMark.Text = verifiedOnly and "βœ“" or ""
	VCStroke.Color = verifiedOnly and Color3.fromRGB(80, 220, 120) or Color3.fromRGB(80, 75, 130)
end)

local function clearGrid()
	for _, c in ipairs(ScrollFrame:GetChildren()) do
		if c:IsA("Frame") or c:IsA("TextButton") then c:Destroy() end
	end
end

local function createCard(decalId, order)
	local card = Instance.new("TextButton")
	card.Size = UDim2.new(0, 76, 0, 90)
	card.BackgroundColor3 = Color3.fromRGB(28, 26, 42)
	card.BorderSizePixel = 0
	card.Text = ""
	card.AutoButtonColor = false
	card.LayoutOrder = order
	card.Parent = ScrollFrame

	local cc = Instance.new("UICorner"); cc.CornerRadius = UDim.new(0, 8); cc.Parent = card
	local cs = Instance.new("UIStroke"); cs.Color = Color3.fromRGB(60, 55, 95); cs.Thickness = 1; cs.Parent = card

	local img = Instance.new("ImageLabel")
	img.Size = UDim2.new(1, -8, 0, 58); img.Position = UDim2.new(0, 4, 0, 4)
	img.BackgroundColor3 = Color3.fromRGB(20, 18, 32)
	img.Image = "rbxthumb://type=Asset&id=" .. decalId .. "&w=150&h=150"
	img.ScaleType = Enum.ScaleType.Fit; img.BorderSizePixel = 0; img.Parent = card
	local ic = Instance.new("UICorner"); ic.CornerRadius = UDim.new(0, 6); ic.Parent = img

	local lbl = Instance.new("TextLabel")
	lbl.Size = UDim2.new(1, -4, 0, 26); lbl.Position = UDim2.new(0, 2, 0, 63)
	lbl.BackgroundTransparency = 1; lbl.Text = decalId
	lbl.TextColor3 = Color3.fromRGB(180, 170, 220); lbl.TextSize = 9
	lbl.Font = Enum.Font.Gotham; lbl.TextWrapped = true; lbl.Parent = card

	card.MouseEnter:Connect(function()
		TweenService:Create(card, TweenInfo.new(0.1), {BackgroundColor3 = Color3.fromRGB(50, 45, 75)}):Play()
		TweenService:Create(cs, TweenInfo.new(0.1), {Color = Color3.fromRGB(120, 100, 200)}):Play()
	end)
	card.MouseLeave:Connect(function()
		TweenService:Create(card, TweenInfo.new(0.1), {BackgroundColor3 = Color3.fromRGB(28, 26, 42)}):Play()
		TweenService:Create(cs, TweenInfo.new(0.1), {Color = Color3.fromRGB(60, 55, 95)}):Play()
	end)
	card.MouseButton1Click:Connect(function()
		DecalBox.Text = decalId
		updatePreview(decalId)
	end)
end

local function fetchDecals(keyword, limit, pageNum, verified)
	StatusLabel.Text = "⏳ Loading..."
	StatusLabel.Visible = true
	ScrollFrame.Visible = false
	clearGrid()

	local url = API_BASE
		.. "?limit=" .. tostring(limit)
		.. "&pageNumber=" .. tostring(pageNum)
		.. "&keyword=" .. HttpService:UrlEncode(keyword)
		.. "&includeOnlyVerifiedCreators=" .. tostring(verified)

	local ok, res = pcall(function()
		return HttpService:RequestAsync({ Url = url, Method = "GET" })
	end)

	if not ok or not res or not res.Success then
		StatusLabel.Text = "❌ Fetch failed. Enable HTTP in executor."
		return
	end

	local ps, data = pcall(HttpService.JSONDecode, HttpService, res.Body)
	if not ps or not data then
		StatusLabel.Text = "❌ Parse error."
		return
	end

	local items = data.data
	if not items or #items == 0 then
		StatusLabel.Text = "No results found."
		return
	end

	StatusLabel.Visible = false
	ScrollFrame.Visible = true
	TotalLabel.Text = data.totalResults and (data.totalResults .. " total") or ""
	PageLabel.Text = "Page " .. (pageNum + 1)

	for i, item in ipairs(items) do
		createCard(tostring(item.id), i)
	end

	task.wait()
	local rows = math.ceil(#items / 3)
	ScrollFrame.CanvasSize = UDim2.new(0, 0, 0, rows * 96 + 10)
	ScrollFrame.CanvasPosition = Vector2.new(0, 0)
end

local function doSearch()
	currentKeyword = SearchBox.Text:match("^%s*(.-)%s*$")
	currentLimit = math.clamp(tonumber(LimitBox.Text) or 50, 1, 100)
	currentPage = 0
	fetchDecals(currentKeyword, currentLimit, currentPage, verifiedOnly)
end

SearchBtn.MouseButton1Click:Connect(doSearch)
SearchBox.FocusLost:Connect(function(enter) if enter then doSearch() end end)

PrevBtn.MouseButton1Click:Connect(function()
	if currentPage > 0 then
		currentPage -= 1
		fetchDecals(currentKeyword, currentLimit, currentPage, verifiedOnly)
	end
end)

NextBtn.MouseButton1Click:Connect(function()
	currentPage += 1
	fetchDecals(currentKeyword, currentLimit, currentPage, verifiedOnly)
end)

StoreBtn.MouseButton1Click:Connect(function()
	storeOpen = not storeOpen
	tweenStoreOpen(storeOpen)
	if storeOpen and StatusLabel.Text == "" then
		fetchDecals("", 50, 0, false)
	end
end)

CloseStoreBtn.MouseButton1Click:Connect(function()
	storeOpen = false
	tweenStoreOpen(false)
end)

-- Hover effects
StoreBtn.MouseEnter:Connect(function() TweenService:Create(StoreBtn, TweenInfo.new(0.12), {BackgroundColor3 = Color3.fromRGB(60, 60, 100)}):Play() end)
StoreBtn.MouseLeave:Connect(function() TweenService:Create(StoreBtn, TweenInfo.new(0.12), {BackgroundColor3 = Color3.fromRGB(45, 45, 70)}):Play() end)
SearchBtn.MouseEnter:Connect(function() TweenService:Create(SearchBtn, TweenInfo.new(0.12), {BackgroundColor3 = Color3.fromRGB(100, 90, 180)}):Play() end)
SearchBtn.MouseLeave:Connect(function() TweenService:Create(SearchBtn, TweenInfo.new(0.12), {BackgroundColor3 = Color3.fromRGB(80, 70, 150)}):Play() end)
The Cat Shelter 🐱 Advanced Decal & Skin System UI (Open Source)
Mobile Friendly
2,197 views
4 months ago

Advanced Decal & Skin System UI (Open Source)

WORKING
Free

Description

A powerful and fully animated customization UI designed for The Cat Shelter 🐱.

This script allows players to customize their cat’s appearance using decals, live preview system, and an integrated decal marketplace. Includes smooth animations, pagination system, verified filter, and real-time preview updates.

Built with a clean modern UI design and optimized for performance.

Features

  • Customization System
  • Decal Store
  • UI / UX
  • Technical Features
  • Config System

Related Scripts