diff --git a/.editorconfig b/.editorconfig
index fafdfed64..3c28c76bd 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,3 +16,9 @@ indent_style = tab
 indent_size = 4
 trim_trailing_whitespace = true
 insert_final_newline = true
+
+[*.yml]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
new file mode 100644
index 000000000..7f75f399f
--- /dev/null
+++ b/.github/workflows/package.yml
@@ -0,0 +1,170 @@
+name: Package
+
+on: push
+
+jobs:
+  Build:
+    runs-on: windows-2019
+
+    strategy:
+      fail-fast: false
+      matrix:
+        build_type: [Debug, Release]
+        library_linkage: [Dynamic, Static]
+        architecture: [win32, win64]
+
+    env:
+      GENERATOR: Visual Studio 16 2019
+      VCPKG_DEFAULT_TRIPLET: ${{ matrix.architecture == 'win32' && 'x86' || 'x64' }}-${{ matrix.library_linkage == 'Dynamic' && 'windows' || 'windows-static-md' }}
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Install Dependencies
+        run: |-
+          echo "Using vcpkg triplet: $env:VCPKG_DEFAULT_TRIPLET"
+          C:\vcpkg\vcpkg install freetype[core]
+
+      - name: Configure CMake
+        run: >-
+          cmake -B Build
+          -G "$env:GENERATOR"
+          -A ${{ matrix.architecture == 'win32' && 'Win32' || 'x64'  }}
+          -DRMLUI_WARNINGS_AS_ERRORS=ON
+          -DBUILD_SHARED_LIBS=${{ matrix.library_linkage == 'Dynamic' && 'ON' || 'OFF' }}
+          -DVCPKG_TARGET_TRIPLET="$env:VCPKG_DEFAULT_TRIPLET"
+          -DCMAKE_TOOLCHAIN_FILE="C:/vcpkg/scripts/buildsystems/vcpkg.cmake"
+          -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/Install"
+          -DCMAKE_INSTALL_BINDIR=Bin-${{matrix.library_linkage}}/${{matrix.build_type}}
+          -DCMAKE_INSTALL_LIBDIR=Bin-${{matrix.library_linkage}}/${{matrix.build_type}}
+          -DCMAKE_INSTALL_INCLUDEDIR=Include
+          -DRMLUI_INSTALL_TARGETS_DIR=Bin-${{matrix.library_linkage}}/CMake
+
+      - name: Build
+        run: cmake --build Build --config ${{ matrix.build_type }}
+
+      - name: Install
+        run: cmake --install Build --config ${{ matrix.build_type }}
+
+      - name: Upload artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: build-${{ matrix.architecture }}-${{ matrix.build_type }}-${{ matrix.library_linkage }}
+          path: ${{github.workspace}}/Install/
+          if-no-files-found: error
+
+  Samples:
+    runs-on: windows-2019
+
+    strategy:
+      fail-fast: false
+      matrix:
+        architecture: [win32, win64]
+
+    env:
+      GENERATOR: Visual Studio 16 2019
+      VCPKG_DEFAULT_TRIPLET: ${{ matrix.architecture == 'win32' && 'x86' || 'x64' }}-windows-release
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Install Dependencies
+        run: |-
+          echo "Using vcpkg triplet: $env:VCPKG_DEFAULT_TRIPLET"
+          C:\vcpkg\vcpkg install freetype[core] glfw3[core] lua[core] lunasvg[core]
+          cd Dependencies
+          git clone --depth 1 --branch v0.2 https://github.com/Samsung/rlottie.git
+          cd rlottie
+          cmake -B build -G "$env:GENERATOR" -A ${{ matrix.architecture == 'win32' && 'Win32' || 'x64' }} -DBUILD_SHARED_LIBS=ON -DLOTTIE_MODULE=OFF
+          cmake --build build --target rlottie --config Release -- "/clp:ErrorsOnly"
+
+      - name: Configure CMake
+        run: >-
+          cmake -B Build
+          -G "$env:GENERATOR"
+          -A ${{ matrix.architecture == 'win32' && 'Win32' || 'x64' }}
+          --preset samples-all
+          -DRMLUI_BACKEND=GLFW_GL3
+          -DBUILD_SHARED_LIBS=ON
+          -DRMLUI_WARNINGS_AS_ERRORS=ON
+          -DVCPKG_TARGET_TRIPLET="$env:VCPKG_DEFAULT_TRIPLET"
+          -DCMAKE_TOOLCHAIN_FILE="C:/vcpkg/scripts/buildsystems/vcpkg.cmake"
+          -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/Install"
+
+      - name: Build
+        working-directory: ${{github.workspace}}/Build
+        run: cmake --build . --config Release
+
+      - name: Install
+        working-directory: ${{github.workspace}}/Build
+        run: cmake --install . --config Release
+
+      - name: Copy licenses
+        run: |-
+          cp Include/RmlUi/Core/Containers/LICENSE.txt Install/LICENSE.Core.ThirdParty.txt
+          cp Source/Debugger/LICENSE.txt Install/LICENSE.Debugger.ThirdParty.txt
+          echo @'
+          The rlottie library includes source code licensed under Mozilla Public License Version 2.0.
+          The source for this code can be found in the rlottie library at the following URL:
+          https://github.com/Samsung/rlottie/blob/29b391b95913877b7234543da8b4a9ec6d8175d0/src/vector/vinterpolator.cpp
+          '@ > "Install/share/LICENSE.Lottie.ThirdParty.txt"
+
+      - name: Write readme
+        run: |-
+          $rmlui_version=""
+          if ("$env:GITHUB_REF" -like "refs/tags/*") {
+            $rmlui_version=" $env:GITHUB_REF" -replace "refs/tags/", ""
+          }
+          $date=git show $env:GITHUB_SHA --no-patch --format=%cd --date=iso
+
+          echo @"
+          RmlUi$rmlui_version library and sample binaries for ${{ matrix.architecture }}.
+
+          https://github.com/mikke89/RmlUi
+
+          Built using $env:GENERATOR on $date (run $env:GITHUB_RUN_ID).
+          Commit id: $env:GITHUB_SHA
+          "@ > "Install/Build.txt"
+
+      - uses: actions/upload-artifact@v4
+        with:
+          name: samples-${{ matrix.architecture }}
+          path: ${{github.workspace}}/Install/
+          if-no-files-found: error
+
+  Package:
+    needs: [Build, Samples]
+    runs-on: windows-2019
+
+    strategy:
+      fail-fast: true
+      matrix:
+        architecture: [win32, win64]
+
+    steps:
+      - uses: actions/download-artifact@v4
+        with:
+          pattern: build-${{ matrix.architecture }}-*
+          merge-multiple: false
+
+      - uses: actions/download-artifact@v4
+        with:
+          name: samples-${{ matrix.architecture }}
+          path: Samples
+
+      - name: List artifact contents
+        run: |
+          Get-ChildItem -Recurse | ForEach-Object { $_.FullName }
+
+      - name: Zip artifacts
+        run: |
+          7z a RmlUi-vs2019-${{ matrix.architecture }}.zip ./
+
+      - name: Artifact SHA256 sum
+        run: |
+          cmake -E sha256sum RmlUi-vs2019-${{ matrix.architecture }}.zip
+
+      - uses: actions/upload-artifact@v4
+        with:
+          name: RmlUi-vs2019-${{ matrix.architecture }}
+          path: RmlUi-vs2019-${{ matrix.architecture }}.zip
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2ffb7fea2..8a59d5bc1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,11 +102,17 @@ else()
 	endif()
 endif()
 
