commit cde4f9326384265f7372bd5a1a2354b557edc805 Author: haixinsc Date: Mon Mar 24 22:39:52 2025 +0800 first commit diff --git a/development/LICENSE b/development/LICENSE new file mode 100644 index 0000000..e90cf5c --- /dev/null +++ b/development/LICENSE @@ -0,0 +1,22 @@ +MIT License with Additional Stipulations + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +1. A clear and visible attribution to the original author (you) must be provided in an obvious place in any product, documentation, or interface where the software is utilized. This attribution should include your name and a reference to the original source repository. + +2. The Software, or any modified version of it, must not be sold or charged for. However, selling physical products that include the Software is permitted, provided that a clear and visible attribution to the original author (you) is included in an obvious place in the product or accompanying documentation. + +3. Any modifications made to the Software must be made available upon request by any party. This includes providing access to the modified source code, along with any accompanying documentation necessary for understanding the modifications. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/development/README.md b/development/README.md new file mode 100644 index 0000000..da7785a --- /dev/null +++ b/development/README.md @@ -0,0 +1,162 @@ +# RGMII Toolkit +Software deployment Toolkit for Quectel RM5xxx series 5G modems utilizing an m.2 to RJ45 adapter (RGMII) + +Current Branch: **Development** + +Please PR to this branch instead of main :) + +Fork development, and PR development to development :) + + +#### [JUMP TO HOW TO USE](#how-to-use) +**Currently:** This will allow you to install or if already installed, update, remove, or modify: + - Simple Admin: A simple web interface for managing your Quectel m.2 modem through it's gateway address + - It will install socat-at-bridge: sets up ttyOUT and ttyOUT2 for AT commands. You'll be able to use the `atcmd` command as well for an interactive at command session from adb, ssh, or ttyd + - It will install simplefirewall: A simple firewall that blocks definable incoming ports and a TTL mangle option/modifier. As of now only the TTL is controllable through Simple Admin. You can edit port block options and TTL from the 3rd option in the toolkit + - Tailscale: A magic VPN for accessing Simple Admin, SSH, and ttyd on the go. The Toolkit installs the Tailscale client directly to the modem and allows you to login and configure other settings. Head over to tailscale.com to sign up for a free account and learn more. + - Schedule a Daily Reboot at a specified time + - A fix for certain modems that don't start in CFUN=1 mode + - Entware/OPKG: A package installer/manager/repo + - Run `opkg help` to see how to use it + - These packages are installable: https://bin.entware.net/armv7sf-k3.2/Packages.html + - TTYd: A shell session right from your browser + - Currently this uses port 443 but SSL/TLS is not in use (http only for now) + - Entware/OPKG is required so it will install it if it isn't installed + - This will replace the stock Quectel login and passwd binaries with ones from entware + + + +**My goal** is for this to also include any new useful scripts or software for this modem and others that support RGMII mode. +## Screenshots + +![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_toolkit.png?raw=true) +![Home](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_home.png?raw=true) +![Simple Network](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_simplenetwork.png?raw=true) +![Simple Scan](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_simplescan.png?raw=true) +![Simple Settings](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_simplesettings.png?raw=true) +![SMS](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_sms.png?raw=true) +![Console](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_console.png?raw=true) +![Device Info](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/dev_deviceinfo.png?raw=true) + +# Devleopment Branch: the below commands will download the beta/work in progress toolkit + +## How to Use +**To run the Toolkit:** + - Open ADB & Fastboot++ covered in [Using ADB](https://github.com/iamromulan/quectel-rgmii-configuration-notes?tab=readme-ov-file#unlocking-and-using-adb) or just use adb + - Make sure your modem is connected by USB to your computer + - Run `adb devices` to make sure your modem is detected by adb + - Run `adb shell ping 8.8.8.8` to make sure the shell can access the internet. If you get an error, make sure the modem is connected to a cellular network and make sure `AT+QMAPWAC=1` as covered in the troubleshooting section: [I Can't get internet access from the Ethernet port (Common)](https://github.com/iamromulan/quectel-rgmii-configuration-notes/tree/main?tab=readme-ov-file#i-cant-get-internet-access-from-the-ethernet-port-common) + - If you don't get an error you should be getting replies back endlessly, press `CTRL-C` to stop it. + - Simply Copy/Paste this into your Command Prompt/Shell +```bash +adb shell "cd /tmp && wget -O RMxxx_rgmii_toolkit.sh https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/development/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh" && cd / +``` + +**Or, if you want to stay in the modems shell when you are done** + +``` +adb shell +``` +Then run +``` +cd /tmp && wget -O RMxxx_rgmii_toolkit.sh https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/development/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh && cd / +``` +**You should see:** +![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulantoolkit.png?raw=true) + +## Tailscale Installation and Config + +> :warning: Your modem must already be connected to the internet for this to install +### Installation: +Open up the toolkit main menu and **press 4** to enter the Tailscale menu + +![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/tailscalemenu.png?raw=true) + +**Press 1, wait for it to install. This is a very large file for the system so give it some time.** + +**Once done and it says Tailscale installed successfully press 2/enter to configure it.** + +![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/tailscaleconfig.png?raw=true) + +If you want to, enable the Tailscale Web UI on port 8088 for configuration from the browser later by **pressing 1/enter**. + +To do it in the toolkit: +First time connecting you'll be given a link to login with + - Press 3 to just connect only. + - Press 4 to connect and enable SSH access (remote command line) over tailscale. + - Press 5 to reconnect with SSH off while connected with SSH on + - Press 6 to disconnect + - Press 7 to Logout + +That's it! From another device running tailscale you should be able to access your modem through the IP assigned to it by your tailnet. To access SSH from another device on the tailnet, open a terminal/command prompt and type + + tailscale ssh root@(IP or Hostname) +IP or Hostname being the IP or hostname assigned to it in your tailnet + + - Note that your SSH client must be able to give you a link to sign in with upon connecting. That's how the session is authorized. Works fine in Windows CMD or on Android use JuiceSSH. +## Advanced/Beta + +### Entware/OPKG installation + + +It isn't perfect yet so it goes here under Advanced/Beta for now. +Here's what you gotta know about going into it: + + - After installing, the `opkg` command will work + - You can run `opkg list` to see a list of installable packages, or head over to https://bin.entware.net/armv7sf-k3.2/Packages.html + - Everything opkg does is installed to /opt + - `/opt` is actually located at `/usrdata/opt` to save space but is + mounted at `/opt` + - Anything `opkg` installs will not be available in the system path by + default but you can get around this either: + +#### Temporarily: + Run this at the start of each adb shell or SSH shell session + + export PATH=/opt/bin:/opt/sbin:$PATH + +#### Permanently: +Symbolic linking each binary installed by the package to `/bin` and `/sbin` from `/opt/bin` and `/opt/sbin` +For example, if you were to install zerotier: + + opkg install zerotier + ln -sf /opt/bin/zerotier-one /bin + ln -sf /opt/bin/zerotier-cli /bin + ln -sf /opt/bin/zerotier-idtool /bin + +Now you can run those 3 binaries from the shell anytime since they are linked in a place already part of the system path. + +I plan to create a watchdog service for /opt/bin and /opt/sbin that will automaticly link new packages to /bin or /sbin later on in order to combat this. + +### TTYd installation + +It isn't perfect yet so it goes here under Advanced/Beta for now. +Here's what you gotta know about going into it: + + - This listens on port 443 for http requests (no SSL/TLS yet) + - This will automaticly install entware and patch the login and passwd binaries with ones from entware + - It will ask you to set a password for the `root` user account + - TTYd doesn't seem to be too mobile friendly for now but I optimized it the best i could for now so it is at least usable through a smartphone browser. Hopefully the startup script can be improved even more later. + +## Acknowledgements +### GitHub Users/Individuals: +Thank You to: + +[Nate Carlson](https://github.com/natecarlson) for the Original Telnet Deamon/socat bridge usage and the Original RGMII Notes + +[aesthernr](https://github.com/aesthernr) for creating the Original Simple Admin + +[rbflurry](https://github.com/rbflurry/) for inital Simple Admin fixes + +[dr-dolomite](https://github.com/dr-dolomite) for some major stat page improvements and this repos first approved external PR! + +[tarunVreddy](https://github.com/tarunVreddy) for helping with the SA band aggregation parse + +### Existing projects: +Simpleadmin heavily uses the AT Command Parsing Scripts (Basically a copy with new changes and tweaks) of Dairyman's Rooter Source https://github.com/ofmodemsandmen/ROOterSource2203 + +Tailscale was obtained through Tailscale's static build page. Since these modems have a 32-bit ARM processor on-board I used the arm package. https://pkgs.tailscale.com/stable/#static + +Entware/opkg was obtained through [Entware's wiki](https://github.com/Entware/Entware/wiki/Alternative-install-vs-standard) and the installer heavily modified by [iamromulan](https://github.com/iamromulan) for use with Quectel modems + +TTYd was obtained from the [TTYd Project](https://github.com/tsl0922/ttyd) diff --git a/development/RMxxx_rgmii_toolkit.sh b/development/RMxxx_rgmii_toolkit.sh new file mode 100644 index 0000000..99403b6 --- /dev/null +++ b/development/RMxxx_rgmii_toolkit.sh @@ -0,0 +1,962 @@ +#!/bin/sh +# Define toolkit paths +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin +GITUSER="iamromulan" +GITTREE="development" +GITMAINTREE="main" +GITDEVTREE="development" +TMP_DIR="/tmp" +USRDATA_DIR="/usrdata" +SOCAT_AT_DIR="/usrdata/socat-at-bridge" +SOCAT_AT_SYSD_DIR="/usrdata/socat-at-bridge/systemd_units" +SIMPLE_ADMIN_DIR="/usrdata/simpleadmin" +SIMPLE_FIREWALL_DIR="/usrdata/simplefirewall" +SIMPLE_FIREWALL_SCRIPT="$SIMPLE_FIREWALL_DIR/simplefirewall.sh" +SIMPLE_FIREWALL_SYSTEMD_DIR="$SIMPLE_FIREWALL_DIR/systemd" +TAILSCALE_DIR="/usrdata/tailscale" +TAILSCALE_SYSD_DIR="/usrdata/tailscale/systemd" +# AT Command Script Variables and Functions +DEVICE_FILE="/dev/smd7" +TIMEOUT=4 # Set a timeout for the response +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} + +# Basic AT commands without socat bridge for fast responce commands only +start_listening() { + cat "$DEVICE_FILE" > /tmp/device_readout & + CAT_PID=$! +} + +send_at_command() { + echo -e "\e[1;31mThis only works for basic quick responding commands!\e[0m" # Red + echo -e "\e[1;36mType 'install' to simply type atcmd in shell from now on\e[0m" + echo -e "\e[1;36mThe installed version is much better than this portable version\e[0m" + echo -e "\e[1;32mEnter AT command (or type 'exit' to quit): \e[0m" + read at_command + if [ "$at_command" = "exit" ]; then + return 1 + fi + + if [ "$at_command" = "install" ]; then + install_update_at_socat + echo -e "\e[1;32mInstalled. Type atcmd from adb shell or ssh to start an AT Command session\e[0m" + return 1 + fi + echo -e "${at_command}\r" > "$DEVICE_FILE" +} + +wait_for_response() { + local start_time=$(date +%s) + local current_time + local elapsed_time + + echo -e "\e[1;32mCommand sent, waiting for response...\e[0m" + while true; do + if grep -qe "OK" -e "ERROR" /tmp/device_readout; then + echo -e "\e[1;32mResponse received:\e[0m" + cat /tmp/device_readout + return 0 + fi + current_time=$(date +%s) + elapsed_time=$((current_time - start_time)) + if [ "$elapsed_time" -ge "$TIMEOUT" ]; then + echo -e "\e[1;31mError: Response timed out.\e[0m" # Red + echo -e "\e[1;32mIf the responce takes longer than a second or 2 to respond this will not work\e[0m" # Green + echo -e "\e[1;36mType install to install the better version of this that will work.\e[0m" # Cyan + return 1 + fi + sleep 1 + done +} + +cleanup() { + kill "$CAT_PID" + wait "$CAT_PID" 2>/dev/null + rm -f /tmp/device_readout +} + +send_at_commands() { + if [ -c "$DEVICE_FILE" ]; then + while true; do + start_listening + send_at_command + if [ $? -eq 1 ]; then + cleanup + break + fi + wait_for_response + cleanup + done + else + echo -e "\e[1;31mError: Device $DEVICE_FILE does not exist!\e[0m" + fi +} + +# Check for existing Entware/opkg installation, install if not installed +ensure_entware_installed() { + remount_rw + if [ ! -f "/opt/bin/opkg" ]; then + echo -e "\e[1;32mInstalling Entware/OPKG\e[0m" + cd /tmp && wget --no-check-certificate -O installentware.sh "http://gitea.hapyle.work:33000/taotao/webui/raw/development/installentware.sh" && chmod +x installentware.sh && ./installentware.sh + if [ "$?" -ne 0 ]; then + echo -e "\e[1;31mEntware/OPKG installation failed. Please check your internet connection or the repository URL.\e[0m" + exit 1 + fi + cd / + else + echo -e "\e[1;32mEntware/OPKG is already installed.\e[0m" + if [ "$(readlink /bin/login)" != "/opt/bin/login" ]; then + opkg update && opkg install shadow-login shadow-passwd shadow-useradd + if [ "$?" -ne 0 ]; then + echo -e "\e[1;31mPackage installation failed. Please check your internet connection and try again.\e[0m" + exit 1 + fi + + # Replace the login and passwd binaries and set home for root to a writable directory + rm /opt/etc/shadow + rm /opt/etc/passwd + cp /etc/shadow /opt/etc/ + cp /etc/passwd /opt/etc + mkdir -p /usrdata/root/bin + touch /usrdata/root/.profile + echo "# Set PATH for all shells" > /usrdata/root/.profile + echo "export PATH=/bin:/usr/sbin:/usr/bin:/sbin:/opt/sbin:/opt/bin:/usrdata/root/bin" >> /usrdata/root/.profile + chmod +x /usrdata/root/.profile + sed -i '1s|/home/root:/bin/sh|/usrdata/root:/bin/bash|' /opt/etc/passwd + rm /bin/login /usr/bin/passwd + ln -sf /opt/bin/login /bin + ln -sf /opt/bin/passwd /usr/bin/ + ln -sf /opt/bin/useradd /usr/bin/ + echo -e "\e[1;31mPlease set the root password.\e[0m" + /opt/bin/passwd + + # Install basic and useful utilities + opkg install mc htop dfc lsof + ln -sf /opt/bin/mc /bin + ln -sf /opt/bin/htop /bin + ln -sf /opt/bin/dfc /bin + ln -sf /opt/bin/lsof /bin + fi + + if [ ! -f "/usrdata/root/.profile" ]; then + opkg update && opkg install shadow-useradd + mkdir -p /usrdata/root/bin + touch /usrdata/root/.profile + echo "# Set PATH for all shells" > /usrdata/root/.profile + echo "export PATH=/bin:/usr/sbin:/usr/bin:/sbin:/opt/sbin:/opt/bin:/usrdata/root/bin" >> /usrdata/root/.profile + chmod +x /usrdata/root/.profile + sed -i '1s|/home/root:/bin/sh|/usrdata/root:/bin/bash|' /opt/etc/passwd + fi + fi + if [ ! -f "/opt/sbin/useradd" ]; then + echo "useradd does not exist. Installing shadow-useradd..." + opkg install shadow-useradd + else + echo "useradd already exists. Continuing..." + fi + + if [ ! -f "/usr/bin/curl" ] && [ ! -f "/opt/bin/curl" ]; then + echo "curl does not exist. Installing curl..." + opkg update && opkg install curl + if [ "$?" -ne 0 ]; then + echo -e "\e[1;31mFailed to install curl. Please check your internet connection and try again.\e[0m" + exit 1 + fi + else + echo "curl already exists. Continuing..." + fi +} + +#Uninstall Entware if the Users chooses +uninstall_entware() { + echo -e '\033[31mInfo: Starting Entware/OPKG uninstallation...\033[0m' + + # Stop services + systemctl stop rc.unslung.service + /opt/etc/init.d/rc.unslung stop + rm /lib/systemd/system/multi-user.target.wants/rc.unslung.service + rm /lib/systemd/system/rc.unslung.service + + systemctl stop opt.mount + rm /lib/systemd/system/multi-user.target.wants/start-opt-mount.service + rm /lib/systemd/system/opt.mount + rm /lib/systemd/system/start-opt-mount.service + + # Unmount /opt if mounted + mountpoint -q /opt && umount /opt + + # Remove Entware installation directory + rm -rf /usrdata/opt + rm -rf /opt + + # Reload systemctl daemon + systemctl daemon-reload + + # Optionally, clean up any modifications to /etc/profile or other system files + # Restore original link to login binary compiled by Quectel + rm /bin/login + ln /bin/login.shadow /bin/login + + echo -e '\033[32mInfo: Entware/OPKG has been uninstalled successfully.\033[0m' +} + +# function to configure the fetures of simplefirewall +configure_simple_firewall() { + if [ ! -f "$SIMPLE_FIREWALL_SCRIPT" ]; then + echo -e "\033[0;31mSimplefirewall is not installed, would you like to install it?\033[0m" + echo -e "\033[0;32m1) Yes\033[0m" + echo -e "\033[0;31m2) No\033[0m" + read -p "Enter your choice (1-2): " install_choice + + case $install_choice in + 1) + install_simple_firewall + ;; + 2) + return + ;; + *) + echo -e "\033[0;31mInvalid choice. Please select either 1 or 2.\033[0m" + ;; + esac + fi + + echo -e "\e[1;32mConfigure Simple Firewall:\e[0m" + echo -e "\e[38;5;208m1) Configure incoming port block\e[0m" + echo -e "\e[38;5;27m2) Configure TTL\e[0m" + read -p "Enter your choice (1-2): " menu_choice + + case $menu_choice in + 1) + # Original ports configuration code with exit option + current_ports_line=$(grep '^PORTS=' "$SIMPLE_FIREWALL_SCRIPT") + ports=$(echo "$current_ports_line" | cut -d'=' -f2 | tr -d '()' | tr ' ' '\n' | grep -o '[0-9]\+') + echo -e "\e[1;32mCurrent configured ports:\e[0m" + echo "$ports" | awk '{print NR") "$0}' + + while true; do + echo -e "\e[1;32mEnter a port number to add/remove, or type 'done' or 'exit' to finish:\e[0m" + read port + if [ "$port" = "done" ] || [ "$port" = "exit" ]; then + if [ "$port" = "exit" ]; then + echo -e "\e[1;31mExiting without making changes...\e[0m" + return + fi + break + elif ! echo "$port" | grep -qE '^[0-9]+$'; then + echo -e "\e[1;31mInvalid input: Please enter a numeric value.\e[0m" + elif echo "$ports" | grep -q "^$port\$"; then + ports=$(echo "$ports" | grep -v "^$port\$") + echo -e "\e[1;32mPort $port removed.\e[0m" + else + ports=$(echo "$ports"; echo "$port" | grep -o '[0-9]\+') + echo -e "\e[1;32mPort $port added.\e[0m" + fi + done + + if [ "$port" != "exit" ]; then + new_ports_line="PORTS=($(echo "$ports" | tr '\n' ' '))" + sed -i "s/$current_ports_line/$new_ports_line/" "$SIMPLE_FIREWALL_SCRIPT" + fi + ;; + 2) + # TTL configuration code + ttl_value=$(cat /usrdata/simplefirewall/ttlvalue) + if [ "$ttl_value" -eq 0 ]; then + echo -e "\e[1;31mTTL is not set.\e[0m" + else + echo -e "\e[1;32mTTL value is set to $ttl_value.\e[0m" + fi + + echo -e "\e[1;31mType 'exit' to cancel.\e[0m" + read -p "What do you want the TTL value to be: " new_ttl_value + if [ "$new_ttl_value" = "exit" ]; then + echo -e "\e[1;31mExiting TTL configuration...\e[0m" + return + elif ! echo "$new_ttl_value" | grep -qE '^[0-9]+$'; then + echo -e "\e[1;31mInvalid input: Please enter a numeric value.\e[0m" + return + else + /usrdata/simplefirewall/ttl-override stop + echo "$new_ttl_value" > /usrdata/simplefirewall/ttlvalue + /usrdata/simplefirewall/ttl-override start + echo -e "\033[0;32mTTL value updated to $new_ttl_value.\033[0m" + fi + ;; + *) + echo -e "\e[1;31mInvalid choice. Please select either 1 or 2.\e[0m" + ;; + esac + + systemctl restart simplefirewall + echo -e "\e[1;32mFirewall configuration updated.\e[0m" +} + +set_simpleadmin_passwd(){ + ensure_entware_installed + opkg update + opkg install libaprutil + wget --no-check-certificate -O /usrdata/root/bin/htpasswd http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/htpasswd && chmod +x /usrdata/root/bin/htpasswd + wget --no-check-certificate -O /usrdata/root/bin/simplepasswd http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/simplepasswd && chmod +x /usrdata/root/bin/simplepasswd + echo -e "\e[1;32mTo change your simpleadmin (admin) password in the future...\e[0m" + echo -e "\e[1;32mIn the console type simplepasswd and press enter\e[0m" + /usrdata/root/bin/simplepasswd + +} + +set_root_passwd() { + echo -e "\e[1;31mPlease set the root/console password.\e[0m" + /opt/bin/passwd +} + +# Function to install/update Simple Admin +install_simple_admin() { + while true; do + echo -e "\e[1;32mWhat version of Simple Admin do you want to install? This will start a webserver on port 80/443 on test build\e[0m" + echo -e "\e[1;32m1) Stable current version, (Main Branch)\e[0m" + echo -e "\e[1;31m2) Install Test Build (Development Branch)\e[0m" + echo -e "\e[0;33m3) Return to Main Menu\e[0m" + echo -e "\e[1;32mSelect your choice: \e[0m" + read choice + + case $choice in + 1) + echo -e "\e[1;32mYou are using the development toolkit; Use the one from main if you want the stable version right now\e[0m" + break + ;; + 2) + ensure_entware_installed + echo -e "\e[1;31m2) Installing simpleadmin from the development test branch\e[0m" + mkdir /usrdata/simpleupdates > /dev/null 2>&1 + mkdir /usrdata/simpleupdates/scripts > /dev/null 2>&1 + wget --no-check-certificate -O /usrdata/simpleupdates/scripts/update_socat-at-bridge.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleupdates/scripts/update_socat-at-bridge.sh && chmod +x /usrdata/simpleupdates/scripts/update_socat-at-bridge.sh + echo -e "\e[1;32mInstalling/updating dependency: socat-at-bridge\e[0m" + echo -e "\e[1;32mPlease Wait....\e[0m" + /usrdata/simpleupdates/scripts/update_socat-at-bridge.sh + echo -e "\e[1;32m Dependency: socat-at-bridge has been updated/installed.\e[0m" + sleep 1 + wget --no-check-certificate -O /usrdata/simpleupdates/scripts/update_simplefirewall.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleupdates/scripts/update_simplefirewall.sh && chmod +x /usrdata/simpleupdates/scripts/update_simplefirewall.sh + echo -e "\e[1;32mInstalling/updating dependency: simplefirewall\e[0m" + echo -e "\e[1;32mPlease Wait....\e[0m" + /usrdata/simpleupdates/scripts/update_simplefirewall.sh + echo -e "\e[1;32m Dependency: simplefirewall has been updated/installed.\e[0m" + sleep 1 + set_simpleadmin_passwd + wget --no-check-certificate -O /usrdata/simpleupdates/scripts/update_simpleadmin.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleupdates/scripts/update_simpleadmin.sh && chmod +x /usrdata/simpleupdates/scripts/update_simpleadmin.sh + echo -e "\e[1;32mInstalling/updating: Simpleadmin content\e[0m" + echo -e "\e[1;32mPlease Wait....\e[0m" + /usrdata/simpleupdates/scripts/update_simpleadmin.sh + echo -e "\e[1;32mSimpleadmin content has been updated/installed.\e[0m" + sleep 1 + break + ;; + 3) + echo "Returning to main menu..." + break + ;; + *) + echo "Invalid choice. Please try again." + ;; + esac + done +} + +# Function to Uninstall Simpleadmin and dependencies +uninstall_simpleadmin_components() { + echo -e "\e[1;32mStarting the uninstallation process for Simpleadmin components.\e[0m" + echo -e "\e[1;32mNote: Uninstalling certain components may affect the functionality of others.\e[0m" + remount_rw + + # Uninstall Simple Firewall + echo -e "\e[1;32mDo you want to uninstall Simplefirewall?\e[0m" + echo -e "\e[1;31mIf you do, the TTL part of simpleadmin will no longer work.\e[0m" + echo -e "\e[1;32m1) Yes\e[0m" + echo -e "\e[1;31m2) No\e[0m" + read -p "Enter your choice (1 or 2): " choice_simplefirewall + if [ "$choice_simplefirewall" -eq 1 ]; then + echo "Uninstalling Simplefirewall..." + systemctl stop simplefirewall + systemctl stop ttl-override + rm -f /lib/systemd/system/simplefirewall.service + rm -f /lib/systemd/system/ttl-override.service + systemctl daemon-reload + rm -rf "$SIMPLE_FIREWALL_DIR" + echo "Simplefirewall uninstalled." + fi + + # Uninstall socat-at-bridge + echo -e "\e[1;32mDo you want to uninstall socat-at-bridge?\e[0m" + echo -e "\e[1;31mIf you do, AT commands and the stat page will no longer work. atcmd won't either.\e[0m" + echo -e "\e[1;32m1) Yes\e[0m" + echo -e "\e[1;31m2) No\e[0m" + read -p "Enter your choice (1 or 2): " choice_socat_at_bridge + if [ "$choice_socat_at_bridge" -eq 1 ]; then + echo -e "\033[0;32mRemoving installed AT Socat Bridge services...\033[0m" + systemctl stop at-telnet-daemon > /dev/null 2>&1 + systemctl disable at-telnet-daemon > /dev/null 2>&1 + systemctl stop socat-smd11 > /dev/null 2>&1 + systemctl stop socat-smd11-to-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd11-from-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd7 > /dev/null 2>&1 + systemctl stop socat-smd7-to-ttyIN2 > /dev/null 2>&1 + systemctl stop socat-smd7-to-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd7-from-ttyIN2 > /dev/null 2>&1 + systemctl stop socat-smd7-from-ttyIN > /dev/null 2>&1 + rm /lib/systemd/system/at-telnet-daemon.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11-to-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11-from-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-to-ttyIN2.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-to-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-from-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-from-ttyIN2.service > /dev/null 2>&1 + systemctl daemon-reload > /dev/null 2>&1 + rm -rf "$SOCAT_AT_DIR" > /dev/null 2>&1 + rm -rf "$SOCAT_AT_DIR" > /dev/null 2>&1 + rm -rf "/usrdata/micropython" > /dev/null 2>&1 + rm -rf "/usrdata/at-telnet" > /dev/null 2>&1 + echo -e "\033[0;32mAT Socat Bridge services removed!...\033[0m" + fi + + # Uninstall ttyd + echo -e "\e[1;32mDo you want to uninstall ttyd (simpleadmin console)?\e[0m" + echo -e "\e[1;31mWarning: Do not uninstall if you are currently using ttyd to do this!!!\e[0m" + echo -e "\e[1;32m1) Yes\e[0m" + echo -e "\e[1;31m2) No\e[0m" + read -p "Enter your choice (1 or 2): " choice_simpleadmin + if [ "$choice_simpleadmin" -eq 1 ]; then + echo -e "\e[1;34mUninstalling ttyd...\e[0m" + systemctl stop ttyd + rm -rf /usrdata/ttyd + rm /lib/systemd/system/ttyd.service + rm /lib/systemd/system/multi-user.target.wants/ttyd.service + rm /bin/ttyd + echo -e "\e[1;32mttyd has been uninstalled.\e[0m" + fi + + echo "Uninstalling the rest of Simpleadmin..." + + # Check if Lighttpd service is installed and remove it if present + if [ -f "/lib/systemd/system/lighttpd.service" ]; then + echo "Lighttpd detected, uninstalling Lighttpd and its modules..." + systemctl stop lighttpd + opkg --force-remove --force-removal-of-dependent-packages remove lighttpd-mod-authn_file lighttpd-mod-auth lighttpd-mod-cgi lighttpd-mod-openssl lighttpd-mod-proxy lighttpd + rm -rf $LIGHTTPD_DIR + fi + + systemctl stop simpleadmin_generate_status + systemctl stop simpleadmin_httpd + rm -f /lib/systemd/system/simpleadmin_httpd.service + rm -f /lib/systemd/system/simpleadmin_generate_status.service + systemctl daemon-reload + rm -rf "$SIMPLE_ADMIN_DIR" + echo "The rest of Simpleadmin and Lighttpd (if present) uninstalled." + remount_ro + + echo "Uninstallation process completed." +} + +# Function for Tailscale Submenu +tailscale_menu() { + while true; do + echo -e "\e[1;32mTailscale Menu\e[0m" + echo -e "\e[1;32m1) Install/Update Tailscale\e[0m" + echo -e "\e[1;36m2) Configure Tailscale\e[0m" + echo -e "\e[1;31m3) Return to Main Menu\e[0m" + read -p "Enter your choice: " tailscale_choice + + case $tailscale_choice in + 1) install_update_tailscale;; + 2) configure_tailscale;; + 3) break;; + *) echo "Invalid option";; + esac + done +} + +# Function to install, update, or remove Tailscale +install_update_tailscale() { +echo -e "\e[1;31m2) Installing tailscale from the $GITTREE branch\e[0m" + ensure_entware_installed + mkdir /usrdata/simpleupdates > /dev/null 2>&1 + mkdir /usrdata/simpleupdates/scripts > /dev/null 2>&1 + wget --no-check-certificate -O /usrdata/simpleupdates/scripts/update_tailscale.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleupdates/scripts/update_tailscale.sh && chmod +x /usrdata/simpleupdates/scripts/update_tailscale.sh + echo -e "\e[1;32mInstalling/updating: Tailscale\e[0m" + echo -e "\e[1;32mPlease Wait....\e[0m" + remount_rw + /usrdata/simpleupdates/scripts/update_tailscale.sh + echo -e "\e[1;32m Tailscale has been updated/installed.\e[0m" +} + +# Function to Configure Tailscale +configure_tailscale() { + while true; do + echo "Configure Tailscale" + echo -e "\e[38;5;40m1) Enable Tailscale Web UI at http://192.168.225.1:8088 (Gateway on port 8088)\e[0m" # Green + echo -e "\e[38;5;196m2) Disable Tailscale Web UI\e[0m" # Red + echo -e "\e[38;5;27m3) Connect to Tailnet\e[0m" # Brown + echo -e "\e[38;5;87m4) Connect to Tailnet with SSH ON\e[0m" # Light cyan + echo -e "\e[38;5;105m5) Reconnect to Tailnet with SSH OFF\e[0m" # Light magenta + echo -e "\e[38;5;172m6) Disconnect from Tailnet (reconnects at reboot)\e[0m" # Light yellow + echo -e "\e[1;31m7) Logout from tailscale account\e[0m" + echo -e "\e[38;5;27m8) Return to Tailscale Menu\e[0m" + read -p "Enter your choice: " config_choice + + case $config_choice in + 1) + remount_rw + cd /lib/systemd/system/ + wget --no-check-certificate -O tailscale-webui.service http://gitea.hapyle.work:33000/taotao/webui/raw/development/tailscale/systemd/tailscale-webui.service + wget --no-check-certificate -O tailscale-webui-trigger.service http://gitea.hapyle.work:33000/taotao/webui/raw/development/tailscale/systemd/tailscale-webui-trigger.service + ln -sf /lib/systemd/system/tailscale-webui-trigger.service /lib/systemd/system/multi-user.target.wants/ + systemctl daemon-reload + echo "Tailscale Web UI Enabled" + echo "Starting Web UI..." + systemctl start tailscale-webui + echo "Web UI started!" + remount_ro + ;; + 2) + remount_rw + systemctl stop tailscale-webui + systemctl disable tailscale-webui-trigger + rm /lib/systemd/system/multi-user.target.wants/tailscale-webui.service + rm /lib/systemd/system/multi-user.target.wants/tailscale-webui-trigger.service + rm /lib/systemd/system/tailscale-webui.service + rm /lib/systemd/system/tailscale-webui-trigger.service + systemctl daemon-reload + echo "Tailscale Web UI Stopped and Disabled" + remount_ro + ;; + 3) $TAILSCALE_DIR/tailscale up --accept-dns=false --reset;; + 4) $TAILSCALE_DIR/tailscale up --ssh --accept-dns=false --reset;; + 5) $TAILSCALE_DIR/tailscale up --accept-dns=false --reset;; + 6) $TAILSCALE_DIR/tailscale down;; + 7) $TAILSCALE_DIR/tailscale logout;; + 8) break;; + *) echo "Invalid option";; + esac + done +} + +# Function to manage Daily Reboot Timer +manage_reboot_timer() { + # Remount root filesystem as read-write + mount -o remount,rw / + + # Check if the rebootmodem service, timer, or trigger already exists + if [ -f /lib/systemd/system/rebootmodem.service ] || [ -f /lib/systemd/system/rebootmodem.timer ] || [ -f /lib/systemd/system/rebootmodem-trigger.service ]; then + echo -e "\e[1;32mThe rebootmodem service/timer/trigger is already installed.\e[0m" + echo -e "\e[1;32m1) Change\e[0m" # Green + echo -e "\e[1;31m2) Remove\e[0m" # Red + read -p "Enter your choice (1 for Change, 2 for Remove): " reboot_choice + + case $reboot_choice in + 2) + # Stop and disable timer and trigger service by removing symlinks + systemctl stop rebootmodem.timer + systemctl stop rebootmodem-trigger.service + + # Remove symbolic links and files + rm -f /lib/systemd/system/multi-user.target.wants/rebootmodem-trigger.service + rm -f /lib/systemd/system/rebootmodem.service + rm -f /lib/systemd/system/rebootmodem.timer + rm -f /lib/systemd/system/rebootmodem-trigger.service + rm -f "$USRDATA_DIR/reboot_modem.sh" + + # Reload systemd to apply changes + systemctl daemon-reload + + echo -e "\e[1;32mRebootmodem service, timer, trigger, and script removed successfully.\e[0m" + ;; + 1) + printf "Enter the new time for daily reboot (24-hour format in Coordinated Universal Time, HH:MM): " + read new_time + + # Validate the new time format using grep + if ! echo "$new_time" | grep -qE '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'; then + echo "Invalid time format. Exiting." + exit 1 + else + # Remove old symlinks and script + rm -f /lib/systemd/system/multi-user.target.wants/rebootmodem-trigger.service + rm -f "$USRDATA_DIR/reboot_modem.sh" + + # Set the user time to the new time and recreate the service, timer, trigger, and script + user_time=$new_time + create_service_and_timer + fi + ;; + *) + echo -e "\e[1;31mInvalid choice. Exiting.\e[0m" + exit 1 + ;; + esac + else + printf "Enter the time for daily reboot (24-hour format in UTC, HH:MM): " + read user_time + + # Validate the time format using grep + if ! echo "$user_time" | grep -qE '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'; then + echo "Invalid time format. Exiting." + exit 1 + else + create_service_and_timer + fi + fi + + # Remount root filesystem as read-only + mount -o remount,ro / +} + +# Function to create systemd service and timer files with the user-specified time for the reboot timer +create_service_and_timer() { + remount_rw + # Define the path for the modem reboot script + MODEM_REBOOT_SCRIPT="$USRDATA_DIR/reboot_modem.sh" + + # Create the modem reboot script + echo "#!/bin/sh +/bin/echo -e 'AT+CFUN=1,1 \r' > /dev/smd7" > "$MODEM_REBOOT_SCRIPT" + + # Make the script executable + chmod +x "$MODEM_REBOOT_SCRIPT" + + # Create the systemd service file for reboot + echo "[Unit] +Description=Reboot Modem Daily + +[Service] +Type=oneshot +ExecStart=/bin/sh /usrdata/reboot_modem.sh +Restart=no +RemainAfterExit=no" > /lib/systemd/system/rebootmodem.service + + # Create the systemd timer file with the user-specified time + echo "[Unit] +Description=Starts rebootmodem.service daily at the specified time + +[Timer] +OnCalendar=*-*-* $user_time:00 +Persistent=false" > /lib/systemd/system/rebootmodem.timer + + # Create a trigger service that starts the timer at boot + echo "[Unit] +Description=Trigger the rebootmodem timer at boot + +[Service] +Type=oneshot +ExecStart=/bin/systemctl start rebootmodem.timer +RemainAfterExit=yes" > /lib/systemd/system/rebootmodem-trigger.service + + # Create symbolic links for the trigger service in the wanted directory + ln -sf /lib/systemd/system/rebootmodem-trigger.service /lib/systemd/system/multi-user.target.wants/ + + # Reload systemd to recognize the new timer and trigger service + systemctl daemon-reload + sleep 2s + + # Start the trigger service, which will start the timer + systemctl start rebootmodem-trigger.service + remount_ro + + # Confirmation + echo -e "\e[1;32mRebootmodem-trigger service created and started successfully.\e[0m" + echo -e "\e[1;32mReboot schedule set successfully. The modem will reboot daily at $user_time UTC.\e[0m" +} + +manage_cfun_fix() { + cfun_service_path="/lib/systemd/system/cfunfix.service" + cfun_fix_script="/usrdata/cfun_fix.sh" + + mount -o remount,rw / + + if [ -f "$cfun_service_path" ]; then + echo -e "\e[1;32mThe CFUN fix is already installed. Do you want to remove it?\e[0m" # Green + echo -e "\e[1;32m1) Yes\e[0m" # Green + echo -e "\e[1;31m2) No\e[0m" # Red + read -p "Enter your choice: " choice + + if [ "$choice" = "1" ]; then + echo "Removing CFUN fix..." + systemctl stop cfunfix.service + rm -f /lib/systemd/system/multi-user.target.wants/cfunfix.service + rm -f "$cfun_service_path" + rm -f "$cfun_fix_script" + systemctl daemon-reload + echo "CFUN fix has been removed." + else + echo "Returning to main menu..." + fi + else + echo -e "\e[1;32mInstalling CFUN fix...\e[0m" + + # Create the CFUN fix script + echo "#!/bin/sh +/bin/echo -e 'AT+CFUN=1 \r' > /dev/smd7" > "$cfun_fix_script" + chmod +x "$cfun_fix_script" + + # Create the systemd service file to execute the CFUN fix script at boot + echo "[Unit] +Description=CFUN Fix Service +After=network.target + +[Service] +Type=oneshot +ExecStart=$cfun_fix_script +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target" > "$cfun_service_path" + + ln -sf "$cfun_service_path" "/lib/systemd/system/multi-user.target.wants/" + systemctl daemon-reload + mount -o remount,ro / + echo -e "\e[1;32mCFUN fix has been installed and will execute at every boot.\e[0m" + fi +} + + +install_sshd() { + if [ -d "/usrdata/sshd" ]; then + echo -e "\e[1;31mSSHD is currently installed.\e[0m" + echo -e "Do you want to update or uninstall?" + echo -e "1.) Update" + echo -e "2.) Uninstall" + read -p "Select an option (1 or 2): " sshd_choice + + case $sshd_choice in + 1) + echo -e "\e[1;31m2) Installing sshd from the $GITTREE branch\e[0m" + ;; + 2) + echo -e "\e[1;31mUninstalling SSHD...\e[0m" + systemctl stop sshd + rm /lib/systemd/system/sshd.service + opkg remove openssh-server-pam + echo -e "\e[1;32mSSHD has been uninstalled successfully.\e[0m" + return 0 + ;; + *) + echo -e "\e[1;31mInvalid option. Please select 1 or 2.\e[0m" + return 1 + ;; + esac + fi + + # Proceed with installation or updating if not uninstalling + ensure_entware_installed + mkdir /usrdata/simpleupdates > /dev/null 2>&1 + mkdir /usrdata/simpleupdates/scripts > /dev/null 2>&1 + wget --no-check-certificate -O /usrdata/simpleupdates/scripts/update_sshd.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleupdates/scripts/update_sshd.sh && chmod +x /usrdata/simpleupdates/scripts/update_sshd.sh + echo -e "\e[1;32mInstalling/updating: SSHd\e[0m" + echo -e "\e[1;32mPlease Wait....\e[0m" + /usrdata/simpleupdates/scripts/update_sshd.sh + echo -e "\e[1;32m SSHd has been updated/installed.\e[0m" +} + + +# Main menu + +ARCH=$(uname -a) +if echo "$ARCH" | grep -q "aarch64"; then + cd /tmp && wget --no-check-certificate -O RM55x_rcPCIe_toolkit.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development-SDXPINN/RM55x_rcPCIe_toolkit.sh && chmod +x RM55x_rcPCIe_toolkit.sh && ./RM55x_rcPCIe_toolkit.sh && cd / + exit 0 +elif echo "$ARCH" | grep -q "armv7l"; then + # Continue if architecture is armv7l + echo "Architecture is armv7l, continuing..." +else + echo "Unsupported architecture." + exit 1 +fi + +while true; do +echo " .%+: " +echo " .*@@@-. " +echo " :@@@@- " +echo " @@@@#. " +echo " -@@@@#. " +echo " :. %@@@@: -# " +echo " .+- #@@@@%.+@- " +echo " .#- . +@@@@# #@- " +echo " -@*@*@% @@@@@::@@= " +echo ".+%@@@@@@@@@%=. =@@@@# #@@- .. " +echo " .@@@@@: :@@@@@ =@@@..%= " +echo " -::@-.+. @@@@@.=@@@- =@- " +echo " .@- .@@@@@:.@@@* @@. " +echo " .%- -@@@@@:=@@@@ @@# " +echo " .#- .%@@@@@@#. +@@@@@.#@@@@ @@@." +echo " .*- .@@@@@@@@@@=. @@@@@@ @@@@@ @@@:" +echo " :. .%@@@@@@@@@@@%. .@@@@@+:@@@@@ @@@-" +echo " -@@@@@@@@@@@@@@@..@@@@@@.-@@@@@ .@@@-" +echo " -@@@@@@@@@@%. .@@@@@@. @@@@@+ =@@@=" +echo " =@@@@@@@@* .@@@@@@. @@@@@@..@@@@-" +echo " #@@@@@@@@-*@@@@@%..@@@@@@+ #@@@@-" +echo " @@@@@@:.-@@@@@@. @@@@@@= %@@@@@." +echo " .@@@@. *@@@@@@- .+@@@@@@-.@@@@@@+ " +echo " %@@. =@@@@@*. +@@@@@@%.-@@@@@@% " +echo " .@@ .@@@@@= :@@@@@@@@..@@@@@@@= " +echo " =@.+@@@@@. -@@@@@@@*.:@@@@@@@*. " +echo " %.*@@@@= .@@@@@@@-.:@@@@@@@+. " +echo " ..@@@@= .@@@@@@: #@@@@@@@: " +echo " .@@@@ +@@@@..%@@@@@+. " +echo " .@@@. @@@@.:@@@@+. " +echo " @@@. @@@. @@@* .@. " +echo " :@@@ %@@..@@#. *@ " +echo " -*: .@@* :@@. @@. -..@@ " +echo " =@@@@@@.*@- :@% @* =@:=@# " +echo " .@@@-+@@@@:%@..%- ...@%:@@: " +echo " .@@. @@-%@: .%@@*@@%. " +echo " :@@ :+ *@ *@@#*@@@. " +echo " =@@@.@@@@ " +echo " .*@@@:=@@@@: " +echo " .@@@@:.@@@@@: " +echo " .@@@@#.-@@@@@. " +echo " #@@@@: =@@@@@- " +echo " .@@@@@..@@@@@@* " +echo " -@@@@@. @@@@@@#. " +echo " -@@@@@ @@@@@@% " +echo " @@@@@. #@@@@@@. " +echo " :@@@@# =@@@@@@% " +echo " @@@@@: @@@@@@@: " +echo " *@@@@ @@@@@@@. " +echo " .@@@@ @@@@@@@ " +echo " #@@@. @@@@@@* " +echo " @@@# @@@@@@@ " +echo " .@@+=@@@@@@. " +echo " *@@@@@@ " +echo " :@@@@@= " +echo " .@@@@@@. " +echo " :@@@@@*. " +echo " .=@@@@@- " +echo " :+##+. " + + echo -e "\e[92m" + echo "Welcome to iamromulan's RGMII Toolkit script for Quectel RMxxx Series modems!" + echo "Visit https://github.com/iamromulan for more!" + echo -e "\e[0m" + echo "Select an option:" + echo -e "\e[0m" + echo -e "\e[96m1) Send AT Commands\e[0m" # Cyan + echo -e "\e[93m2) Install Simple Admin\e[0m" # Yellow + echo -e "\e[95m3) Set Simpleadmin (admin) password\e[0m" # Light Purple + echo -e "\e[94m4) Set Console/ttyd (root) password\e[0m" # Light Blue + echo -e "\e[91m5) Uninstall Simple Admin\e[0m" # Light Red + echo -e "\e[95m6) Simple Firewall Management\e[0m" # Light Purple + echo -e "\e[94m7) Tailscale Management\e[0m" # Light Blue + echo -e "\e[92m8) Install/Change or remove Daily Reboot Timer\e[0m" # Light Green + echo -e "\e[96m9) Install/Uninstall CFUN 0 Fix\e[0m" # Cyan (repeated color for additional options) + echo -e "\e[91m10) Uninstall Entware/OPKG\e[0m" # Light Red + echo -e "\e[92m11) Install Speedtest.net CLI app (speedtest command)\e[0m" # Light Green + echo -e "\e[92m12) Install Fast.com CLI app (fast command)(tops out at 40Mbps)\e[0m" # Light Green + echo -e "\e[92m13) Install OpenSSH Server\e[0m" # Light Green + echo -e "\e[93m14) Exit\e[0m" # Yellow (repeated color for exit option) + read -p "Enter your choice: " choice + + case $choice in + 1) + send_at_commands + ;; + 2) + install_simple_admin + ;; + 3) set_simpleadmin_passwd + ;; + 4) + set_root_passwd + ;; + 5) + uninstall_simpleadmin_components + ;; + 6) + configure_simple_firewall + ;; + + 7) + tailscale_menu + ;; + 8) + manage_reboot_timer + ;; + 9) + manage_cfun_fix + ;; + 10) + echo -e "\033[31mAre you sure you want to uninstall entware?\033[0m" + echo -e "\033[31m1) Yes\033[0m" + echo -e "\033[31m2) No\033[0m" + read -p "Select an option (1 or 2): " user_choice + + case $user_choice in + 1) + # If yes, uninstall existing entware + echo -e "\033[31mUninstalling existing entware...\033[0m" + uninstall_entware # Assuming uninstall_entware is a defined function or command + echo -e "\033[31mEntware has been uninstalled.\033[0m" + ;; + 2) + # If no, exit the script + echo -e "\033[31mUninstallation cancelled.\033[0m" + exit # Use 'exit' to terminate the script outside a loop + ;; + *) + # Handle invalid input + echo -e "\033[31mInvalid option. Please select 1 or 2.\033[0m" + ;; + esac + ;; + + 11) + ensure_entware_installed + echo -e "\e[1;32mInstalling Speedtest.net CLI (speedtest command)\e[0m" + remount_rw + mkdir /usrdata/root + mkdir /usrdata/root/bin + cd /usrdata/root/bin + wget --no-check-certificate https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-linux-armhf.tgz + tar -xzf ookla-speedtest-1.2.0-linux-armhf.tgz + rm ookla-speedtest-1.2.0-linux-armhf.tgz + rm speedtest.md + cd / + ln -sf /usrdata/root/bin/speedtest /bin + remount_ro + echo -e "\e[1;32mSpeedtest CLI (speedtest command) installed!!\e[0m" + echo -e "\e[1;32mTry running the command 'speedtest'\e[0m" + echo -e "\e[1;32mNote that it will not work unless you login to the root account first\e[0m" + echo -e "\e[1;32mNormaly only an issue in adb, ttyd and ssh you are forced to login\e[0m" + echo -e "\e[1;32mIf in adb just type login and then try to run the speedtest command\e[0m" + ;; + 12) + echo -e "\e[1;32mInstalling fast.com CLI (fast command)\e[0m" + remount_rw + mkdir /usrdata/root + mkdir /usrdata/root/bin + cd /usrdata/root/bin + wget --no-check-certificate -O fast http://gitea.hapyle.work:33000/taotao/webui/raw/development/fast_linux_arm && chmod +x fast + cd / + ln -sf /usrdata/root/bin/fast /bin + remount_ro + echo -e "\e[1;32mFast.com CLI (speedtest command) installed!!\e[0m" + echo -e "\e[1;32mTry running the command 'fast'\e[0m" + echo -e "\e[1;32mThe fast.com test tops out at 40Mbps on the modem\e[0m" + ;; + 13) + install_sshd + ;; + 14) + echo -e "\e[1;32mGoodbye!\e[0m" + break + ;; + *) + echo -e "\e[1;31mInvalid option\e[0m" + ;; + esac +done diff --git a/development/at-list.md b/development/at-list.md new file mode 100644 index 0000000..9ae80ee --- /dev/null +++ b/development/at-list.md @@ -0,0 +1,109 @@ +# Useful AT Commands + +You can send more than one command at once by sperating them with ``;`` and not including the AT part. ``AT+QENG="servingcell";+QCAINFO`` for example to see the info from both ``AT+QENG="servingcell"`` and ``AT+QCAINFO`` + + +## PCIe RC Ethernet mode setup + +### For RM500-RM521 modems +``AT+QETH="eth_driver","r8125",1;+QCFG="pcie/mode",1;+QCFG="usbnet",1;+QMAP="MPDN_rule",0,1,0,1,1,"FF:FF:FF:FF:FF:FF";+QMAP="DHCPV4DNS","disable";+QCFG="usbcfg",0x2C7C,0x0801,1,1,1,1,1,2,0;+CFUN=1,1`` + +This will do the following: + +- Set the 2.5Gig Ethernet driver as active +- Enable PCIe RC mode +- Set to ECM mode via USB and AP mode connection behavior +- Enable IPPT +- Enable DNS IPPT (disables onboard proxy) +- Force Enables ADB Access +- Reboots after all the above + + + +### For x70 modems (RM550/551) + +For BETA versions of firmware: the adb value 2 trick still works so one and done: + +``AT+QCFG="pcie/mode",1;+QCFG="usbnet",1;+QCFG="usbcfg",0x2C7C,0x0122,1,1,1,1,1,2,0;+CFUN=1,1`` + +OR if you are running the latest non-beta firmware + +``AT+QCFG="pcie/mode",1;+QCFG="usbnet",1`` + +Then unlock ADB: + +Ask the modem for its adb code by sending: ``AT+QADBKEY?`` + +It'll respond with something like ``+QADBKEY: 29229988`` + +Take that number and paste it in this generator: https://onecompiler.com/python/3znepjcsq (hint: where it says STDIN) + +You should get something like + +``AT+QADBKEY="mrX4zOPwdSIEjfM"`` + +Send that command to the modem and adb will be able to be turned on with the next command + +Now you can turn it on with the usbcfg command ``AT+QCFG="usbcfg"`` + +***Be super careful, this controls what ports are on/off over USB.*** + +Run it and you will get the current settings. Something like this: + +``+QCFG: "usbcfg",0x2C7C,0x0122,1,1,1,1,1,0,0`` + +Send ``AT+QCFG="usbcfg",0x2C7C,0x0122,1,1,1,1,1,1,0`` to enable adb + +Now you can reboot: ``AT+CFUN=1,1`` + + + +This will do the following: + +- Enable PCIe RC mode (Driver selection is automatic now) +- Set to ECM mode via USB and AP mode connection behavior +- Force Enables ADB Access +- Reboots after all the above + +Tip: APN automatic selection will somtimes choose the wrong APN. You may need to set your APN after powering up with the SIM inserted. + +## The List + - ``AT+CFUN=1,1`` (reboot) + - ``AT+CFUN=0;CFUN=1`` (Disconnect then reconnect)(tip: run this after chnaging APN and you don't have to reboot) + - ``AT+QMAPWAC? ``(get current status of auto connect, 0=disabled 1=enabled) +- ``AT+QMAPWAC=1`` (enable auto connect internet for ethernet) +- ``AT+QMAPWAC=0`` (disable auto connect for ethernet; use when you want internet over USB to work; IPPT must be disabled) +- ``AT+QUIMSLOT?`` (get active sim slot; 1=Slot 1; 2=Slot 2) + - ``AT+QUIMSLOT=1`` (switch to sim slot 1) + - ``AT+QUIMSLOT=2`` (switch to sim slot 2) + - ``AT+CGDCONT?`` (Get active APN profle st 1 through 8) + - ``AT+QMBNCFG="AutoSel",0;+QMBNCFG="Deactivate"`` (Disable Automatic APN selection)(You will need to set your APN when you switch SIMs or Slots)(Can also set APN after you switch the run ``AT+CFUN=0;CFUN=1`` + - ``AT+CGDCONT=1,"IPV4V6","APNHERE"`` (Sets APN profile 1 to APNHERE using both IPV4 and IPV6) + - ``AT+GSN`` (Show current IMEI) + - ``AT+EGMR=0,7`` (Show current IMEI) + - ``AT+EGMR=1,7,"IMEIGOESHERE"`` (sets/repairs IMEI) + - ``AT+QCFG="usbcfg",0x2C7C,0x0801,1,1,1,1,1,2,0`` (enables adb bypasses adb key) + - ``AT+QENG="servingcell"`` (shows anchor band and network connection status) +- ``AT+QCAINFO`` (Show all connected bands/CA info) +- ``AT+QNWPREFCFG="mode_pref"`` (Check what the current network search mode is set to) +- ``AT+QNWPREFCFG="mode_pref",AUTO`` (Set network search mode to automatic) +- ``AT+QNWPREFCFG="mode_pref",NR5G:LTE`` (Set network search mode to 5GNR and 4GLTE only) +- ``AT+QNWPREFCFG="mode_pref",NR5G`` (Set network search mode to 5GNR only) +- ``AT+QNWPREFCFG="mode_pref",LTE`` (Set network search mode to 4GLTE only) +- ``AT+QNWPREFCFG="nr5g_disable_mode"`` (Check to see if SA or NSA NR5G is disabled) +- ``AT+QNWPREFCFG="nr5g_disable_mode",0`` (Enable Both SA and NSA 5GNR) +- ``AT+QNWPREFCFG="nr5g_disable_mode",1`` (Disable SA 5GNR only) +- ``AT+QNWPREFCFG="nr5g_disable_mode",2`` (Disable NSA 5GNR only) +- ``AT+QNWPREFCFG="nr5g_band"`` (Get current SA 5GNR bandlock settings) +- ``AT+QNWPREFCFG="nsa_nr5g_band"`` (Get current NSA 5GNR bandlock settings) +- ``AT+QNWPREFCFG="nr5g_band",1:2:3:4:5:6`` (Example: Lock to SA 5G/NR bands n1,n2,n3,n4,n5, and n6) +- ``AT+QNWPREFCFG="nsa_nr5g_band",1:2:3:4:5:6`` (Example: Lock to SA 5G/NR bands n1,n2,n3,n4,n5, and n6) +- ``AT+QNWPREFCFG="lte_band"`` (Get current 4GLTE bandlock settings) +- ``AT+QNWPREFCFG="lte_band",1:2:3:4:5:6`` (Example: Lock to 4G/LTE bands 1,2,3,4,5, and 6) +- ``AT+QMAP="WWAN"`` (Show currently assigned IPv4 and IPv6 from the provider) +- ``AT+QMAP="LANIP"`` (Show current DHCP range and Gateway address for VLAN0) +- ``AT+QMAP="LANIP",IP_start_range,IP_end_range,Gateway_IP `` (Set IPv4 Start/End range and Gateway IP of DHCP for VLAN0) +- ``AT+QMAP="DHCPV4DNS","disable"`` (disable the onboard DNS proxy; recommended for IPPT) +- ``AT+QMAP="MPDN_rule",0,1,0,1,1,"FF:FF:FF:FF:FF:FF"`` (Turn on IP Passthrough for Ethernet) +(:warning: On the RM551E-GL you must specify the ethernet devices MAC address instead of FF:FF:FF...) +- ``AT+QMAP="MPDN_rule",0`` (turn off IPPT/clear MPDN rule 0; Remember to run AT+QMAPWAC=1 and reboot after) diff --git a/development/download b/development/download new file mode 100644 index 0000000..fc67251 --- /dev/null +++ b/development/download @@ -0,0 +1,114 @@ +#!/bin/sh + +# Determine the absolute path of the script +SCRIPT_PATH=$(realpath "$0") + +download() { + if [ "$1" = "github" ]; then + download_github_directory "$2" "$3" + else + download_file "$1" "$2" + fi +} + +download_file() { + url="$1" + output="${2:-$(basename "$url")}" + + echo "Attempting to download file from URL: $url" + echo "Saving to output: $output" + + # Ensure the output directory exists + mkdir -p "$(dirname "$output")" + + if command -v curl > /dev/null 2>&1; then + curl -L -o "$output" "$url" + elif command -v wget > /dev/null 2>&1; then + wget -O "$output" "$url" + else + echo "Error: Neither curl nor wget is available." + exit 1 + fi +} + +download_github_directory() { + github_url="$1" + output_dir="${2:-.}" # Set output directory to the provided parameter or current directory + + repo_info=$(echo "$github_url" | sed -n 's|https://github.com/\([^/]*\)/\([^/]*\)/tree/\([^/]*\)/\(.*\)|\1 \2 \3 \4|p') + owner=$(echo "$repo_info" | cut -d' ' -f1) + repo=$(echo "$repo_info" | cut -d' ' -f2) + branch=$(echo "$repo_info" | cut -d' ' -f3) + directory=$(echo "$repo_info" | cut -d' ' -f4) + + echo "Owner: $owner, Repo: $repo, Branch: $branch, Directory: $directory" + + if [ -z "$owner" ] || [ -z "$repo" ] || [ -z "$branch" ] || [ -z "$directory" ]; then + echo "Error: Invalid GitHub URL." + exit 1 + fi + + echo "Output directory set to: $output_dir" + + api_url="https://api.github.com/repos/$owner/$repo/contents/$directory?ref=$branch" + echo "Fetching directory contents from API URL: $api_url" + + if command -v curl > /dev/null 2>&1; then + contents=$(curl -s -H "Accept: application/vnd.github.v3+json" "$api_url") + elif command -v wget > /dev/null 2>&1; then + contents=$(wget -qO- --header="Accept: application/vnd.github.v3+json" "$api_url") + else + echo "Error: Neither curl nor wget is available." + exit 1 + fi + + echo "API Response Contents:" + echo "$contents" + + # Use awk to parse JSON content and prepare commands + echo "$contents" | awk -v output_dir="$output_dir" -v owner="$owner" -v repo="$repo" -v branch="$branch" -v script_path="$SCRIPT_PATH" ' + BEGIN { + RS="},"; FS=","; # Set Record Separator to "}," and Field Separator to "," + } + /"type": "file"/ { + file_path = ""; download_url = ""; + for (i = 1; i <= NF; i++) { + if ($i ~ /"path": "/) { + gsub(/.*"path": "/, "", $i); + gsub(/".*/, "", $i); + file_path = $i; + } + if ($i ~ /"download_url": "/) { + gsub(/.*"download_url": "/, "", $i); + gsub(/".*/, "", $i); + download_url = $i; + } + } + if (file_path && download_url) { + output_file_path = output_dir "/" file_path; + print "Calling download_file for:", download_url, "to", output_file_path; + system("'"$SCRIPT_PATH"'" " \"" download_url "\" \"" output_file_path "\""); + } + } + /"type": "dir"/ { + sub_dir = ""; + for (i = 1; i <= NF; i++) { + if ($i ~ /"path": "/) { + gsub(/.*"path": "/, "", $i); + gsub(/".*/, "", $i); + sub_dir = $i; + } + } + if (sub_dir) { + print "Calling download_github_directory for sub-directory:", sub_dir; + system("'"$SCRIPT_PATH"'" " github \"https://github.com/" owner "/" repo "/tree/" branch "/" sub_dir "\" \"" output_dir "\""); + } + }' +} + +if [ "$#" -eq 0 ]; then + echo "Usage: download [output_directory]" + exit 1 +else + download "$@" +fi diff --git a/development/fast_linux_arm b/development/fast_linux_arm new file mode 100644 index 0000000..4ff1760 Binary files /dev/null and b/development/fast_linux_arm differ diff --git a/development/installentware.sh b/development/installentware.sh new file mode 100644 index 0000000..d7fcb35 --- /dev/null +++ b/development/installentware.sh @@ -0,0 +1,167 @@ +#!/bin/sh +# Modified by iamromulan to set up a proper entware environment for Quectel RM5xx series m.2 modems +TYPE='generic' +#|---------|-----------------| +#| TARGET | Quectel Modem | +#| ARCH | armv7sf-k3.2 | +#| LOADER | ld-linux.so.3 | +#| GLIBC | 2.27 | +#|---------|-----------------| +unset LD_LIBRARY_PATH +unset LD_PRELOAD +ARCH=armv7sf-k3.2 +LOADER=ld-linux.so.3 +GLIBC=2.27 +PRE_OPKG_PATH=$(which opkg) + +# Remount filesystem as read-write +mount -o remount,rw / + +create_opt_mount() { + # Bind /usrdata/opt to /opt + echo -e '\033[32mInfo: Setting up /opt mount to /usrdata/opt...\033[0m' + cat < /lib/systemd/system/opt.mount +[Unit] +Description=Bind /usrdata/opt to /opt + +[Mount] +What=/usrdata/opt +Where=/opt +Type=none +Options=bind + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl start opt.mount + + # Additional systemd service to ensure opt.mount starts at boot + echo -e '\033[32mInfo: Creating service to start opt.mount at boot...\033[0m' + cat < /lib/systemd/system/start-opt-mount.service +[Unit] +Description=Ensure opt.mount is started at boot +After=network.target + +[Service] +Type=oneshot +ExecStart=/bin/systemctl start opt.mount + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + ln -s /lib/systemd/system/start-opt-mount.service /lib/systemd/system/multi-user.target.wants/start-opt-mount.service +} + +if [ -n "$PRE_OPKG_PATH" ]; then + # Automatically rename the existing opkg binary + mv "$PRE_OPKG_PATH" "${PRE_OPKG_PATH}_old" + echo -e "\033[32mFactory/Already existing opkg has been renamed to opkg_old.\033[0m" +else + echo "Info: no existing opkg binary detected, proceeding with installation" +fi + +echo -e '\033[32mInfo: Creating /opt mount pointed to /usrdata/opt ...\033[0m' +create_opt_mount +echo -e '\033[32mInfo: Proceeding with main installation ...\033[0m' +# no need to create many folders. entware-opt package creates most +for folder in bin etc lib/opkg tmp var/lock +do + if [ -d "/opt/$folder" ]; then + echo -e '\033[31mWarning: Folder /opt/$folder exists!\033[0m' + echo -e '\033[31mWarning: If something goes wrong please clean /opt folder and try again.\033[0m' + else + mkdir -p /opt/$folder + fi +done + +echo -e '\033[32mInfo: Opkg package manager deployment...\033[0m' +URL=http://bin.entware.net/${ARCH}/installer +wget --no-check-certificate $URL/opkg -O /opt/bin/opkg +chmod 755 /opt/bin/opkg +wget --no-check-certificate $URL/opkg.conf -O /opt/etc/opkg.conf + +echo -e '\033[32mInfo: Basic packages installation...\033[0m' +/opt/bin/opkg update +/opt/bin/opkg install entware-opt + +# Fix for multiuser environment +chmod 777 /opt/tmp + +for file in passwd group shells shadow gshadow; do + if [ $TYPE = 'generic' ]; then + if [ -f /etc/$file ]; then + ln -sf /etc/$file /opt/etc/$file + else + [ -f /opt/etc/$file.1 ] && cp /opt/etc/$file.1 /opt/etc/$file + fi + else + if [ -f /opt/etc/$file.1 ]; then + cp /opt/etc/$file.1 /opt/etc/$file + fi + fi +done + +[ -f /etc/localtime ] && ln -sf /etc/localtime /opt/etc/localtime + +# Create and enable rc.unslung service +echo -e '\033[32mInfo: Creating rc.unslung (Entware init.d service)...\033[0m' +cat < /lib/systemd/system/rc.unslung.service +[Unit] +Description=Start Entware services + +[Service] +Type=oneshot +# Add a delay to give /opt time to mount +ExecStartPre=/bin/sleep 5 +ExecStart=/opt/etc/init.d/rc.unslung start +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +ln -s /lib/systemd/system/rc.unslung.service /lib/systemd/system/multi-user.target.wants/rc.unslung.service +systemctl start rc.unslung.service +echo -e '\033[32mInfo: Congratulations!\033[0m' +echo -e '\033[32mInfo: If there are no errors above then Entware was successfully initialized.\033[0m' +echo -e '\033[32mInfo: Add /opt/bin & /opt/sbin to $PATH variable\033[0m' +ln -sf /opt/bin/opkg /bin +echo -e '\033[32mInfo: Patching Quectel Login Binary\033[0m' +opkg update && opkg install shadow-login shadow-passwd shadow-useradd + if [ "$?" -ne 0 ]; then + echo -e "\e[1;31mPackage installation failed. Please check your internet connection and try again.\e[0m" + exit 1 + fi + + # Replace the login and passwd binaries and set home for root to a writable directory + rm /opt/etc/shadow + rm /opt/etc/passwd + cp /etc/shadow /opt/etc/ + cp /etc/passwd /opt/etc + mkdir /usrdata/root + mkdir /usrdata/root/bin + touch /usrdata/root/.profile + echo "# Set PATH for all shells" > /usrdata/root/.profile + echo "export PATH=/bin:/usr/sbin:/usr/bin:/sbin:/opt/sbin:/opt/bin:/usrdata/root/bin" >> /usrdata/root/.profile + chmod +x /usrdata/root/.profile + sed -i '1s|/home/root:/bin/sh|/usrdata/root:/bin/bash|' /opt/etc/passwd + rm /bin/login /usr/bin/passwd + ln -sf /opt/bin/login /bin + ln -sf /opt/bin/passwd /usr/bin/ + ln -sf /opt/bin/useradd /usr/bin/ + echo -e "\e[1;31mPlease set the root password.\e[0m" + /usr/bin/passwd + + # Install basic and useful utilites + opkg install mc htop dfc lsof + ln -sf /opt/bin/mc /bin + ln -sf /opt/bin/htop /bin + ln -sf /opt/bin/dfc /bin + ln -sf /opt/bin/lsof /bin +# Remount filesystem as read-only +mount -o remount,ro / diff --git a/development/simpleadmin/.rev b/development/simpleadmin/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/simpleadmin/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/simpleadmin/console/.profile b/development/simpleadmin/console/.profile new file mode 100644 index 0000000..5713bf4 --- /dev/null +++ b/development/simpleadmin/console/.profile @@ -0,0 +1,7 @@ +#!/bin/bash + +#Path +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin + +#Post-login execution +/usrdata/simpleadmin/console/menu/start_menu.sh \ No newline at end of file diff --git a/development/simpleadmin/console/menu/LAN_settings.sh b/development/simpleadmin/console/menu/LAN_settings.sh new file mode 100644 index 0000000..5a6a28d --- /dev/null +++ b/development/simpleadmin/console/menu/LAN_settings.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +CONFIG_FILE="/etc/data/mobileap_cfg.xml" + +# Display Messages in Colors +display_random_color() { + local msg="$1" + local colors=(33 34 35 36 37) # ANSI color codes for yellow, blue, magenta, cyan, white + local num_colors=${#colors[@]} + local random_color_index=$(($RANDOM % num_colors)) # Pick a random index from the colors array + echo -e "\033[${colors[$random_color_index]}m$msg\033[0m" +} + +display_green() { + echo -e "\033[0;32m$1\033[0m" +} + +display_red() { + echo -e "\033[0;31m$1\033[0m" +} + +# Check and Install xml binary if not present +check_and_install_xml() { + if [ ! -f "/opt/bin/xml" ]; then + echo "xml binary not found. Attempting to install xmlstarlet..." + opkg update + opkg install xmlstarlet + # Verify installation + if [ ! -f "/opt/bin/xml" ]; then + echo "Failed to install xmlstarlet. Exiting..." + exit 1 + fi + fi + echo "xml binary is available." +} + +# Edit XML Value +edit_xml_value() { + local node="$1" + local new_value="$2" + /opt/bin/xml ed -L -u "$node" -v "$new_value" "$CONFIG_FILE" +} + +# Get Current XML Value +get_current_value() { + /opt/bin/xml sel -t -v "$1" "$CONFIG_FILE" +} + +# Enable/Disable Menu +enable_disable_menu() { + local node="$1" + local current_value=$(get_current_value "$node") + echo "Current status: $([ "$current_value" == "1" ] && echo "Enabled" || echo "Disabled")" + echo "1. Enable" + echo "2. Disable" + read -p "Choose an option to toggle (1-2): " choice + local new_value="$([ "$choice" == "1" ] && echo "1" || echo "0")" + edit_xml_value "$node" "$new_value" + display_green "After making changes, please reboot to have them take effect." +} + +# Edit Simple Value +edit_simple_value() { + local node="$1" + local description="$2" + local current=$(get_current_value "$node") + echo "Current $description: $current" + read -p "Enter new $description: " new_value + edit_xml_value "$node" "$new_value" + display_green "After making changes, please reboot to have them take effect." +} + +# Edit DHCP IP Range +edit_dhcp_range() { + local start_ip=$(get_current_value "//MobileAPLanCfg/DHCPCfg/StartIP") + local end_ip=$(get_current_value "//MobileAPLanCfg/DHCPCfg/EndIP") + echo "Current Start IP: $start_ip" + echo "Current End IP: $end_ip" + read -p "Enter new Start IP: " new_start_ip + read -p "Enter new End IP: " new_end_ip + edit_xml_value "//MobileAPLanCfg/DHCPCfg/StartIP" "$new_start_ip" + edit_xml_value "//MobileAPLanCfg/DHCPCfg/EndIP" "$new_end_ip" + display_green "After making changes, please reboot to have them take effect." +} + +# Reboot the system +reboot_system() { + echo "Rebooting system..." + atcmd 'AT+CFUN=1,1' # Ensure this command is correct for your system + echo "System reboot initiated. Good luck." +} + +# Main Menu +main_menu() { + while true; do + clear + display_red "Warning, these changes can break access over the network. Know what you are doing, and be prepared to use ADB to fix this just in case." + echo "Configuration Menu" + echo "------------------" + display_green "1. Edit Gateway IPV4 Address" + display_green "2. Edit Gateway URL" + display_green "3. Edit LAN DHCP Start/End Range" + display_green "4. Edit LAN Subnet Mask" + display_green "5. Edit DHCPv6 Base address" + display_green "6. Toggle IPv4 NAT" + display_green "7. Toggle IPv6 NAT" + display_green "8. Toggle DHCP Server" + display_green "9. Toggle DHCPv4" + display_green "10. Toggle DHCPv6" + display_green "11. Toggle WAN Autoconnect" + display_green "12. Toggle WAN AutoReconnect" + display_green "13. Toggle Roaming" + display_green "14. Toggle WAN DNSv4 Passthrough" + display_green "15. Toggle WAN DNSv6 Passthrough" + display_green "16. Toggle IPPT NAT/Ability to access gateway while in IPPT mode" + display_green "17. Toggle UPnP" + display_green "18. Reboot System" + display_green "19. Exit" + echo + read -p "Select an option (1-19): " option + + case "$option" in + 1) edit_simple_value "//MobileAPLanCfg/APIPAddr" "Gateway IPV4 Address";; + 2) edit_simple_value "//MobileAPLanCfg/GatewayURL" "Gateway URL";; + 3) edit_dhcp_range;; + 4) edit_simple_value "//MobileAPLanCfg/SubNetMask" "LAN Subnet Mask";; + 5) edit_simple_value "//MobileAPLanCfg/ULAIPv6BaseAddr" "DHCPv6 Base Address";; + 6) enable_disable_menu "//MobileAPNatCfg/IPv4NATDisable";; + 7) enable_disable_menu "//MobileAPNatv6Cfg/EnableIPv6NAT";; + 8) enable_disable_menu "//MobileAPLanCfg/EnableDHCPServer";; + 9) enable_disable_menu "//MobileAPLanCfg/EnableIPV4";; + 10) enable_disable_menu "//MobileAPLanCfg/EnableIPV6";; + 11) enable_disable_menu "//MobileAPWanCfg/AutoConnect";; + 12) enable_disable_menu "//MobileAPWanCfg/ReConnect";; + 13) enable_disable_menu "//MobileAPWanCfg/Roaming";; + 14) enable_disable_menu "//Dhcpv4Cfg/EnableDhcpv4Dns";; + 15) enable_disable_menu "//Dhcpv6Cfg/EnableDhcpv6Dns";; + 16) enable_disable_menu "//IPPassthroughFeatureWithNAT";; + 17) enable_disable_menu "//MobileAPSrvcCfg/UPnP";; + 18) reboot_system;; + 19) break;; + *) echo "Invalid option. Please try again.";; + esac + done +} + +# Start by checking and installing xml if necessary, then mount filesystem as rw and run the menu +mount -o remount,rw / +check_and_install_xml +main_menu diff --git a/development/simpleadmin/console/menu/sfirewall_settings.sh b/development/simpleadmin/console/menu/sfirewall_settings.sh new file mode 100644 index 0000000..7701bf2 --- /dev/null +++ b/development/simpleadmin/console/menu/sfirewall_settings.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +SIMPLE_FIREWALL_DIR="/usrdata/simplefirewall" +SIMPLE_FIREWALL_SCRIPT="$SIMPLE_FIREWALL_DIR/simplefirewall.sh" +SIMPLE_FIREWALL_SYSTEMD_DIR="$SIMPLE_FIREWALL_DIR/systemd" + +# Display Messages in Colors +display_random_color() { + local msg="$1" + local colors=(33 34 35 36 37) # ANSI color codes for yellow, blue, magenta, cyan, white + local num_colors=${#colors[@]} + local random_color_index=$(($RANDOM % num_colors)) # Pick a random index from the colors array + echo -e "\033[${colors[$random_color_index]}m$msg\033[0m" +} + +display_green() { + echo -e "\033[0;32m$1\033[0m" +} + +display_red() { + echo -e "\033[0;31m$1\033[0m" +} + +set_portblocks() { + current_ports_line=$(grep '^PORTS=' "$SIMPLE_FIREWALL_SCRIPT") + ports=$(echo "$current_ports_line" | cut -d'=' -f2 | tr -d '()' | tr ' ' '\n' | grep -o '[0-9]\+') + echo -e "\e[1;32mCurrent configured ports:\e[0m" + echo "$ports" | awk '{print NR") "$0}' + + while true; do + echo -e "\e[1;32mEnter a port number to add/remove, or type 'done' or 'exit' to finish:\e[0m" + read port + if [ "$port" = "done" ] || [ "$port" = "exit" ]; then + if [ "$port" = "exit" ]; then + echo -e "\e[1;31mExiting without making changes...\e[0m" + return + fi + break + elif ! echo "$port" | grep -qE '^[0-9]+$'; then + echo -e "\e[1;31mInvalid input: Please enter a numeric value.\e[0m" + elif echo "$ports" | grep -q "^$port\$"; then + ports=$(echo "$ports" | grep -v "^$port\$") + echo -e "\e[1;32mPort $port removed.\e[0m" + else + ports=$(echo "$ports"; echo "$port" | grep -o '[0-9]\+') + echo -e "\e[1;32mPort $port added.\e[0m" + fi + done + + if [ "$port" != "exit" ]; then + new_ports_line="PORTS=($(echo "$ports" | tr '\n' ' '))" + sed -i "s/$current_ports_line/$new_ports_line/" "$SIMPLE_FIREWALL_SCRIPT" + fi +} + +set_ttl(){ +# TTL configuration code + ttl_value=$(cat /usrdata/simplefirewall/ttlvalue) + if [ "$ttl_value" -eq 0 ]; then + echo -e "\e[1;31mTTL is not set.\e[0m" + else + echo -e "\e[1;32mTTL value is set to $ttl_value.\e[0m" + fi + + echo -e "\e[1;31mType 'exit' to cancel.\e[0m" + read -p "What do you want the TTL value to be: " new_ttl_value + if [ "$new_ttl_value" = "exit" ]; then + echo -e "\e[1;31mExiting TTL configuration...\e[0m" + return + elif ! echo "$new_ttl_value" | grep -qE '^[0-9]+$'; then + echo -e "\e[1;31mInvalid input: Please enter a numeric value.\e[0m" + return + else + /usrdata/simplefirewall/ttl-override stop + echo "$new_ttl_value" > /usrdata/simplefirewall/ttlvalue + /usrdata/simplefirewall/ttl-override start + echo -e "\033[0;32mTTL value updated to $new_ttl_value.\033[0m" + fi +} + +# function to configure the fetures of simplefirewall +simple_firewall_menu() { + if [ ! -f "$SIMPLE_FIREWALL_SCRIPT" ]; then + display_random_color "Simplefirewall is not installed, would you like to install it?" + display_green "1) Yes" + display_red "2) No" + read -p "Enter your choice (1-2): " install_choice + + case $install_choice in + 1) + install_simple_firewall + ;; + 2) + return + ;; + *) + display_red "Invalid choice. Please select either 1 or 2." + ;; + esac + fi + + display_random_color "Configure Simple Firewall:" + display_green "1) Configure incoming port block" + display_green "2) Configure TTL" + display_green "3) Exit to Main Menu" + read -p "Enter your choice (1-2): " menu_choice + + case $menu_choice in + 1) + set_portblocks + ;; + 2) + set_ttl + ;; + 3) + return + ;; + *) + echo -e "\e[1;31mInvalid choice. Please select either 1 or 2.\e[0m" + ;; + esac + + systemctl restart simplefirewall + echo -e "\e[1;32mFirewall configuration updated.\e[0m" +} + +# Main execution +mount -o remount,rw / +simple_firewall_menu \ No newline at end of file diff --git a/development/simpleadmin/console/menu/start_menu.sh b/development/simpleadmin/console/menu/start_menu.sh new file mode 100644 index 0000000..aea19f8 --- /dev/null +++ b/development/simpleadmin/console/menu/start_menu.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# Define executable files path +MENU_SH=/usrdata/simpleadmin/console/menu + +# Display Messages in Colors +display_random_color() { + local msg="$1" + local colors=(33 34 35 36 37) # ANSI color codes for yellow, blue, magenta, cyan, white + local num_colors=${#colors[@]} + local random_color_index=$(($RANDOM % num_colors)) # Pick a random index from the colors array + echo -e "\033[${colors[$random_color_index]}m$msg\033[0m" +} + +display_green() { + echo -e "\033[0;32m$1\033[0m" +} + +display_red() { + echo -e "\033[0;31m$1\033[0m" +} + +# Menus + +toolkit_menu() { + while true; do + display_random_color "Run a Toolkit version" + display_green "Select an option:" + echo "------------------" + display_green "1. Get and run the Toolkit" + display_random_color "2. Get and run the Development/unstable Toolkit" + display_random_color "3. Exit (Enter Root Shell)" + echo + read -p "Select an option (1-3): " option + + case "$option" in + 1) cd /tmp && wget --no-check-certificate -O RMxxx_rgmii_toolkit.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh && cd / ;; + 2) cd /tmp && wget --no-check-certificate -O RMxxx_rgmii_toolkit.sh http://gitea.hapyle.work:33000/taotao/webui/raw/development/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh && cd / ;; + 3) break ;; + *) echo "Invalid option. Please try again." ;; + esac + done +} + +apps_menu() { + while true; do + display_random_color "Run a modem App" + display_green "Select an option:" + echo "------------------" + display_random_color "1. Open File Browser/Editor (mc)" + display_random_color "2. View Used/Available space" + display_random_color "3. Open Task Manager/View CPU Load" + display_random_color "4. Run speedtest.net test" + display_random_color "5. Run fast.com test (30Mbps max)" + display_green "6. Go Back" + echo + read -p "Select an option (1-6): " option + + case "$option" in + 1) mc ;; + 2) dfc ;; + 3) htop ;; + 4) speedtest ;; + 5) fast ;; + 6) break ;; + *) echo "Invalid option. Please try again." ;; + esac + done +} + +settings_menu() { + while true; do + display_random_color "Welcome to" && display_green "iamromulan's" && display_random_color "Simple Console Menu" + display_green "Select an option:" + echo "------------------" + display_green "1. LAN Settings" + display_green "2. simplefirewall settings (TTL and Port Block)" + display_green "3. Change simpleadmin (admin) password" + display_green "4. Change root password (shell/ssh/console)" + display_green "5. Go back" + echo + read -p "Select an option (1-5): " option + + case "$option" in + 1) $MENU_SH/LAN_settings.sh ;; + 2) $MENU_SH/sfirewall_settings.sh ;; + 3) simplepasswd ;; + 4) passwd ;; + 5) break ;; + *) echo "Invalid option. Please try again." ;; + esac + done +} + +main_menu() { + while true; do + display_green "Welcome to iamromulan's Simple Console Menu" + display_green "To get back to this from the root shell, just type 'menu'" + display_green "Select an option:" + echo "------------------" + display_random_color "1. Apps" + display_random_color "2. Settings" + display_random_color "3. Toolkit" + display_random_color "4. Exit (Enter Root Shell)" + echo + read -p "Select an option (1-4): " option + + case "$option" in + 1) apps_menu ;; + 2) settings_menu ;; + 3) toolkit_menu ;; + 4) break ;; + *) echo "Invalid option. Please try again." ;; + esac + done +} + +main_menu diff --git a/development/simpleadmin/console/services/ethernet_watchdog.sh b/development/simpleadmin/console/services/ethernet_watchdog.sh new file mode 100644 index 0000000..f24780d --- /dev/null +++ b/development/simpleadmin/console/services/ethernet_watchdog.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Define the command to execute when the ethernet port breaks +command_to_execute="/usrdata/socat-at-bridge/atcmd 'AT+CFUN=1,1'" + +# Define the monitoring function +watch() { + while true; do + # Extract the last 60 lines of dmesg and count the specific pattern occurrences + count=$(dmesg | tail -60 | grep -e "eth0: cmd = 0xff, should be 0x47" -e "eth0: pci link is down" | grep -c "eth0") + + # Check if the count of patterns is 4 or more + if [ "$count" -ge 4 ]; then + echo "Condition met, executing command..." + eval "$command_to_execute" + # Optionally, add a break here if you want the script to stop after executing the command + # break + fi + + # Sleep for 3 seconds before checking again + sleep 3 + done +} + +# Initial delay before starting monitoring +sleep 30 +watch diff --git a/development/simpleadmin/console/services/ping_watchdog.sh b/development/simpleadmin/console/services/ping_watchdog.sh new file mode 100644 index 0000000..6662402 --- /dev/null +++ b/development/simpleadmin/console/services/ping_watchdog.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Define the hostname or IP address to ping +HOSTNAME="google.com" + +# Number of pings to attempt +PING_COUNT=6 + +# Initialize a counter for successful pings +success_count=0 + +# Attempt to ping the specified number of times +for i in $(seq 1 $PING_COUNT); do + # Ping the hostname with a timeout of 1 second per ping + if ping -c 1 -W 1 $HOSTNAME &> /dev/null; then + ((success_count++)) + else + echo "Ping attempt $i failed." + fi +done + +# Check if all pings failed +if [ $success_count -eq 0 ]; then + echo "All $PING_COUNT ping attempts failed, executing AT command." + /bin/atcmd 'AT+CFUN=1,1' +else + echo "$success_count out of $PING_COUNT ping attempts were successful." +fi diff --git a/development/simpleadmin/console/services/systemd/ping_watchdog.service b/development/simpleadmin/console/services/systemd/ping_watchdog.service new file mode 100644 index 0000000..3ebe17b --- /dev/null +++ b/development/simpleadmin/console/services/systemd/ping_watchdog.service @@ -0,0 +1,14 @@ +[Unit] +Description=Ping Watchdog Service +Wants=network.target +After=network.target + +[Service] +Type=simple +ExecStartPre=/bin/sleep 60 # Sleep for 60 seconds to ensure the network is ready +ExecStart=/usrdata/simpleadmin/console/services/ping_watchdog.sh +Restart=on-failure +RestartSec=30s + +[Install] +WantedBy=multi-user.target diff --git a/development/simpleadmin/console/ttyd.bash b/development/simpleadmin/console/ttyd.bash new file mode 100644 index 0000000..6ca7120 --- /dev/null +++ b/development/simpleadmin/console/ttyd.bash @@ -0,0 +1,15 @@ +#!/bin/bash + +# Check if /usrdata/socat-at-bridge/atcmd exists +#if [ -f "/usrdata/socat-at-bridge/atcmd" ]; then + # Read the serial number +# serial_number=$(/usrdata/socat-at-bridge/atcmd 'AT+EGMR=0,5' | grep '+EGMR:' | cut -d '"' -f2) + # Read the firmware revision +# firmware_revision=$(/usrdata/socat-at-bridge/atcmd 'AT+QGMR' | grep -o 'RM[0-9A-Z].*') +#else +# serial_number="UNKNOWN" +# firmware_revision="UNKNOWN" +#fi +# +# Start a login session +exec /bin/login diff --git a/development/simpleadmin/htpasswd b/development/simpleadmin/htpasswd new file mode 100644 index 0000000..b05dcb8 Binary files /dev/null and b/development/simpleadmin/htpasswd differ diff --git a/development/simpleadmin/lighttpd.conf b/development/simpleadmin/lighttpd.conf new file mode 100644 index 0000000..ad22369 --- /dev/null +++ b/development/simpleadmin/lighttpd.conf @@ -0,0 +1,47 @@ +server.modules = ( + "mod_redirect", + "mod_cgi", + "mod_proxy", + "mod_openssl", + "mod_authn_file", +) + +server.username = "www-data" +server.groupname = "dialout" + +server.port = 80 +server.document-root = "/usrdata/simpleadmin/www" +index-file.names = ( "index.html" ) + +auth.backend = "htpasswd" +auth.backend.htpasswd.userfile = "/opt/etc/.htpasswd" + +$SERVER["socket"] == "0.0.0.0:443" { + ssl.engine = "enable" + ssl.privkey= "/usrdata/simpleadmin/server.key" + ssl.pemfile= "/usrdata/simpleadmin/server.crt" + ssl.acme-tls-1 = "/etc/simpleadmin/dehydrated/tls-alpn-01" + ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2") # (lighttpd 1.4.56 default; recommended to accept only TLSv1.2 and TLSv1.3) + auth.require = ( "/" => ( + "method" => "basic", + "realm" => "Authorized users only", + "require" => "valid-user" + ) + ) +} + +# Redirect everything to https +$HTTP["scheme"] == "http" { + url.redirect = ("" => "https://${url.authority}${url.path}${qsa}") +} + +# Anything in /cgi-bin will be run as a script +$HTTP["url"] =~ "/cgi-bin/" { + cgi.assign = ( "" => "" ) +} + +# Handle proxy to ttyd if it's running +$HTTP["url"] =~ "(^/console)" { + proxy.header = ("map-urlpath" => ( "/console" => "/" ), "upgrade" => "enable" ) + proxy.server = ( "" => ("" => ( "host" => "127.0.0.1", "port" => 8080 ))) +} diff --git a/development/simpleadmin/script/create_watchcat.sh b/development/simpleadmin/script/create_watchcat.sh new file mode 100644 index 0000000..087f2c7 --- /dev/null +++ b/development/simpleadmin/script/create_watchcat.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +# Function to create and run the Watchcat script +create_and_run_watchcat_script() { + local ip=$1 + local timeout=$2 + local failure_count=$3 + local script_path="/usrdata/simpleadmin/script/watchcat.sh" + + # Create the script with the watchcat logic + sudo cat << EOF > $script_path +#!/bin/sh + +failures=0 + +while :; do + if ping -c 1 $ip > /dev/null 2>&1; then + failures=0 + else + failures=\$((failures + 1)) + if [ "\$failures" -ge "$failure_count" ]; then + echo "Rebooting system due to \$failures consecutive ping failures." + /sbin/reboot + exit 0 + fi + fi + sleep $timeout +done +EOF + + # Make the watchcat script executable + chmod +x $script_path + + # Create a JSON to be fetched later + echo "{\"enabled\": true, \"track_ip\": \"$ip\", \"ping_timeout\": $timeout, \"ping_failure_count\": $failure_count}" > /usrdata/simpleadmin/script/watchcat.json + + # Check if the script was created successfully + if [ -f "$script_path" ]; then + # Make the script executable + chmod +x "$script_path" + + # Run the script in the background + # nohup /bin/sh "$script_path" & + /bin/sh "$script_path" & + + echo "Watchcat script created and running." + else + echo "Failed to create the Watchcat script." + echo "Please check the script path: $script_path" + fi +} + +# Check if the script is called with the required parameters +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Call the function with the provided arguments +create_and_run_watchcat_script "$1" "$2" "$3" \ No newline at end of file diff --git a/development/simpleadmin/script/remove_watchcat.sh b/development/simpleadmin/script/remove_watchcat.sh new file mode 100644 index 0000000..a217cb7 --- /dev/null +++ b/development/simpleadmin/script/remove_watchcat.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Function to remove the Watchcat script and JSON file +remove_watchcat_script() { + local script_path="/usrdata/simpleadmin/script/watchcat.sh" + local json_path="/usrdata/simpleadmin/script/watchcat.json" + + # Mount as read-write + mount -o remount,rw / + + # Remove the watchcat script if it exists + if [ -f "$script_path" ]; then + rm "$script_path" + echo "Removed $script_path" + else + echo "$script_path does not exist" + fi + + # Remove the JSON file if it exists + if [ -f "$json_path" ]; then + rm "$json_path" + echo "Removed $json_path" + else + echo "$json_path does not exist" + fi + + # Mount as read-only + mount -o remount,ro / +} + +# Call the function to remove the scripts +remove_watchcat_script \ No newline at end of file diff --git a/development/simpleadmin/script/ttl_script.sh b/development/simpleadmin/script/ttl_script.sh new file mode 100644 index 0000000..ec9b285 --- /dev/null +++ b/development/simpleadmin/script/ttl_script.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Check if the required parameters are provided +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assign the provided parameters to variables +mode="$1" +ttl_value="$2" + +# Check if iptables is still set +ttlcheck=$(/opt/bin/sudo /usr/sbin/iptables -w 5 -t mangle -vnL | grep TTL | awk '{print $13}') + +# If TTL is still set, manually remove values +if [ ! -z "${ttlcheck}" ]; then + /opt/bin/sudo /usr/sbin/iptables -w 5 -t mangle -D POSTROUTING -o rmnet+ -j TTL --ttl-set "${ttlcheck}" &>/dev/null || true + /opt/bin/sudo /usr/sbin/ip6tables -w 5 -t mangle -D POSTROUTING -o rmnet+ -j HL --hl-set "${ttlcheck}" &>/dev/null || true +fi + +# Handle the enable/disable mode +case "${mode}" in + enable) + # Echo TTL to file + echo "${ttl_value}" > /usrdata/simplefirewall/ttlvalue + + # Set Start Service + /opt/bin/sudo /usrdata/simplefirewall/ttl-override start + ;; + disable) + # Remove TTL value file + rm -f /usrdata/simplefirewall/ttlvalue + + # Stop the service + /opt/bin/sudo /usrdata/simplefirewall/ttl-override stop + ;; + *) + echo "Invalid mode: ${mode}" + echo "Usage: $0 " + exit 1 + ;; +esac \ No newline at end of file diff --git a/development/simpleadmin/simplepasswd b/development/simpleadmin/simplepasswd new file mode 100644 index 0000000..f84f796 --- /dev/null +++ b/development/simpleadmin/simplepasswd @@ -0,0 +1,5 @@ +#/bin/bash + +echo -e "\e[1;31mPlease set your simpleadmin (User: admin) web login password.\e[0m" +/usrdata/root/bin/htpasswd -c /opt/etc/.htpasswd admin +echo -e "\e[1;32mPassword set.\e[0m" \ No newline at end of file diff --git a/development/simpleadmin/systemd/lighttpd.service b/development/simpleadmin/systemd/lighttpd.service new file mode 100644 index 0000000..104efe1 --- /dev/null +++ b/development/simpleadmin/systemd/lighttpd.service @@ -0,0 +1,14 @@ +[Unit] +Description=Lighttpd Daemon +After=network.target opt.mount + +[Service] +Type=simple +PIDFile=/opt/var/run/lighttpd.pid +ExecStartPre=/opt/sbin/lighttpd -tt -f /usrdata/simpleadmin/lighttpd.conf +ExecStart=/opt/sbin/lighttpd -D -f /usrdata/simpleadmin/lighttpd.conf +ExecReload=/bin/kill -USR1 $MAINPID +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/development/simpleadmin/systemd/ttyd.service b/development/simpleadmin/systemd/ttyd.service new file mode 100644 index 0000000..22fbd23 --- /dev/null +++ b/development/simpleadmin/systemd/ttyd.service @@ -0,0 +1,12 @@ +[Unit] +Description=TTYD Service +After=network.target + +[Service] +Type=simple +ExecStartPre=/bin/sleep 5 +ExecStart=/usrdata/simpleadmin/console/ttyd -i 127.0.0.1 -p 8080 -t 'theme={"foreground":"white","background":"black"}' -t fontSize=25 --writable /usrdata/simpleadmin/console/ttyd.bash +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/development/simpleadmin/www/cgi-bin/get_atcommand b/development/simpleadmin/www/cgi-bin/get_atcommand new file mode 100644 index 0000000..4841ad9 --- /dev/null +++ b/development/simpleadmin/www/cgi-bin/get_atcommand @@ -0,0 +1,35 @@ +#!/bin/bash +QUERY_STRING=$(echo "${QUERY_STRING}" | sed 's/;//g') +function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; } + +if [ "${QUERY_STRING}" ]; then + export IFS="&" + for cmd in ${QUERY_STRING}; do + if [ "$(echo $cmd | grep '=')" ]; then + key=$(echo $cmd | awk -F '=' '{print $1}') + value=$(echo $cmd | awk -F '=' '{print $2}') + eval $key=$value + fi + done +fi + +x=$(urldecode "$atcmd") +MYATCMD=$(printf '%b\n' "${atcmd//%/\\x}") +if [ -n "${MYATCMD}" ]; then + # Initialize wait time to 200 ms + wait_time=200 + while true; do + runcmd=$(echo -en "$x\r\n" | microcom -t $wait_time /dev/ttyOUT2) + # Check if "OK" or "ERROR" is present in the response + if [[ $runcmd =~ "OK" ]] || [[ $runcmd =~ "ERROR" ]]; then + break # Exit the loop if "OK" or "ERROR" is found + fi + # If neither "OK" nor "ERROR" is found, increment wait time by 1 second + ((wait_time++)) + done +fi + +echo "Content-type: text/plain" +echo $x +echo "" +echo $runcmd diff --git a/development/simpleadmin/www/cgi-bin/get_ping b/development/simpleadmin/www/cgi-bin/get_ping new file mode 100644 index 0000000..414fbbd --- /dev/null +++ b/development/simpleadmin/www/cgi-bin/get_ping @@ -0,0 +1,19 @@ +#!/bin/bash + +# This script will ping 8.8.8.8 and return the result +# If the ping is successful, it will return "OK" +# If the ping fails, it will return "ERROR" + +# Send the ping command and store the output +ping_output=$(ping -c 1 8.8.8.8) + +# Check if the output contains "0% packet loss" +if echo "$ping_output" | grep -q "0% packet loss"; then + echo "Content-type: text/plain" + echo "" + echo "OK" +else + echo "Content-type: text/plain" + echo "" + echo "ERROR" +fi diff --git a/development/simpleadmin/www/cgi-bin/get_ttl_status b/development/simpleadmin/www/cgi-bin/get_ttl_status new file mode 100644 index 0000000..dc2d54b --- /dev/null +++ b/development/simpleadmin/www/cgi-bin/get_ttl_status @@ -0,0 +1,20 @@ +#!/bin/bash + +# Check iptables for ttlvalue +ttlvalue=$(/opt/bin/sudo /usr/sbin/iptables -w 5 -t mangle -vnL | grep TTL | awk '{print $13}' | head -n1) +ttlenabled=true; + +# Set Variables +if [ -z "${ttlvalue}" ]; then + ttlvalue=0 + ttlenabled=false +fi + +echo "Content-type: text/json" +echo "" +cat < $watch_script +#!/bin/bash + +ip_or_dns="$IpDNS" +cooldown=$cooldown +action="$action" +fail_count=0 +max_failures=$failures + +# Process the action variable. + +# Create a JSON file containing the parameters of the script +echo -n '{"ip_or_dns":"$ip_or_dns","cooldown":$cooldown,"action":"$action","fail_count":$fail_count,"max_failures":$max_failures}' > /tmp/watchatParams.json + + +while true; do + if ping -c 1 -W 1 \$ip_or_dns > /dev/null; then + fail_count=0 + echo "Success at \$(date)" >> /tmp/watchat.log + # Convert /tmp/watchat.log to json format + echo -n '{"log":[' > /tmp/watchat.json + cat /tmp/watchat.log | sed 's/$/,/' | tr -d '\n' | sed 's/,$//' >> /tmp/watchat.json + echo -n ']}' >> /tmp/watchat.json + else + ((fail_count++)) + if [ \$fail_count -ge \$max_failures ]; then + case "\$action" in + reboot) + echo "Rebooting system..." + /sbin/reboot + ;; + switch_sim) + echo "Switching SIM..." + echo -ne "AT+CNMI=2,1\r" > /dev/ttyOUT2 + sleep 1 + echo "Switching SIM at \$(date)" >> /tmp/watchat.log + ;; + none) + echo "No action taken." + echo "No action taken at \$(date)" >> /tmp/watchat.log + ;; + *) + echo "Unknown action: \$action" + ;; + esac + # Reset fail count + fail_count=0 + fi + fi + echo "Fail count: \$fail_count at \$(date)" >> /tmp/watchat.log + # Convert /tmp/watchat.log to json format + echo -n '{"log":[' > /tmp/watchat.json + cat /tmp/watchat.log | sed 's/$/,/' | tr -d '\n' | sed 's/,$//' >> /tmp/watchat.json + echo -n ']}' >> /tmp/watchat.json + sleep \$cooldown +done +EOF + + chmod +x $watch_script + + cat < /lib/systemd/system/watchcat.service +[Unit] +Description=Ping Watcher Service + +[Service] +ExecStart=$watch_script +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + + ln -s /lib/systemd/system/watchcat.service /etc/systemd/system/multi-user.target.wants/watchcat.service + + systemctl daemon-reload + systemctl start watchcat.service + + response="Script created at $watch_script and made executable. Service created and started." + +elif [ "$status" == "disabled" ]; then + watch_script="/usrdata/simpleadmin/script/watchat.sh" + rm -f $watch_script + + systemctl stop watchcat.service + rm -f /lib/systemd/system/watchcat.service + rm -f /etc/systemd/system/multi-user.target.wants/watchcat.service + + systemctl daemon-reload + + response="Script removed at $watch_script. Service stopped and removed." +else + response="Invalid status. Please provide either enabled or disabled." +fi + +echo "Content-type: text/plain" +echo "" +echo "$response" \ No newline at end of file diff --git a/development/simpleadmin/www/cgi-bin/user_atcommand b/development/simpleadmin/www/cgi-bin/user_atcommand new file mode 100644 index 0000000..2e18517 --- /dev/null +++ b/development/simpleadmin/www/cgi-bin/user_atcommand @@ -0,0 +1,26 @@ +#!/bin/bash +QUERY_STRING=$(echo "${QUERY_STRING}" | sed 's/;//g') +function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; } + +if [ "${QUERY_STRING}" ]; then + export IFS="&" + for cmd in ${QUERY_STRING}; do + if [ "$(echo $cmd | grep '=')" ]; then + key=$(echo $cmd | awk -F '=' '{print $1}') + value=$(echo $cmd | awk -F '=' '{print $2}') + eval $key=$value + fi + done +fi + +x=$(urldecode "$atcmd") +MYATCMD=$(printf '%b\n' "${atcmd//%/\\x}") +if [ -n "${MYATCMD}" ]; then + # Capture the response and remove ANSI color codes using awk + runcmd=$(atcmd '$x' | awk '{ gsub(/\x1B\[[0-9;]*[mG]/, "") }1') +fi + +echo "Content-type: text/plain" +echo $x +echo "" +echo "$runcmd" diff --git a/development/simpleadmin/www/cgi-bin/watchcat_maker b/development/simpleadmin/www/cgi-bin/watchcat_maker new file mode 100644 index 0000000..23ea3aa --- /dev/null +++ b/development/simpleadmin/www/cgi-bin/watchcat_maker @@ -0,0 +1,72 @@ +#!/bin/bash + +# Decode URL-encoded strings +function urldecode() { + local data=${1//+/ } + printf '%b' "${data//%/\\x}" +} + +# Parse QUERY_STRING +QUERY_STRING=$(echo "${QUERY_STRING}" | sed 's/;//g') +if [ "${QUERY_STRING}" ]; then + export IFS="&" + for cmd in ${QUERY_STRING}; do + if [[ "$cmd" == *"="* ]]; then + key=$(echo $cmd | awk -F '=' '{print $1}') + value=$(echo $cmd | awk -F '=' '{print $2}') + eval $key=$(urldecode $value) + fi + done +fi + +# Set default values +WATCHCAT_ENABLED=${WATCHCAT_ENABLED:-"disable"} +TRACK_IP=${TRACK_IP:-"1.1.1.1"} +PING_TIMEOUT=${PING_TIMEOUT:-30} +PING_FAILURE_COUNT=${PING_FAILURE_COUNT:-3} + +# Validate input +if ! [[ "$WATCHCAT_ENABLED" =~ ^(enable|disable)$ ]]; then + echo "Content-type: text/plain" + echo "" + echo "Invalid value for WATCHCAT_ENABLED. Use 'enable' or 'disable'." + exit 1 +fi + +if ! [[ "$TRACK_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + echo "Content-type: text/plain" + echo "" + echo "Invalid IP address format for TRACK_IP." + exit 1 +fi + +if ! [[ "$PING_TIMEOUT" =~ ^[0-9]+$ ]] || [ "$PING_TIMEOUT" -le 0 ]; then + echo "Content-type: text/plain" + echo "" + echo "PING_TIMEOUT must be a positive integer." + exit 1 +fi + +if ! [[ "$PING_FAILURE_COUNT" =~ ^[0-9]+$ ]] || [ "$PING_FAILURE_COUNT" -le 0 ]; then + echo "Content-type: text/plain" + echo "" + echo "PING_FAILURE_COUNT must be a positive integer." + exit 1 +fi + +# Implement the Watchcat logic +if [ "$WATCHCAT_ENABLED" == "enable" ]; then + echo "Content-type: text/plain" + echo "" + echo "Watchcat is enabled. Tracking IP: $TRACK_IP, Ping timeout: $PING_TIMEOUT seconds, Ping failure count: $PING_FAILURE_COUNT" + # Call the create script here and use the needed parameters + sudo /usrdata/simpleadmin/script/create_watchcat.sh "$TRACK_IP" "$PING_TIMEOUT" "$PING_FAILURE_COUNT" +else + echo "Content-type: text/plain" + echo "" + echo "Watchcat is disabled." + # Call the remove script here + sudo /usrdata/simpleadmin/script/remove_watchcat.sh +fi + +exit 0 \ No newline at end of file diff --git a/development/simpleadmin/www/css/bootstrap.min.css b/development/simpleadmin/www/css/bootstrap.min.css new file mode 100644 index 0000000..3993414 --- /dev/null +++ b/development/simpleadmin/www/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--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-black:#000;--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-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,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-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img: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 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.form-floating>.form-control:disabled~label::after,.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .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.73.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(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon: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.73.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");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .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(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon: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");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@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:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-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,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@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-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@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-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.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;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:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.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:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin:calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/development/simpleadmin/www/css/styles.css b/development/simpleadmin/www/css/styles.css new file mode 100644 index 0000000..461294f --- /dev/null +++ b/development/simpleadmin/www/css/styles.css @@ -0,0 +1,95 @@ +/* import poppins */ +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"); + +/* import fontawesome icons */ +@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"); + +* { + font-family: "Poppins", sans-serif; +} + +.custom-checkbox .form-check-input { + margin-right: 1px; +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1000; + display: flex; + justify-content: center; + align-items: center; +} + +.loading-modal { + background-color: #fff; + padding: 3rem; + border-radius: 10px; + text-align: center; +} + +.loading-text { + font-size: 18px; + color: #333; +} + +.loader { + width: 64px; + height: 64px; + border: 3px dotted #000; + border-style: solid solid dotted dotted; + border-radius: 50%; + display: inline-block; + position: relative; + box-sizing: border-box; + animation: rotation 2s linear infinite; + margin-bottom: 2rem; +} + +.loader::after { + content: ""; + box-sizing: border-box; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + margin: auto; + border: 3px dotted #0b5ed7; + border-style: solid solid dotted; + width: 24px; + height: 24px; + border-radius: 50%; + animation: rotationBack 1s linear infinite; + transform-origin: center center; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +@keyframes rotationBack { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(-360deg); + } +} + +.is-warning { + background-color: #ffb70f !important; + color: #000 !important; +} + +.is-medium { + font-weight: 600; +} diff --git a/development/simpleadmin/www/deviceinfo.html b/development/simpleadmin/www/deviceinfo.html new file mode 100644 index 0000000..2d9ffd9 --- /dev/null +++ b/development/simpleadmin/www/deviceinfo.html @@ -0,0 +1,520 @@ + + + + + + 模块管理 + + + + + + + + + + +
+
+ +
+
+
+
设备信息
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
制造商
型号
固件版本
电话号码
IMSI
ICCID
IMEI + + + +
LAN IP
WWAN IPv4
WWAN IPv6
版本SimpleAdminRev-Alpha-0.9
项目贡献者 + + + + + +
+
+
+
+ +
+
+
+ + + + + + +
+
+ + + + diff --git a/development/simpleadmin/www/favicon.ico b/development/simpleadmin/www/favicon.ico new file mode 100644 index 0000000..1d757c4 Binary files /dev/null and b/development/simpleadmin/www/favicon.ico differ diff --git a/development/simpleadmin/www/index.html b/development/simpleadmin/www/index.html new file mode 100644 index 0000000..2b39bee --- /dev/null +++ b/development/simpleadmin/www/index.html @@ -0,0 +1,1865 @@ + + + + + + 模块管理 + + + + + + + + + + + + + +
+
+ + +
+
+
+
+ + + +

