From f33a59370ade94db6ad7928d6e454385d4e84ab8 Mon Sep 17 00:00:00 2001
From: KazushiM <85604869+KazushiMe@users.noreply.github.com>
Date: Wed, 25 Jan 2023 18:58:43 +0800
Subject: [PATCH] Introduce redesigned project homepage; Fix #64 #65
- loaderConfigurator now renamed to pages, with configurator section supporting both v2 and v3 Cust
---
.github/workflows/deploy_configurator.yml | 6 +-
README.md | 38 +-
.../loader/source/oc/customize.cpp | 1 -
.../stratosphere/loader/source/oc/pcv/pcv.cpp | 4 +-
.../stratosphere/loader/source/patch.py | 2 +-
Source/loaderConfigurator/.gitignore | 2 -
Source/loaderConfigurator/dist/index.html | 22 -
Source/loaderConfigurator/dist/main.js | 234 -
Source/loaderConfigurator/dist/output.css | 4071 -----------------
Source/loaderConfigurator/ldr_config_old.py | 173 -
Source/loaderConfigurator/package.json | 8 -
Source/loaderConfigurator/src/index.html | 22 -
Source/loaderConfigurator/src/main.ts | 320 --
Source/loaderConfigurator/src/style.css | 39 -
Source/loaderConfigurator/tailwind.config.js | 10 -
.../common/include/sysclk/client/ipc.h | 8 +
Source/sys-clk-OC/manager/src/ipc/client.h | 8 -
Source/sys-clk-OC/overlay/src/ipc.h | 9 -
{Source/loaderConfigurator => pages}/build.sh | 2 +-
pages/dist/index.html | 382 ++
pages/dist/main.js | 414 ++
pages/src/index.html | 382 ++
pages/src/main.ts | 605 +++
23 files changed, 1810 insertions(+), 4952 deletions(-)
delete mode 100644 Source/loaderConfigurator/.gitignore
delete mode 100644 Source/loaderConfigurator/dist/index.html
delete mode 100644 Source/loaderConfigurator/dist/main.js
delete mode 100644 Source/loaderConfigurator/dist/output.css
delete mode 100755 Source/loaderConfigurator/ldr_config_old.py
delete mode 100644 Source/loaderConfigurator/package.json
delete mode 100644 Source/loaderConfigurator/src/index.html
delete mode 100644 Source/loaderConfigurator/src/main.ts
delete mode 100644 Source/loaderConfigurator/src/style.css
delete mode 100644 Source/loaderConfigurator/tailwind.config.js
rename {Source/loaderConfigurator => pages}/build.sh (70%)
create mode 100644 pages/dist/index.html
create mode 100644 pages/dist/main.js
create mode 100644 pages/src/index.html
create mode 100644 pages/src/main.ts
diff --git a/.github/workflows/deploy_configurator.yml b/.github/workflows/deploy_configurator.yml
index f9f0cba9..e01792b7 100644
--- a/.github/workflows/deploy_configurator.yml
+++ b/.github/workflows/deploy_configurator.yml
@@ -38,16 +38,16 @@ jobs:
uses: devatherock/minify-js@v1.0.3
with:
# File to minify or a folder containing files to minify. By default, all files in current folder and its subfolders will be minified
- directory: './Source/loaderConfigurator/dist' # optional
+ directory: './pages/dist' # optional
# Path where the minified files will be saved. By default, the minified files will be saved in the original file path
- output: './Source/loaderConfigurator/dist_min' # optional
+ output: './pages/dist_min' # optional
# Indicates if the output files should have the suffix '.min' added after the name. Default is true
add_suffix: false # optional
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
- path: './Source/loaderConfigurator/dist_min'
+ path: './pages/dist_min'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
diff --git a/README.md b/README.md
index 319b0b75..f4d73eb2 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
+[Project Homepage](https://kazushime.github.io/Switch-OC-Suite)
**DISCLAIMER: USE AT YOUR OWN RISK!**
@@ -15,20 +16,13 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
- Erista variant (HAC-001)
- CPU Overclock (Safe: 1785 MHz)
- Unsafe
-
- - Due to the limit of board power draw or power IC
- - Unlockable frequencies up to 2091 MHz
- - See [README for sys-clk-OC](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md)
-
-
+ - Unsafe
+ - Due to the limit of board power draw or power IC
+ - Unlockable frequencies up to 2091 MHz
+ - See [README for sys-clk-OC](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md)
- DRAM Overclock (Safe: 1862.4 MHz)
- Unsafe
-
- - Up to 2131 MHz with DRAM bus overvolting depending on your DRAM chip
-
-
+ - Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your DRAM chip
- Modded sys-clk and ReverseNX-RT
- CPU & GPU frequency governor (Experimental)
@@ -38,20 +32,12 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
- Mariko variant (HAC-001-01, HDH-001, HEG-001)
- CPU / GPU Overclock (Safe: 1963 / 998 MHz)
- Unsafe
-
- - Due to the limit of board power draw or power IC
- - Unlockable frequencies up to 2397 / 1305 MHz or 2295 / 1267 MHz
- - See [README for sys-clk-OC](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md)
-
-
+ - Unsafe
+ - Due to the limit of board power draw or power IC
+ - Unlockable frequencies up to 2397 / 1305 MHz or 2295 / 1267 MHz
+ - See [README for sys-clk-OC](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md)
- DRAM Overclock (Safe: 1996.8 MHz)
- Unsafe
-
- - [DRAM bus overvolting](https://gist.github.com/KazushiMe/6bb0fcbefe0e03b1274079522516d56d).
-
-
- Modded sys-clk and ReverseNX-RT
- Auto CPU Boost
@@ -99,7 +85,7 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW.
| CPU Volt | 1235 mV Max | 1257 mV Max |
| GPU OC | 1305 MHz Max | N/A |
| RAM OC | 1996 MHz Max | 1862 MHz Max |
- | RAM Volt | N/A | Disabled |
+ | RAM Volt | Disabled | Disabled |
| RAM Timing | Auto-Adjusted | Disabled |
@@ -151,5 +137,5 @@ When compilation is done, uncompress the kip to make it work with configurator:
- RetroNX team for [sys-clk](https://github.com/retronx-team/sys-clk)
- SciresM and Reswitched Team for the state-of-the-art [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) CFW of Switch
- Switchbrew [wiki](http://switchbrew.org/wiki/) for Switch in-depth info
-- Switchroot for their [modified L4T kernel and device tree]((https://gitlab.com/switchroot/kernel))
+- Switchroot for their [modified L4T kernel and device tree](https://gitlab.com/switchroot/kernel)
- ZatchyCatGames for RE and original OC loader patches for Atmosphere
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 7caf1f54..57352099 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -71,7 +71,6 @@ volatile CustomizeTable C = {
.marikoEmcVolt = 0,
/* Erista CPU:
- * Not tested but enabled by default.
* - Max Voltage in mV
*/
.eristaCpuMaxVolt = 1235,
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
index 19a6fa84..e0cac740 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
@@ -31,8 +31,8 @@ Result MemFreqPllmLimit(u32* ptr) {
void SafetyCheck() {
if (C.custRev != CUST_REV ||
- C.marikoCpuMaxVolt >= 1300 ||
- C.eristaCpuMaxVolt >= 1300 ||
+ C.marikoCpuMaxVolt > 1300 ||
+ C.eristaCpuMaxVolt > 1300 ||
(C.eristaEmcVolt && (C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)) ||
(C.marikoEmcVolt && (C.marikoEmcVolt < 600'000 || C.marikoEmcVolt > 650'000)))
{
diff --git a/Source/Atmosphere/stratosphere/loader/source/patch.py b/Source/Atmosphere/stratosphere/loader/source/patch.py
index 0cf2b7dc..22c503f7 100755
--- a/Source/Atmosphere/stratosphere/loader/source/patch.py
+++ b/Source/Atmosphere/stratosphere/loader/source/patch.py
@@ -31,7 +31,7 @@ file_replace_str(ldr_process_creation,
namespace ams::ldr {""",
"""#include "ldr_ro_manager.hpp"
-#include "oc/oc_suite.hpp"
+#include "oc/oc_loader.hpp"
namespace ams::ldr {"""),
(""" NsoHeader g_nso_headers[Nso_Count];
diff --git a/Source/loaderConfigurator/.gitignore b/Source/loaderConfigurator/.gitignore
deleted file mode 100644
index 504afef8..00000000
--- a/Source/loaderConfigurator/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-package-lock.json
diff --git a/Source/loaderConfigurator/dist/index.html b/Source/loaderConfigurator/dist/index.html
deleted file mode 100644
index 7daab2d8..00000000
--- a/Source/loaderConfigurator/dist/index.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Source/loaderConfigurator/dist/main.js b/Source/loaderConfigurator/dist/main.js
deleted file mode 100644
index 321c1b52..00000000
--- a/Source/loaderConfigurator/dist/main.js
+++ /dev/null
@@ -1,234 +0,0 @@
-const CUST_REV = 2;
-var buffer;
-function FindMagicOffset(buffer) {
- let view = new DataView(buffer);
- for (let i = 0; i < view.byteLength; i += 4) {
- if (view.getUint32(i, true) == 0x54535543) { // "CUST"
- return i;
- }
- }
- throw new Error("Invalid loader.kip file");
-}
-function CustEntry(name, size, desc, defval, minmax = [0, 1000000], step = 1, extra_validator) {
- this.name = name;
- this.size = size;
- this.desc = desc;
- this.defval = defval;
- this.min = minmax[0];
- this.max = minmax[1];
- this.step = step;
- this.validator = extra_validator;
- this.value = null;
- this.offset = null;
-}
-function InitCustTable() {
- let cust = [
- new CustEntry("mtcConf", 2, "DRAM Timing \
- 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. \
- 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. ", 0, [0, 3]),
- new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz \
- System default: 1785000 \
- ≥ 2397000 will enable overvolting (> 1120 mV) ", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
- new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz \
- System default: 1785000 \
- Must not be higher than marikoCpuMaxClock ", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
- new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV \
- System default: 1120 \
- Acceptable range: 1100 ≤ x ≤ 1300 ", 1235, [1100, 1300]),
- new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz \
- System default: 921600 \
- Tegra X1+ official maximum: 1267200 ", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0),
- new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz \
- Values should be > 1600000, and divided evenly by 3200. \
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0),
- new CustEntry("eristaCpuOCEnable", 4, "Erista CPU Enable Overclock \
- Not tested ", 1, [0, 1]),
- new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV \
- Acceptable range: 1100 ≤ x ≤ 1400 ", 1257, [0, 1400], 100, (x) => x >= 1100),
- new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz \
- Values should be > 1600000, and divided evenly by 3200. \
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0),
- new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV \
- Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
- Not enabled by default ", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000),
- ];
- return cust;
-}
-function ValidateCust(cust) {
- for (let i of cust) {
- if (i.value == 0)
- continue;
- if (i.value < i.min || i.value > i.max) {
- document.getElementById(i.name).focus();
- throw new Error(`Expected range: ${i.min} ≤ ${i.name} ≤ ${i.max}, got ${i.value}`);
- }
- if (i.validator && !i.validator(i.value)) {
- document.getElementById(i.name).focus();
- throw new Error(`Invalid value: ${i.value}(${i.name})\nValidator: ${i.validator}`);
- }
- }
-}
-function SaveCust(cust, buffer) {
- let dict = Object.assign({}, ...cust.map((x) => ({ [x.name]: x })));
- let view = new DataView(buffer);
- let storage = {};
- for (let i of cust) {
- let id = i.name;
- i.value = document.getElementById(id).value;
- storage[i.name] = i.value;
- switch (i.size) {
- case 2:
- view.setUint16(i.offset, i.value, true);
- break;
- case 4:
- view.setUint32(i.offset, i.value, true);
- break;
- default:
- document.getElementById(i.name).focus();
- throw new Error("Unknown size at " + i);
- }
- }
- ValidateCust(cust);
- storage["custRev"] = CUST_REV;
- localStorage.setItem("last_saved", JSON.stringify(storage));
- let a = document.createElement("a");
- a.href = window.URL.createObjectURL(new Blob([buffer], { type: "application/octet-stream" }));
- a.download = "loader.kip";
- a.click();
-}
-function LastSaved() {
- let storage = localStorage.getItem("last_saved");
- if (!storage) {
- return false;
- }
- let sObj = JSON.parse(storage);
- if (!sObj["custRev"] || sObj["custRev"] != CUST_REV) {
- localStorage.removeItem("last_saved");
- return false;
- }
- return true;
-}
-function LoadLastSaved() {
- if (LastSaved()) {
- let storage = localStorage.getItem("last_saved");
- let sObj = JSON.parse(storage);
- for (let key in sObj) {
- if (key == "custRev") {
- continue;
- }
- document.getElementById(key).value = sObj[key];
- }
- }
-}
-function LoadDefault(cust) {
- let dict = Object.assign({}, ...cust.map((x) => ({ [x.name]: x })));
- for (let i of cust) {
- let id = i.name;
- document.getElementById(id).value = i.defval;
- }
-}
-function UpdateHTMLForm(cust) {
- let dict = Object.assign({}, ...cust.map((x) => ({ [x.name]: x })));
- let form = document.getElementById("form");
- for (let i of cust) {
- let id = i.name;
- let input = document.getElementById(id);
- if (!input) {
- let div = document.createElement("div");
- div.classList.add("form-floating", "my-3");
- let label = document.createElement("label");
- label.setAttribute("for", id);
- label.innerHTML = id;
- label.classList.add("form-entry");
- input = document.createElement("input");
- input.classList.add("form-control", "form-entry");
- input.min = dict[i.name].min;
- input.max = dict[i.name].max;
- input.id = id;
- input.type = "number";
- input.step = dict[i.name].step;
- div.appendChild(input);
- div.appendChild(label);
- let desc = dict[i.name].desc;
- if (desc) {
- let tip = document.createElement("small");
- tip.innerHTML = desc;
- tip.classList.add("form-entry");
- tip.setAttribute("for", id);
- div.appendChild(tip);
- }
- form === null || form === void 0 ? void 0 : form.appendChild(div);
- }
- input.value = dict[i.name].value;
- }
- let btn = document.getElementById("load");
- btn.classList.remove("hide");
- if (LastSaved()) {
- btn.innerHTML = "Load Last Saved";
- btn.addEventListener('click', () => {
- LoadLastSaved();
- });
- }
- else {
- btn.addEventListener('click', () => {
- LoadDefault(cust);
- });
- }
- btn = document.getElementById("save");
- btn.classList.remove("hide");
- btn.addEventListener('click', () => {
- try {
- SaveCust(cust, buffer);
- }
- catch (e) {
- console.log(e);
- alert(e);
- }
- });
-}
-function ParseCust(magicOffset, buffer) {
- let view = new DataView(buffer);
- let cust = InitCustTable();
- let offset = magicOffset + 4;
- let rev = view.getUint16(offset, true);
- if (rev != CUST_REV) {
- throw new Error("Unsupported custRev, expected: " + CUST_REV + ", got " + rev);
- }
- offset += 2;
- for (let i of cust) {
- i.offset = offset;
- switch (i.size) {
- case 2:
- i.value = view.getUint16(offset, true);
- break;
- case 4:
- i.value = view.getUint32(offset, true);
- break;
- default:
- document.getElementById(i.name).focus();
- throw new Error("Unknown size at " + i);
- }
- offset += i.size;
- }
- ValidateCust(cust);
- UpdateHTMLForm(cust);
-}
-const fileInput = document.getElementById("file");
-fileInput.addEventListener('change', (event) => {
- let reader = new FileReader();
- reader.readAsArrayBuffer(event.target.files[0]);
- reader.onloadend = (progEvent) => {
- if (progEvent.target.readyState === FileReader.DONE) {
- try {
- buffer = progEvent.target.result;
- let offset = FindMagicOffset(buffer);
- ParseCust(offset, buffer);
- }
- catch (e) {
- console.log(e);
- alert(e);
- fileInput.value = "";
- }
- }
- };
-});
diff --git a/Source/loaderConfigurator/dist/output.css b/Source/loaderConfigurator/dist/output.css
deleted file mode 100644
index 40ed4ded..00000000
--- a/Source/loaderConfigurator/dist/output.css
+++ /dev/null
@@ -1,4071 +0,0 @@
-/*
-! tailwindcss v3.2.2 | MIT License | https://tailwindcss.com
-*/
-
-/*
-1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
-2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
-*/
-
-*,
-::before,
-::after {
- box-sizing: border-box;
- /* 1 */
- border-width: 0;
- /* 2 */
- border-style: solid;
- /* 2 */
- border-color: #e5e7eb;
- /* 2 */
-}
-
-::before,
-::after {
- --tw-content: '';
-}
-
-/*
-1. Use a consistent sensible line-height in all browsers.
-2. Prevent adjustments of font size after orientation changes in iOS.
-3. Use a more readable tab size.
-4. Use the user's configured `sans` font-family by default.
-5. Use the user's configured `sans` font-feature-settings by default.
-*/
-
-html {
- line-height: 1.5;
- /* 1 */
- -webkit-text-size-adjust: 100%;
- /* 2 */
- -moz-tab-size: 4;
- /* 3 */
- -o-tab-size: 4;
- tab-size: 4;
- /* 3 */
- font-family: Inter, sans-serif;
- /* 4 */
- font-feature-settings: normal;
- /* 5 */
-}
-
-/*
-1. Remove the margin in all browsers.
-2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
-*/
-
-body {
- margin: 0;
- /* 1 */
- line-height: inherit;
- /* 2 */
-}
-
-/*
-1. Add the correct height in Firefox.
-2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
-3. Ensure horizontal rules are visible by default.
-*/
-
-hr {
- height: 0;
- /* 1 */
- color: inherit;
- /* 2 */
- border-top-width: 1px;
- /* 3 */
-}
-
-/*
-Add the correct text decoration in Chrome, Edge, and Safari.
-*/
-
-abbr:where([title]) {
- -webkit-text-decoration: underline dotted;
- text-decoration: underline dotted;
-}
-
-/*
-Remove the default font size and weight for headings.
-*/
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-size: inherit;
- font-weight: inherit;
-}
-
-/*
-Reset links to optimize for opt-in styling instead of opt-out.
-*/
-
-a {
- color: inherit;
- text-decoration: inherit;
-}
-
-/*
-Add the correct font weight in Edge and Safari.
-*/
-
-b,
-strong {
- font-weight: bolder;
-}
-
-/*
-1. Use the user's configured `mono` font family by default.
-2. Correct the odd `em` font sizing in all browsers.
-*/
-
-code,
-kbd,
-samp,
-pre {
- font-family: ui-monospace, monospace;
- /* 1 */
- font-size: 1em;
- /* 2 */
-}
-
-/*
-Add the correct font size in all browsers.
-*/
-
-small {
- font-size: 80%;
-}
-
-/*
-Prevent `sub` and `sup` elements from affecting the line height in all browsers.
-*/
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-sup {
- top: -0.5em;
-}
-
-/*
-1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
-2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
-3. Remove gaps between table borders by default.
-*/
-
-table {
- text-indent: 0;
- /* 1 */
- border-color: inherit;
- /* 2 */
- border-collapse: collapse;
- /* 3 */
-}
-
-/*
-1. Change the font styles in all browsers.
-2. Remove the margin in Firefox and Safari.
-3. Remove default padding in all browsers.
-*/
-
-button,
-input,
-optgroup,
-select,
-textarea {
- font-family: inherit;
- /* 1 */
- font-size: 100%;
- /* 1 */
- font-weight: inherit;
- /* 1 */
- line-height: inherit;
- /* 1 */
- color: inherit;
- /* 1 */
- margin: 0;
- /* 2 */
- padding: 0;
- /* 3 */
-}
-
-/*
-Remove the inheritance of text transform in Edge and Firefox.
-*/
-
-button,
-select {
- text-transform: none;
-}
-
-/*
-1. Correct the inability to style clickable types in iOS and Safari.
-2. Remove default button styles.
-*/
-
-button,
-[type='button'],
-[type='reset'],
-[type='submit'] {
- -webkit-appearance: button;
- /* 1 */
- background-color: transparent;
- /* 2 */
- background-image: none;
- /* 2 */
-}
-
-/*
-Use the modern Firefox focus style for all focusable elements.
-*/
-
-:-moz-focusring {
- outline: auto;
-}
-
-/*
-Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
-*/
-
-:-moz-ui-invalid {
- box-shadow: none;
-}
-
-/*
-Add the correct vertical alignment in Chrome and Firefox.
-*/
-
-progress {
- vertical-align: baseline;
-}
-
-/*
-Correct the cursor style of increment and decrement buttons in Safari.
-*/
-
-::-webkit-inner-spin-button,
-::-webkit-outer-spin-button {
- height: auto;
-}
-
-/*
-1. Correct the odd appearance in Chrome and Safari.
-2. Correct the outline style in Safari.
-*/
-
-[type='search'] {
- -webkit-appearance: textfield;
- /* 1 */
- outline-offset: -2px;
- /* 2 */
-}
-
-/*
-Remove the inner padding in Chrome and Safari on macOS.
-*/
-
-::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/*
-1. Correct the inability to style clickable types in iOS and Safari.
-2. Change font properties to `inherit` in Safari.
-*/
-
-::-webkit-file-upload-button {
- -webkit-appearance: button;
- /* 1 */
- font: inherit;
- /* 2 */
-}
-
-/*
-Add the correct display in Chrome and Safari.
-*/
-
-summary {
- display: list-item;
-}
-
-/*
-Removes the default spacing and border for appropriate elements.
-*/
-
-blockquote,
-dl,
-dd,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-hr,
-figure,
-p,
-pre {
- margin: 0;
-}
-
-fieldset {
- margin: 0;
- padding: 0;
-}
-
-legend {
- padding: 0;
-}
-
-ol,
-ul,
-menu {
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-/*
-Prevent resizing textareas horizontally by default.
-*/
-
-textarea {
- resize: vertical;
-}
-
-/*
-1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
-2. Set the default placeholder color to the user's configured gray 400 color.
-*/
-
-input::-moz-placeholder, textarea::-moz-placeholder {
- opacity: 1;
- /* 1 */
- color: #9ca3af;
- /* 2 */
-}
-
-input::placeholder,
-textarea::placeholder {
- opacity: 1;
- /* 1 */
- color: #9ca3af;
- /* 2 */
-}
-
-/*
-Set the default cursor for buttons.
-*/
-
-button,
-[role="button"] {
- cursor: pointer;
-}
-
-/*
-Make sure disabled buttons don't get the pointer cursor.
-*/
-
-:disabled {
- cursor: default;
-}
-
-/*
-1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
-2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
- This can trigger a poorly considered lint error in some tools but is included by design.
-*/
-
-img,
-svg,
-video,
-canvas,
-audio,
-iframe,
-embed,
-object {
- display: block;
- /* 1 */
- vertical-align: middle;
- /* 2 */
-}
-
-/*
-Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
-*/
-
-img,
-video {
- max-width: 100%;
- height: auto;
-}
-
-/* Make elements with the HTML hidden attribute stay hidden by default */
-
-[hidden] {
- display: none;
-}
-
-*, ::before, ::after {
- --tw-border-spacing-x: 0;
- --tw-border-spacing-y: 0;
- --tw-translate-x: 0;
- --tw-translate-y: 0;
- --tw-rotate: 0;
- --tw-skew-x: 0;
- --tw-skew-y: 0;
- --tw-scale-x: 1;
- --tw-scale-y: 1;
- --tw-pan-x: ;
- --tw-pan-y: ;
- --tw-pinch-zoom: ;
- --tw-scroll-snap-strictness: proximity;
- --tw-ordinal: ;
- --tw-slashed-zero: ;
- --tw-numeric-figure: ;
- --tw-numeric-spacing: ;
- --tw-numeric-fraction: ;
- --tw-ring-inset: ;
- --tw-ring-offset-width: 0px;
- --tw-ring-offset-color: #fff;
- --tw-ring-color: rgb(59 130 246 / 0.5);
- --tw-ring-offset-shadow: 0 0 #0000;
- --tw-ring-shadow: 0 0 #0000;
- --tw-shadow: 0 0 #0000;
- --tw-shadow-colored: 0 0 #0000;
- --tw-blur: ;
- --tw-brightness: ;
- --tw-contrast: ;
- --tw-grayscale: ;
- --tw-hue-rotate: ;
- --tw-invert: ;
- --tw-saturate: ;
- --tw-sepia: ;
- --tw-drop-shadow: ;
- --tw-backdrop-blur: ;
- --tw-backdrop-brightness: ;
- --tw-backdrop-contrast: ;
- --tw-backdrop-grayscale: ;
- --tw-backdrop-hue-rotate: ;
- --tw-backdrop-invert: ;
- --tw-backdrop-opacity: ;
- --tw-backdrop-saturate: ;
- --tw-backdrop-sepia: ;
-}
-
-::backdrop {
- --tw-border-spacing-x: 0;
- --tw-border-spacing-y: 0;
- --tw-translate-x: 0;
- --tw-translate-y: 0;
- --tw-rotate: 0;
- --tw-skew-x: 0;
- --tw-skew-y: 0;
- --tw-scale-x: 1;
- --tw-scale-y: 1;
- --tw-pan-x: ;
- --tw-pan-y: ;
- --tw-pinch-zoom: ;
- --tw-scroll-snap-strictness: proximity;
- --tw-ordinal: ;
- --tw-slashed-zero: ;
- --tw-numeric-figure: ;
- --tw-numeric-spacing: ;
- --tw-numeric-fraction: ;
- --tw-ring-inset: ;
- --tw-ring-offset-width: 0px;
- --tw-ring-offset-color: #fff;
- --tw-ring-color: rgb(59 130 246 / 0.5);
- --tw-ring-offset-shadow: 0 0 #0000;
- --tw-ring-shadow: 0 0 #0000;
- --tw-shadow: 0 0 #0000;
- --tw-shadow-colored: 0 0 #0000;
- --tw-blur: ;
- --tw-brightness: ;
- --tw-contrast: ;
- --tw-grayscale: ;
- --tw-hue-rotate: ;
- --tw-invert: ;
- --tw-saturate: ;
- --tw-sepia: ;
- --tw-drop-shadow: ;
- --tw-backdrop-blur: ;
- --tw-backdrop-brightness: ;
- --tw-backdrop-contrast: ;
- --tw-backdrop-grayscale: ;
- --tw-backdrop-hue-rotate: ;
- --tw-backdrop-invert: ;
- --tw-backdrop-opacity: ;
- --tw-backdrop-saturate: ;
- --tw-backdrop-sepia: ;
-}
-
-.container {
- width: 100%;
-}
-
-@media (min-width: 640px) {
- .container {
- max-width: 640px;
- }
-}
-
-@media (min-width: 768px) {
- .container {
- max-width: 768px;
- }
-}
-
-@media (min-width: 1024px) {
- .container {
- max-width: 1024px;
- }
-}
-
-@media (min-width: 1280px) {
- .container {
- max-width: 1280px;
- }
-}
-
-@media (min-width: 1536px) {
- .container {
- max-width: 1536px;
- }
-}
-
-:root {
- --bs-blue: #0d6efd;
- --bs-indigo: #6610f2;
- --bs-purple: #6f42c1;
- --bs-pink: #d63384;
- --bs-red: #dc3545;
- --bs-orange: #fd7e14;
- --bs-yellow: #ffc107;
- --bs-green: #198754;
- --bs-teal: #20c997;
- --bs-cyan: #0dcaf0;
- --bs-white: #fff;
- --bs-gray: #6c757d;
- --bs-gray-dark: #343a40;
- --bs-gray-100: #f8f9fa;
- --bs-gray-200: #e9ecef;
- --bs-gray-300: #dee2e6;
- --bs-gray-400: #ced4da;
- --bs-gray-500: #adb5bd;
- --bs-gray-600: #6c757d;
- --bs-gray-700: #495057;
- --bs-gray-800: #343a40;
- --bs-gray-900: #212529;
- --bs-primary: #0d6efd;
- --bs-secondary: #6c757d;
- --bs-success: #198754;
- --bs-info: #0dcaf0;
- --bs-warning: #ffc107;
- --bs-danger: #dc3545;
- --bs-light: #f8f9fa;
- --bs-dark: #212529;
- --bs-primary-rgb: 13, 110, 253;
- --bs-secondary-rgb: 108, 117, 125;
- --bs-success-rgb: 25, 135, 84;
- --bs-info-rgb: 13, 202, 240;
- --bs-warning-rgb: 255, 193, 7;
- --bs-danger-rgb: 220, 53, 69;
- --bs-light-rgb: 248, 249, 250;
- --bs-dark-rgb: 33, 37, 41;
- --bs-white-rgb: 255, 255, 255;
- --bs-black-rgb: 0, 0, 0;
- --bs-body-color-rgb: 33, 37, 41;
- --bs-body-bg-rgb: 255, 255, 255;
- --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
- --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
- --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
- --bs-body-font-family: var(--bs-font-sans-serif);
- --bs-body-font-size: 1rem;
- --bs-body-font-weight: 400;
- --bs-body-line-height: 1.5;
- --bs-body-color: #212529;
- --bs-body-bg: #fff;
-}
-
-.form-control[type=file] {
- overflow: hidden;
-}
-
-.form-control[type=file]:not(:disabled):not([readonly]) {
- cursor: pointer;
-}
-
-.form-control:focus {
- box-shadow: 0 0 0 1px #2563eb;
-}
-
-.form-control::file-selector-button {
- padding: 0.375rem 0.75rem;
- margin: -0.375rem -0.75rem;
- -webkit-margin-end: 0.75rem;
- margin-inline-end: 0.75rem;
- color: #212529;
- background-color: #e9ecef;
- pointer-events: none;
- border-color: inherit;
- border-style: solid;
- border-width: 0;
- border-inline-end-width: 1px;
- border-radius: 0;
- transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
-}
-
-.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
- background-color: #dde0e3;
-}
-
-.form-control::-webkit-file-upload-button {
- padding: 0.375rem 0.75rem;
- margin: -0.375rem -0.75rem;
- -webkit-margin-end: 0.75rem;
- margin-inline-end: 0.75rem;
- color: #374151;
- background-color: #f3f4f6;
- pointer-events: none;
- border-color: inherit;
- border-style: solid;
- border-width: 0;
- border-inline-end-width: 1px;
- border-radius: 0;
- -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
- transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
-}
-
-.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
- background-color: #dde0e3;
-}
-
-.btn-check[disabled] + .btn {
- pointer-events: none;
- filter: none;
- opacity: 0.65;
-}
-
-.btn-check:disabled + .btn {
- pointer-events: none;
- filter: none;
- opacity: 0.65;
-}
-
-.form-floating {
- position: relative;
-}
-
-.form-floating > .form-control {
- height: calc(3.5rem + 2px);
- line-height: 1.25;
- padding: 1rem 0.75rem;
-}
-
-.form-floating > .form-select {
- height: calc(3.5rem + 2px);
- line-height: 1.25;
-}
-
-.form-floating > label {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%;
- padding: 1rem 0.75rem;
- pointer-events: none;
- border: 1px solid transparent;
- transform-origin: 0 0;
- transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;
-}
-
-.form-floating > .form-control::-moz-placeholder {
- color: transparent;
-}
-
-.form-floating > .form-control::placeholder {
- color: transparent;
-}
-
-.form-floating > .form-control:focus {
- padding-top: 1.625rem;
- padding-bottom: 0.625rem;
-}
-
-.form-floating > .form-control:not(:-moz-placeholder-shown) {
- padding-top: 1.625rem;
- padding-bottom: 0.625rem;
-}
-
-.form-floating > .form-control:not(:placeholder-shown) {
- padding-top: 1.625rem;
- padding-bottom: 0.625rem;
-}
-
-.form-floating > .form-control:-webkit-autofill {
- padding-top: 1.625rem;
- padding-bottom: 0.625rem;
-}
-
-.form-floating > .form-control:focus ~ label {
- opacity: 0.65;
- transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
-}
-
-.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {
- opacity: 0.65;
- transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
-}
-
-.form-floating > .form-control:not(:placeholder-shown) ~ label {
- opacity: 0.65;
- transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
-}
-
-.form-floating > .form-select ~ label {
- opacity: 0.65;
- transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
-}
-
-.input-group > .form-control {
- width: 1%;
-}
-
-.input-group > .form-control:focus {
- z-index: 3;
-}
-
-.input-group .btn {
- position: relative;
- z-index: 2;
-}
-
-.input-group .btn:focus {
- z-index: 3;
-}
-
-.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4) {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.was-validated .form-control:valid {
- border-color: #198754;
- padding-right: calc(1.5em + 0.75rem);
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
- background-repeat: no-repeat;
- background-position: right calc(0.375em + 0.1875rem) center;
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.form-control.is-valid {
- border-color: #198754;
- padding-right: calc(1.5em + 0.75rem);
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
- background-repeat: no-repeat;
- background-position: right calc(0.375em + 0.1875rem) center;
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.was-validated .form-control:valid:focus {
- border-color: #198754;
- box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
-}
-
-.form-control.is-valid:focus {
- border-color: #198754;
- box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
-}
-
-.was-validated textarea.form-control:valid {
- padding-right: calc(1.5em + 0.75rem);
- background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
-}
-
-textarea.form-control.is-valid {
- padding-right: calc(1.5em + 0.75rem);
- background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
-}
-
-.was-validated .input-group .form-control:valid {
- z-index: 1;
-}
-
-.input-group .form-control.is-valid {
- z-index: 1;
-}
-
-.was-validated .input-group .form-control:valid:focus {
- z-index: 3;
-}
-
-.input-group .form-control.is-valid:focus {
- z-index: 3;
-}
-
-.is-invalid ~ .invalid-feedback {
- display: block;
-}
-
-.is-invalid ~ .invalid-tooltip {
- display: block;
-}
-
-.was-validated .form-control:invalid {
- border-color: #dc3545;
- padding-right: calc(1.5em + 0.75rem);
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
- background-repeat: no-repeat;
- background-position: right calc(0.375em + 0.1875rem) center;
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.form-control.is-invalid {
- border-color: #dc3545;
- padding-right: calc(1.5em + 0.75rem);
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
- background-repeat: no-repeat;
- background-position: right calc(0.375em + 0.1875rem) center;
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.was-validated .form-control:invalid:focus {
- border-color: #dc3545;
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
-}
-
-.form-control.is-invalid:focus {
- border-color: #dc3545;
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
-}
-
-.was-validated textarea.form-control:invalid {
- padding-right: calc(1.5em + 0.75rem);
- background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
-}
-
-textarea.form-control.is-invalid {
- padding-right: calc(1.5em + 0.75rem);
- background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
-}
-
-.form-select.is-invalid {
- border-color: #dc3545;
-}
-
-.form-select.is-invalid:not([multiple]):not([size]) {
- padding-right: 4.125rem;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
- background-position: right 0.75rem center, center right 2.25rem;
- background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.form-select.is-invalid:not([multiple])[size="1"] {
- padding-right: 4.125rem;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
- background-position: right 0.75rem center, center right 2.25rem;
- background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
-}
-
-.form-select.is-invalid:focus {
- border-color: #dc3545;
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
-}
-
-.form-check-input.is-invalid {
- border-color: #dc3545;
-}
-
-.form-check-input.is-invalid:checked {
- background-color: #dc3545;
-}
-
-.form-check-input.is-invalid:focus {
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
-}
-
-.form-check-input.is-invalid ~ .form-check-label {
- color: #dc3545;
-}
-
-.was-validated .input-group .form-control:invalid {
- z-index: 2;
-}
-
-.input-group .form-control.is-invalid {
- z-index: 2;
-}
-
-.input-group .form-select.is-invalid {
- z-index: 2;
-}
-
-.was-validated .input-group .form-control:invalid:focus {
- z-index: 3;
-}
-
-.input-group .form-control.is-invalid:focus {
- z-index: 3;
-}
-
-.input-group .form-select.is-invalid:focus {
- z-index: 3;
-}
-
-.btn-check:focus + .btn {
- outline: 0;
- box-shadow: none;
-}
-
-.btn:focus {
- outline: 0;
- box-shadow: none;
-}
-
-.btn-check:checked + .btn {
- box-shadow: none;
-}
-
-.btn-check:active + .btn {
- box-shadow: none;
-}
-
-.btn:active {
- box-shadow: none;
-}
-
-.btn.active {
- box-shadow: none;
-}
-
-.btn-check:checked + .btn:focus {
- box-shadow: none;
-}
-
-.btn-check:active + .btn:focus {
- box-shadow: none;
-}
-
-.btn:active:focus {
- box-shadow: none;
-}
-
-.btn.active:focus {
- box-shadow: none;
-}
-
-.fade {
- transition: opacity 0.15s linear;
-}
-
-.fade:not(.show) {
- opacity: 0;
-}
-
-.collapse:not(.show) {
- display: none;
-}
-
-.collapsing {
- height: 0;
- overflow: hidden;
- transition: height 0.35s ease;
-}
-
-.collapsing.collapse-horizontal {
- width: 0;
- height: auto;
- transition: width 0.35s ease;
-}
-
-.dropdown-menu {
- z-index: 1000;
-}
-
-.dropdown-item.active {
- color: #1f2937;
- -webkit-text-decoration: none;
- text-decoration: none;
- background-color: #0d6efd;
-}
-
-.dropdown-item:active {
- color: #1f2937;
- -webkit-text-decoration: none;
- text-decoration: none;
- background-color: #0d6efd;
-}
-
-.dropdown-item:disabled {
- color: #adb5bd;
- pointer-events: none;
- background-color: transparent;
-}
-
-.dropdown-menu.show {
- display: block;
-}
-
-.dropdown-menu-dark .dropdown-item.active {
- color: #fff;
- background-color: #0d6efd;
-}
-
-.dropdown-menu-dark .dropdown-item:active {
- color: #fff;
- background-color: #0d6efd;
-}
-
-.dropdown-menu-dark .dropdown-item.disabled {
- color: #adb5bd;
-}
-
-.dropdown-menu-dark .dropdown-item:disabled {
- color: #adb5bd;
-}
-
-.nav-tabs .nav-link {
- color: #4b5563;
-}
-
-.nav-tabs .nav-link:hover {
- isolation: isolate;
-}
-
-.nav-tabs .nav-link:focus {
- isolation: isolate;
-}
-
-.nav-tabs .nav-link.disabled {
- color: #9ca3af;
- background-color: transparent;
- border-color: transparent;
-}
-
-.nav-tabs .nav-link.active {
- color: #2563eb;
- border-color: #2563eb;
-}
-
-.nav-tabs .nav-item.show .nav-link {
- color: #2563eb;
- border-color: #2563eb;
-}
-
-.nav-tabs .dropdown-menu {
- margin-top: -1px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
-}
-
-.nav-pills .nav-link {
- background: #f3f4f6;
- color: #4b5563;
- box-shadow: none;
-}
-
-.nav-pills .nav-link.active {
- background: #2563eb;
- color: #fff;
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
-}
-
-.nav-pills .show > .nav-link {
- background: #2563eb;
- color: #fff;
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
-}
-
-.nav-pills .disabled {
- color: #9ca3af;
- background-color: rgba(243, 244, 246, 0.5);
-}
-
-.nav-pills.menu-sidebar .nav-link {
- background-color: transparent;
- box-shadow: none;
- padding: 0 5px;
- border-radius: 0;
-}
-
-.nav-pills.menu-sidebar .nav-link.active {
- color: #1266f1;
- font-weight: 600;
- border-left: 0.125rem solid #1266f1;
-}
-
-.nav-justified > .nav-link {
- -ms-flex-basis: 0;
- flex-basis: 0;
-}
-
-.nav-justified .nav-item {
- -ms-flex-basis: 0;
- flex-basis: 0;
-}
-
-.tab-content > .active {
- display: block;
-}
-
-.navbar-expand .navbar-nav {
- flex-direction: row;
-}
-
-.navbar-expand .navbar-nav .dropdown-menu {
- position: absolute;
-}
-
-.navbar-expand .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
-}
-
-.navbar-expand .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
-}
-
-.navbar-light .navbar-nav .nav-link.disabled {
- color: rgba(0, 0, 0, 0.3);
-}
-
-.navbar-light .navbar-nav .show > .nav-link {
- color: rgba(0, 0, 0, 0.9);
-}
-
-.navbar-light .navbar-nav .nav-link.active {
- color: rgba(0, 0, 0, 0.9);
-}
-
-.navbar-dark .navbar-nav .nav-link.disabled {
- color: rgba(255, 255, 255, 0.25);
-}
-
-.navbar-dark .navbar-nav .show > .nav-link {
- color: #fff;
-}
-
-.navbar-dark .navbar-nav .nav-link.active {
- color: #fff;
-}
-
-.accordion-item:last-of-type .accordion-button.collapsed {
- border-bottom-right-radius: calc(0.5rem - 1px);
- border-bottom-left-radius: calc(0.5rem - 1px);
-}
-
-.btn-close.disabled {
- pointer-events: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
- opacity: 0.25;
-}
-
-.modal {
- z-index: 1055;
-}
-
-.modal-dialog {
- margin: 0.5rem;
-}
-
-.modal.fade .modal-dialog {
- transition: transform 0.3s ease-out;
- transform: translate(0, -50px);
-}
-
-.modal.show .modal-dialog {
- transform: none;
-}
-
-.modal.modal-static .modal-dialog {
- transform: scale(1.02);
-}
-
-.modal-dialog-scrollable .modal-body {
- overflow-y: auto;
-}
-
-.modal-backdrop {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1050;
- width: 100vw;
- height: 100vh;
- background-color: #000;
-}
-
-.modal-backdrop.fade {
- opacity: 0;
-}
-
-.modal-backdrop.show {
- opacity: 0.5;
-}
-
-.modal-body {
- flex: 1 1 auto;
-}
-
-.modal-fullscreen .modal-body {
- overflow-y: auto;
-}
-
-.tooltip {
- position: absolute;
- z-index: 1080;
- display: block;
- margin: 0;
- font-family: var(--bs-font-sans-serif);
- font-style: normal;
- font-weight: 400;
- line-height: 1.5;
- -webkit-text-align: start;
- text-align: start;
- -webkit-text-decoration: none;
- text-decoration: none;
- -webkit-text-shadow: none;
- text-shadow: none;
- -webkit-text-transform: none;
- text-transform: none;
- letter-spacing: normal;
- word-break: normal;
- word-spacing: normal;
- white-space: normal;
- line-break: auto;
- font-size: 0.875rem;
- word-wrap: break-word;
- opacity: 0;
-}
-
-.tooltip.show {
- opacity: 1;
-}
-
-.bs-tooltip-top .tooltip-arrow {
- bottom: 0;
-}
-
-.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {
- bottom: 0;
-}
-
-.bs-tooltip-top .tooltip-arrow::before {
- top: -1px;
- border-width: 0.4rem 0.4rem 0;
- border-top-color: #000;
-}
-
-.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {
- top: -1px;
- border-width: 0.4rem 0.4rem 0;
- border-top-color: #000;
-}
-
-.bs-tooltip-end .tooltip-arrow {
- left: 0;
- width: 0.4rem;
- height: 0.8rem;
-}
-
-.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {
- left: 0;
- width: 0.4rem;
- height: 0.8rem;
-}
-
-.bs-tooltip-end .tooltip-arrow::before {
- right: -1px;
- border-width: 0.4rem 0.4rem 0.4rem 0;
- border-right-color: #000;
-}
-
-.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {
- right: -1px;
- border-width: 0.4rem 0.4rem 0.4rem 0;
- border-right-color: #000;
-}
-
-.bs-tooltip-bottom .tooltip-arrow {
- top: 0;
-}
-
-.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {
- top: 0;
-}
-
-.bs-tooltip-bottom .tooltip-arrow::before {
- bottom: -1px;
- border-width: 0 0.4rem 0.4rem;
- border-bottom-color: #000;
-}
-
-.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {
- bottom: -1px;
- border-width: 0 0.4rem 0.4rem;
- border-bottom-color: #000;
-}
-
-.bs-tooltip-start .tooltip-arrow {
- right: 0;
- width: 0.4rem;
- height: 0.8rem;
-}
-
-.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {
- right: 0;
- width: 0.4rem;
- height: 0.8rem;
-}
-
-.bs-tooltip-start .tooltip-arrow::before {
- left: -1px;
- border-width: 0.4rem 0 0.4rem 0.4rem;
- border-left-color: #000;
-}
-
-.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {
- left: -1px;
- border-width: 0.4rem 0 0.4rem 0.4rem;
- border-left-color: #000;
-}
-
-.tooltip-inner {
- max-width: 200px;
- font-size: 14px;
- padding: 6px 16px;
- color: #fff;
- -webkit-text-align: center;
- text-align: center;
- background-color: #6d6d6d;
- border-radius: 0.25rem;
-}
-
-.popover {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 1070;
- display: block;
- max-width: 276px;
- font-family: var(--bs-font-sans-serif);
- font-style: normal;
- font-weight: 400;
- line-height: 1.5;
- -webkit-text-align: start;
- text-align: start;
- -webkit-text-decoration: none;
- text-decoration: none;
- -webkit-text-shadow: none;
- text-shadow: none;
- -webkit-text-transform: none;
- text-transform: none;
- letter-spacing: normal;
- word-break: normal;
- word-spacing: normal;
- white-space: normal;
- line-break: auto;
- font-size: 0.875rem;
- word-wrap: break-word;
- background-color: #fff;
- background-clip: padding-box;
- border: 0;
- border-radius: 0.5rem;
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
-}
-
-.bs-popover-top > .popover-arrow {
- bottom: calc(-0.5rem - 1px);
-}
-
-.bs-popover-auto[data-popper-placement^=top] > .popover-arrow {
- bottom: calc(-0.5rem - 1px);
-}
-
-.bs-popover-top > .popover-arrow::before {
- bottom: 0;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {
- bottom: 0;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-top > .popover-arrow::after {
- bottom: 1px;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: #fff;
-}
-
-.bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {
- bottom: 1px;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: #fff;
-}
-
-.bs-popover-end > .popover-arrow {
- left: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
-}
-
-.bs-popover-auto[data-popper-placement^=right] > .popover-arrow {
- left: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
-}
-
-.bs-popover-end > .popover-arrow::before {
- left: 0;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {
- left: 0;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-end > .popover-arrow::after {
- left: 1px;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: #fff;
-}
-
-.bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {
- left: 1px;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: #fff;
-}
-
-.bs-popover-bottom > .popover-arrow {
- top: calc(-0.5rem - 1px);
-}
-
-.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {
- top: calc(-0.5rem - 1px);
-}
-
-.bs-popover-bottom > .popover-arrow::before {
- top: 0;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {
- top: 0;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-bottom > .popover-arrow::after {
- top: 1px;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: #fff;
-}
-
-.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {
- top: 1px;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: #fff;
-}
-
-.bs-popover-bottom .popover-header::before {
- position: absolute;
- top: 0;
- left: 50%;
- display: block;
- width: 1rem;
- margin-left: -0.5rem;
- content: "";
- border-bottom: 1px solid #f0f0f0;
-}
-
-.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {
- position: absolute;
- top: 0;
- left: 50%;
- display: block;
- width: 1rem;
- margin-left: -0.5rem;
- content: "";
- border-bottom: 1px solid #f0f0f0;
-}
-
-.bs-popover-start > .popover-arrow {
- right: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
-}
-
-.bs-popover-auto[data-popper-placement^=left] > .popover-arrow {
- right: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
-}
-
-.bs-popover-start > .popover-arrow::before {
- right: 0;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {
- right: 0;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: rgba(0, 0, 0, 0.25);
-}
-
-.bs-popover-start > .popover-arrow::after {
- right: 1px;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: #fff;
-}
-
-.bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {
- right: 1px;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: #fff;
-}
-
-.popover-header {
- padding: 0.5rem 1rem;
- margin-bottom: 0;
- font-size: 1rem;
- background-color: #fff;
- border-bottom: 1px solid rgba(0, 0, 0, 0.2);
- border-top-left-radius: 0.5rem;
- border-top-right-radius: 0.5rem;
- font-weight: 500;
-}
-
-.popover-header:empty {
- display: none;
-}
-
-.popover-body {
- padding: 1rem 1rem;
- color: #212529;
-}
-
-.carousel.pointer-event {
- touch-action: pan-y;
-}
-
-.carousel-item {
- display: none;
- margin-right: -100%;
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden;
- transition: transform 0.6s ease-in-out;
-}
-
-.carousel-item.active {
- display: block;
-}
-
-.carousel-item-next {
- display: block;
-}
-
-.carousel-item-prev {
- display: block;
-}
-
-.carousel-item-next:not(.carousel-item-start) {
- transform: translateX(100%);
-}
-
-.active.carousel-item-end {
- transform: translateX(100%);
-}
-
-.carousel-item-prev:not(.carousel-item-end) {
- transform: translateX(-100%);
-}
-
-.active.carousel-item-start {
- transform: translateX(-100%);
-}
-
-.carousel-fade .carousel-item {
- opacity: 0;
- transition-property: opacity;
- transform: none;
-}
-
-.carousel-fade .carousel-item.active {
- z-index: 1;
- opacity: 1;
-}
-
-.carousel-fade .carousel-item-next.carousel-item-start {
- z-index: 1;
- opacity: 1;
-}
-
-.carousel-fade .carousel-item-prev.carousel-item-end {
- z-index: 1;
- opacity: 1;
-}
-
-.carousel-fade .active.carousel-item-start {
- z-index: 0;
- opacity: 0;
- transition: opacity 0s 0.6s;
-}
-
-.carousel-fade .active.carousel-item-end {
- z-index: 0;
- opacity: 0;
- transition: opacity 0s 0.6s;
-}
-
-.carousel-indicators {
- z-index: 2;
- margin-right: 15%;
- margin-left: 15%;
- list-style: none;
-}
-
-.carousel-indicators [data-bs-target] {
- box-sizing: content-box;
- flex: 0 1 auto;
- width: 30px;
- height: 3px;
- padding: 0;
- margin-right: 3px;
- margin-left: 3px;
- -webkit-text-indent: -999px;
- text-indent: -999px;
- cursor: pointer;
- background-color: #fff;
- background-clip: padding-box;
- border: 0;
- border-top: 10px solid transparent;
- border-bottom: 10px solid transparent;
- opacity: 0.5;
- transition: opacity 0.6s ease;
-}
-
-.carousel-indicators .active {
- opacity: 1;
-}
-
-.carousel-dark .carousel-indicators [data-bs-target] {
- background-color: #000;
-}
-
-.offcanvas {
- z-index: 1045;
-}
-
-.offcanvas-backdrop {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1040;
- width: 100vw;
- height: 100vh;
- background-color: #000;
-}
-
-.offcanvas-backdrop.fade {
- opacity: 0;
-}
-
-.offcanvas-backdrop.show {
- opacity: 0.5;
-}
-
-.offcanvas.show {
- transform: none;
-}
-
-.link-primary {
- color: #0d6efd;
-}
-
-.link-primary:hover {
- color: #0a58ca;
-}
-
-.link-primary:focus {
- color: #0a58ca;
-}
-
-.sticky-top {
- position: sticky;
- top: 0;
- z-index: 1020;
-}
-
-.vr {
- display: inline-block;
- align-self: stretch;
- width: 1px;
- min-height: 1em;
- background-color: currentColor;
- opacity: 0.25;
-}
-
-.animation {
- animation-duration: 1s;
- animation-fill-mode: both;
- padding: auto;
-}
-
-.fade-in {
- animation-name: _fade-in;
-}
-
-.fade-out {
- animation-name: _fade-out;
-}
-
-.animation.infinite {
- animation-iteration-count: infinite;
-}
-
-.animation.delay-1s {
- animation-delay: 1s;
-}
-
-.animation.delay-2s {
- animation-delay: 2s;
-}
-
-.animation.delay-3s {
- animation-delay: 3s;
-}
-
-.animation.delay-4s {
- animation-delay: 4s;
-}
-
-.animation.delay-5s {
- animation-delay: 5s;
-}
-
-.animation.fast {
- animation-duration: 800ms;
-}
-
-.animation.faster {
- animation-duration: 500ms;
-}
-
-.animation.slow {
- animation-duration: 2s;
-}
-
-.animation.slower {
- animation-duration: 3s;
-}
-
-.slide-in-left {
- animation-name: _slide-in-left;
-}
-
-.slide-in-right {
- animation-name: _slide-in-right;
-}
-
-.slide-out-left {
- animation-name: _slide-out-left;
-}
-
-.slide-out-right {
- animation-name: _slide-out-right;
-}
-
-.ripple-surface {
- position: relative;
- overflow: hidden;
- display: inline-block;
- vertical-align: bottom;
-}
-
-.ripple-surface-unbound {
- overflow: visible;
-}
-
-.ripple-wave {
- background-image: radial-gradient(circle, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, transparent 70%);
- border-radius: 50%;
- opacity: 0.5;
- pointer-events: none;
- position: absolute;
- touch-action: none;
- transform: scale(0);
- transition-property: transform, opacity;
- transition-timing-function: cubic-bezier(0, 0, 0.15, 1), cubic-bezier(0, 0, 0.15, 1);
- z-index: 999;
-}
-
-.ripple-wave.active {
- transform: scale(1);
- opacity: 0;
-}
-
-.btn .ripple-wave {
- background-image: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
-}
-
-.ripple-surface-primary .ripple-wave {
- background-image: radial-gradient(circle, rgba(18, 102, 241, 0.2) 0, rgba(18, 102, 241, 0.3) 40%, rgba(18, 102, 241, 0.4) 50%, rgba(18, 102, 241, 0.5) 60%, rgba(18, 102, 241, 0) 70%);
-}
-
-.ripple-surface-secondary .ripple-wave {
- background-image: radial-gradient(circle, rgba(178, 60, 253, 0.2) 0, rgba(178, 60, 253, 0.3) 40%, rgba(178, 60, 253, 0.4) 50%, rgba(178, 60, 253, 0.5) 60%, rgba(178, 60, 253, 0) 70%);
-}
-
-.ripple-surface-success .ripple-wave {
- background-image: radial-gradient(circle, rgba(0, 183, 74, 0.2) 0, rgba(0, 183, 74, 0.3) 40%, rgba(0, 183, 74, 0.4) 50%, rgba(0, 183, 74, 0.5) 60%, rgba(0, 183, 74, 0) 70%);
-}
-
-.ripple-surface-info .ripple-wave {
- background-image: radial-gradient(circle, rgba(57, 192, 237, 0.2) 0, rgba(57, 192, 237, 0.3) 40%, rgba(57, 192, 237, 0.4) 50%, rgba(57, 192, 237, 0.5) 60%, rgba(57, 192, 237, 0) 70%);
-}
-
-.ripple-surface-warning .ripple-wave {
- background-image: radial-gradient(circle, rgba(255, 169, 0, 0.2) 0, rgba(255, 169, 0, 0.3) 40%, rgba(255, 169, 0, 0.4) 50%, rgba(255, 169, 0, 0.5) 60%, rgba(255, 169, 0, 0) 70%);
-}
-
-.ripple-surface-danger .ripple-wave {
- background-image: radial-gradient(circle, rgba(249, 49, 84, 0.2) 0, rgba(249, 49, 84, 0.3) 40%, rgba(249, 49, 84, 0.4) 50%, rgba(249, 49, 84, 0.5) 60%, rgba(249, 49, 84, 0) 70%);
-}
-
-.ripple-surface-light .ripple-wave {
- background-image: radial-gradient(circle, rgba(251, 251, 251, 0.2) 0, rgba(251, 251, 251, 0.3) 40%, rgba(251, 251, 251, 0.4) 50%, rgba(251, 251, 251, 0.5) 60%, rgba(251, 251, 251, 0) 70%);
-}
-
-.ripple-surface-dark .ripple-wave {
- background-image: radial-gradient(circle, rgba(38, 38, 38, 0.2) 0, rgba(38, 38, 38, 0.3) 40%, rgba(38, 38, 38, 0.4) 50%, rgba(38, 38, 38, 0.5) 60%, rgba(38, 38, 38, 0) 70%);
-}
-
-.ripple-surface-white .ripple-wave {
- background-image: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
-}
-
-.ripple-surface-black .ripple-wave {
- background-image: radial-gradient(circle, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, transparent 70%);
-}
-
-.datepicker-toggle-button {
- position: absolute;
- outline: none;
- border: none;
- background-color: transparent;
- right: 10px;
- top: 50%;
- transform: translate(-50%, -50%);
-}
-
-.datepicker-toggle-button:focus {
- color: #2979ff;
-}
-
-.datepicker-toggle-button:hover {
- color: #2979ff;
-}
-
-.datepicker-backdrop {
- width: 100%;
- height: 100%;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: rgba(0, 0, 0, 0.4);
- z-index: 1065;
-}
-
-.datepicker-dropdown-container {
- width: 328px;
- height: 380px;
- background-color: #fff;
- border-radius: 0.5rem;
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
- z-index: 1066;
-}
-
-.datepicker-modal-container {
- display: flex;
- flex-direction: column;
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 328px;
- height: 512px;
- background-color: #fff;
- border-radius: 0.6rem 0.6rem 0.5rem 0.5rem;
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
- z-index: 1066;
-}
-
-.datepicker-header {
- height: 120px;
- padding-right: 24px;
- padding-left: 24px;
- background-color: #2979ff;
- display: flex;
- flex-direction: column;
- border-radius: 0.5rem 0.5rem 0 0;
-}
-
-.datepicker-title {
- height: 32px;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
-}
-
-.datepicker-title-text {
- font-size: 10px;
- font-weight: 400;
- -webkit-text-transform: uppercase;
- text-transform: uppercase;
- letter-spacing: 1.7px;
- color: #fff;
-}
-
-.datepicker-date {
- height: 72px;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
-}
-
-.datepicker-date-text {
- font-size: 34px;
- font-weight: 400;
- color: #fff;
-}
-
-.datepicker-main {
- position: relative;
- height: 100%;
-}
-
-.datepicker-date-controls {
- padding: 10px 12px 0 12px;
- display: flex;
- justify-content: space-between;
- color: rgba(0, 0, 0, 0.64);
-}
-
-.datepicker-view-change-button {
- padding: 10px;
- color: #666;
- font-weight: 500;
- font-size: 0.9rem;
- border-radius: 10px;
- box-shadow: none;
- background-color: transparent;
- margin: 0;
- border: none;
-}
-
-.datepicker-view-change-button:hover {
- background-color: #eee;
-}
-
-.datepicker-view-change-button:focus {
- background-color: #eee;
-}
-
-.datepicker-view-change-button:after {
- content: "";
- display: inline-block;
- width: 0;
- height: 0;
- border-left: 5px solid transparent;
- border-right: 5px solid transparent;
- border-top-width: 5px;
- border-top-style: solid;
- margin: 0 0 0 5px;
- vertical-align: middle;
-}
-
-.datepicker-arrow-controls {
- margin-top: 10px;
-}
-
-.datepicker-previous-button {
- position: relative;
- padding: 0;
- width: 40px;
- height: 40px;
- line-height: 40px;
- border: none;
- outline: none;
- margin: 0;
- color: rgba(0, 0, 0, 0.64);
- background-color: transparent;
- margin-right: 24px;
-}
-
-.datepicker-previous-button:hover {
- background-color: #eee;
- border-radius: 50%;
-}
-
-.datepicker-previous-button:focus {
- background-color: #eee;
- border-radius: 50%;
-}
-
-.datepicker-previous-button::after {
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- position: absolute;
- content: "";
- margin: 15.5px;
- border: 0 solid currentColor;
- border-top-width: 2px;
- border-left-width: 2px;
- transform: translateX(2px) rotate(-45deg);
-}
-
-.datepicker-next-button {
- position: relative;
- padding: 0;
- width: 40px;
- height: 40px;
- line-height: 40px;
- border: none;
- outline: none;
- margin: 0;
- color: rgba(0, 0, 0, 0.64);
- background-color: transparent;
-}
-
-.datepicker-next-button:hover {
- background-color: #eee;
- border-radius: 50%;
-}
-
-.datepicker-next-button:focus {
- background-color: #eee;
- border-radius: 50%;
-}
-
-.datepicker-next-button::after {
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- position: absolute;
- content: "";
- margin: 15.5px;
- border: 0 solid currentColor;
- border-top-width: 2px;
- border-right-width: 2px;
- transform: translateX(-2px) rotate(45deg);
-}
-
-.datepicker-view {
- padding-left: 12px;
- padding-right: 12px;
- outline: none;
-}
-
-.datepicker-table {
- margin-right: auto;
- margin-left: auto;
- width: 304px;
-}
-
-.datepicker-day-heading {
- width: 40px;
- height: 40px;
- -webkit-text-align: center;
- text-align: center;
- font-size: 12px;
- font-weight: 400;
-}
-
-.datepicker-cell {
- -webkit-text-align: center;
- text-align: center;
-}
-
-.datepicker-cell.disabled {
- color: #ccc;
- cursor: default;
- pointer-events: none;
-}
-
-.datepicker-cell.disabled:hover {
- cursor: default;
-}
-
-.datepicker-cell:hover {
- cursor: pointer;
-}
-
-.datepicker-cell:not(.disabled):not(.selected):hover .datepicker-cell-content {
- background-color: #d3d3d3;
-}
-
-.datepicker-cell.selected .datepicker-cell-content {
- background-color: #2979ff;
- color: #fff;
-}
-
-.datepicker-cell:not(.selected).focused .datepicker-cell-content {
- background-color: #eee;
-}
-
-.datepicker-cell.focused .datepicker-cell-content.selected {
- background-color: #2979ff;
-}
-
-.datepicker-cell.current .datepicker-cell-content {
- border: 1px solid #000;
-}
-
-.datepicker-small-cell {
- width: 40px;
- height: 40px;
-}
-
-.datepicker-small-cell-content {
- width: 36px;
- height: 36px;
- line-height: 36px;
- border-radius: 50%;
- font-size: 13px;
-}
-
-.datepicker-large-cell {
- width: 76px;
- height: 42px;
-}
-
-.datepicker-large-cell-content {
- width: 72px;
- height: 40px;
- line-height: 40px;
- padding: 1px 2px;
- border-radius: 999px;
-}
-
-.datepicker-footer {
- height: 56px;
- display: flex;
- position: absolute;
- width: 100%;
- bottom: 0;
- justify-content: flex-end;
- align-items: center;
- padding-left: 12px;
- padding-right: 12px;
-}
-
-.datepicker-footer-btn {
- background-color: #fff;
- color: #2979ff;
- border: none;
- cursor: pointer;
- padding: 0 10px;
- -webkit-text-transform: uppercase;
- text-transform: uppercase;
- font-size: 0.8rem;
- font-weight: 500;
- height: 40px;
- line-height: 40px;
- letter-spacing: 0.1rem;
- border-radius: 10px;
- margin-bottom: 10px;
-}
-
-.datepicker-footer-btn:hover {
- background-color: #eee;
-}
-
-.datepicker-footer-btn:focus {
- background-color: #eee;
-}
-
-.datepicker-clear-btn {
- margin-right: auto;
-}
-
-.timepicker-wrapper {
- touch-action: none;
- z-index: 1065;
- opacity: 0;
- right: 0;
- bottom: 0;
- top: 0;
- left: 0;
- background-color: rgba(0, 0, 0, 0.4);
-}
-
-.timepicker-elements {
- min-width: 310px;
- min-height: 325px;
- background: #fff;
- border-top-right-radius: 0.6rem;
- border-top-left-radius: 0.6rem;
-}
-
-.timepicker-head {
- background-color: #2979ff;
- height: 100px;
- border-top-right-radius: 0.5rem;
- border-top-left-radius: 0.5rem;
- padding: 10px 24px 10px 50px;
-}
-
-.timepicker-button {
- font-size: 0.8rem;
- min-width: 64px;
- box-sizing: border-box;
- font-weight: 500;
- line-height: 40px;
- border-radius: 10px;
- letter-spacing: 0.1rem;
- -webkit-text-transform: uppercase;
- text-transform: uppercase;
- color: #2979ff;
- border: none;
- background-color: transparent;
- transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
- outline: none;
- padding: 0 10px;
- height: 40px;
- margin-bottom: 10px;
-}
-
-.timepicker-button:hover {
- background-color: rgba(0, 0, 0, 0.08);
-}
-
-.timepicker-button:focus {
- outline: none;
- background-color: rgba(0, 0, 0, 0.08);
-}
-
-.timepicker-current {
- font-size: 3.75rem;
- font-weight: 300;
- line-height: 1.2;
- letter-spacing: -0.00833em;
- color: #fff;
- opacity: 0.54;
- border: none;
- background: transparent;
- padding: 0;
-}
-
-.timepicker-current.active {
- opacity: 1;
-}
-
-.timepicker-current-wrapper {
- direction: ltr;
-}
-
-.timepicker-mode-wrapper {
- margin-left: 20px;
- font-size: 18px;
- color: rgba(255, 255, 255, 0.54);
-}
-
-.timepicker-mode-wrapper.active {
- opacity: 1;
-}
-
-.timepicker-clock-wrapper {
- min-width: 310px;
- max-width: 325px;
- min-height: 305px;
- overflow-x: hidden;
- height: 100%;
-}
-
-.timepicker-clock {
- position: relative;
- border-radius: 100%;
- width: 260px;
- height: 260px;
- cursor: default;
- margin: 0 auto;
- background-color: rgba(0, 0, 0, 0.07);
-}
-
-.timepicker-time-tips-minutes.active {
- color: #fff;
- background-color: #2979ff;
- font-weight: 400;
-}
-
-.timepicker-time-tips-inner.active {
- color: #fff;
- background-color: #2979ff;
- font-weight: 400;
-}
-
-.timepicker-time-tips-hours.active {
- color: #fff;
- background-color: #2979ff;
- font-weight: 400;
-}
-
-.timepicker-time-tips-minutes.disabled {
- color: #b3afaf;
- pointer-events: none;
- background-color: transparent;
-}
-
-.timepicker-time-tips-inner.disabled {
- color: #b3afaf;
- pointer-events: none;
- background-color: transparent;
-}
-
-.timepicker-time-tips-hours.disabled {
- color: #b3afaf;
- pointer-events: none;
- background-color: transparent;
-}
-
-.timepicker-dot {
- font-weight: 300;
- line-height: 1.2;
- letter-spacing: -0.00833em;
- color: #fff;
- font-size: 3.75rem;
- opacity: 0.54;
- border: none;
- background: transparent;
- padding: 0;
-}
-
-.timepicker-middle-dot {
- top: 50%;
- left: 50%;
- width: 6px;
- height: 6px;
- transform: translate(-50%, -50%);
- border-radius: 50%;
- background-color: #2979ff;
-}
-
-.timepicker-hand-pointer {
- background-color: #2979ff;
- bottom: 50%;
- height: 40%;
- left: calc(50% - 1px);
- transform-origin: center bottom 0;
- width: 2px;
-}
-
-.timepicker-time-tips.active {
- color: #fff;
-}
-
-.timepicker-circle {
- top: -21px;
- left: -15px;
- width: 4px;
- border: 14px solid #2979ff;
- height: 4px;
- box-sizing: content-box;
- border-radius: 100%;
-}
-
-.timepicker-hour-mode {
- padding: 0;
- background-color: transparent;
- border: none;
- color: #fff;
- opacity: 0.54;
- cursor: pointer;
-}
-
-.timepicker-hour {
- cursor: pointer;
-}
-
-.timepicker-minute {
- cursor: pointer;
-}
-
-.timepicker-hour-mode:hover {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-hour-mode:focus {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-hour:hover {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-hour:focus {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-minute:hover {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-minute:focus {
- background-color: rgba(0, 0, 0, 0.15);
- outline: none;
-}
-
-.timepicker-hour-mode.active {
- color: #fff;
- opacity: 1;
-}
-
-.timepicker-hour.active {
- color: #fff;
- opacity: 1;
-}
-
-.timepicker-minute.active {
- color: #fff;
- opacity: 1;
-}
-
-.timepicker-footer {
- border-bottom-left-radius: 0.5rem;
- border-bottom-right-radius: 0.5rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- height: 56px;
- padding-left: 12px;
- padding-right: 12px;
- background-color: #fff;
-}
-
-.timepicker-container {
- max-height: calc(100% - 64px);
- overflow-y: auto;
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
-}
-
-.timepicker-icon-up.active {
- opacity: 1;
-}
-
-.timepicker-icon-down.active {
- opacity: 1;
-}
-
-.timepicker-toggle-button {
- position: absolute;
- outline: none;
- border: none;
- background-color: transparent;
- right: 10px;
- top: 50%;
- transform: translate(-50%, -50%);
- transition: all 0.3s ease;
- cursor: pointer;
-}
-
-.timepicker-toggle-button:hover {
- color: #2979ff;
-}
-
-.timepicker-toggle-button:focus {
- color: #2979ff;
-}
-
-.timepicker-input:focus + .timepicker-toggle-button {
- color: #2979ff;
-}
-
-.timepicker-input:focus + .timepicker-toggle-button i {
- color: #2979ff;
-}
-
-.timepicker a.timepicker-toggle-button {
- right: 1px;
-}
-
-.timepicker-toggle-button.timepicker-icon {
- right: 1px;
-}
-
-.timepicker-modal .fade.show {
- opacity: 1;
-}
-
-.stepper {
- position: relative;
- padding: 0;
- margin: 0;
- width: 100%;
- list-style: none;
- overflow: hidden;
- transition: height 0.2s ease-in-out;
-}
-
-.stepper:not(.stepper-vertical) {
- display: flex;
- justify-content: space-between;
-}
-
-.stepper:not(.stepper-vertical) .stepper-content {
- position: absolute;
- width: 100%;
- padding: 1rem;
-}
-
-.stepper:not(.stepper-vertical) .stepper-step {
- flex: auto;
- height: 4.5rem;
-}
-
-.stepper:not(.stepper-vertical) .stepper-step:first-child .stepper-head {
- padding-left: 1.5rem;
-}
-
-.stepper:not(.stepper-vertical) .stepper-step:last-child .stepper-head {
- padding-right: 1.5rem;
-}
-
-.stepper:not(.stepper-vertical) .stepper-step:not(:first-child) .stepper-head:before {
- flex: 1;
- height: 1px;
- width: 100%;
- margin-right: 0.5rem;
- content: "";
- background-color: rgba(0, 0, 0, 0.1);
-}
-
-.stepper:not(.stepper-vertical) .stepper-step:not(:last-child) .stepper-head:after {
- flex: 1;
- height: 1px;
- width: 100%;
- margin-left: 0.5rem;
- content: "";
- background-color: rgba(0, 0, 0, 0.1);
-}
-
-.stepper:not(.stepper-vertical) .stepper-head-icon {
- margin: 1.5rem 0.5rem 1.5rem 0;
-}
-
-.stepper.stepper-mobile {
- justify-content: center;
- align-items: flex-end;
-}
-
-.stepper.stepper-mobile.stepper-progress-bar .stepper-head-icon {
- display: none;
-}
-
-.stepper.stepper-mobile .stepper-step {
- flex: unset;
- height: -moz-fit-content;
- height: fit-content;
- margin: 1rem 0 1rem 0;
-}
-
-.stepper.stepper-mobile .stepper-step:not(:last-child) .stepper-head:after {
- margin-left: 0;
-}
-
-.stepper.stepper-mobile .stepper-step:not(:first-child) .stepper-head:before {
- margin-right: 0;
-}
-
-.stepper.stepper-mobile .stepper-step:not(:last-child):not(:first-child) .stepper-head {
- padding-left: 0.25rem;
- padding-right: 0.25rem;
-}
-
-.stepper.stepper-mobile .stepper-head-icon {
- font-size: 0;
- margin: 0;
- height: 0.5rem;
- width: 0.5rem;
- z-index: 1;
-}
-
-.stepper.stepper-mobile .stepper-head-text {
- display: none;
-}
-
-.stepper.stepper-mobile .stepper-content {
- top: 2.56rem;
-}
-
-@media (prefers-reduced-motion: reduce) {
- .form-control::file-selector-button {
- transition: none;
- }
-
- .form-control::-webkit-file-upload-button {
- -webkit-transition: none;
- transition: none;
- }
-
- .form-switch .form-check-input {
- transition: none;
- }
-
- .form-range::-webkit-slider-thumb {
- -webkit-transition: none;
- transition: none;
- }
-
- .form-range::-moz-range-thumb {
- -moz-transition: none;
- transition: none;
- }
-
- .form-floating > label {
- transition: none;
- }
-
- .fade {
- transition: none;
- }
-
- .collapsing {
- transition: none;
- }
-
- .collapsing.collapse-horizontal {
- transition: none;
- }
-
- .accordion-button::after {
- transition: none;
- }
-
- .modal.fade .modal-dialog {
- transition: none;
- }
-
- .carousel-item {
- transition: none;
- }
-
- .carousel-fade .active.carousel-item-start {
- transition: none;
- }
-
- .carousel-fade .active.carousel-item-end {
- transition: none;
- }
-
- .carousel-control-prev {
- transition: none;
- }
-
- .carousel-control-next {
- transition: none;
- }
-
- .carousel-indicators [data-bs-target] {
- transition: none;
- }
-
- .spinner-border {
- animation-duration: 1.5s;
- }
-
- .spinner-grow {
- animation-duration: 1.5s;
- }
-}
-
-@media (min-width: 576px) {
- .navbar-expand-sm {
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- .navbar-expand-sm .navbar-nav {
- flex-direction: row;
- }
-
- .navbar-expand-sm .navbar-nav .dropdown-menu {
- position: absolute;
- }
-
- .navbar-expand-sm .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
- }
-
- .navbar-expand-sm .navbar-nav-scroll {
- overflow: visible;
- }
-
- .navbar-expand-sm .navbar-collapse {
- display: flex !important;
- -ms-flex-basis: auto;
- flex-basis: auto;
- }
-
- .navbar-expand-sm .navbar-toggler {
- display: none;
- }
-
- .navbar-expand-sm .offcanvas-header {
- display: none;
- }
-
- .navbar-expand-sm .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
- }
-
- .navbar-expand-sm .offcanvas-top {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-sm .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-sm .offcanvas-body {
- display: flex;
- -ms-flex-grow: 0;
- flex-grow: 0;
- padding: 0;
- overflow-y: visible;
- }
-
- .modal-dialog {
- max-width: 500px;
- margin: 1.75rem auto;
- }
-
- .modal-dialog-scrollable {
- height: calc(100% - 3.5rem);
- }
-
- .modal-dialog-centered {
- min-height: calc(100% - 3.5rem);
- }
-
- .modal-sm {
- max-width: 300px;
- }
-
- .sticky-sm-top {
- position: sticky;
- top: 0;
- z-index: 1020;
- }
-}
-
-@media (min-width: 768px) {
- .navbar-expand-md {
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- .navbar-expand-md .navbar-nav {
- flex-direction: row;
- }
-
- .navbar-expand-md .navbar-nav .dropdown-menu {
- position: absolute;
- }
-
- .navbar-expand-md .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
- }
-
- .navbar-expand-md .navbar-nav-scroll {
- overflow: visible;
- }
-
- .navbar-expand-md .navbar-collapse {
- display: flex !important;
- -ms-flex-basis: auto;
- flex-basis: auto;
- }
-
- .navbar-expand-md .navbar-toggler {
- display: none;
- }
-
- .navbar-expand-md .offcanvas-header {
- display: none;
- }
-
- .navbar-expand-md .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
- }
-
- .navbar-expand-md .offcanvas-top {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-md .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-md .offcanvas-body {
- display: flex;
- -ms-flex-grow: 0;
- flex-grow: 0;
- padding: 0;
- overflow-y: visible;
- }
-
- .sticky-md-top {
- position: sticky;
- top: 0;
- z-index: 1020;
- }
-}
-
-@media (min-width: 992px) {
- .navbar-expand-lg {
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- .navbar-expand-lg .navbar-nav {
- flex-direction: row;
- }
-
- .navbar-expand-lg .navbar-nav .dropdown-menu {
- position: absolute;
- }
-
- .navbar-expand-lg .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
- }
-
- .navbar-expand-lg .navbar-nav-scroll {
- overflow: visible;
- }
-
- .navbar-expand-lg .navbar-collapse {
- display: flex !important;
- -ms-flex-basis: auto;
- flex-basis: auto;
- }
-
- .navbar-expand-lg .navbar-toggler {
- display: none;
- }
-
- .navbar-expand-lg .offcanvas-header {
- display: none;
- }
-
- .navbar-expand-lg .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
- }
-
- .navbar-expand-lg .offcanvas-top {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-lg .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-lg .offcanvas-body {
- display: flex;
- -ms-flex-grow: 0;
- flex-grow: 0;
- padding: 0;
- overflow-y: visible;
- }
-
- .modal-lg {
- max-width: 800px;
- }
-
- .modal-xl {
- max-width: 800px;
- }
-
- .sticky-lg-top {
- position: sticky;
- top: 0;
- z-index: 1020;
- }
-}
-
-@media (min-width: 1200px) {
- .navbar-expand-xl {
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- .navbar-expand-xl .navbar-nav {
- flex-direction: row;
- }
-
- .navbar-expand-xl .navbar-nav .dropdown-menu {
- position: absolute;
- }
-
- .navbar-expand-xl .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
- }
-
- .navbar-expand-xl .navbar-nav-scroll {
- overflow: visible;
- }
-
- .navbar-expand-xl .navbar-collapse {
- display: flex !important;
- -ms-flex-basis: auto;
- flex-basis: auto;
- }
-
- .navbar-expand-xl .navbar-toggler {
- display: none;
- }
-
- .navbar-expand-xl .offcanvas-header {
- display: none;
- }
-
- .navbar-expand-xl .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
- }
-
- .navbar-expand-xl .offcanvas-top {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-xl .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-xl .offcanvas-body {
- display: flex;
- -ms-flex-grow: 0;
- flex-grow: 0;
- padding: 0;
- overflow-y: visible;
- }
-
- .modal-xl {
- max-width: 1140px;
- }
-
- .sticky-xl-top {
- position: sticky;
- top: 0;
- z-index: 1020;
- }
-}
-
-@media (min-width: 1400px) {
- .navbar-expand-xxl {
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- .navbar-expand-xxl .navbar-nav {
- flex-direction: row;
- }
-
- .navbar-expand-xxl .navbar-nav .dropdown-menu {
- position: absolute;
- }
-
- .navbar-expand-xxl .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
- }
-
- .navbar-expand-xxl .navbar-nav-scroll {
- overflow: visible;
- }
-
- .navbar-expand-xxl .navbar-collapse {
- display: flex !important;
- -ms-flex-basis: auto;
- flex-basis: auto;
- }
-
- .navbar-expand-xxl .navbar-toggler {
- display: none;
- }
-
- .navbar-expand-xxl .offcanvas-header {
- display: none;
- }
-
- .navbar-expand-xxl .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
- -ms-flex-grow: 1;
- flex-grow: 1;
- visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
- transition: none;
- transform: none;
- }
-
- .navbar-expand-xxl .offcanvas-top {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-xxl .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
- }
-
- .navbar-expand-xxl .offcanvas-body {
- display: flex;
- -ms-flex-grow: 0;
- flex-grow: 0;
- padding: 0;
- overflow-y: visible;
- }
-
- .sticky-xxl-top {
- position: sticky;
- top: 0;
- z-index: 1020;
- }
-}
-
-@media (max-width: 575.98px) {
- .modal-fullscreen-sm-down {
- width: 100vw;
- max-width: none;
- height: 100%;
- margin: 0;
- }
-
- .modal-fullscreen-sm-down .modal-content {
- height: 100%;
- border: 0;
- border-radius: 0;
- }
-
- .modal-fullscreen-sm-down .modal-header {
- border-radius: 0;
- }
-
- .modal-fullscreen-sm-down .modal-body {
- overflow-y: auto;
- }
-
- .modal-fullscreen-sm-down .modal-footer {
- border-radius: 0;
- }
-}
-
-@media (max-width: 767.98px) {
- .modal-fullscreen-md-down {
- width: 100vw;
- max-width: none;
- height: 100%;
- margin: 0;
- }
-
- .modal-fullscreen-md-down .modal-content {
- height: 100%;
- border: 0;
- border-radius: 0;
- }
-
- .modal-fullscreen-md-down .modal-header {
- border-radius: 0;
- }
-
- .modal-fullscreen-md-down .modal-body {
- overflow-y: auto;
- }
-
- .modal-fullscreen-md-down .modal-footer {
- border-radius: 0;
- }
-}
-
-@media (max-width: 991.98px) {
- .modal-fullscreen-lg-down {
- width: 100vw;
- max-width: none;
- height: 100%;
- margin: 0;
- }
-
- .modal-fullscreen-lg-down .modal-content {
- height: 100%;
- border: 0;
- border-radius: 0;
- }
-
- .modal-fullscreen-lg-down .modal-header {
- border-radius: 0;
- }
-
- .modal-fullscreen-lg-down .modal-body {
- overflow-y: auto;
- }
-
- .modal-fullscreen-lg-down .modal-footer {
- border-radius: 0;
- }
-}
-
-@media (max-width: 1199.98px) {
- .modal-fullscreen-xl-down {
- width: 100vw;
- max-width: none;
- height: 100%;
- margin: 0;
- }
-
- .modal-fullscreen-xl-down .modal-content {
- height: 100%;
- border: 0;
- border-radius: 0;
- }
-
- .modal-fullscreen-xl-down .modal-header {
- border-radius: 0;
- }
-
- .modal-fullscreen-xl-down .modal-body {
- overflow-y: auto;
- }
-
- .modal-fullscreen-xl-down .modal-footer {
- border-radius: 0;
- }
-}
-
-@media (max-width: 1399.98px) {
- .modal-fullscreen-xxl-down {
- width: 100vw;
- max-width: none;
- height: 100%;
- margin: 0;
- }
-
- .modal-fullscreen-xxl-down .modal-content {
- height: 100%;
- border: 0;
- border-radius: 0;
- }
-
- .modal-fullscreen-xxl-down .modal-header {
- border-radius: 0;
- }
-
- .modal-fullscreen-xxl-down .modal-body {
- overflow-y: auto;
- }
-
- .modal-fullscreen-xxl-down .modal-footer {
- border-radius: 0;
- }
-}
-
-@media (prefers-reduced-motion) {
- .animation {
- transition: none !important;
- animation: unset !important;
- }
-}
-
-@media screen and (min-width: 320px) and (max-width: 820px) and (orientation: landscape) {
- .datepicker-modal-container .datepicker-header {
- height: 100%;
- }
-
- .datepicker-modal-container .datepicker-date {
- margin-top: 100px;
- }
-
- .datepicker-modal-container .datepicker-day-cell {
- width: 32x;
- height: 32x;
- }
-
- .datepicker-modal-container {
- flex-direction: row;
- width: 475px;
- height: 360px;
- }
-
- .datepicker-modal-container.datepicker-day-cell {
- width: 36px;
- height: 36px;
- }
-}
-
-@media screen and (min-width: 320px) and (max-width: 825px) and (orientation: landscape) {
- .timepicker-elements {
- flex-direction: row !important;
- border-bottom-left-radius: 0.5rem;
- min-width: auto;
- min-height: auto;
- overflow-y: auto;
- }
-
- .timepicker-head {
- border-top-right-radius: 0;
- border-bottom-left-radius: 0;
- padding: 10px;
- padding-right: 10px !important;
- height: auto;
- min-height: 305px;
- }
-
- .timepicker-head-content {
- flex-direction: column;
- }
-
- .timepicker-mode-wrapper {
- justify-content: space-around !important;
- flex-direction: row !important;
- }
-
- .timepicker-current {
- font-size: 3rem;
- font-weight: 400;
- }
-
- .timepicker-dot {
- font-size: 3rem;
- font-weight: 400;
- }
-}
-
-@keyframes _spinner-grow {
- 0% {
- transform: scale(0);
- }
-
- 50% {
- opacity: 1;
- transform: none;
- }
-}
-
-@keyframes _fade-in {
- from {
- opacity: 0;
- }
-
- to {
- opacity: 1;
- }
-}
-
-@keyframes _fade-out {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- }
-}
-
-@keyframes _fade-in-down {
- from {
- opacity: 0;
- transform: translate3d(0, -100%, 0);
- }
-
- to {
- opacity: 1;
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _fade-in-left {
- from {
- opacity: 0;
- transform: translate3d(-100%, 0, 0);
- }
-
- to {
- opacity: 1;
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _fade-in-right {
- from {
- opacity: 0;
- transform: translate3d(100%, 0, 0);
- }
-
- to {
- opacity: 1;
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _fade-in-up {
- from {
- opacity: 0;
- transform: translate3d(0, 100%, 0);
- }
-
- to {
- opacity: 1;
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _fade-out-down {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- transform: translate3d(0, 100%, 0);
- }
-}
-
-@keyframes _fade-out-left {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- transform: translate3d(-100%, 0, 0);
- }
-}
-
-@keyframes _fade-out-right {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- transform: translate3d(100%, 0, 0);
- }
-}
-
-@keyframes _fade-out-up {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- transform: translate3d(0, -100%, 0);
- }
-}
-
-@keyframes _slide-in-down {
- from {
- visibility: visible;
- transform: translate3d(0, -100%, 0);
- }
-
- to {
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _slide-in-left {
- from {
- visibility: visible;
- transform: translate3d(-100%, 0, 0);
- }
-
- to {
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _slide-in-right {
- from {
- visibility: visible;
- transform: translate3d(100%, 0, 0);
- }
-
- to {
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _slide-in-up {
- from {
- visibility: visible;
- transform: translate3d(0, 100%, 0);
- }
-
- to {
- transform: translate3d(0, 0, 0);
- }
-}
-
-@keyframes _slide-out-down {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- visibility: hidden;
- transform: translate3d(0, 100%, 0);
- }
-}
-
-@keyframes _slide-out-left {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- visibility: hidden;
- transform: translate3d(-100%, 0, 0);
- }
-}
-
-@keyframes _slide-out-right {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- visibility: hidden;
- transform: translate3d(100%, 0, 0);
- }
-}
-
-@keyframes _slide-out-up {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- visibility: hidden;
- transform: translate3d(0, -100%, 0);
- }
-}
-
-@keyframes _slide-down {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- transform: translate3d(0, 100%, 0);
- }
-}
-
-@keyframes _slide-left {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- transform: translate3d(-100%, 0, 0);
- }
-}
-
-@keyframes _slide-right {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- transform: translate3d(100%, 0, 0);
- }
-}
-
-@keyframes _slide-up {
- from {
- transform: translate3d(0, 0, 0);
- }
-
- to {
- transform: translate3d(0, -100%, 0);
- }
-}
-
-@keyframes _zoom-in {
- from {
- opacity: 0;
- transform: scale3d(0.3, 0.3, 0.3);
- }
-
- 50% {
- opacity: 1;
- }
-}
-
-@keyframes _zoom-out {
- from {
- opacity: 1;
- }
-
- 50% {
- opacity: 0;
- transform: scale3d(0.3, 0.3, 0.3);
- }
-
- to {
- opacity: 0;
- }
-}
-
-@keyframes _tada {
- from {
- transform: scale3d(1, 1, 1);
- }
-
- 10% {
- transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
- }
-
- 20% {
- transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
- }
-
- 30% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
- }
-
- 50% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
- }
-
- 70% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
- }
-
- 90% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
- }
-
- 40% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
- }
-
- 60% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
- }
-
- 80% {
- transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
- }
-
- to {
- transform: scale3d(1, 1, 1);
- }
-}
-
-@keyframes _pulse {
- from {
- transform: scale3d(1, 1, 1);
- }
-
- 50% {
- transform: scale3d(1.05, 1.05, 1.05);
- }
-
- to {
- transform: scale3d(1, 1, 1);
- }
-}
-
-@keyframes _show-up-clock {
- 0% {
- opacity: 0;
- transform: scale(0.7);
- }
-
- to {
- opacity: 1;
- transform: scale(1);
- }
-}
-
-.visible {
- visibility: visible;
-}
-
-.collapse {
- visibility: collapse;
-}
-
-.static {
- position: static;
-}
-
-.fixed {
- position: fixed;
-}
-
-.absolute {
- position: absolute;
-}
-
-.relative {
- position: relative;
-}
-
-.sticky {
- position: sticky;
-}
-
-.my-10 {
- margin-top: 2.5rem;
- margin-bottom: 2.5rem;
-}
-
-.my-3 {
- margin-top: 0.75rem;
- margin-bottom: 0.75rem;
-}
-
-.block {
- display: block;
-}
-
-.inline {
- display: inline;
-}
-
-.flex {
- display: flex;
-}
-
-.table {
- display: table;
-}
-
-.hidden {
- display: none;
-}
-
-.h-full {
- height: 100%;
-}
-
-.w-full {
- width: 100%;
-}
-
-.max-w-md {
- max-width: 28rem;
-}
-
-.transform {
- transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
-}
-
-.resize {
- resize: both;
-}
-
-.flex-row {
- flex-direction: row;
-}
-
-.flex-col {
- flex-direction: column;
-}
-
-.items-center {
- align-items: center;
-}
-
-.justify-center {
- justify-content: center;
-}
-
-.justify-between {
- justify-content: space-between;
-}
-
-.justify-around {
- justify-content: space-around;
-}
-
-.justify-evenly {
- justify-content: space-evenly;
-}
-
-.rounded-lg {
- border-radius: 0.5rem;
-}
-
-.bg-slate-200 {
- --tw-bg-opacity: 1;
- background-color: rgb(226 232 240 / var(--tw-bg-opacity));
-}
-
-.bg-slate-100 {
- --tw-bg-opacity: 1;
- background-color: rgb(241 245 249 / var(--tw-bg-opacity));
-}
-
-.p-6 {
- padding: 1.5rem;
-}
-
-.shadow-lg {
- --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
-}
-
-.outline {
- outline-style: solid;
-}
-
-.blur {
- --tw-blur: blur(8px);
- filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
-}
-
-.filter {
- filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
-}
-
-label.form-entry {
- margin-bottom: 0.5rem;
- display: inline-block;
- --tw-text-opacity: 1;
- color: rgb(51 65 85 / var(--tw-text-opacity));
-}
-
-@media (prefers-color-scheme: dark) {
- label.form-entry {
- --tw-text-opacity: 1;
- color: rgb(226 232 240 / var(--tw-text-opacity));
- }
-}
-
-input.form-entry {
- margin: 0px;
- display: block;
- width: 100%;
- border-radius: 0.25rem;
- border-width: 1px;
- border-style: solid;
- --tw-border-opacity: 1;
- border-color: rgb(203 213 225 / var(--tw-border-opacity));
- --tw-bg-opacity: 1;
- background-color: rgb(241 245 249 / var(--tw-bg-opacity));
- background-clip: padding-box;
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.375rem;
- padding-bottom: 0.375rem;
- font-size: 1rem;
- line-height: 1.5rem;
- font-weight: 400;
- --tw-text-opacity: 1;
- color: rgb(71 85 105 / var(--tw-text-opacity));
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
- transition-duration: 150ms;
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-input.form-entry:focus {
- --tw-border-opacity: 1;
- border-color: rgb(37 99 235 / var(--tw-border-opacity));
- --tw-bg-opacity: 1;
- background-color: rgb(241 245 249 / var(--tw-bg-opacity));
- --tw-text-opacity: 1;
- color: rgb(51 65 85 / var(--tw-text-opacity));
- outline: 2px solid transparent;
- outline-offset: 2px;
-}
-
-@media (prefers-color-scheme: dark) {
- input.form-entry {
- --tw-border-opacity: 1;
- border-color: rgb(75 85 99 / var(--tw-border-opacity));
- --tw-bg-opacity: 1;
- background-color: rgb(15 23 42 / var(--tw-bg-opacity));
- --tw-text-opacity: 1;
- color: rgb(203 213 225 / var(--tw-text-opacity));
- }
-
- input.form-entry:focus {
- --tw-border-opacity: 1;
- border-color: rgb(147 197 253 / var(--tw-border-opacity));
- --tw-bg-opacity: 1;
- background-color: rgb(30 41 59 / var(--tw-bg-opacity));
- --tw-text-opacity: 1;
- color: rgb(226 232 240 / var(--tw-text-opacity));
- }
-}
-
-small.form-entry {
- margin-top: 0.25rem;
- display: block;
- padding-left: 0.75rem;
- padding-right: 0.75rem;
- padding-top: 0.375rem;
- padding-bottom: 0.375rem;
- font-size: 0.75rem;
- line-height: 1rem;
- --tw-text-opacity: 1;
- color: rgb(71 85 105 / var(--tw-text-opacity));
-}
-
-@media (prefers-color-scheme: dark) {
- small.form-entry {
- --tw-text-opacity: 1;
- color: rgb(148 163 184 / var(--tw-text-opacity));
- }
-}
-
-a.link-primary {
- margin-bottom: 1rem;
- --tw-text-opacity: 1;
- color: rgb(59 130 246 / var(--tw-text-opacity));
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
- transition-duration: 300ms;
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-a.link-primary:hover {
- --tw-text-opacity: 1;
- color: rgb(37 99 235 / var(--tw-text-opacity));
-}
-
-a.link-primary:active {
- --tw-text-opacity: 1;
- color: rgb(29 78 216 / var(--tw-text-opacity));
-}
-
-div.btn {
- margin-top: 0.75rem;
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- -moz-column-gap: 1.5rem;
- column-gap: 1.5rem;
-}
-
-button.btn {
- display: inline-block;
- border-radius: 0.25rem;
- padding-left: 1.5rem;
- padding-right: 1.5rem;
- padding-top: 0.625rem;
- padding-bottom: 0.625rem;
- font-size: 0.75rem;
- line-height: 1rem;
- font-weight: 500;
- text-transform: uppercase;
- line-height: 1.25;
- --tw-text-opacity: 1;
- color: rgb(241 245 249 / var(--tw-text-opacity));
- --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
- transition-duration: 150ms;
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-button.btn:hover {
- --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
-}
-
-button.btn:focus {
- --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
- outline: 2px solid transparent;
- outline-offset: 2px;
- --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
- box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
-}
-
-button.btn:active {
- --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
-}
-
-button.btn-primary {
- --tw-bg-opacity: 1;
- background-color: rgb(37 99 235 / var(--tw-bg-opacity));
-}
-
-button.btn-primary:hover {
- --tw-bg-opacity: 1;
- background-color: rgb(29 78 216 / var(--tw-bg-opacity));
-}
-
-button.btn-primary:focus {
- --tw-bg-opacity: 1;
- background-color: rgb(29 78 216 / var(--tw-bg-opacity));
-}
-
-button.btn-primary:active {
- --tw-bg-opacity: 1;
- background-color: rgb(30 58 138 / var(--tw-bg-opacity));
-}
-
-button.btn-secondary {
- --tw-bg-opacity: 1;
- background-color: rgb(71 85 105 / var(--tw-bg-opacity));
-}
-
-button.btn-secondary:hover {
- --tw-bg-opacity: 1;
- background-color: rgb(51 65 85 / var(--tw-bg-opacity));
-}
-
-button.btn-secondary:focus {
- --tw-bg-opacity: 1;
- background-color: rgb(51 65 85 / var(--tw-bg-opacity));
-}
-
-button.btn-secondary:active {
- --tw-bg-opacity: 1;
- background-color: rgb(15 23 42 / var(--tw-bg-opacity));
-}
-
-.hide {
- display: none !important;
-}
-
-@media (prefers-color-scheme: dark) {
- .dark\:bg-slate-900 {
- --tw-bg-opacity: 1;
- background-color: rgb(15 23 42 / var(--tw-bg-opacity));
- }
-
- .dark\:bg-slate-800 {
- --tw-bg-opacity: 1;
- background-color: rgb(30 41 59 / var(--tw-bg-opacity));
- }
-}
diff --git a/Source/loaderConfigurator/ldr_config_old.py b/Source/loaderConfigurator/ldr_config_old.py
deleted file mode 100755
index 809f5fd0..00000000
--- a/Source/loaderConfigurator/ldr_config_old.py
+++ /dev/null
@@ -1,173 +0,0 @@
-#!python3
-cust_conf = {
-# DRAM Timing:
-# 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density (Default).
-# 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
-# 2: ENTIRE_TABLE_ERISTA:
-# 3: ENTIRE_TABLE_MARIKO: Replace the entire max mtc table with customized one (provided by user).
- "mtcConf": 0,
-# Mariko CPU:
-# - Max Clock in kHz:
-# Default: 1785000
-# >= 2193000 will enable overvolting (> 1120 mV)
-# - Boost Clock in kHz:
-# Default: 1785000
-# Boost clock will be applied when applications request higher CPU frequency for quicker loading.
-# - Max Voltage in mV:
-# Default voltage: 1120
-# Haven't tested anything higher than 1220.
- "marikoCpuMaxClock": 2397000,
- "marikoCpuBoostClock": 1785000,
- "marikoCpuMaxVolt": 1220,
-# Mariko GPU:
-# - Max Clock in kHz:
-# Default: 921600
-# NVIDIA Maximum: 1267200
- "marikoGpuMaxClock": 1305600,
-# Mariko EMC:
-# - RAM Clock in kHz:
-# Values should be > 1600000, and divided evenly by 9600.
-# [WARNING]
-# RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM:
-# - Graphical glitches
-# - System instabilities
-# - NAND corruption
-# Timings from auto-adjustment have been tested safe for up to 1996.8 MHz for all DRAM chips.
- "marikoEmcMaxClock": 1996800,
-# Erista CPU:
-# Not tested and not enabled by default.
-# - Enable Overclock
-# Require modificaitions towards NewCpuTables!
-# - Max Voltage in mV
- "eristaCpuOCEnable": 0,
- "eristaCpuMaxVolt": 0,
-# Erista EMC:
-# - RAM Clock in kHz
-# [WARNING]
-# RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM:
-# - Graphical glitches
-# - System instabilities
-# - NAND corruption
-# - RAM Voltage in uV
-# Range: 600'000 to 1250'000 uV
-# Value should be divided evenly by 12'500
-# Default(HOS): 1125'000
-# Not enabled by default.
- "eristaEmcMaxClock": 1862400,
- "eristaEmcVolt": 0
-}
-
-cust_range = {
- "mtcConf": (0, 3),
- "marikoCpuMaxClock": (1785000, 3000000),
- "marikoCpuBoostClock": (1785000, 3000000),
- "marikoCpuMaxVolt": (1100, 1300),
- "marikoGpuMaxClock": (768000, 1536000),
- "marikoEmcMaxClock": (1612800, 2400000),
- "eristaCpuMaxVolt": (1100, 1400),
- "eristaEmcMaxClock": (1600000, 2400000),
- "eristaEmcVolt": (1100000, 1250000)
-}
-
-
-import struct
-import argparse
-
-cust_rev = 2
-cust_head = ["cust", "custRev"]
-cust_body = ["mtcConf",
- "marikoCpuMaxClock", "marikoCpuBoostClock", "marikoCpuMaxVolt", "marikoGpuMaxClock", "marikoEmcMaxClock",
- "eristaCpuOCEnable", "eristaCpuMaxVolt", "eristaEmcMaxClock", "eristaEmcVolt"]
-cust_key = [*cust_head, *cust_body]
-cust_val_num = len(cust_conf) - 1
-cust_fmt = '<4s2H' + str(cust_val_num) + 'I'
-cust_head_fmt = '<4s1H'
-cust_body_fmt = '<1H' + str(cust_val_num) + 'I'
-
-parser = argparse.ArgumentParser(description='Loader Configurator v'+str(cust_rev))
-parser.add_argument("file", help="Path of loader.kip")
-parser.add_argument("--save", "-s", action="store_true", help="Save configuration to loader.kip")
-parser.add_argument("--ignore", action="store_true", help="Ignore range safety check")
-args = parser.parse_args()
-
-def KIPCustParse(file_loc, conf_print=True) -> (int, dict):
- with open(file_loc, "rb") as file:
- header_str = b'KIP1Loader'
- header = file.read(len(header_str))
- file.seek(0)
- cust_magic = b'CUST'
- cust_pos = file.read().find(cust_magic)
-
- if header != header_str or cust_pos == -1:
- raise Exception("\n Invalid kip file!")
-
- file.seek(cust_pos)
- cust_size = struct.calcsize(cust_fmt)
- cust_buf = file.read(cust_size)
- cust_val = struct.unpack(cust_fmt, cust_buf)
- cust_dict = dict(zip(cust_key, cust_val))
-
- if cust_dict['custRev'] != cust_rev:
- raise Exception(f"\n custRev does NOT match, expected: {cust_rev}, got: {cust_dict['custRev']}!")
-
- [cust_dict.pop(key) for key in cust_head]
-
- if conf_print:
- print("Configuration from file")
- [print(f"- {i:20s} : {cust_dict[i]:8d}") for i in cust_dict]
-
- return (cust_pos, cust_dict)
-
-
-def CustRangeCheck(cust):
- range_error_str = ""
- for i in cust_range:
- val = int(cust[i])
- if val and (val < cust_range[i][0] or val > cust_range[i][1]) :
- range_error_str += f"\n- {i:20s} = {val:8d}, Expected range: {[*cust_range[i]]}"
-
- if range_error_str:
- raise ValueError(range_error_str)
-
-
-def KIPCustSave(file_loc, cust_pos, cust_dict, range_check=True, cust_to_save={}):
- missing = set(cust_body) - set(cust_to_save.keys())
- if missing:
- missing_str = "\n Invalid cust! Missing: "
- for i in missing:
- missing_str += f"\n- {i}"
- raise Exception(missing_str)
-
- if range_check:
- CustRangeCheck(cust_to_save)
-
- diff_count = 0
- for i in cust_body:
- diff_str = ""
- if cust_dict[i] != cust_conf[i]:
- diff_str = f"-> {cust_conf[i]:8d}"
- diff_count += 1
- print(f"- {i:20s} : {cust_dict[i]:8d} {diff_str}")
-
- if not diff_count:
- print("Cust is identical, abort saving!")
- return
-
- with open(file_loc, "rb+") as file:
- cust_bin = struct.pack(cust_body_fmt, *[cust_to_save[i] for i in cust_body])
- file.seek(cust_pos + struct.calcsize(cust_head_fmt))
- file.write(cust_bin)
-
- print("Done!")
-
-
-def main(file_loc, ignore=False, save=False):
- (cust_pos, cust_dict) = KIPCustParse(file_loc, conf_print=(not save))
-
- if save:
- print("Saving new configuration...")
- KIPCustSave(file_loc, cust_pos, cust_dict, range_check=(not ignore), cust_to_save=cust_conf)
-
-
-if __name__ == "__main__":
- main(args.file, args.ignore, args.save)
diff --git a/Source/loaderConfigurator/package.json b/Source/loaderConfigurator/package.json
deleted file mode 100644
index a763f8c7..00000000
--- a/Source/loaderConfigurator/package.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "devDependencies": {
- "tailwindcss": "^3.2.2"
- },
- "dependencies": {
- "tw-elements": "^1.0.0-alpha12"
- }
-}
diff --git a/Source/loaderConfigurator/src/index.html b/Source/loaderConfigurator/src/index.html
deleted file mode 100644
index 7daab2d8..00000000
--- a/Source/loaderConfigurator/src/index.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Source/loaderConfigurator/src/main.ts b/Source/loaderConfigurator/src/main.ts
deleted file mode 100644
index 0333ec8e..00000000
--- a/Source/loaderConfigurator/src/main.ts
+++ /dev/null
@@ -1,320 +0,0 @@
-const CUST_REV = 2;
-var buffer: string | ArrayBuffer;
-
-function FindMagicOffset(buffer) {
- let view = new DataView(buffer);
- for (let i = 0; i < view.byteLength; i += 4) {
- if (view.getUint32(i, true) == 0x54535543) { // "CUST"
- return i;
- }
- }
- throw new Error("Invalid loader.kip file");
-}
-
-function CustEntry(name: string, size: number, desc: string, defval: number, minmax = [0, 1_000_000], step = 1, extra_validator?) {
- this.name = name;
- this.size = size;
- this.desc = desc;
- this.defval = defval;
- this.min = minmax[0];
- this.max = minmax[1];
- this.step = step;
- this.validator = extra_validator;
- this.value = null;
- this.offset = null;
-}
-
-function InitCustTable() {
- let cust = [
- new CustEntry(
- "mtcConf",
- 2,
- "DRAM Timing \
- 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. \
- 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. ",
- 0,
- [0, 3],
- ),
- new CustEntry(
- "marikoCpuMaxClock",
- 4,
- "Mariko CPU Max Clock in kHz \
- System default: 1785000 \
- ≥ 2397000 will enable overvolting (> 1120 mV) ",
- 2397_000,
- [1785_000, 3000_000],
- 100,
- (x: number) => (x % 100) == 0
- ),
- new CustEntry(
- "marikoCpuBoostClock",
- 4,
- "Mariko CPU Boost Clock in kHz \
- System default: 1785000 \
- Must not be higher than marikoCpuMaxClock ",
- 1785_000,
- [1785_000, 3000_000],
- 100,
- (x: number) => (x % 100) == 0
- ),
- new CustEntry(
- "marikoCpuMaxVolt",
- 4,
- "Mariko CPU Max Voltage in mV \
- System default: 1120 \
- Acceptable range: 1100 ≤ x ≤ 1300 ",
- 1235,
- [1100, 1300],
- ),
- new CustEntry(
- "marikoGpuMaxClock",
- 4,
- "Mariko GPU Max Clock in kHz \
- System default: 921600 \
- Tegra X1+ official maximum: 1267200 ",
- 1305_600,
- [768_000, 1536_000],
- 100,
- (x: number) => (x % 100) == 0
- ),
- new CustEntry(
- "marikoEmcMaxClock",
- 4,
- "Mariko RAM Max Clock in kHz \
- Values should be > 1600000, and divided evenly by 3200. \
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
- 1996_800,
- [1612_800, 2400_000],
- 3200,
- (x: number) => (x % 3200) == 0
- ),
- new CustEntry(
- "eristaCpuOCEnable",
- 4,
- "Erista CPU Enable Overclock \
- Not tested ",
- 1,
- [0, 1]
- ),
- new CustEntry(
- "eristaCpuMaxVolt",
- 4,
- "Erista CPU Max Voltage in mV \
- Acceptable range: 1100 ≤ x ≤ 1400 ",
- 1257,
- [0, 1400],
- 100,
- (x: number) => x >= 1100
- ),
- new CustEntry(
- "eristaEmcMaxClock",
- 4,
- "Erista RAM Max Clock in kHz \
- Values should be > 1600000, and divided evenly by 3200. \
- WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
- 1862_400,
- [1600_000, 2400_000],
- 3200,
- (x: number) => (x % 3200) == 0
- ),
- new CustEntry(
- "eristaEmcVolt",
- 4,
- "Erista RAM Voltage in uV \
- Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
- Not enabled by default ",
- 0,
- [0, 1250_000],
- 12500,
- (x: number) => (x % 12500) == 0 && x >= 1100000
- ),
- ];
- return cust;
-}
-
-function ValidateCust(cust) {
- for (let i of cust) {
- if (i.value == 0)
- continue;
- if (i.value < i.min || i.value > i.max) {
- document.getElementById(i.name)!.focus();
- throw new Error(`Expected range: ${i.min} ≤ ${i.name} ≤ ${i.max}, got ${i.value}`);
- }
- if (i.validator && !i.validator(i.value)) {
- document.getElementById(i.name)!.focus();
- throw new Error(`Invalid value: ${i.value}(${i.name})\nValidator: ${i.validator}`);
- }
- }
-}
-
-function SaveCust(cust, buffer) {
- let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
- let view = new DataView(buffer);
- let storage = {};
- for (let i of cust) {
- let id = i.name;
- i.value = (document.getElementById(id) as HTMLInputElement).value;
- storage[i.name] = i.value;
- switch (i.size) {
- case 2:
- view.setUint16(i.offset, i.value, true);
- break;
- case 4:
- view.setUint32(i.offset, i.value, true);
- break;
- default:
- document.getElementById(i.name)!.focus();
- throw new Error("Unknown size at " + i);
- }
- }
- ValidateCust(cust);
- storage["custRev"] = CUST_REV;
- localStorage.setItem("last_saved", JSON.stringify(storage));
- let a = document.createElement("a");
- a.href = window.URL.createObjectURL(new Blob([buffer], {type: "application/octet-stream"}));
- a.download = "loader.kip";
- a.click();
-}
-
-function LastSaved() {
- let storage = localStorage.getItem("last_saved");
- if (!storage) {
- return false;
- }
- let sObj = JSON.parse(storage);
- if (!sObj["custRev"] || sObj["custRev"] != CUST_REV) {
- localStorage.removeItem("last_saved");
- return false;
- }
- return true;
-}
-
-function LoadLastSaved() {
- if (LastSaved()) {
- let storage = localStorage.getItem("last_saved");
- let sObj = JSON.parse(storage!);
- for (let key in sObj) {
- if (key == "custRev") {
- continue;
- }
- (document.getElementById(key) as HTMLInputElement).value = sObj[key];
- }
- }
-}
-
-function LoadDefault(cust: any[]) {
- let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
- for (let i of cust) {
- let id = i.name;
- (document.getElementById(id) as HTMLInputElement).value = i.defval;
- }
-}
-
-function UpdateHTMLForm(cust: any[]) {
- let dict = Object.assign({}, ...cust.map((x: { name: any; }) => ({[x.name]: x})));
- let form = document.getElementById("form");
- for (let i of cust) {
- let id = i.name;
- let input = document.getElementById(id) as HTMLInputElement;
- if (!input) {
- let div = document.createElement("div");
- div.classList.add("form-floating", "my-3");
- let label = document.createElement("label");
- label.setAttribute("for", id);
- label.innerHTML = id;
- label.classList.add("form-entry");
- input = document.createElement("input");
- input.classList.add("form-control", "form-entry");
- input.min = dict[i.name].min;
- input.max = dict[i.name].max;
- input.id = id;
- input.type = "number";
- input.step = dict[i.name].step;
- div.appendChild(input);
- div.appendChild(label);
-
- let desc = dict[i.name].desc;
- if (desc) {
- let tip = document.createElement("small");
- tip.innerHTML = desc;
- tip.classList.add("form-entry");
- tip.setAttribute("for", id);
- div.appendChild(tip);
- }
-
- form?.appendChild(div);
- }
- input.value = dict[i.name].value;
- }
-
- let btn = document.getElementById("load")!;
- btn.classList.remove("hide");
- if (LastSaved()) {
- btn.innerHTML = "Load Last Saved";
- btn.addEventListener('click', () => {
- LoadLastSaved();
- });
- } else {
- btn.addEventListener('click', () => {
- LoadDefault(cust);
- });
- }
-
- btn = document.getElementById("save")!;
- btn.classList.remove("hide");
- btn.addEventListener('click', () => {
- try {
- SaveCust(cust, buffer);
- } catch (e) {
- console.log(e);
- alert(e);
- }
- });
-}
-
-function ParseCust(magicOffset, buffer) {
- let view = new DataView(buffer);
- let cust = InitCustTable();
- let offset = magicOffset + 4;
- let rev = view.getUint16(offset, true);
- if (rev != CUST_REV) {
- throw new Error("Unsupported custRev, expected: " + CUST_REV + ", got " + rev);
- }
- offset += 2;
- for (let i of cust) {
- i.offset = offset;
- switch (i.size) {
- case 2:
- i.value = view.getUint16(offset, true);
- break;
- case 4:
- i.value = view.getUint32(offset, true);
- break;
- default:
- document.getElementById(i.name)!.focus();
- throw new Error("Unknown size at " + i);
- }
- offset += i.size;
- }
- ValidateCust(cust);
- UpdateHTMLForm(cust);
-}
-
-const fileInput = document.getElementById("file") as HTMLInputElement;
-fileInput.addEventListener('change', (event) => {
- let reader = new FileReader();
- reader.readAsArrayBuffer((event.target as HTMLInputElement).files![0]);
- reader.onloadend = (progEvent) => {
- if (progEvent.target!.readyState === FileReader.DONE) {
- buffer = progEvent.target!.result!;
- try {
- let offset = FindMagicOffset(buffer);
- ParseCust(offset, buffer);
- } catch (e) {
- console.log(e);
- alert(e);
- fileInput.value = "";
- }
- }
- }
-})
\ No newline at end of file
diff --git a/Source/loaderConfigurator/src/style.css b/Source/loaderConfigurator/src/style.css
deleted file mode 100644
index 03978d39..00000000
--- a/Source/loaderConfigurator/src/style.css
+++ /dev/null
@@ -1,39 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-label.form-entry {
- @apply inline-block mb-2 text-slate-700 dark:text-slate-200;
-}
-
-input.form-entry {
- @apply block w-full px-3 py-1.5 text-base font-normal text-slate-600 dark:text-slate-300 bg-slate-100 dark:bg-slate-900 bg-clip-padding border border-solid border-slate-300 dark:border-gray-600 rounded transition ease-in-out m-0 focus:text-slate-700 dark:focus:text-slate-200 focus:bg-slate-100 dark:focus:bg-slate-800 focus:border-blue-600 dark:focus:border-blue-300 focus:outline-none;
-}
-
-small.form-entry {
- @apply block mt-1 text-xs text-slate-600 dark:text-slate-400 px-3 py-1.5;
-}
-
-a.link-primary {
- @apply text-blue-500 hover:text-blue-600 active:text-blue-700 transition duration-300 ease-in-out mb-4;
-}
-
-div.btn {
- @apply grid grid-cols-2 gap-x-6 mt-3;
-}
-
-button.btn {
- @apply inline-block px-6 py-2.5 text-slate-100 font-medium text-xs leading-tight uppercase rounded shadow-md hover:shadow-lg focus:shadow-lg focus:outline-none focus:ring-0 active:shadow-lg transition duration-150 ease-in-out;
-}
-
-button.btn-primary {
- @apply bg-blue-600 hover:bg-blue-700 focus:bg-blue-700 active:bg-blue-900;
-}
-
-button.btn-secondary {
- @apply bg-slate-600 hover:bg-slate-700 focus:bg-slate-700 active:bg-slate-900;
-}
-
-.hide {
- display: none !important;
-}
diff --git a/Source/loaderConfigurator/tailwind.config.js b/Source/loaderConfigurator/tailwind.config.js
deleted file mode 100644
index 96b69fd8..00000000
--- a/Source/loaderConfigurator/tailwind.config.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-module.exports = {
- content: ['./dist/*.html', './dist/*.js', './node_modules/tw-elements/dist/js/**/*.js'],
- theme: {
- extend: {},
- },
- plugins: [
- require('tw-elements/dist/plugin')
- ],
-}
diff --git a/Source/sys-clk-OC/common/include/sysclk/client/ipc.h b/Source/sys-clk-OC/common/include/sysclk/client/ipc.h
index 5056a078..0f6c37f9 100644
--- a/Source/sys-clk-OC/common/include/sysclk/client/ipc.h
+++ b/Source/sys-clk-OC/common/include/sysclk/client/ipc.h
@@ -10,6 +10,10 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "types.h"
#include "../config.h"
#include "../clocks.h"
@@ -40,3 +44,7 @@ static inline Result sysclkIpcRemoveOverride(SysClkModule module)
{
return sysclkIpcSetOverride(module, 0);
}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Source/sys-clk-OC/manager/src/ipc/client.h b/Source/sys-clk-OC/manager/src/ipc/client.h
index 1b90c52a..51b62722 100644
--- a/Source/sys-clk-OC/manager/src/ipc/client.h
+++ b/Source/sys-clk-OC/manager/src/ipc/client.h
@@ -22,13 +22,5 @@
#include "ipc.h"
-#if defined(__SWITCH__) && defined(__cplusplus)
-extern "C" {
-#endif
-
#include
#include
-
-#if defined(__SWITCH__) && defined(__cplusplus)
-}
-#endif
diff --git a/Source/sys-clk-OC/overlay/src/ipc.h b/Source/sys-clk-OC/overlay/src/ipc.h
index 56c9bcdf..3ce147a9 100644
--- a/Source/sys-clk-OC/overlay/src/ipc.h
+++ b/Source/sys-clk-OC/overlay/src/ipc.h
@@ -10,14 +10,5 @@
#pragma once
-#if defined(__cplusplus)
-extern "C"
-{
-#endif
-
#include
#include
-
-#if defined(__cplusplus)
-}
-#endif
diff --git a/Source/loaderConfigurator/build.sh b/pages/build.sh
similarity index 70%
rename from Source/loaderConfigurator/build.sh
rename to pages/build.sh
index 70d687f3..a3fbff59 100755
--- a/Source/loaderConfigurator/build.sh
+++ b/pages/build.sh
@@ -2,5 +2,5 @@
[ -d "./dist" ] || mkdir ./dist
cp -Rf ./src/*.html ./dist/
+# README_HTML=`pandoc -f gfm -t html5 ../README.md`
tsc ./src/main.ts --outDir ./dist/ -lib es2015,dom -t es2015
-npx tailwindcss -i ./src/style.css -o ./dist/output.css
diff --git a/pages/dist/index.html b/pages/dist/index.html
new file mode 100644
index 00000000..aedc5605
--- /dev/null
+++ b/pages/dist/index.html
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+ Switch OC Suite
+
+
+
+
+
+
+
+
+
+
+
+
+ Switch OC Suite
+ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.
+
+
+
+
+
+
+
+
+ Read Me
+ DISCLAIMER: 🚨USE AT YOUR OWN RISK!🚨
+
+
+ Overclocking in general will shorten the lifespan of some
+ hardware components. YOU ARE RESPONSIBLE for any problem or
+ potential damage if unsafe frequencies are ENABLED in
+ sys-clk-OC. Issues like asking for bypassing limit will BE IGNORED OR
+ CLOSED WITHOUT REPLY.
+
+
+ Due to HorizonOS design, instabilities from unsafe RAM clocks may
+ cause filesystem corruption. Always make backup before enabling
+ DRAM OC.
+
+
+
+
+ Features
+
+ For Erista variant (HAC-001)
+
+ CPU Overclock (Safe: 1785 MHz)
+
+ Unsafe
+
+ Due to the limit of board power draw or power IC
+ Unlockable frequencies up to 2091 MHz
+ See README
+ for sys-clk-OC
+
+
+
+
+ DRAM Overclock (Safe: 1862.4 MHz)
+
+ Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your
+ DRAM chip
+
+
+ Modded sys-clk and ReverseNX-RT
+
+ CPU & GPU frequency governor (Experimental)
+ Set charging current (100 mA - 2000 mA) and charging limit (20% -
+ 100%)
+ Global Profile
+ Sync ReverseNX Mode
+
+
+
+
+
+ For Mariko variant (HAC-001-01, HDH-001, HEG-001)
+
+ CPU / GPU Overclock (Safe: 1963 / 998 MHz)
+
+ Unsafe
+
+ Due to the limit of board power draw or power IC
+ Unlockable frequencies up to 2397 / 1305 MHz or 2295 / 1267 MHz
+ See README
+ for sys-clk-OC
+
+
+
+
+ DRAM Overclock (Safe: 1996.8 MHz)
+ Modded sys-clk and ReverseNX-RT
+
+ Auto CPU Boost
+ CPU & GPU frequency governor (Experimental)
+ Set charging current (100 mA - 2000 mA) and charging limit (20% -
+ 100%)
+ Global Profile
+ Sync ReverseNX Mode
+
+
+
+
+
+ Auto CPU Boost
+
+ For faster game loading
+ Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is
+ stressed (mainly I/O operations).
+ Effective only when charger is connected.
+
+
+
+ CPU & GPU frequency governor (Experimental)
+
+ Adjust frequency based on load. Might decrease power draw but can
+ introduce stutters. Can be turned off for specific titles.
+
+
+
+ Setting charge limit (20% - 100%)
+
+ Long-term use of charge limit may render the battery gauge
+ inaccurate. Performing full cycles could help recalibration, or try battery_desync_fix_nx .
+
+
+
+ Global profile
+
+ Designated a dummy title id 0xA111111111111111.
+ Priority: "Temp overrides" > "Application profile" > "Global
+ profile" > "System default".
+
+
+
+ Sync ReverseNX Mode
+
+ No need to change clocks manually after toggling modes in ReverseNX
+ (-RT and -Tool)
+
+
+
+ System Settings (Optional)
+ See system_settings.md
+
+ Installation
+
+ Download latest release .
+ Copy all files in SdOut to the root of SD card.
+ Grab x.x.x_loader.kip for your Atmosphere version, rename it to loader.kip and
+ place it in /atmosphere/kips/.
+ Customization via online loader configurator
+
+ Default config
+
+
+
+ Defaults
+ Mariko
+ Erista
+
+
+
+
+ CPU OC
+ 2397 MHz Max
+ 2091 MHz Max
+
+
+ CPU Boost
+ 1785 MHz
+ N/A
+
+
+ CPU Volt
+ 1235 mV Max
+ 1257 mV Max
+
+
+ GPU OC
+ 1305 MHz Max
+ N/A
+
+
+ RAM OC
+ 1996 MHz Max
+ 1862 MHz Max
+
+
+ RAM Volt
+ Disabled
+ Disabled
+
+
+ RAM Timing
+ Auto-Adjusted
+ Disabled
+
+
+
+
+
+ Hekate-ipl bootloader Only
+
+ Add kip1=atmosphere/kips/loader.kip to boot entry
+ section in bootloader/hekate_ipl.ini.
+
+
+
+
+ Deprecated: patching sysmodules manually
+
+ This method is only served as reference as it could damage your MMC file system if not handled
+ properly.
+ Patched sysmodules would be persistent until pcv or ptm was updated in new HOS (normally in
+ x.0.0).
+
+ Tools:
+
+
+
+
+ Dump prod.keys with Lockpick_RCM
+ Dump HOS firmware with TegraExplorer
+ Configure and run test_patch.sh to generate patched pcv & ptm sysmodules in nca
+ Replace nca in SYSTEM:/Contents/registered/ with TegraExplorer
+ ValidateAcidSignature() should be stubbed to allow unsigned sysmodules to load (a.k.a.
+ loader_patch)
+
+
+
+
+ How to build this project
+
+ Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with
+ devkitpro.
+ Before compiling Atmosphere loader, run patch.py in
+ Atmosphere/stratosphere/loader/source/ to insert oc module into loader sysmodule.
+
+ When compilation is done, uncompress the kip to make it work with configurator:
+ hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip
+
+
+
+ Frequently Asked Questions
+
+ How to enable unsafe frequencies in sys-clk-OC?
+ Above all else, you should know what "unsafe" means and issues might arise .
+ See the end of README in sys-clk-OC . Add this line allow_unsafe_freq=1 into
+ /config/sys-clk/config.ini
+
+
+
+ I would like to bypass limit enforced in sys-clk to improve handheld performance without charger
+ connected.
+ Never will it be implemented here, or work out of the box.
+ You have to modify the code yourself for your own use. If you are to share modified binaries you have
+ made based on this project publicly, make sure to comply with GPL v2 licenses.
+
+
+
+
+
+ Acknowledgement
+
+
+
+
+
+
+
+
+
+
+
+
+ Download
+ Get latest version of Switch OC Suite and its corresponding Atmosphere package here.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Configurator
+ Configure frequencies and voltages to suit your hardware and yourself.
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/dist/main.js b/pages/dist/main.js
new file mode 100644
index 00000000..ee21df64
--- /dev/null
+++ b/pages/dist/main.js
@@ -0,0 +1,414 @@
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+/* Config: Cust */
+var CUST_REV;
+var buffer;
+class CustEntry {
+ constructor(name, size, desc, defval, minmax = [0, 1000000], step = 1, extra_validator) {
+ this.name = name;
+ this.size = size;
+ this.desc = desc;
+ this.defval = defval;
+ this.min = minmax[0];
+ this.max = minmax[1];
+ this.step = step;
+ this.validator = extra_validator;
+ this.value = null;
+ this.offset = null;
+ }
+ ;
+}
+var CustTable = null;
+const CustTableV2 = [
+ new CustEntry("mtcConf", 2, "DRAM Timing \
+ 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default) \
+ 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. ", 0, [0, 3]),
+ new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz \
+ System default: 1785000 \
+ 2397000 might be unreachable for some SoCs. ", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz \
+ System default: 1785000 \
+ Boost clock will be applied when applications request higher CPU frequency for quicker loading. \
+ This will be set regardless of whether sys-clk is enabled. ", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV \
+ System default: 1120 \
+ Acceptable range: 1100 ≤ x ≤ 1300 ", 1235, [1100, 1300]),
+ new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz \
+ System default: 921600 \
+ Tegra X1+ official maximum: 1267200 \
+ 1305600 might be unreachable for some SoCs. ", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0),
+ new CustEntry("eristaOCEnable", 4, "Erista CPU Enable Overclock \
+ Not tested ", 1, [0, 1]),
+ new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV \
+ Acceptable range: 1100 ≤ x ≤ 1300 ", 1235, [0, 1300], 1, (x) => x >= 1100),
+ new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0),
+ new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV \
+ Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
+ Not enabled by default ", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000),
+];
+const CustTableV3 = [
+ new CustEntry("mtcConf", 2, "DRAM Timing \
+ 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default) \
+ 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. \
+ 2 : NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.", 0, [0, 3]),
+ new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz \
+ System default: 1785000 \
+ 2397000 might be unreachable for some SoCs. ", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz \
+ System default: 1785000 \
+ Boost clock will be applied when applications request higher CPU frequency for quicker loading. \
+ This will be set regardless of whether sys-clk is enabled. ", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV \
+ System default: 1120 \
+ Acceptable range: 1100 ≤ x ≤ 1300 ", 1235, [1100, 1300]),
+ new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz \
+ System default: 921600 \
+ Tegra X1+ official maximum: 1267200 \
+ 1305600 might be unreachable for some SoCs. ", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0),
+ new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0),
+ new CustEntry("marikoEmcVolt", 4, "Mariko RAM Voltage in uV \
+ Acceptable range: 600000 ≤ x ≤ 650000 \
+ Value should be divided evenly by 5'000 \
+ Not enabled by default. \
+ This will not work without sys-clk-OC. ", 1, [0, 1]),
+ new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV \
+ Acceptable range: 1100 ≤ x ≤ 1300 ", 1235, [0, 1300], 1, (x) => x >= 1100),
+ new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0),
+ new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV \
+ Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
+ Not enabled by default ", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000),
+];
+function FindMagicOffset(buffer) {
+ let view = new DataView(buffer);
+ for (let i = 0; i < view.byteLength; i += 4) {
+ if (view.getUint32(i, true) == 0x54535543) { // "CUST"
+ return i;
+ }
+ }
+ throw new Error("Invalid loader.kip file");
+}
+class ErrorToolTip {
+ constructor(id) {
+ this.id = id;
+ this.element = document.getElementById(id);
+ }
+ ;
+ setMsg(msg) {
+ this.message = msg;
+ }
+ show() {
+ if (this.element) {
+ this.element.setAttribute("aria-invalid", "true");
+ if (this.message) {
+ this.element.setAttribute("title", this.message);
+ this.element.parentElement.setAttribute("data-tooltip", this.message);
+ this.element.parentElement.setAttribute("data-placement", "top");
+ }
+ }
+ }
+ ;
+ clear() {
+ if (this.element) {
+ this.element.removeAttribute("aria-invalid");
+ this.element.removeAttribute("title");
+ this.element.parentElement.removeAttribute("data-tooltip");
+ this.element.parentElement.removeAttribute("data-placement");
+ }
+ }
+ addChangeListener() {
+ if (this.element) {
+ this.element.addEventListener('change', (_evt) => {
+ let obj = CustTable.filter((obj) => { return obj.name === this.id; })[0];
+ obj.value = Number(this.element.value);
+ ValidateCustEntry(obj);
+ });
+ }
+ }
+}
+;
+function ValidateCustEntry(entry) {
+ let elementId = entry.name;
+ let tip = new ErrorToolTip(elementId);
+ tip.clear();
+ if (entry.value == 0)
+ return null;
+ if (entry.value < entry.min || entry.value > entry.max) {
+ tip.setMsg(`Expected range: [${entry.min}, ${entry.max}], got ${entry.value}.`);
+ tip.show();
+ return tip;
+ }
+ if (entry.validator && !entry.validator(entry.value)) {
+ tip.setMsg(`Invalid value: ${entry.value}. Did not pass this validator: ${entry.validator}.`);
+ tip.show();
+ return tip;
+ }
+ return null;
+}
+function ValidateCust() {
+ let tooltips = [];
+ for (let i of CustTable) {
+ let tip = ValidateCustEntry(i);
+ if (tip) {
+ tooltips.push(tip);
+ }
+ }
+ if (tooltips.length > 0) {
+ throw new Error("Invalid cust");
+ }
+}
+function SaveCust(buffer) {
+ let view = new DataView(buffer);
+ let storage = {};
+ storage["custRev"] = CUST_REV;
+ for (let i of CustTable) {
+ let id = i.name;
+ i.value = Number(document.getElementById(id).value);
+ storage[i.name] = i.value;
+ switch (i.size) {
+ case 2:
+ view.setUint16(i.offset, i.value, true);
+ break;
+ case 4:
+ view.setUint32(i.offset, i.value, true);
+ break;
+ default:
+ document.getElementById(i.name).focus();
+ throw new Error("Unknown size at " + i);
+ }
+ }
+ ValidateCust();
+ localStorage.setItem("last_saved", JSON.stringify(storage));
+ let a = document.createElement("a");
+ a.href = window.URL.createObjectURL(new Blob([buffer], { type: "application/octet-stream" }));
+ a.download = "loader.kip";
+ a.click();
+}
+function LastSaved() {
+ let storage = localStorage.getItem("last_saved");
+ if (!storage) {
+ return false;
+ }
+ let sObj = JSON.parse(storage);
+ if (!sObj["custRev"] || sObj["custRev"] != CUST_REV) {
+ localStorage.removeItem("last_saved");
+ return false;
+ }
+ return true;
+}
+function LoadLastSaved() {
+ if (LastSaved()) {
+ let storage = localStorage.getItem("last_saved");
+ let sObj = JSON.parse(storage);
+ for (let key in sObj) {
+ if (key == "custRev") {
+ continue;
+ }
+ document.getElementById(key).value = sObj[key];
+ }
+ }
+}
+function LoadDefault() {
+ for (let i of CustTable) {
+ let id = i.name;
+ document.getElementById(id).value = String(i.defval);
+ }
+}
+function ClearHTMLForm() {
+ var _a, _b;
+ if (!CustTable) {
+ return;
+ }
+ for (let i of CustTable) {
+ let id = i.name;
+ let input = document.getElementById(id);
+ (_b = (_a = input.parentElement) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.remove();
+ }
+}
+function UpdateHTMLForm() {
+ let dict = Object.assign({}, ...CustTable.map((x) => ({ [x.name]: x })));
+ let form = document.getElementById("form");
+ for (let i of CustTable) {
+ let id = i.name;
+ let input = document.getElementById(id);
+ if (!input) {
+ let grid = document.createElement("div");
+ grid.classList.add("grid");
+ // Label and input box
+ input = document.createElement("input");
+ input.min = dict[i.name].min;
+ input.max = dict[i.name].max;
+ input.id = id;
+ input.type = "number";
+ input.step = dict[i.name].step;
+ let label = document.createElement("label");
+ label.setAttribute("for", id);
+ label.innerHTML = id;
+ label.appendChild(input);
+ grid.appendChild(label);
+ // Description in blockquote style
+ let desc = dict[i.name].desc;
+ let block = document.createElement("blockquote");
+ block.style["margin-top"] = "0";
+ block.innerHTML = desc;
+ block.setAttribute("for", id);
+ grid.appendChild(block);
+ grid.style["margin-top"] = "3rem";
+ form.appendChild(grid);
+ let tooltip = new ErrorToolTip(id);
+ tooltip.addChangeListener();
+ }
+ input.value = dict[i.name].value;
+ }
+ let default_btn = document.getElementById("load_default");
+ default_btn.removeAttribute("disabled");
+ default_btn.addEventListener('click', () => {
+ LoadDefault();
+ });
+ if (LastSaved()) {
+ let last_btn = document.getElementById("load_saved");
+ last_btn.removeAttribute("disabled");
+ last_btn.addEventListener('click', () => {
+ LoadLastSaved();
+ });
+ }
+ let save_btn = document.getElementById("save");
+ save_btn.removeAttribute("disabled");
+ save_btn.addEventListener('click', () => {
+ try {
+ SaveCust(buffer);
+ }
+ catch (e) {
+ console.log(e);
+ alert(e);
+ }
+ });
+}
+function ParseCust(magicOffset, buffer) {
+ let view = new DataView(buffer);
+ let offset = magicOffset + 4;
+ let rev = view.getUint16(offset, true);
+ if (rev != 2 && rev != 3) {
+ throw new Error("Unsupported custRev, expected: 2 or 3, got " + rev);
+ }
+ CUST_REV = rev;
+ document.getElementById("cust_rev").innerHTML = `Cust V${CUST_REV} is loaded.`;
+ if (rev == 2) {
+ CustTable = CustTableV2;
+ }
+ else {
+ CustTable = CustTableV3;
+ }
+ offset += 2;
+ for (let i of CustTable) {
+ i.offset = offset;
+ switch (i.size) {
+ case 2:
+ i.value = view.getUint16(offset, true);
+ break;
+ case 4:
+ i.value = view.getUint32(offset, true);
+ break;
+ default:
+ document.getElementById(i.name).focus();
+ throw new Error("Unknown size at " + i);
+ }
+ offset += i.size;
+ }
+}
+const fileInput = document.getElementById("file");
+fileInput.addEventListener('change', (event) => {
+ let reader = new FileReader();
+ reader.readAsArrayBuffer(event.target.files[0]);
+ reader.onloadend = (progEvent) => {
+ if (progEvent.target.readyState === FileReader.DONE) {
+ buffer = progEvent.target.result;
+ try {
+ let offset = FindMagicOffset(buffer);
+ ClearHTMLForm();
+ ParseCust(offset, buffer);
+ ValidateCust();
+ UpdateHTMLForm();
+ }
+ catch (e) {
+ console.log(e);
+ alert(e);
+ fileInput.value = "";
+ }
+ }
+ };
+});
+function fetchRelease() {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ const responseFromSuite = yield fetch('https://api.github.com/repos/KazushiMe/Switch-OC-Suite/releases/latest', {
+ method: 'GET',
+ headers: {
+ Accept: 'application/json',
+ },
+ });
+ if (!responseFromSuite.ok) {
+ throw new Error(`Failed to fetch latest release info from GitHub: ${responseFromSuite.status}`);
+ }
+ const resultFromSuite = yield responseFromSuite.json();
+ const latestVerFromSuite = resultFromSuite.tag_name;
+ const correspondingVerFromAMS = latestVerFromSuite.split(".").slice(0, 3).join(".");
+ const loaderKip = resultFromSuite.assets.filter((obj) => {
+ return obj.name.endsWith("loader.kip");
+ })[0];
+ const sdOut = resultFromSuite.assets.filter((obj) => {
+ return obj.name.endsWith(".zip");
+ })[0];
+ const amsReleaseUrl = `https://github.com/Atmosphere-NX/Atmosphere/releases/tags/${correspondingVerFromAMS}`;
+ let info = {
+ OCSuiteVer: latestVerFromSuite,
+ LoaderKipUrl: loaderKip.browser_download_url,
+ SdOutZipUrl: sdOut.browser_download_url,
+ AMSVer: correspondingVerFromAMS,
+ AMSUrl: amsReleaseUrl
+ };
+ return info;
+ }
+ catch (e) {
+ console.log(e);
+ alert(e);
+ }
+ });
+}
+function updateDownloadUrls() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const updateHref = (id, name, url) => {
+ let element = document.getElementById(id);
+ element.innerHTML = name;
+ element.removeAttribute("aria-busy");
+ element.setAttribute("href", url);
+ };
+ let info = yield fetchRelease();
+ if (info) {
+ const loaderKipName = `loader.kip ${info.OCSuiteVer}`;
+ updateHref("loader_kip_btn", loaderKipName, info.LoaderKipUrl);
+ const sdOutName = `SdOut.zip ${info.OCSuiteVer}`;
+ updateHref("sdout_zip_btn", sdOutName, info.SdOutZipUrl);
+ const amsName = `Atmosphere-NX ${info.AMSVer}`;
+ updateHref("ams_btn", amsName, info.AMSUrl);
+ }
+ });
+}
+addEventListener('DOMContentLoaded', (_evt) => __awaiter(this, void 0, void 0, function* () {
+ yield updateDownloadUrls();
+}));
diff --git a/pages/src/index.html b/pages/src/index.html
new file mode 100644
index 00000000..aedc5605
--- /dev/null
+++ b/pages/src/index.html
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+ Switch OC Suite
+
+
+
+
+
+
+
+
+
+
+
+
+ Switch OC Suite
+ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2.
+
+
+
+
+
+
+
+
+ Read Me
+ DISCLAIMER: 🚨USE AT YOUR OWN RISK!🚨
+
+
+ Overclocking in general will shorten the lifespan of some
+ hardware components. YOU ARE RESPONSIBLE for any problem or
+ potential damage if unsafe frequencies are ENABLED in
+ sys-clk-OC. Issues like asking for bypassing limit will BE IGNORED OR
+ CLOSED WITHOUT REPLY.
+
+
+ Due to HorizonOS design, instabilities from unsafe RAM clocks may
+ cause filesystem corruption. Always make backup before enabling
+ DRAM OC.
+
+
+
+
+ Features
+
+ For Erista variant (HAC-001)
+
+ CPU Overclock (Safe: 1785 MHz)
+
+ Unsafe
+
+ Due to the limit of board power draw or power IC
+ Unlockable frequencies up to 2091 MHz
+ See README
+ for sys-clk-OC
+
+
+
+
+ DRAM Overclock (Safe: 1862.4 MHz)
+
+ Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your
+ DRAM chip
+
+
+ Modded sys-clk and ReverseNX-RT
+
+ CPU & GPU frequency governor (Experimental)
+ Set charging current (100 mA - 2000 mA) and charging limit (20% -
+ 100%)
+ Global Profile
+ Sync ReverseNX Mode
+
+
+
+
+
+ For Mariko variant (HAC-001-01, HDH-001, HEG-001)
+
+ CPU / GPU Overclock (Safe: 1963 / 998 MHz)
+
+ Unsafe
+
+ Due to the limit of board power draw or power IC
+ Unlockable frequencies up to 2397 / 1305 MHz or 2295 / 1267 MHz
+ See README
+ for sys-clk-OC
+
+
+
+
+ DRAM Overclock (Safe: 1996.8 MHz)
+ Modded sys-clk and ReverseNX-RT
+
+ Auto CPU Boost
+ CPU & GPU frequency governor (Experimental)
+ Set charging current (100 mA - 2000 mA) and charging limit (20% -
+ 100%)
+ Global Profile
+ Sync ReverseNX Mode
+
+
+
+
+
+ Auto CPU Boost
+
+ For faster game loading
+ Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is
+ stressed (mainly I/O operations).
+ Effective only when charger is connected.
+
+
+
+ CPU & GPU frequency governor (Experimental)
+
+ Adjust frequency based on load. Might decrease power draw but can
+ introduce stutters. Can be turned off for specific titles.
+
+
+
+ Setting charge limit (20% - 100%)
+
+ Long-term use of charge limit may render the battery gauge
+ inaccurate. Performing full cycles could help recalibration, or try battery_desync_fix_nx .
+
+
+
+ Global profile
+
+ Designated a dummy title id 0xA111111111111111.
+ Priority: "Temp overrides" > "Application profile" > "Global
+ profile" > "System default".
+
+
+
+ Sync ReverseNX Mode
+
+ No need to change clocks manually after toggling modes in ReverseNX
+ (-RT and -Tool)
+
+
+
+ System Settings (Optional)
+ See system_settings.md
+
+ Installation
+
+ Download latest release .
+ Copy all files in SdOut to the root of SD card.
+ Grab x.x.x_loader.kip for your Atmosphere version, rename it to loader.kip and
+ place it in /atmosphere/kips/.
+ Customization via online loader configurator
+
+ Default config
+
+
+
+ Defaults
+ Mariko
+ Erista
+
+
+
+
+ CPU OC
+ 2397 MHz Max
+ 2091 MHz Max
+
+
+ CPU Boost
+ 1785 MHz
+ N/A
+
+
+ CPU Volt
+ 1235 mV Max
+ 1257 mV Max
+
+
+ GPU OC
+ 1305 MHz Max
+ N/A
+
+
+ RAM OC
+ 1996 MHz Max
+ 1862 MHz Max
+
+
+ RAM Volt
+ Disabled
+ Disabled
+
+
+ RAM Timing
+ Auto-Adjusted
+ Disabled
+
+
+
+
+
+ Hekate-ipl bootloader Only
+
+ Add kip1=atmosphere/kips/loader.kip to boot entry
+ section in bootloader/hekate_ipl.ini.
+
+
+
+
+ Deprecated: patching sysmodules manually
+
+ This method is only served as reference as it could damage your MMC file system if not handled
+ properly.
+ Patched sysmodules would be persistent until pcv or ptm was updated in new HOS (normally in
+ x.0.0).
+
+ Tools:
+
+
+
+
+ Dump prod.keys with Lockpick_RCM
+ Dump HOS firmware with TegraExplorer
+ Configure and run test_patch.sh to generate patched pcv & ptm sysmodules in nca
+ Replace nca in SYSTEM:/Contents/registered/ with TegraExplorer
+ ValidateAcidSignature() should be stubbed to allow unsigned sysmodules to load (a.k.a.
+ loader_patch)
+
+
+
+
+ How to build this project
+
+ Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with
+ devkitpro.
+ Before compiling Atmosphere loader, run patch.py in
+ Atmosphere/stratosphere/loader/source/ to insert oc module into loader sysmodule.
+
+ When compilation is done, uncompress the kip to make it work with configurator:
+ hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip
+
+
+
+ Frequently Asked Questions
+
+ How to enable unsafe frequencies in sys-clk-OC?
+ Above all else, you should know what "unsafe" means and issues might arise .
+ See the end of README in sys-clk-OC . Add this line allow_unsafe_freq=1 into
+ /config/sys-clk/config.ini
+
+
+
+ I would like to bypass limit enforced in sys-clk to improve handheld performance without charger
+ connected.
+ Never will it be implemented here, or work out of the box.
+ You have to modify the code yourself for your own use. If you are to share modified binaries you have
+ made based on this project publicly, make sure to comply with GPL v2 licenses.
+
+
+
+
+
+ Acknowledgement
+
+
+
+
+
+
+
+
+
+
+
+
+ Download
+ Get latest version of Switch OC Suite and its corresponding Atmosphere package here.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Configurator
+ Configure frequencies and voltages to suit your hardware and yourself.
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/src/main.ts b/pages/src/main.ts
new file mode 100644
index 00000000..4af12410
--- /dev/null
+++ b/pages/src/main.ts
@@ -0,0 +1,605 @@
+/* Config: Cust */
+var CUST_REV: number | null;
+var buffer: string | ArrayBuffer;
+
+class CustEntry {
+ name: string;
+ size: number;
+ desc: string;
+ defval: number;
+ min: number;
+ max: number;
+ step: number;
+ validator: Function | null;
+ value: number | null;
+ offset: number | null;
+
+ constructor(name: string, size: number, desc: string, defval: number, minmax = [0, 1_000_000], step = 1, extra_validator?) {
+ this.name = name;
+ this.size = size;
+ this.desc = desc;
+ this.defval = defval;
+ this.min = minmax[0];
+ this.max = minmax[1];
+ this.step = step;
+ this.validator = extra_validator;
+ this.value = null;
+ this.offset = null;
+ };
+}
+
+var CustTable: Array | null = null;
+
+const CustTableV2: Array = [
+ new CustEntry(
+ "mtcConf",
+ 2,
+ "DRAM Timing \
+ 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default) \
+ 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. ",
+ 0,
+ [0, 3],
+ ),
+ new CustEntry(
+ "marikoCpuMaxClock",
+ 4,
+ "Mariko CPU Max Clock in kHz \
+ System default: 1785000 \
+ 2397000 might be unreachable for some SoCs. ",
+ 2397_000,
+ [1785_000, 3000_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoCpuBoostClock",
+ 4,
+ "Mariko CPU Boost Clock in kHz \
+ System default: 1785000 \
+ Boost clock will be applied when applications request higher CPU frequency for quicker loading. \
+ This will be set regardless of whether sys-clk is enabled. ",
+ 1785_000,
+ [1785_000, 3000_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoCpuMaxVolt",
+ 4,
+ "Mariko CPU Max Voltage in mV \
+ System default: 1120 \
+ Acceptable range: 1100 ≤ x ≤ 1300 ",
+ 1235,
+ [1100, 1300],
+ ),
+ new CustEntry(
+ "marikoGpuMaxClock",
+ 4,
+ "Mariko GPU Max Clock in kHz \
+ System default: 921600 \
+ Tegra X1+ official maximum: 1267200 \
+ 1305600 might be unreachable for some SoCs. ",
+ 1305_600,
+ [768_000, 1536_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoEmcMaxClock",
+ 4,
+ "Mariko RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
+ 1996_800,
+ [1612_800, 2400_000],
+ 3200,
+ (x: number) => (x % 3200) == 0
+ ),
+ new CustEntry(
+ "eristaOCEnable",
+ 4,
+ "Erista CPU Enable Overclock \
+ Not tested ",
+ 1,
+ [0, 1]
+ ),
+ new CustEntry(
+ "eristaCpuMaxVolt",
+ 4,
+ "Erista CPU Max Voltage in mV \
+ Acceptable range: 1100 ≤ x ≤ 1300 ",
+ 1235,
+ [0, 1300],
+ 1,
+ (x: number) => x >= 1100
+ ),
+ new CustEntry(
+ "eristaEmcMaxClock",
+ 4,
+ "Erista RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
+ 1862_400,
+ [1600_000, 2400_000],
+ 3200,
+ (x: number) => (x % 3200) == 0
+ ),
+ new CustEntry(
+ "eristaEmcVolt",
+ 4,
+ "Erista RAM Voltage in uV \
+ Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
+ Not enabled by default ",
+ 0,
+ [0, 1250_000],
+ 12500,
+ (x: number) => (x % 12500) == 0 && x >= 1100000
+ ),
+];
+
+const CustTableV3: Array = [
+ new CustEntry(
+ "mtcConf",
+ 2,
+ "DRAM Timing \
+ 0 : AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default) \
+ 1 : AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density. \
+ 2 : NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.",
+ 0,
+ [0, 3],
+ ),
+ new CustEntry(
+ "marikoCpuMaxClock",
+ 4,
+ "Mariko CPU Max Clock in kHz \
+ System default: 1785000 \
+ 2397000 might be unreachable for some SoCs. ",
+ 2397_000,
+ [1785_000, 3000_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoCpuBoostClock",
+ 4,
+ "Mariko CPU Boost Clock in kHz \
+ System default: 1785000 \
+ Boost clock will be applied when applications request higher CPU frequency for quicker loading. \
+ This will be set regardless of whether sys-clk is enabled. ",
+ 1785_000,
+ [1785_000, 3000_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoCpuMaxVolt",
+ 4,
+ "Mariko CPU Max Voltage in mV \
+ System default: 1120 \
+ Acceptable range: 1100 ≤ x ≤ 1300 ",
+ 1235,
+ [1100, 1300],
+ ),
+ new CustEntry(
+ "marikoGpuMaxClock",
+ 4,
+ "Mariko GPU Max Clock in kHz \
+ System default: 921600 \
+ Tegra X1+ official maximum: 1267200 \
+ 1305600 might be unreachable for some SoCs. ",
+ 1305_600,
+ [768_000, 1536_000],
+ 100,
+ (x: number) => (x % 100) == 0
+ ),
+ new CustEntry(
+ "marikoEmcMaxClock",
+ 4,
+ "Mariko RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
+ 1996_800,
+ [1612_800, 2400_000],
+ 3200,
+ (x: number) => (x % 3200) == 0
+ ),
+ new CustEntry(
+ "marikoEmcVolt",
+ 4,
+ "Mariko RAM Voltage in uV \
+ Acceptable range: 600000 ≤ x ≤ 650000 \
+ Value should be divided evenly by 5'000 \
+ Not enabled by default. \
+ This will not work without sys-clk-OC. ",
+ 1,
+ [0, 1]
+ ),
+ new CustEntry(
+ "eristaCpuMaxVolt",
+ 4,
+ "Erista CPU Max Voltage in mV \
+ Acceptable range: 1100 ≤ x ≤ 1300 ",
+ 1235,
+ [0, 1300],
+ 1,
+ (x: number) => x >= 1100
+ ),
+ new CustEntry(
+ "eristaEmcMaxClock",
+ 4,
+ "Erista RAM Max Clock in kHz \
+ Values should be > 1600000, and divided evenly by 3200. \
+ WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM ",
+ 1862_400,
+ [1600_000, 2400_000],
+ 3200,
+ (x: number) => (x % 3200) == 0
+ ),
+ new CustEntry(
+ "eristaEmcVolt",
+ 4,
+ "Erista RAM Voltage in uV \
+ Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500. \
+ Not enabled by default ",
+ 0,
+ [0, 1250_000],
+ 12500,
+ (x: number) => (x % 12500) == 0 && x >= 1100000
+ ),
+];
+
+function FindMagicOffset(buffer) {
+ let view = new DataView(buffer);
+ for (let i = 0; i < view.byteLength; i += 4) {
+ if (view.getUint32(i, true) == 0x54535543) { // "CUST"
+ return i;
+ }
+ }
+ throw new Error("Invalid loader.kip file");
+}
+
+class ErrorToolTip {
+ id: string;
+ message: string | null;
+ element: HTMLElement | null;
+
+ constructor(id: string) {
+ this.id = id;
+ this.element = document.getElementById(id);
+ };
+
+ setMsg(msg: string) {
+ this.message = msg;
+ }
+
+ show() {
+ if (this.element) {
+ this.element.setAttribute("aria-invalid", "true");
+ if (this.message) {
+ this.element.setAttribute("title", this.message);
+ this.element.parentElement!.setAttribute("data-tooltip", this.message);
+ this.element.parentElement!.setAttribute("data-placement", "top");
+ }
+ }
+ };
+
+ clear() {
+ if (this.element) {
+ this.element.removeAttribute("aria-invalid");
+ this.element.removeAttribute("title");
+ this.element.parentElement!.removeAttribute("data-tooltip");
+ this.element.parentElement!.removeAttribute("data-placement");
+ }
+ }
+
+ addChangeListener() {
+ if (this.element) {
+ this.element.addEventListener('change', (_evt) => {
+ let obj = CustTable!.filter((obj) => { return obj.name === this.id; })[0];
+ obj.value = Number((this.element as HTMLInputElement).value);
+ ValidateCustEntry(obj);
+ });
+ }
+ }
+};
+
+function ValidateCustEntry(entry: CustEntry): ErrorToolTip | null {
+ let elementId = entry.name;
+ let tip = new ErrorToolTip(elementId);
+ tip.clear();
+ if (entry.value! == 0)
+ return null;
+ if (entry.value! < entry.min || entry.value! > entry.max) {
+ tip.setMsg(`Expected range: [${entry.min}, ${entry.max}], got ${entry.value}.`);
+ tip.show();
+ return tip;
+ }
+ if (entry.validator && !entry.validator(entry.value)) {
+ tip.setMsg(`Invalid value: ${entry.value}. Did not pass this validator: ${entry.validator}.`);
+ tip.show();
+ return tip;
+ }
+ return null;
+}
+
+function ValidateCust() {
+ let tooltips: Array = [];
+
+ for (let i of CustTable!) {
+ let tip = ValidateCustEntry(i);
+ if (tip) {
+ tooltips.push(tip);
+ }
+ }
+
+ if (tooltips.length > 0) {
+ throw new Error("Invalid cust");
+ }
+}
+
+function SaveCust(buffer) {
+ let view = new DataView(buffer);
+ let storage = {};
+ storage["custRev"] = CUST_REV;
+
+ for (let i of CustTable!) {
+ let id = i.name;
+ i.value = Number((document.getElementById(id) as HTMLInputElement).value);
+ storage[i.name] = i.value;
+ switch (i.size) {
+ case 2:
+ view.setUint16(i.offset!, i.value, true);
+ break;
+ case 4:
+ view.setUint32(i.offset!, i.value, true);
+ break;
+ default:
+ document.getElementById(i.name)!.focus();
+ throw new Error("Unknown size at " + i);
+ }
+ }
+ ValidateCust();
+
+ localStorage.setItem("last_saved", JSON.stringify(storage));
+ let a = document.createElement("a");
+ a.href = window.URL.createObjectURL(new Blob([buffer], { type: "application/octet-stream" }));
+ a.download = "loader.kip";
+ a.click();
+}
+
+function LastSaved() {
+ let storage = localStorage.getItem("last_saved");
+ if (!storage) {
+ return false;
+ }
+ let sObj = JSON.parse(storage);
+ if (!sObj["custRev"] || sObj["custRev"] != CUST_REV) {
+ localStorage.removeItem("last_saved");
+ return false;
+ }
+ return true;
+}
+
+function LoadLastSaved() {
+ if (LastSaved()) {
+ let storage = localStorage.getItem("last_saved");
+ let sObj = JSON.parse(storage!);
+ for (let key in sObj) {
+ if (key == "custRev") {
+ continue;
+ }
+ (document.getElementById(key) as HTMLInputElement).value = sObj[key];
+ }
+ }
+}
+
+function LoadDefault() {
+ for (let i of CustTable!) {
+ let id = i.name;
+ (document.getElementById(id) as HTMLInputElement).value = String(i.defval);
+ }
+}
+
+function ClearHTMLForm() {
+ if (!CustTable) {
+ return;
+ }
+ for (let i of CustTable) {
+ let id = i.name;
+ let input = document.getElementById(id) as HTMLInputElement;
+ input.parentElement?.parentElement?.remove();
+ }
+}
+
+function UpdateHTMLForm() {
+ let dict = Object.assign({}, ...CustTable!.map((x: { name: any; }) => ({ [x.name]: x })));
+ let form = document.getElementById("form")!;
+ for (let i of CustTable!) {
+ let id = i.name;
+ let input = document.getElementById(id) as HTMLInputElement;
+ if (!input) {
+ let grid = document.createElement("div");
+ grid.classList.add("grid");
+
+ // Label and input box
+ input = document.createElement("input");
+ input.min = dict[i.name].min;
+ input.max = dict[i.name].max;
+ input.id = id;
+ input.type = "number";
+ input.step = dict[i.name].step;
+ let label = document.createElement("label");
+ label.setAttribute("for", id);
+ label.innerHTML = id;
+ label.appendChild(input);
+ grid.appendChild(label);
+
+ // Description in blockquote style
+ let desc = dict[i.name].desc!;
+ let block = document.createElement("blockquote");
+ block.style["margin-top"] = "0";
+ block.innerHTML = desc;
+ block.setAttribute("for", id);
+ grid.appendChild(block);
+
+ grid.style["margin-top"] = "3rem";
+ form.appendChild(grid);
+
+ let tooltip = new ErrorToolTip(id);
+ tooltip.addChangeListener();
+ }
+ input.value = dict[i.name].value;
+ }
+
+ let default_btn = document.getElementById("load_default")!;
+ default_btn.removeAttribute("disabled");
+ default_btn.addEventListener('click', () => {
+ LoadDefault();
+ });
+
+ if (LastSaved()) {
+ let last_btn = document.getElementById("load_saved")!;
+ last_btn.removeAttribute("disabled");
+ last_btn.addEventListener('click', () => {
+ LoadLastSaved();
+ });
+ }
+
+ let save_btn = document.getElementById("save")!;
+ save_btn.removeAttribute("disabled");
+ save_btn.addEventListener('click', () => {
+ try {
+ SaveCust(buffer);
+ } catch (e) {
+ console.log(e);
+ alert(e);
+ }
+ });
+}
+
+function ParseCust(magicOffset, buffer) {
+ let view = new DataView(buffer);
+ let offset = magicOffset + 4;
+ let rev = view.getUint16(offset, true);
+ if (rev != 2 && rev != 3) {
+ throw new Error("Unsupported custRev, expected: 2 or 3, got " + rev);
+ }
+ CUST_REV = rev;
+ document.getElementById("cust_rev")!.innerHTML = `Cust V${CUST_REV} is loaded.`;
+ if (rev == 2) {
+ CustTable = CustTableV2;
+ } else {
+ CustTable = CustTableV3;
+ }
+
+ offset += 2;
+ for (let i of CustTable) {
+ i.offset = offset;
+ switch (i.size) {
+ case 2:
+ i.value = view.getUint16(offset, true);
+ break;
+ case 4:
+ i.value = view.getUint32(offset, true);
+ break;
+ default:
+ document.getElementById(i.name)!.focus();
+ throw new Error("Unknown size at " + i);
+ }
+ offset += i.size;
+ }
+}
+
+const fileInput = document.getElementById("file") as HTMLInputElement;
+fileInput.addEventListener('change', (event) => {
+ let reader = new FileReader();
+ reader.readAsArrayBuffer((event.target as HTMLInputElement).files![0]);
+ reader.onloadend = (progEvent) => {
+ if (progEvent.target!.readyState === FileReader.DONE) {
+ buffer = progEvent.target!.result!;
+ try {
+ let offset = FindMagicOffset(buffer);
+ ClearHTMLForm();
+ ParseCust(offset, buffer);
+ ValidateCust();
+ UpdateHTMLForm();
+ } catch (e) {
+ console.log(e);
+ alert(e);
+ fileInput.value = "";
+ }
+ }
+ }
+})
+
+/* GitHub Release fetch */
+type ReleaseInfo = {
+ OCSuiteVer: string;
+ LoaderKipUrl: string;
+ SdOutZipUrl: string;
+ AMSVer: string;
+ AMSUrl: string;
+};
+
+async function fetchRelease(): Promise {
+ try {
+ const responseFromSuite = await fetch('https://api.github.com/repos/KazushiMe/Switch-OC-Suite/releases/latest', {
+ method: 'GET',
+ headers: {
+ Accept: 'application/json',
+ },
+ });
+ if (!responseFromSuite.ok) {
+ throw new Error(`Failed to fetch latest release info from GitHub: ${responseFromSuite.status}`);
+ }
+
+ const resultFromSuite = await responseFromSuite.json();
+ const latestVerFromSuite = resultFromSuite.tag_name;
+ const correspondingVerFromAMS = latestVerFromSuite.split(".").slice(0, 3).join(".");
+
+ const loaderKip = resultFromSuite.assets.filter((obj) => {
+ return obj.name.endsWith("loader.kip");
+ })[0];
+ const sdOut = resultFromSuite.assets.filter((obj) => {
+ return obj.name.endsWith(".zip");
+ })[0];
+ const amsReleaseUrl = `https://github.com/Atmosphere-NX/Atmosphere/releases/tags/${correspondingVerFromAMS}`;
+
+ let info: ReleaseInfo = {
+ OCSuiteVer: latestVerFromSuite,
+ LoaderKipUrl: loaderKip.browser_download_url,
+ SdOutZipUrl: sdOut.browser_download_url,
+ AMSVer: correspondingVerFromAMS,
+ AMSUrl: amsReleaseUrl
+ };
+ return info;
+ } catch (e) {
+ console.log(e);
+ alert(e);
+ }
+}
+
+async function updateDownloadUrls() {
+ const updateHref = (id: string, name: string, url: string) => {
+ let element = document.getElementById(id)!;
+ element.innerHTML = name;
+ element.removeAttribute("aria-busy");
+ element.setAttribute("href", url);
+ };
+
+ let info = await fetchRelease();
+ if (info) {
+ const loaderKipName = `loader.kip ${info.OCSuiteVer}`;
+ updateHref("loader_kip_btn", loaderKipName, info.LoaderKipUrl);
+
+ const sdOutName = `SdOut.zip ${info.OCSuiteVer}`;
+ updateHref("sdout_zip_btn", sdOutName, info.SdOutZipUrl);
+
+ const amsName = `Atmosphere-NX ${info.AMSVer}`;
+ updateHref("ams_btn", amsName, info.AMSUrl);
+ }
+}
+
+addEventListener('DOMContentLoaded', async (_evt) => {
+ await updateDownloadUrls();
+});
\ No newline at end of file