+set(RMLUI_INSTALL_TARGETS_DIR "" CACHE STRING "Override the install directory for the generated CMake targets.")
+mark_as_advanced(RMLUI_INSTALL_TARGETS_DIR)
+
 # Add custom CMake modules path for external dependencies
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake/Modules")
 list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Dependencies")
 
 include(GNUInstallDirs)
+if(NOT RMLUI_INSTALL_TARGETS_DIR)
+	set(RMLUI_INSTALL_TARGETS_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/RmlUi")
+endif()
 
 include("CMake/Dependencies.cmake")
 include("CMake/TargetOptions.cmake")
@@ -146,7 +152,7 @@ set_target_properties(rmlui PROPERTIES EXPORT_NAME "RmlUi")
 install(TARGETS rmlui EXPORT RmlUiTargets)
 
 install(EXPORT RmlUiTargets
-	DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/RmlUi"
+	DESTINATION "${RMLUI_INSTALL_TARGETS_DIR}"
 	NAMESPACE RmlUi::
 	FILE RmlUiTargets.cmake
 )
@@ -169,15 +175,15 @@ install(FILES
 	"${CMAKE_CURRENT_BINARY_DIR}/install/RmlUiConfigVersion.cmake"
 	"${CMAKE_CURRENT_SOURCE_DIR}/CMake/Dependencies.cmake"
 	DESTINATION
-	"${CMAKE_INSTALL_LIBDIR}/cmake/RmlUi"
+	"${RMLUI_INSTALL_TARGETS_DIR}"
 )
 
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/CMake/Modules"
-	DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/RmlUi"
+	DESTINATION "${RMLUI_INSTALL_TARGETS_DIR}"
 )
 
 if(RMLUI_IS_ROOT_PROJECT)
-	# Export build targets, except if RmlUi is included from a parent project using `add_subdirectory`.
+	# Export build targets if RmlUi is the top-level project.
 	export(EXPORT RmlUiTargets
 		NAMESPACE RmlUi::
 		FILE RmlUiTargets.cmake
diff --git a/CMakePresets.json b/CMakePresets.json
index 6584681e6..11ecd2466 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -1,16 +1,6 @@
 {
 	"version": 3,
 	"configurePresets": [
-		{
-			"name": "cmake-warnings",
-			"hidden": true,
-			"warnings": {
-				"dev": true
-			},
-			"errors": {
-				"dev": true
-			}
-		},
 		{
 			"name": "samples",
 			"cacheVariables": {
@@ -36,12 +26,17 @@
 		},
 		{
 			"name": "dev",
-			"inherits": "cmake-warnings",
 			"installDir": "Install",
 			"cacheVariables": {
 				"RMLUI_SAMPLES": true,
 				"BUILD_TESTING": true,
 				"BUILD_SHARED_LIBS": false
+			},
+			"warnings": {
+				"dev": true
+			},
+			"errors": {
+				"dev": true
 			}
 		},
 		{
@@ -52,13 +47,6 @@
 				"RMLUI_SVG_PLUGIN": true,
 				"RMLUI_LUA_BINDINGS": true
 			}
-		},
-		{
-			"name": "ci",
-			"inherits": "cmake-warnings",
-			"cacheVariables": {
-				"RMLUI_WARNINGS_AS_ERRORS": true
-			}
 		}
 	]
 }