+
+ +
+
+ +
+
+
+ + + +

+
+ +
+
+ +
+
+
+ + + +

+
+ +
+
+ +
+
+
+ + + +

+
+ +
+
+
+ +
+
+
+
网络信息
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SIM卡
运营商
运营商代码
接入点APN
签约速率Mbps
网络模式
频段
带宽
频点
PCI
IPv4
IPv6
在线时长
+
+
+ +
+
+ +
+
+
信号
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
评估
流量统计
CSQNone
小区ID
eNB ID
LAC ID
SS_RSRQ4G +
+
+ +
+
+ + + None + +
SS_RSRQ5G +
+
+ +
+
+ + + None + +
RSRP4G +
+
+ +
+
+ + + None + +
SS_RSRP5G +
+
+ +
+
+ + + None + +
SINR4G +
+
+ +
+
+ + + None + +
SINR5G +
+
+ +
+
+ + + None + +
+
+
+ +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/development/simpleadmin/www/js/alpinejs.min.js b/development/simpleadmin/www/js/alpinejs.min.js new file mode 100644 index 0000000..a69fd22 --- /dev/null +++ b/development/simpleadmin/www/js/alpinejs.min.js @@ -0,0 +1,5 @@ +(()=>{var Xe=!1,Ze=!1,V=[],Qe=-1;function Kt(e){En(e)}function En(e){V.includes(e)||V.push(e),Sn()}function ye(e){let t=V.indexOf(e);t!==-1&&t>Qe&&V.splice(t,1)}function Sn(){!Ze&&!Xe&&(Xe=!0,queueMicrotask(An))}function An(){Xe=!1,Ze=!0;for(let e=0;ee.effect(t,{scheduler:r=>{tt?Kt(r):r()}}),et=e.raw}function rt(e){D=e}function Ht(e){let t=()=>{};return[n=>{let i=D(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}var qt=[],Ut=[],Wt=[];function Gt(e){Wt.push(e)}function be(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Ut.push(t))}function Jt(e){qt.push(e)}function Yt(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function nt(e,t){!e._x_attributeCleanups||Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}var ot=new MutationObserver(it),st=!1;function se(){ot.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),st=!0}function at(){On(),ot.disconnect(),st=!1}var ae=[],ct=!1;function On(){ae=ae.concat(ot.takeRecords()),ae.length&&!ct&&(ct=!0,queueMicrotask(()=>{Tn(),ct=!1}))}function Tn(){it(ae),ae.length=0}function h(e){if(!st)return e();at();let t=e();return se(),t}var lt=!1,ve=[];function Xt(){lt=!0}function Zt(){lt=!1,it(ve),ve=[]}function it(e){if(lt){ve=ve.concat(e);return}let t=[],r=[],n=new Map,i=new Map;for(let o=0;os.nodeType===1&&t.push(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.push(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{nt(s,o)}),n.forEach((o,s)=>{qt.forEach(a=>a(s,o))});for(let o of r)if(!t.includes(o)&&(Ut.forEach(s=>s(o)),o._x_cleanups))for(;o._x_cleanups.length;)o._x_cleanups.pop()();t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.includes(o)||!o.isConnected||(delete o._x_ignoreSelf,delete o._x_ignore,Wt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function we(e){return F(L(e))}function N(e,t,r){return e._x_dataStack=[t,...L(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function L(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?L(e.host):e.parentNode?L(e.parentNode):[]}function F(e){let t=new Proxy({},{ownKeys:()=>Array.from(new Set(e.flatMap(r=>Object.keys(r)))),has:(r,n)=>e.some(i=>i.hasOwnProperty(n)),get:(r,n)=>(e.find(i=>{if(i.hasOwnProperty(n)){let o=Object.getOwnPropertyDescriptor(i,n);if(o.get&&o.get._x_alreadyBound||o.set&&o.set._x_alreadyBound)return!0;if((o.get||o.set)&&o.enumerable){let s=o.get,a=o.set,c=o;s=s&&s.bind(t),a=a&&a.bind(t),s&&(s._x_alreadyBound=!0),a&&(a._x_alreadyBound=!0),Object.defineProperty(i,n,{...c,get:s,set:a})}return!0}return!1})||{})[n],set:(r,n,i)=>{let o=e.find(s=>s.hasOwnProperty(n));return o?o[n]=i:e[e.length-1][n]=i,!0}});return t}function Ee(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Se(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>Cn(n,i),s=>ut(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function Cn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function ut(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),ut(e[t[0]],t.slice(1),r)}}var Qt={};function y(e,t){Qt[e]=t}function ce(e,t){return Object.entries(Qt).forEach(([r,n])=>{let i=null;function o(){if(i)return i;{let[s,a]=ft(t);return i={interceptor:Se,...s},be(t,a),i}}Object.defineProperty(e,`$${r}`,{get(){return n(t,o())},enumerable:!1})}),e}function er(e,t,r,...n){try{return r(...n)}catch(i){X(i,e,t)}}function X(e,t,r=void 0){Object.assign(e,{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} + +${r?'Expression: "'+r+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var Ae=!0;function Oe(e){let t=Ae;Ae=!1;let r=e();return Ae=t,r}function R(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return tr(...e)}var tr=dt;function rr(e){tr=e}function dt(e,t){let r={};ce(r,e);let n=[r,...L(e)],i=typeof t=="function"?Rn(n,t):Mn(n,t,e);return er.bind(null,e,t,i)}function Rn(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(F([n,...e]),i);Te(r,o)}}var pt={};function Nn(e,t){if(pt[e])return pt[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(async()=>{ ${e} })()`:e,o=(()=>{try{return new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`)}catch(s){return X(s,t,e),Promise.resolve()}})();return pt[e]=o,o}function Mn(e,t,r){let n=Nn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=F([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>X(l,r,t));n.finished?(Te(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Te(i,l,a,s,r)}).catch(l=>X(l,r,t)).finally(()=>n.result=void 0)}}}function Te(e,t,r,n,i){if(Ae&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Te(e,s,r,n)).catch(s=>X(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var mt="x-";function O(e=""){return mt+e}function nr(e){mt=e}var ht={};function p(e,t){return ht[e]=t,{before(r){if(!ht[r]){console.warn("Cannot find directive `${directive}`. `${name}` will use the default order of execution");return}let n=H.indexOf(r);H.splice(n>=0?n:H.indexOf("DEFAULT"),0,e)}}}function le(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=_t(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(ir((o,s)=>n[o]=s)).filter(or).map(In(n,r)).sort(Dn).map(o=>Pn(e,o))}function _t(e){return Array.from(e).map(ir()).filter(t=>!or(t))}var gt=!1,ue=new Map,sr=Symbol();function ar(e){gt=!0;let t=Symbol();sr=t,ue.set(t,[]);let r=()=>{for(;ue.get(t).length;)ue.get(t).shift()();ue.delete(t)},n=()=>{gt=!1,r()};e(r),n()}function ft(e){let t=[],r=a=>t.push(a),[n,i]=Ht(e);return t.push(i),[{Alpine:j,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:R.bind(R,e)},()=>t.forEach(a=>a())]}function Pn(e,t){let r=()=>{},n=ht[t.type]||r,[i,o]=ft(e);Yt(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),gt?ue.get(sr).push(n):n())};return s.runCleanups=o,s}var Ce=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),Re=e=>e;function ir(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=cr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var cr=[];function Z(e){cr.push(e)}function or({name:e}){return lr().test(e)}var lr=()=>new RegExp(`^${mt}([^:^.]+)\\b`);function In(e,t){return({name:r,value:n})=>{let i=r.match(lr()),o=r.match(/:([a-zA-Z0-9\-:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var xt="DEFAULT",H=["ignore","ref","data","id","bind","init","for","model","modelable","transition","show","if",xt,"teleport"];function Dn(e,t){let r=H.indexOf(e.type)===-1?xt:e.type,n=H.indexOf(t.type)===-1?xt:t.type;return H.indexOf(r)-H.indexOf(n)}function q(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function T(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>T(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)T(n,t,!1),n=n.nextElementSibling}function S(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var ur=!1;function dr(){ur&&S("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),ur=!0,document.body||S("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + + + +
+
+ + +
+
+
+
+
+
频段锁定
+
+ +
+
+
+
+
+ 获取支持的频段... +
+
+ +
+
+ + +
+
+
+ +
+
+
+
+
网络工具
+
+
+
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ +
+
+
频段锁定
+
+ + +
+ +
+
+ Num Cells + +
+
+ +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + + + + + + + +
+
+ +
+
+
+ + +
+
+ + + + + + + + + + diff --git a/development/simpleadmin/www/scanner.html b/development/simpleadmin/www/scanner.html new file mode 100644 index 0000000..8332d61 --- /dev/null +++ b/development/simpleadmin/www/scanner.html @@ -0,0 +1,2389 @@ + + + + + + 模块管理 + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
运营商扫描
+
+
+ + + + + + + + + + + + + + + +
网络运营商频段频率PCIRSRP信号
+
+
+
+ + +
+ 运营商扫描将扫描您所在区域的所有4G和5G。扫描可能会断开您的网络,并需要几分钟才能完成. +
+
+
+
+ + +
+
+
+
+
+
+
+ +
+
+
+
小区扫描
+
+ + + + + + + + + + + + + +
网络频率PCIRSRP信号
+
+
+
+ + +
+ 小区扫描将仅扫描您网络供应商的4G或5G +
+
+
+
+ + +
+
+
+
+
+
+
+
+ + + + diff --git a/development/simpleadmin/www/settings.html b/development/simpleadmin/www/settings.html new file mode 100644 index 0000000..f41866e --- /dev/null +++ b/development/simpleadmin/www/settings.html @@ -0,0 +1,669 @@ + + + + + + 模块管理 + + + + + + + + + + + +
+
+ + +
+
+
+
AT 终端
+
+
+
+ + +
+
+
+ + +
+ 用逗号分隔多个命令. +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
一键工具
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
重启 + +
重置AT命令设置 + +
IP 直通 + + + + +
USB调制解调器协议 + + + +
DNS 代理 + + +
+
+
+
+
+
+
+
+
TTL和网络扫描设置
+
+ + +
+
+
+ TTL激活 +
+ +
+ TTL不活动 +
+
+
+
+
+
+
+
+ +
+ 将TTL值设置为0以禁用。 +
+
+
+ +
+
+ +
+
+
+
+ + + + + + +
+
+ + + + diff --git a/development/simpleadmin/www/sms.html b/development/simpleadmin/www/sms.html new file mode 100644 index 0000000..f1c7dc4 --- /dev/null +++ b/development/simpleadmin/www/sms.html @@ -0,0 +1,419 @@ + + + + + + + 模块管理 + + + + + + + + + + + + + +
+
+ +
+
+
+
短信收件箱
+
+
+
+
+
+

正在获取短信...

+
+ + + + + + + + + +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
发送短信
+
+
+ + +
+
+ + +
+ + +
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/development/simpleadmin/www/watchcat.html b/development/simpleadmin/www/watchcat.html new file mode 100644 index 0000000..4b3bb72 --- /dev/null +++ b/development/simpleadmin/www/watchcat.html @@ -0,0 +1,276 @@ + + + + + + Simple Admin + + + + + + + + + + + + + + +
+
+ +
+
+
+
Simple Watchcat
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
Simple Watchcat Logs
+
+
+
+ + +
+
+
+ +
+
+
+
+
+ + + + diff --git a/development/simpleadmin/www/watchcat_backup.html b/development/simpleadmin/www/watchcat_backup.html new file mode 100644 index 0000000..e73e921 --- /dev/null +++ b/development/simpleadmin/www/watchcat_backup.html @@ -0,0 +1,351 @@ + + + + + + Simple Admin + + + + + + + + + + + + + + + +
+
+ +
+
+
+
Simple Watchcat
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+ + + + +
+
+ +
+
+
+
+
+
+
Simple Watchcat Logs
+
+
+
+ + +
+
+
+
+
+
+
+
+ + + + diff --git a/development/simplefirewall/.rev b/development/simplefirewall/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/simplefirewall/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/simplefirewall/simplefirewall.sh b/development/simplefirewall/simplefirewall.sh new file mode 100644 index 0000000..f208465 --- /dev/null +++ b/development/simplefirewall/simplefirewall.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Define the ports you want to block +PORTS=("80" "443") # Default ports, will be modified by the install script + +# First, allow specified ports on bridge0, eth0, and tailscale0 +for port in "${PORTS[@]}"; do + iptables -A INPUT -i bridge0 -p tcp --dport $port -j ACCEPT + iptables -A INPUT -i eth0 -p tcp --dport $port -j ACCEPT + iptables -A INPUT -i tailscale0 -p tcp --dport $port -j ACCEPT +done + +# Then, block specified ports on all other interfaces +for port in "${PORTS[@]}"; do + iptables -A INPUT -p tcp --dport $port -j DROP +done diff --git a/development/simplefirewall/systemd/simplefirewall.service b/development/simplefirewall/systemd/simplefirewall.service new file mode 100644 index 0000000..9a46330 --- /dev/null +++ b/development/simplefirewall/systemd/simplefirewall.service @@ -0,0 +1,11 @@ +[Unit] +Description=Simple Firewall Setup +After=network.target + +[Service] +Type=oneshot +ExecStart=/bin/bash /usrdata/simplefirewall/simplefirewall.sh +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/development/simplefirewall/systemd/ttl-override.service b/development/simplefirewall/systemd/ttl-override.service new file mode 100644 index 0000000..9f00cca --- /dev/null +++ b/development/simplefirewall/systemd/ttl-override.service @@ -0,0 +1,12 @@ +[Unit] +Description=TTL Override +After=ql-netd.service +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usrdata/simplefirewall/ttl-override start +User=root + +[Install] +WantedBy=multi-user.target diff --git a/development/simplefirewall/ttl-override b/development/simplefirewall/ttl-override new file mode 100644 index 0000000..1fe9e25 --- /dev/null +++ b/development/simplefirewall/ttl-override @@ -0,0 +1,53 @@ +#! /bin/bash + +# Adapted from https://github.com/natecarlson/quectel-rgmii-configuration-notes/blob/main/files/ttl-override +# Uses ttlvalue file to read what ttl should be set to + + +if [ -f /usrdata/simplefirewall/ttlvalue ]; +then + ttlfile=$(&2 + exit 1 + fi +else + # Couldnt find ttlvalue file, lets generate one with 0 ttlvalue (0 = disabled) + touch /usrdata/simplefirewall/ttlvalue && echo '0' > /usrdata/simplefirewall/ttlvalue + exit 1 +fi + +case "$1" in +start) + if (( $TTLVALUE > 0 )); then + echo "Adding TTL override rules: " + iptables -t mangle -I POSTROUTING -o rmnet+ -j TTL --ttl-set ${TTLVALUE} + ip6tables -t mangle -I POSTROUTING -o rmnet+ -j HL --hl-set ${TTLVALUE} + else + echo "TTLVALUE set to 0, nothing to do..." + fi + echo "done" + ;; + stop) + if (( $TTLVALUE > 0 )); then + echo "Removing TTL override rules: " + iptables -t mangle -D POSTROUTING -o rmnet+ -j TTL --ttl-set ${TTLVALUE} &>/dev/null || true + ip6tables -t mangle -D POSTROUTING -o rmnet+ -j HL --hl-set ${TTLVALUE} &>/dev/null || true + else + echo "TTLVALUE set to 0, nothing to do..." + fi + echo "done" + ;; + restart) + $0 stop + $0 start + ;; + *) + echo "Usage ttl-override { start | stop | restart }" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/development/simplefirewall/ttlvalue b/development/simplefirewall/ttlvalue new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/development/simplefirewall/ttlvalue @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/development/simpleupdates/.rev b/development/simpleupdates/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/simpleupdates/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/simpleupdates/scripts/update_simpleadmin.sh b/development/simpleupdates/scripts/update_simpleadmin.sh new file mode 100644 index 0000000..6a4749b --- /dev/null +++ b/development/simpleupdates/scripts/update_simpleadmin.sh @@ -0,0 +1,240 @@ +#!/bin/bash + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="simpleadmin" +SERVICE_FILE="/lib/systemd/system/install_simpleadmin.service" +SERVICE_NAME="install_simpleadmin" +TMP_SCRIPT="/tmp/install_simpleadmin.sh" +LOG_FILE="/tmp/install_simpleadmin.log" +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin + +# Tmp Script dependent constants +SIMPLE_ADMIN_DIR="/usrdata/simpleadmin" +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} + +# Installation Prep +remount_rw +systemctl daemon-reload +rm $SERVICE_FILE > /dev/null 2>&1 +rm $SERVICE_NAME > /dev/null 2>&1 + +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +GITUSER="iamromulan" +GITTREE="development" +SIMPLE_ADMIN_DIR="/usrdata/simpleadmin" +export HOME=/usrdata/root +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin + +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} +remount_rw +uninstall_simpleadmin() { + echo "Uninstalling Simpleadmin..." + + # Check if Lighttpd service is installed and remove it if present + if [ -f "/lib/systemd/system/lighttpd.service" ]; then + echo "Lighttpd detected, uninstalling Lighttpd webserver and its modules..." + systemctl stop lighttpd + rm -f /lib/systemd/system/lighttpd.service + opkg --force-remove --force-removal-of-dependent-packages remove lighttpd-mod-authn_file lighttpd-mod-auth lighttpd-mod-cgi lighttpd-mod-openssl lighttpd-mod-proxy lighttpd + fi + echo -e "\e[1;34mUninstalling simpleadmin content...\e[0m" + systemctl stop simpleadmin_generate_status + systemctl stop simpleadmin_httpd + rm -f /lib/systemd/system/simpleadmin_httpd.service + rm -f /lib/systemd/system/simpleadmin_generate_status.service + systemctl daemon-reload + + echo -e "\e[1;34mUninstalling ttyd...\e[0m" + systemctl stop ttyd + rm -rf /usrdata/ttyd + rm -rf "$SIMPLE_ADMIN_DIR" + rm -f /lib/systemd/system/ttyd.service + rm -f /lib/systemd/system/multi-user.target.wants/ttyd.service + rm -f /bin/ttyd + echo -e "\e[1;32mttyd has been uninstalled.\e[0m" + + echo "Uninstallation process completed." +} + +install_lighttpd() { + # Check for simpleadmin_httpd service and remove if exists + if [ -f "/lib/systemd/system/simpleadmin_httpd.service" ]; then + systemctl stop simpleadmin_httpd + rm /lib/systemd/system/simpleadmin_httpd.service + rm /lib/systemd/system/multi-user.target.wants/simpleadmin_httpd.service + fi + + /opt/bin/opkg install sudo lighttpd lighttpd-mod-auth lighttpd-mod-authn_file lighttpd-mod-cgi lighttpd-mod-openssl lighttpd-mod-proxy + # Ensure rc.unslung doesn't try to start it + # Dynamically find and remove any Lighttpd-related init script + for script in /opt/etc/init.d/*lighttpd*; do + if [ -f "$script" ]; then + echo "Removing existing Lighttpd init script: $script" + rm "$script" # Remove the script if it contains 'lighttpd' in its name + fi + done + systemctl stop lighttpd + echo -e "\033[0;32mInstalling/Updating Lighttpd...\033[0m" + mkdir -p "$SIMPLE_ADMIN_DIR" + wget --no-check-certificate -O "$SIMPLE_ADMIN_DIR/lighttpd.conf" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/lighttpd.conf + wget --no-check-certificate -O "/lib/systemd/system/lighttpd.service" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/systemd/lighttpd.service + ln -sf "/lib/systemd/system/lighttpd.service" "/lib/systemd/system/multi-user.target.wants/" + echo "www-data ALL = (root) NOPASSWD: /usr/sbin/iptables, /usr/sbin/ip6tables, /usrdata/simplefirewall/ttl-override, /bin/echo, /bin/cat" > /opt/etc/sudoers.d/www-data + + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \ + -subj "/C=US/ST=MI/L=Romulus/O=RMIITools/CN=localhost" \ + -keyout $SIMPLE_ADMIN_DIR/server.key -out $SIMPLE_ADMIN_DIR/server.crt + systemctl daemon-reload + systemctl start lighttpd + + echo -e "\033[0;32mLighttpd installation/update complete.\033[0m" +} + +install_simpleadmin() { +remount_rw +echo -e "\e[1;31m2) Installing simpleadmin from the $GITTREE branch\e[0m" + mkdir $SIMPLE_ADMIN_DIR + mkdir $SIMPLE_ADMIN_DIR/systemd + mkdir $SIMPLE_ADMIN_DIR/script + mkdir $SIMPLE_ADMIN_DIR/console + mkdir $SIMPLE_ADMIN_DIR/console/menu + mkdir $SIMPLE_ADMIN_DIR/console/services + mkdir $SIMPLE_ADMIN_DIR/console/services/systemd + mkdir $SIMPLE_ADMIN_DIR/www + mkdir $SIMPLE_ADMIN_DIR/www/cgi-bin + mkdir $SIMPLE_ADMIN_DIR/www/css + mkdir $SIMPLE_ADMIN_DIR/www/js + cd $SIMPLE_ADMIN_DIR/systemd + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/systemd/lighttpd.service + sleep 1 + cd $SIMPLE_ADMIN_DIR/script + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/script/ttl_script.sh + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/script/remove_watchcat.sh + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/script/create_watchcat.sh + sleep 1 + cd $SIMPLE_ADMIN_DIR/console + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/.profile + sleep 1 + cd $SIMPLE_ADMIN_DIR/console/menu + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/menu/start_menu.sh + ln -f $SIMPLE_ADMIN_DIR/console/menu/start_menu.sh /usrdata/root/bin/menu + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/menu/sfirewall_settings.sh + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/menu/start_menu.sh + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/menu/LAN_settings.sh + sleep 1 + cd $SIMPLE_ADMIN_DIR/www + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/deviceinfo.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/favicon.ico + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/index.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/network.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/settings.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/sms.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/scanner.html + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/watchcat.html + sleep 1 + cd $SIMPLE_ADMIN_DIR/www/js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/alpinejs.min.js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/bootstrap.bundle.min.js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/dark-mode.js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/generate-freq-box.js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/parse-settings.js + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/js/populate-checkbox.js + sleep 1 + cd $SIMPLE_ADMIN_DIR/www/css + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/css/bootstrap.min.css + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/css/styles.css + sleep 1 + cd $SIMPLE_ADMIN_DIR/www/cgi-bin + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_atcommand + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/user_atcommand + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_ping + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_sms + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_ttl_status + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/set_ttl + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/send_sms + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_uptime + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/get_watchcat_status + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/set_watchcat + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/www/cgi-bin/watchcat_maker + sleep 1 + cd / + chmod +x $SIMPLE_ADMIN_DIR/www/cgi-bin/* + chmod +x $SIMPLE_ADMIN_DIR/script/* + chmod +x $SIMPLE_ADMIN_DIR/console/menu/* + chmod +x $SIMPLE_ADMIN_DIR/console/.profile + cp -f $SIMPLE_ADMIN_DIR/console/.profile /usrdata/root/.profile + chmod +x /usrdata/root/.profile + cp -rf $SIMPLE_ADMIN_DIR/systemd/* /lib/systemd/system + sleep 1 + systemctl daemon-reload + sleep 1 +} +install_ttyd() { + echo -e "\e[1;34mStarting ttyd installation process...\e[0m" + cd $SIMPLE_ADMIN_DIR/console + curl -L -o ttyd http://gitea.hapyle.work:33000/taotao/webui/raw/development/ttyd.armhf && chmod +x ttyd + wget --no-check-certificate "http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/console/ttyd.bash" && chmod +x ttyd.bash + cd $SIMPLE_ADMIN_DIR/systemd/ + wget --no-check-certificate "http://gitea.hapyle.work:33000/taotao/webui/raw/development/simpleadmin/systemd/ttyd.service" + cp -f $SIMPLE_ADMIN_DIR/systemd/ttyd.service /lib/systemd/system/ + ln -sf /usrdata/simpleadmin/ttyd /bin + + # Enabling and starting ttyd service + systemctl daemon-reload + ln -sf /lib/systemd/system/ttyd.service /lib/systemd/system/multi-user.target.wants/ + systemctl start ttyd + if [ "$?" -ne 0 ]; then + echo -e "\e[1;31mFailed to start ttyd service. Please check the systemd service file and ttyd binary.\e[0m" + exit 1 + fi + + echo -e "\e[1;32mInstallation Complete! ttyd server is up.\e[0m" +} +uninstall_simpleadmin +install_lighttpd +install_simpleadmin +install_ttyd +remount_ro +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/scripts/update_simplefirewall.sh b/development/simpleupdates/scripts/update_simplefirewall.sh new file mode 100644 index 0000000..b6a3e4b --- /dev/null +++ b/development/simpleupdates/scripts/update_simplefirewall.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="simplefirewall" +SERVICE_FILE="/lib/systemd/system/install_simplefirewall.service" +SERVICE_NAME="install_simplefirewall" +TMP_SCRIPT="/tmp/install_simple_firewall.sh" +LOG_FILE="/tmp/install_simplefirewall.log" + +# Tmp Script dependent constants +SIMPLE_FIREWALL_DIR="/usrdata/simplefirewall" +SIMPLE_FIREWALL_SCRIPT="$SIMPLE_FIREWALL_DIR/simplefirewall.sh" +SIMPLE_FIREWALL_SYSTEMD_DIR="$SIMPLE_FIREWALL_DIR/systemd" + +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} +remount_rw +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +GITUSER="iamromulan" +GITTREE="development" +SIMPLE_FIREWALL_DIR="/usrdata/simplefirewall" +SIMPLE_FIREWALL_SCRIPT="$SIMPLE_FIREWALL_DIR/simplefirewall.sh" +SIMPLE_FIREWALL_SYSTEMD_DIR="$SIMPLE_FIREWALL_DIR/systemd" + +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} +remount_rw +# Function to remove Simple Firewall +uninstall_simple_firewall() { + echo "Uninstalling Simplefirewall..." + systemctl stop simplefirewall + systemctl stop ttl-override + rm -f /lib/systemd/system/simplefirewall.service + rm -f /lib/systemd/system/ttl-override.service + systemctl daemon-reload + rm -rf "$SIMPLE_FIREWALL_DIR" + echo "Simplefirewall uninstalled." +} +# Function to install Simple Firewall +install_simple_firewall() { + systemctl stop simplefirewall + systemctl stop ttl-override + echo -e "\033[0;32mInstalling/Updating Simple Firewall...\033[0m" + mount -o remount,rw / + mkdir -p "$SIMPLE_FIREWALL_DIR" + mkdir -p "$SIMPLE_FIREWALL_SYSTEMD_DIR" + wget --no-check-certificate -O "$SIMPLE_FIREWALL_DIR/simplefirewall.sh" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simplefirewall/simplefirewall.sh + wget --no-check-certificate -O "$SIMPLE_FIREWALL_DIR/ttl-override" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simplefirewall/ttl-override + wget --no-check-certificate -O "$SIMPLE_FIREWALL_DIR/ttlvalue" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simplefirewall/ttlvalue + chmod 666 $SIMPLE_FIREWALL_DIR/ttlvalue + chmod +x "$SIMPLE_FIREWALL_DIR/simplefirewall.sh" + chmod +x "$SIMPLE_FIREWALL_DIR/ttl-override" + wget --no-check-certificate -O "$SIMPLE_FIREWALL_SYSTEMD_DIR/simplefirewall.service" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simplefirewall/systemd/simplefirewall.service + wget --no-check-certificate -O "$SIMPLE_FIREWALL_SYSTEMD_DIR/ttl-override.service" http://gitea.hapyle.work:33000/taotao/webui/raw/development/simplefirewall/systemd/ttl-override.service + cp -rf $SIMPLE_FIREWALL_SYSTEMD_DIR/* /lib/systemd/system + ln -sf "/lib/systemd/system/simplefirewall.service" "/lib/systemd/system/multi-user.target.wants/" + ln -sf "/lib/systemd/system/ttl-override.service" "/lib/systemd/system/multi-user.target.wants/" + systemctl daemon-reload + systemctl start simplefirewall + systemctl start ttl-override + echo -e "\033[0;32mSimple Firewall installation/update complete.\033[0m" + } +uninstall_simple_firewall +install_simple_firewall +remount_ro +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/scripts/update_simpleupdates.sh b/development/simpleupdates/scripts/update_simpleupdates.sh new file mode 100644 index 0000000..a5bede5 --- /dev/null +++ b/development/simpleupdates/scripts/update_simpleupdates.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# WORK IN PROGRESS + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="simpleupdates" +SERVICE_FILE="/lib/systemd/system/install_simpleupdates.service" +SERVICE_NAME="install_simpleupdates" +TMP_SCRIPT="/tmp/install_simpleupdates.sh" +LOG_FILE="/tmp/install_simpleupdates.log" + +# Tmp Script dependent constants + + +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +install_simpleupdates() { +# CONTENT +} +install_simpleupdates +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/scripts/update_socat-at-bridge.sh b/development/simpleupdates/scripts/update_socat-at-bridge.sh new file mode 100644 index 0000000..ee1eb14 --- /dev/null +++ b/development/simpleupdates/scripts/update_socat-at-bridge.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="socat-at-bridge" +SERVICE_FILE="/lib/systemd/system/install_socat-at-bridge.service" +SERVICE_NAME="install_socat-at-bridge" +TMP_SCRIPT="/tmp/install_socat-at-bridge.sh" +LOG_FILE="/tmp/install_socat-at-bridge.log" + +# Tmp Script dependent constants +SOCAT_AT_DIR="/usrdata/socat-at-bridge" +SOCAT_AT_SYSD_DIR="/usrdata/socat-at-bridge/systemd_units" +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} +remount_rw +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +GITUSER="iamromulan" +GITTREE="development" +SOCAT_AT_DIR="/usrdata/socat-at-bridge" +SOCAT_AT_SYSD_DIR="/usrdata/socat-at-bridge/systemd_units" + +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} +remount_rw +uninstall_at_socat() { + echo -e "\033[0;32mRemoving installed AT Socat Bridge services...\033[0m" + systemctl stop at-telnet-daemon > /dev/null 2>&1 + systemctl disable at-telnet-daemon > /dev/null 2>&1 + systemctl stop socat-smd11 > /dev/null 2>&1 + systemctl stop socat-smd11-to-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd11-from-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd7 > /dev/null 2>&1 + systemctl stop socat-smd7-to-ttyIN2 > /dev/null 2>&1 + systemctl stop socat-smd7-to-ttyIN > /dev/null 2>&1 + systemctl stop socat-smd7-from-ttyIN2 > /dev/null 2>&1 + systemctl stop socat-smd7-from-ttyIN > /dev/null 2>&1 + rm /lib/systemd/system/at-telnet-daemon.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11-to-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd11-from-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-to-ttyIN2.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-to-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-from-ttyIN.service > /dev/null 2>&1 + rm /lib/systemd/system/socat-smd7-from-ttyIN2.service > /dev/null 2>&1 + systemctl daemon-reload > /dev/null 2>&1 + rm -rf "$SOCAT_AT_DIR" > /dev/null 2>&1 +} + +install_at_socat() { + # Install service units + echo -e "\033[0;32mInstalling AT Socat Bridge services...\033[0m" + mkdir $SOCAT_AT_DIR + cd $SOCAT_AT_DIR + mkdir $SOCAT_AT_SYSD_DIR + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/socat-armel-static + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/killsmd7bridge + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/atcmd + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/atcmd11 + cd $SOCAT_AT_SYSD_DIR + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd11.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd11-from-ttyIN.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd11-to-ttyIN.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-killsmd7bridge.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd7-from-ttyIN2.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd7-to-ttyIN2.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/raw/development/socat-at-bridge/systemd_units/socat-smd7.service + + # Set execute permissions + cd $SOCAT_AT_DIR + chmod +x socat-armel-static + chmod +x killsmd7bridge + chmod +x atcmd + chmod +x atcmd11 + + # Link new command for AT Commands from the shell + ln -sf $SOCAT_AT_DIR/atcmd /bin + ln -sf $SOCAT_AT_DIR/atcmd11 /bin + + # Install service units + echo -e "\033[0;32mAdding AT Socat Bridge systemd service units...\033[0m" + cp -rf $SOCAT_AT_SYSD_DIR/*.service /lib/systemd/system + ln -sf /lib/systemd/system/socat-killsmd7bridge.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd11.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd11-to-ttyIN.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd11-from-ttyIN.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd7.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd7-to-ttyIN2.service /lib/systemd/system/multi-user.target.wants/ + ln -sf /lib/systemd/system/socat-smd7-from-ttyIN2.service /lib/systemd/system/multi-user.target.wants/ + systemctl daemon-reload + systemctl start socat-smd11 + sleep 2s + systemctl start socat-smd11-to-ttyIN + systemctl start socat-smd11-from-ttyIN + echo -e "\033[0;32mAT Socat Bridge service online: smd11 to ttyOUT\033[0m" + systemctl start socat-killsmd7bridge + sleep 1s + systemctl start socat-smd7 + sleep 2s + systemctl start socat-smd7-to-ttyIN2 + systemctl start socat-smd7-from-ttyIN2 + echo -e "\033[0;32mAT Socat Bridge service online: smd7 to ttyOUT2\033[0m" + remount_ro + cd / + echo -e "\033[0;32mAT Socat Bridge services Installed!\033[0m" +} +uninstall_at_socat +install_at_socat +remount_ro +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/scripts/update_sshd.sh b/development/simpleupdates/scripts/update_sshd.sh new file mode 100644 index 0000000..41b35ac --- /dev/null +++ b/development/simpleupdates/scripts/update_sshd.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="simpleupdates" +SERVICE_FILE="/lib/systemd/system/install_sshd.service" +SERVICE_NAME="install_sshd" +TMP_SCRIPT="/tmp/install_sshd.sh" +LOG_FILE="/tmp/install_sshd.log" + +# Tmp Script dependent constants + + +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +GITUSER="iamromulan" +GITTREE="development" + +install_sshd() { +echo -e "\e[1;32mOpenSSH Server\e[0m" + remount_rw + + mkdir /usrdata/sshd + wget --no-check-certificate -O /lib/systemd/system/sshd.service "http://gitea.hapyle.work:33000/taotao/webui/blob/development/sshd/sshd.service" + ln -sf "/lib/systemd/system/sshd.service" "/lib/systemd/system/multi-user.target.wants/" + + opkg install openssh-server-pam + for script in /opt/etc/init.d/*sshd*; do + if [ -f "$script" ]; then + echo "Removing existing sshd init script: $script" + rm "$script" # Remove the script if it contains 'sshd' in its name + fi + done + /opt/bin/ssh-keygen -A + systemctl daemon-reload + systemctl enable sshd + + # Enable PAM and PermitRootLogin + sed -i "s/^.*UsePAM .*/UsePAM yes/" "/opt/etc/ssh/sshd_config" + sed -i "s/^.*PermitRootLogin .*/PermitRootLogin yes/" "/opt/etc/ssh/sshd_config" + + # Ensure the sshd user exists in the /opt/etc/passwd file + grep "sshd:x:106" /opt/etc/passwd || echo "sshd:x:106:65534:Linux User,,,:/opt/run/sshd:/bin/nologin" >> /opt/etc/passwd + systemctl start sshd + + echo -e "\e[1;32mOpenSSH installed!!\e[0m" +} +install_sshd +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/scripts/update_tailscale.sh b/development/simpleupdates/scripts/update_tailscale.sh new file mode 100644 index 0000000..5c986e6 --- /dev/null +++ b/development/simpleupdates/scripts/update_tailscale.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# Define constants +GITUSER="iamromulan" +GITTREE="development" +DIR_NAME="tailscale" +SERVICE_FILE="/lib/systemd/system/install_tailscale.service" +SERVICE_NAME="install_tailscale" +TMP_SCRIPT="/tmp/install_tailscale.sh" +LOG_FILE="/tmp/install_sshd.log" +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin + +# Tmp Script dependent constants +TAILSCALE_DIR="/usrdata/tailscale/" +TAILSCALE_SYSD_DIR="/usrdata/tailscale/systemd" +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} + +# Installation Prep +remount_rw +systemctl daemon-reload +rm $SERVICE_FILE > /dev/null 2>&1 +rm $SERVICE_NAME > /dev/null 2>&1 + +# Create the systemd service file +cat < "$SERVICE_FILE" +[Unit] +Description=Update $DIR_NAME temporary service + +[Service] +Type=oneshot +ExecStart=/bin/bash $TMP_SCRIPT > $LOG_FILE 2>&1 + +[Install] +WantedBy=multi-user.target +EOF + +# Create and populate the temporary shell script for installation +cat < "$TMP_SCRIPT" +#!/bin/bash + +export HOME=/usrdata/root +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/usrdata/root/bin +GITUSER="iamromulan" +GITTREE="development" +TAILSCALE_DIR="/usrdata/tailscale/" +TAILSCALE_SYSD_DIR="/usrdata/tailscale/systemd" + +# Function to remount file system as read-write +remount_rw() { + mount -o remount,rw / +} + +# Function to remount file system as read-only +remount_ro() { + mount -o remount,ro / +} + +install_update_tailscale() { + echo "Checking if Tailscale is already installed..." + if [ -f "$TAILSCALE_DIR/tailscale" ]; then + echo "Tailscale binary found. Updating Tailscale..." + ln -sf "$TAILSCALE_DIR/tailscale" "/usrdata/root/bin/tailscale" + echo y | $TAILSCALE_DIR/tailscale update + echo -e "\e[32mTailscale updated!\e[0m" + remount_ro + exit 0 + else + echo "Installing Tailscale..." + mkdir -p "$TAILSCALE_DIR" "$TAILSCALE_SYSD_DIR" + echo "Downloading binary files..." + cd /usrdata + curl -O https://pkgs.tailscale.com/stable/tailscale_1.70.0_arm.tgz + tar -xzf tailscale_1.70.0_arm.tgz + rm tailscale_1.70.0_arm.tgz + cd /usrdata/tailscale_1.70.0_arm + mv tailscale tailscaled "$TAILSCALE_DIR/" + rm -rf /usrdata/tailscale_1.70.0_arm + echo "Downloading systemd files..." + cd "$TAILSCALE_SYSD_DIR" + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/blob/development/tailscale/systemd/tailscaled.service + wget --no-check-certificate http://gitea.hapyle.work:33000/taotao/webui/blob/development/tailscale/systemd/tailscaled.defaults + sleep 2s + echo "Setting Permissions..." + chmod +x "$TAILSCALE_DIR/tailscaled" "$TAILSCALE_DIR/tailscale" + echo "Copying systemd units..." + cp -rf "$TAILSCALE_SYSD_DIR"/* /lib/systemd/system/ + ln -sf /lib/systemd/system/tailscaled.service /lib/systemd/system/multi-user.target.wants/ + systemctl daemon-reload + echo "Starting Tailscaled..." + systemctl start tailscaled + cd / + remount_ro + echo -e "\e[32mTailscale installed successfully.\e[0m" + exit 0 + fi +} + +# Execute the function +install_update_tailscale +exit 0 +EOF + +# Make the temporary script executable +chmod +x "$TMP_SCRIPT" + +# Reload systemd to recognize the new service and start the update +systemctl daemon-reload +systemctl start $SERVICE_NAME diff --git a/development/simpleupdates/simpleadmin.conf b/development/simpleupdates/simpleadmin.conf new file mode 100644 index 0000000..2d8e77d --- /dev/null +++ b/development/simpleupdates/simpleadmin.conf @@ -0,0 +1,12 @@ +# Enable or disable update checks +CONF_ENABLED=no +# Check for updates at boot +CHECK_AT_BOOT=no +# Update frequency: daily, weekly, monthly, or none +UPDATE_FREQUENCY=none +# Scheduled time for updates (24-hour UTC format) +SCHEDULED_TIME=00:00 +# Day for weekly updates (e.g., Mon, Tues, Wed, Thurs, Fri) +WEEKLY_DAY= +# Date for monthly updates (e.g., 15 for the 15th of the month) +MONTHLY_DATE= diff --git a/development/simpleupdates/simpleupdate b/development/simpleupdates/simpleupdate new file mode 100644 index 0000000..8235c5b --- /dev/null +++ b/development/simpleupdates/simpleupdate @@ -0,0 +1,255 @@ +#!/bin/bash + +# Configuration and directories +CONFIG_FILE="/usrdata/simpleupdates/simpleupdate.conf" +GITUSER="iamromulan" +GITTREE="main" +DIRECTORIES=("simpleadmin" "socat-at-bridge" "simplefirewall" "tailscale" "ttyd") +BASE_URL="https://raw.githubusercontent.com/$GITUSER/quectel-rgmii-toolkit/$GITTREE" +LOG_FILE="/tmp/simpleupdate.log" + +# Load configuration +load_config() { + if [[ -f "$CONFIG_FILE" ]]; then + source "$CONFIG_FILE" + else + echo "Configuration file ($CONFIG_FILE) not found." + exit 1 + fi +} + +# Function to trim the log file to the last 100 lines +trim_log_file() { + tail -n 100 "$LOG_FILE" > "$LOG_FILE.tmp" && mv "$LOG_FILE.tmp" "$LOG_FILE" +} + +# Function to check for updates +check_for_updates() { + echo "$(date): Checking for updates..." + for dir in "${DIRECTORIES[@]}"; do + local remote_rev=$(wget -qO- "$BASE_URL/$dir/.rev") + local local_rev_file="/usrdata/$dir/.rev" + + if [[ ! -f "$local_rev_file" ]]; then + echo "No local revision file found for $dir, skipping." + continue + fi + + local local_rev=$(cat "$local_rev_file") + + if [[ "$remote_rev" -gt "$local_rev" ]]; then + echo "Update available for $dir, updating..." + wget -qO "/tmp/update_${dir}.sh" "$BASE_URL/simpleupdates/scripts/update_${dir}.sh" + chmod +x "/tmp/update_${dir}.sh" + "/tmp/update_${dir}.sh" + else + echo "$dir is up to date." + fi + done + trim_log_file + wait_to_update +} + +# Function to wait and trigger updates based on scheduling +wait_to_update() { + echo "Waiting for the next update check according to schedule..." + while true; do + local current_time=$(date "+%H:%M") + local current_day=$(date "+%a") + local current_date=$(date "+%d") + + case $UPDATE_FREQUENCY in + daily) + if [[ "$current_time" == "$SCHEDULED_TIME" ]]; then + check_for_updates + fi + ;; + weekly) + if [[ "$current_day" == "$WEEKLY_DAY" && "$current_time" == "$SCHEDULED_TIME" ]]; then + check_for_updates + fi + ;; + monthly) + if [[ "$current_date" == "$MONTHLY_DATE" && "$current_time" == "$SCHEDULED_TIME" ]]; then + check_for_updates + fi + ;; + none) + echo "Update checking is disabled by frequency setting." + exit 0 + ;; + esac + sleep 30 # Sleep for 30 seconds for more granular checks + trim_log_file + done +} + +# Daemon mode to wait and trigger updates based on scheduling +daemon_mode() { + load_config + exec > >(tee -a "$LOG_FILE") 2>&1 + echo "Daemon mode started." + # Validate only one update frequency is defined + frequency_count=0 + [[ "$UPDATE_FREQUENCY" == "daily" ]] && ((frequency_count++)) + [[ "$UPDATE_FREQUENCY" == "weekly" ]] && ((frequency_count++)) + [[ "$UPDATE_FREQUENCY" == "monthly" ]] && ((frequency_count++)) + + if [[ $frequency_count -gt 1 ]]; then + echo "Error: More than one update frequency is defined. Exiting." + exit 1 + elif [[ $frequency_count -eq 0 && "$UPDATE_FREQUENCY" != "none" ]]; then + echo "Error: No valid update frequency defined. Exiting." + exit 1 + fi + if [[ "$CONF_ENABLED" == "no" ]]; then + echo "Updates are disabled in the configuration." + exit 0 +fi + +if [[ "$CHECK_AT_BOOT" == "yes" ]]; then + check_for_updates +else + wait_to_update +fi +} + +# Function to check for updates +force_check_for_updates() { + echo "$(date): Checking for updates..." + for dir in "${DIRECTORIES[@]}"; do + local remote_rev=$(wget -qO- "$BASE_URL/$dir/.rev") + local local_rev_file="/usrdata/$dir/.rev" + + if [[ ! -f "$local_rev_file" ]]; then + echo "No local revision file found for $dir, skipping." + continue + fi + + local local_rev=$(cat "$local_rev_file") + + if [[ "$remote_rev" -gt "$local_rev" ]]; then + echo "Update available for $dir, updating..." + wget -qO "/tmp/update_${dir}.sh" "$BASE_URL/simpleupdates/scripts/update_${dir}.sh" + chmod +x "/tmp/update_${dir}.sh" + "/tmp/update_${dir}.sh" + else + echo "$dir is up to date." + fi + done + exit 0 +} + +# Helper function to load and update the configuration +update_config() { + local key="$1" + local value="$2" + if grep -q "^$key=" "$CONFIG_FILE"; then + sed -i "s|^$key=.*|$key=$value|" "$CONFIG_FILE" + else + echo "$key=$value" >> "$CONFIG_FILE" + fi +} + +# Display the current configuration status +status() { + echo "Current Configuration Status:" + if [[ -f "$CONFIG_FILE" ]]; then + while IFS= read -r line; do + echo "$line" + done < "$CONFIG_FILE" + else + echo "Configuration file not found." + fi +} + +# Enable automatic updates +enable_updates() { + update_config "CONF_ENABLED" "yes" + echo "Automatic updates have been enabled." +} + +# Disable automatic updates +disable_updates() { + update_config "CONF_ENABLED" "no" + echo "Automatic updates have been disabled." +} + +# Interactive setup for the update configuration +setup() { + read -p "Enable automatic updates? [yes/no]: " enable_updates + if [[ "$enable_updates" == "yes" ]]; then + enable_updates + else + disable_updates + fi + + read -p "Check for updates at boot? [yes/no]: " check_boot + update_config "CHECK_AT_BOOT" "$check_boot" + + read -p "Update frequency (none, daily, weekly, monthly): " frequency + update_config "UPDATE_FREQUENCY" "$frequency" + + case $frequency in + daily) + read -p "Scheduled time (HH:MM in 24-hour format): " time + update_config "SCHEDULED_TIME" "$time" + ;; + weekly) + echo "Please enter the day of the week." + read -p "Day (full name or abbreviation, e.g., Monday or Mon): " day_input + # Normalize input to abbreviated form + day_abbr=$(date -d "$day_input" +%a 2>/dev/null) + if [[ $? -ne 0 ]]; then + echo "Invalid day of the week. Please try again." + return 1 + fi + update_config "WEEKLY_DAY" "$day_abbr" + read -p "Scheduled time (HH:MM in 24-hour format): " time + update_config "SCHEDULED_TIME" "$time" + ;; + monthly) + read -p "Date of the month (1-31): " date + update_config "MONTHLY_DATE" "$date" + read -p "Scheduled time (HH:MM in 24-hour format): " time + update_config "SCHEDULED_TIME" "$time" + ;; + *) + echo "No scheduling will be set." + ;; + esac + + echo "Update configuration has been set." +} + +# Command operations: status, enable, disable, update, setup +case "$1" in + d) + daemon_mode + ;; + update) + load_config + force_check_for_updates + ;; + status) + status + ;; + enable) + enable_updates + ;; + disable) + disable_updates + ;; + setup) + setup + ;; + *) + echo "Usage:" + echo "d: Run as a background check daemon" + echo "update: Force check for and install updates" + echo "status: Display current set update schedule" + echo "enable: Enable automatic updates" + echo "disable: Disable automatic updates" + echo "setup: Set up an automatic update schedule" + ;; +esac diff --git a/development/simpleupdates/systemd/simpleupdated.service b/development/simpleupdates/systemd/simpleupdated.service new file mode 100644 index 0000000..c6926be --- /dev/null +++ b/development/simpleupdates/systemd/simpleupdated.service @@ -0,0 +1,10 @@ +[Unit] +Description=Simple Update Daemon +After=network.target + +[Service] +Type=simple +ExecStart=/usrdata/simpleupdates/simpleupdate d + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/.rev b/development/socat-at-bridge/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/socat-at-bridge/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/socat-at-bridge/atcmd b/development/socat-at-bridge/atcmd new file mode 100644 index 0000000..df5006c --- /dev/null +++ b/development/socat-at-bridge/atcmd @@ -0,0 +1,69 @@ +#!/bin/bash + +DEVICE=/dev/ttyOUT2 +BAUD=115200 + +# Function to setup device communication parameters +setup_device() { + stty -F $DEVICE cs8 $BAUD ignbrk -brkint -icrnl -imaxbel \ + -opost -onlcr -isig -icanon -iexten -echo -echoe -echok \ + -echoctl -echoke noflsh -ixon -crtscts +} + +# Function to send AT command and capture the output +send_at_command() { + local command="$1" + + # Clear the device buffer before sending a new command + echo -n > $DEVICE + + # Send the AT command, preserving the integrity of the input + echo -e "$command\r" > $DEVICE + + # Use a temporary file to capture the command output + tmpfile=$(mktemp) + + # Start reading the device output to the temporary file + cat $DEVICE > "$tmpfile" & + CAT_PID=$! + + # Monitor the output file for "OK" or "ERROR" + while ! grep -qe "OK" -e "ERROR" "$tmpfile"; do + sleep 1 + done + + # Kill the `cat` process after capturing the response + kill $CAT_PID + wait $CAT_PID 2>/dev/null + + # Display the response + cat "$tmpfile" | while IFS= read -r line; do + echo -e "\033[0;32m$line\033[0m" + done + + # Clean up + rm "$tmpfile" +} + +# Prepare the device for communication +setup_device + +# Check if an AT command is provided as an argument +if [ $# -gt 0 ]; then + # Concatenate all arguments to handle commands with spaces and/or quotes correctly + FULL_CMD="$*" + send_at_command "$FULL_CMD" +else + echo -e "\033[0;36mType 'exit' to end the session.\033[0m" + while true; do + echo -en "\033[0;36mEnter AT Command: \033[0m" + read user_input + + if [[ "$user_input" == "exit" ]]; then + echo -e "\033[0;32mExiting...\033[0m" + break + fi + + send_at_command "$user_input" + done +fi diff --git a/development/socat-at-bridge/atcmd11 b/development/socat-at-bridge/atcmd11 new file mode 100644 index 0000000..f18f78e --- /dev/null +++ b/development/socat-at-bridge/atcmd11 @@ -0,0 +1,69 @@ +#!/bin/bash + +DEVICE=/dev/ttyOUT +BAUD=115200 + +# Function to setup device communication parameters +setup_device() { + stty -F $DEVICE cs8 $BAUD ignbrk -brkint -icrnl -imaxbel \ + -opost -onlcr -isig -icanon -iexten -echo -echoe -echok \ + -echoctl -echoke noflsh -ixon -crtscts +} + +# Function to send AT command and capture the output +send_at_command() { + local command="$1" + + # Clear the device buffer before sending a new command + echo -n > $DEVICE + + # Send the AT command, preserving the integrity of the input + echo -e "$command\r" > $DEVICE + + # Use a temporary file to capture the command output + tmpfile=$(mktemp) + + # Start reading the device output to the temporary file + cat $DEVICE > "$tmpfile" & + CAT_PID=$! + + # Monitor the output file for "OK" or "ERROR" + while ! grep -qe "OK" -e "ERROR" "$tmpfile"; do + sleep 1 + done + + # Kill the `cat` process after capturing the response + kill $CAT_PID + wait $CAT_PID 2>/dev/null + + # Display the response + cat "$tmpfile" | while IFS= read -r line; do + echo -e "\033[0;32m$line\033[0m" + done + + # Clean up + rm "$tmpfile" +} + +# Prepare the device for communication +setup_device + +# Check if an AT command is provided as an argument +if [ $# -gt 0 ]; then + # Concatenate all arguments to handle commands with spaces and/or quotes correctly + FULL_CMD="$*" + send_at_command "$FULL_CMD" +else + echo -e "\033[0;36mType 'exit' to end the session.\033[0m" + while true; do + echo -en "\033[0;36mEnter AT Command: \033[0m" + read user_input + + if [[ "$user_input" == "exit" ]]; then + echo -e "\033[0;32mExiting...\033[0m" + break + fi + + send_at_command "$user_input" + done +fi diff --git a/development/socat-at-bridge/killsmd7bridge b/development/socat-at-bridge/killsmd7bridge new file mode 100644 index 0000000..9f63b06 --- /dev/null +++ b/development/socat-at-bridge/killsmd7bridge @@ -0,0 +1,4 @@ +#!/bin/bash + +# Look for the process by its command and kill it so socat can have smd7 instead +pkill -f "/usr/bin/port_bridge smd7 at_usb2 1" diff --git a/development/socat-at-bridge/socat-armel-static b/development/socat-at-bridge/socat-armel-static new file mode 100644 index 0000000..26357d6 Binary files /dev/null and b/development/socat-at-bridge/socat-armel-static differ diff --git a/development/socat-at-bridge/systemd_units/socat-killsmd7bridge.service b/development/socat-at-bridge/systemd_units/socat-killsmd7bridge.service new file mode 100644 index 0000000..23bfd46 --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-killsmd7bridge.service @@ -0,0 +1,9 @@ +[Unit] +Description=Kill port_bridge process on boot + +[Service] +Type=oneshot +ExecStart=/usrdata/socat-at-bridge/killsmd7bridge + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd11-from-ttyIN.service b/development/socat-at-bridge/systemd_units/socat-smd11-from-ttyIN.service new file mode 100644 index 0000000..6ba283e --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd11-from-ttyIN.service @@ -0,0 +1,14 @@ +[Unit] +Description=Read from /dev/ttyIN and write to smd11 +BindsTo=socat-smd11.service +After=socat-smd11.service + +[Service] +ExecStart=/bin/bash -c "/bin/cat /dev/ttyIN > /dev/smd11" +ExecStartPost=/bin/sleep 2s +StandardInput=tty-force +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd11-to-ttyIN.service b/development/socat-at-bridge/systemd_units/socat-smd11-to-ttyIN.service new file mode 100644 index 0000000..f72da6b --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd11-to-ttyIN.service @@ -0,0 +1,14 @@ +[Unit] +Description=Read from /dev/smd11 and write to ttyIN +BindsTo=socat-smd11.service +After=socat-smd11.service + +[Service] +ExecStart=/bin/bash -c "/bin/cat /dev/smd11 > /dev/ttyIN" +ExecStartPost=/bin/sleep 2s +StandardInput=tty-force +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd11.service b/development/socat-at-bridge/systemd_units/socat-smd11.service new file mode 100644 index 0000000..76a6137 --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd11.service @@ -0,0 +1,13 @@ +[Unit] +Description=Socat Serial Emulation for smd11 +After=ql-netd.service + +[Service] +ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0,group=20,perm=660 pty,link=/dev/ttyOUT,raw,echo=1,group=20,perm=660 +# Add a delay to prevent the clients from starting too early +ExecStartPost=/bin/sleep 2s +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd7-from-ttyIN2.service b/development/socat-at-bridge/systemd_units/socat-smd7-from-ttyIN2.service new file mode 100644 index 0000000..4821e57 --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd7-from-ttyIN2.service @@ -0,0 +1,14 @@ +[Unit] +Description=Read from /dev/ttyIN2 and write to smd7 +BindsTo=socat-smd7.service +After=socat-smd7.service + +[Service] +ExecStart=/bin/bash -c "/bin/cat /dev/ttyIN2 > /dev/smd7" +ExecStartPost=/bin/sleep 2s +StandardInput=tty-force +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd7-to-ttyIN2.service b/development/socat-at-bridge/systemd_units/socat-smd7-to-ttyIN2.service new file mode 100644 index 0000000..75755b3 --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd7-to-ttyIN2.service @@ -0,0 +1,14 @@ +[Unit] +Description=Read from /dev/smd7 and write to ttyIN2 +BindsTo=socat-smd7.service +After=socat-smd7.service + +[Service] +ExecStart=/bin/bash -c "/bin/cat /dev/smd7 > /dev/ttyIN2" +ExecStartPost=/bin/sleep 2s +StandardInput=tty-force +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/socat-at-bridge/systemd_units/socat-smd7.service b/development/socat-at-bridge/systemd_units/socat-smd7.service new file mode 100644 index 0000000..e524283 --- /dev/null +++ b/development/socat-at-bridge/systemd_units/socat-smd7.service @@ -0,0 +1,13 @@ +[Unit] +Description=Socat Serial Emulation for smd7 +After=ql-netd.service + +[Service] +ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN2,raw,echo=0,group=20,perm=660 pty,link=/dev/ttyOUT2,raw,echo=1,group=20,perm=660 +# Add a delay to prevent the clients from starting too early +ExecStartPost=/bin/sleep 2s +Restart=always +RestartSec=1s + +[Install] +WantedBy=multi-user.target diff --git a/development/sshd/.rev b/development/sshd/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/sshd/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/sshd/sshd.service b/development/sshd/sshd.service new file mode 100644 index 0000000..f6c3ca8 --- /dev/null +++ b/development/sshd/sshd.service @@ -0,0 +1,17 @@ +[Unit] +Description=OpenBSD Secure Shell server +Documentation=man:sshd(8) man:sshd_config(5) +After=network.target + +[Service] +Type=simple +PIDFile=/opt/var/run/sshd.pid +ExecStartPre=/opt/sbin/sshd -t +ExecStart=/opt/sbin/sshd -D +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure + +[Install] +WantedBy=multi-user.target +Alias=sshd.service diff --git a/development/tailscale/.rev b/development/tailscale/.rev new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/development/tailscale/.rev @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/development/tailscale/systemd/tailscale-webui-trigger.service b/development/tailscale/systemd/tailscale-webui-trigger.service new file mode 100644 index 0000000..b2d5913 --- /dev/null +++ b/development/tailscale/systemd/tailscale-webui-trigger.service @@ -0,0 +1,8 @@ +[Unit] +Description=Trigger the Tailscale Web UI +After=tailscaled.service + +[Service] +Type=oneshot +ExecStart=/bin/systemctl start tailscale-webui +RemainAfterExit=yes diff --git a/development/tailscale/systemd/tailscale-webui.service b/development/tailscale/systemd/tailscale-webui.service new file mode 100644 index 0000000..2e51e26 --- /dev/null +++ b/development/tailscale/systemd/tailscale-webui.service @@ -0,0 +1,12 @@ +Description=Tailscale Web Interface +After=tailscaled.service +Requires=tailscaled.service + +[Service] +Type=simple +ExecStartPre=/bin/sleep 5 +ExecStart=/usrdata/tailscale/tailscale web --listen 0.0.0.0:8088 +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/development/tailscale/systemd/tailscaled.defaults b/development/tailscale/systemd/tailscaled.defaults new file mode 100644 index 0000000..e8384a4 --- /dev/null +++ b/development/tailscale/systemd/tailscaled.defaults @@ -0,0 +1,8 @@ +# Set the port to listen on for incoming VPN packets. +# Remote nodes will automatically be informed about the new port number, +# but you might want to configure this in order to set external firewall +# settings. +PORT="41641" + +# Extra flags you might want to pass to tailscaled. +FLAGS="" diff --git a/development/tailscale/systemd/tailscaled.service b/development/tailscale/systemd/tailscaled.service new file mode 100644 index 0000000..3ae5e8a --- /dev/null +++ b/development/tailscale/systemd/tailscaled.service @@ -0,0 +1,16 @@ +[Unit] +Description=Tailscale node agent +Documentation=https://tailscale.com/kb/ +Wants=network-pre.target +After=network-pre.target NetworkManager.service systemd-resolved.service + +[Service] +EnvironmentFile=/usrdata/tailscale/systemd/tailscaled.defaults +ExecStartPre=/usrdata/tailscale/tailscaled --cleanup +ExecStart=/usrdata/tailscale/tailscaled --statedir=/usrdata/tailscale/ --port=${PORT} $FLAGS +ExecStopPost=/usrdata/tailscale/tailscaled --cleanup +Restart=on-failure +Type=notify + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/development/ttyd.armhf b/development/ttyd.armhf new file mode 100644 index 0000000..52b932d Binary files /dev/null and b/development/ttyd.armhf differ