Full Code of AndrewMohawk/Aurora for AI

master 039dbea649db cached
40 files
578.6 KB
179.9k tokens
179 symbols
1 requests
Download .txt
Showing preview only (601K chars total). Download the full file or copy to clipboard to get everything.
Repository: AndrewMohawk/Aurora
Branch: master
Commit: 039dbea649db
Files: 40
Total size: 578.6 KB

Directory structure:
gitextract_hr3ssai2/

├── .gitignore
├── Aurora.py
├── README.md
├── VERSION
├── __init__.py
├── apt-requirements.txt
├── aurora.service
├── extensions/
│   ├── Aurora_Ambient_16x9.py
│   ├── Aurora_Ambient_AutoCrop.py
│   ├── Aurora_Ambient_NoCrop.py
│   ├── Aurora_AudioSpectogram.py
│   ├── Aurora_Configure.py
│   ├── Aurora_Meteor.py
│   ├── Aurora_Rainbow.py
│   ├── __init__.py
│   └── exampleExtension.py
├── install.sh
├── requirements.txt
├── update.sh
└── webserver/
    ├── static/
    │   ├── css/
    │   │   ├── bootstrap.css
    │   │   └── style.css
    │   ├── images/
    │   │   ├── icons/
    │   │   │   └── license.txt
    │   │   └── undraw/
    │   │       └── _license_and_link.rtf
    │   ├── js/
    │   │   ├── aurora-configure.js
    │   │   ├── aurora-generic.js
    │   │   ├── aurora-index.js
    │   │   ├── aurora-view.js
    │   │   ├── custom.js
    │   │   └── jquery.js
    │   └── menu/
    │       └── menu-main.html
    └── templates/
        ├── about.html
        ├── configure.html
        ├── footer.html
        ├── header.html
        ├── index.html
        ├── menu-colors.html
        ├── menu-footer.html
        ├── menu-share.html
        ├── status.json
        └── view.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
#Aurora test things
colourtest_from_youtube.mp4
*.jpg
*.jpeg
env/*
.vscode/
audio.py
auroraspec.py
spec.py
nohup.out
config.ini 
config.ini.bak 
LEDTests/


# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

================================================
FILE: Aurora.py
================================================
# Main Aurora python file, runs the webserver and the Aurora client, configs are loaded from extensions directory
import cherrypy
import os
import multiprocessing
import time
import json
import cherrypy
import configparser
import threading
import glob
import importlib
import inspect
import base64
import logging
from urllib.request import urlopen
import board
import neopixel
import cv2
import sys
from shutil import copyfile

from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader("webserver/templates"))


class AuroraManager:
    def __init__(self):
        self.config_file = "./config.ini"
        self.config = {}  # config dict
        self.extensions = {}

        self.extensions_dir = False
        self.current_extension = False
        self.current_extension_name = False
        self.current_extension_meta = False
        self.screenshot_path = False
        self.extension_started = False
        self.loopRunning = False
        self.messages = []
        self.enabled = False
        self.screenshot_b64 = ""
        self.pixel_image_b64 = ""
        self.vid = False
        self.neoPixels = False

        # Ironically we need to load the config to figure out the logging level, so if config fails... S.O.L
        # process config file
        self.loadConfig()

        # Set logging
        logging.basicConfig(
            format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p"
        )
        self.debug = bool(os.environ["AURORA_DEBUG"])
        logging.info(
            "DEBUG OS ENV: {} status: {}".format(
                os.environ["AURORA_DEBUG"], bool(os.environ["AURORA_DEBUG"])
            )
        )
        if self.debug == True:
            logging.getLogger().setLevel(logging.DEBUG)
        else:
            logging.info("SET DEBUG OFF")
            logging.getLogger().setLevel(logging.ERROR)

        # Setup NeoPixels
        self.setupNeoPixels()

        # Setup HDMI
        self.setupHDMI()

        # populate extensions
        self.populateExtensions()

        # set/load the extension
        self.setCurrentExtension(self.current_extension_name)

    def setupNeoPixels(self):
        try:
            # this is janky and show() speed is impacted by the number of pixels, but we cant re-init this :(
            self.neoPixels = neopixel.NeoPixel(board.D18, 500, auto_write=False)
            self.neoPixels.fill((0, 0, 0))  # turn them off when we initialise
            self.neoPixels.show()  # ironic.
        except Exception as e:
            # Lets not get here chaps.
            self.log(
                "Error during initialisation of NeoPixel:{}".format(str(e)),
            )
            sys.exit(1)

    def setupHDMI(self):
        self.vid = False
        # Setup HDMI input
        try:
            # Try Setup Video Capture devices
            for i in range(0, 10):
                self.log("Trying video device {}.".format(i))
                testVid = cv2.VideoCapture(i)
                test, frame = testVid.read()
                if test:
                    self.vid = testVid
                    self.log("Using video device {}.".format(i))
                    self.vid.set(cv2.CAP_PROP_SATURATION, 255)
                    break
                else:
                    logging.error("device {} failed".format(i))
            if self.vid == False:
                self.log("Failed to initialise video device")
                sys.exit(1)

            self.vid.set(cv2.CAP_PROP_BUFFERSIZE, 2)
            self.vid_w = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.vid_h = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
            self.log(
                "Initialized Aurora with feed of {} x {}".format(self.vid_w, self.vid_h)
            )

            # Lets save initial values
            self.config["HDMI_INITIAL"]["HDMI_BRIGHTNESS"] = str(
                int(self.vid.get(cv2.CAP_PROP_BRIGHTNESS))
            )
            self.config["HDMI_INITIAL"]["HDMI_SATURATION"] = str(
                int(self.vid.get(cv2.CAP_PROP_SATURATION))
            )
            self.config["HDMI_INITIAL"]["HDMI_CONTRAST"] = str(
                int(self.vid.get(cv2.CAP_PROP_CONTRAST))
            )
            self.config["HDMI_INITIAL"]["HDMI_HUE"] = str(
                int(self.vid.get(cv2.CAP_PROP_HUE))
            )
            self.saveConfig()
            self.log(
                "Default State:\nBrightness:{} Saturation: {} Contrast: {} Hue: {}".format(
                    int(self.vid.get(cv2.CAP_PROP_BRIGHTNESS)),
                    int(self.vid.get(cv2.CAP_PROP_SATURATION)),
                    int(self.vid.get(cv2.CAP_PROP_CONTRAST)),
                    int(self.vid.get(cv2.CAP_PROP_HUE)),
                )
            )
        except Exception as e:
            # Lets not get here either!
            self.log("Error during initialisation of HDMI capture:{}".format(str(e)))
            sys.exit(1)

        # Let set the HDMI input settings
        try:
            self.vid.set(
                cv2.CAP_PROP_SATURATION, int(self.config["HDMI"]["HDMI_SATURATION"])
            )
            self.vid.set(
                cv2.CAP_PROP_BRIGHTNESS, int(self.config["HDMI"]["HDMI_BRIGHTNESS"])
            )
            self.vid.set(
                cv2.CAP_PROP_CONTRAST, int(self.config["HDMI"]["HDMI_CONTRAST"])
            )
            self.vid.set(cv2.CAP_PROP_HUE, int(self.config["HDMI"]["HDMI_HUE"]))

        except Exception as e:
            # Lets not get here chaps.
            self.log("Error during initialisation of HDMI:{}".format(str(e)))
            sys.exit(1)

    def saveConfig(self):
        with open(self.config_file, "w") as configfile:
            self.config.write(configfile)
        self.loadConfig()

    def log(self, message):
        logging.info(message)

    # Load the config file
    def loadConfig(self):
        self.config = configparser.ConfigParser()
        self.config.optionxform = str
        self.config.read(self.config_file)

        # Lets load the enviroment variables
        for key, val in self.config["AURORA"].items():
            os.environ[key] = val
            self.log("setting key {} to {}".format(key, val))

        # Setup extensions dir
        self.extensions_dir = self.config["EXTENSIONS"]["directory"]

        # Set default extension
        self.current_extension_name = self.config["EXTENSIONS"]["current_extension"]

        # set screenshotpath
        self.screenshot_path = self.config["GENERAL"]["screenshot_path"]

        # set pixel image path
        self.pixel_image_path = self.config["GENERAL"]["pixel_image_path"]

        # set enabled flag
        self.enabled = self.config.getboolean("GENERAL", "enabled")

    # Get a particular extension
    def getExtensionClass(self, extension_name, extension_dir):
        module = importlib.import_module(
            extension_dir + "." + extension_name, package=extension_name
        )
        importlib.reload(module)
        x = False
        try:
            extensionClass = getattr(module, extension_name)
            x = extensionClass(self.neoPixels, self.vid)
            logging.info(
                "Loaded: {} from ./{}/{}.py".format(
                    x.Name, extension_dir, extension_name
                )
            )
        except Exception as e:
            self.addMessage(
                "Could not load module from ./{}/{}.py error: {}".format(
                    extension_dir, extension_name, str(e)
                )
            )
            logging.info(
                "Could not load module from ./{}/{}.py error: {}".format(
                    extension_dir, extension_name, str(e)
                )
            )

        return x

    def fetchMeta(self, extension, filename):
        if extension == False:
            return False
        extension_meta = {}
        extension_meta["Author"] = extension.Author
        extension_meta["Description"] = extension.Description
        extension_meta["Name"] = extension.Name
        extension_meta["FileName"] = filename
        return extension_meta

    # Populate all the extensions from the extensions class
    def populateExtensions(self):
        self.extensions = {}
        extension_dir = self.extensions_dir
        for file in glob.glob("./{}/*.py".format(extension_dir)):
            filename = os.path.splitext(os.path.basename(file))[0]

            # Ignore __ files
            if filename.startswith("__"):
                continue

            if filename not in ["exampleExtension2", "Aurora_Configure"]:
                x = self.getExtensionClass(filename, extension_dir)
                if x != False:
                    extension_meta = self.fetchMeta(x, filename)
                    self.extensions[filename] = extension_meta

    def addMessage(self, msg):

        if msg not in self.messages:
            self.messages.append(msg)

    # Get the current extension to be run
    def getCurrentExtension(self):
        os.environ["AURORA_CURRENT_EXTENSION_NAME"] = self.current_extension_name
        current_extension = self.getExtensionClass(
            self.current_extension_name, self.extensions_dir
        )
        self.current_extension = current_extension
        return current_extension

    def setCurrentExtension(self, new_current_extension):
        tempExt = self.getExtensionClass(new_current_extension, self.extensions_dir)
        if tempExt != False:

            while self.loopRunning == True:
                # lets wait this out or things get REEAAAL funky
                time.sleep(0.001)

            if self.extension_started == True:
                self.tearDownExtension()
                self.extension_started = False

            self.current_extension = tempExt
            self.current_extension_name = new_current_extension

            os.environ["AURORA_CURRENT_EXTENSION_NAME"] = new_current_extension
            self.current_extension_meta = self.fetchMeta(
                self.current_extension, new_current_extension
            )
            self.setupExtension()

            if new_current_extension != "Aurora_Configure":
                self.config.set(
                    "EXTENSIONS", "current_extension", self.current_extension_name
                )
                self.saveConfig()
                self.extension_started = True

    def takeScreenshot(self):
        self.current_extension.takeScreenShot(self.screenshot_path)

    def makePixelImage(self):
        self.current_extension.makePixelFrame(self.pixel_image_path)

    def setupExtension(self):
        self.current_extension.setup()
        self.extension_started = True

    def tearDownExtension(self):
        self.extension_started = False
        self.current_extension.teardown()

    def loop(self):
        if self.enabled == True:  # only if the entire thing is enabled
            if self.extension_started != False:  # only loop if the extension is started
                # lets let other processes know we are in the middle of a loop
                self.loopRunning = True
                try:
                    self.current_extension.visualise()
                except Exception as e:
                    self.log("Error in visualise: {}".format(str(e)))
                self.loopRunning = False


class Aurora_Webserver(object):
    def __init__(self, Manager):
        self.manager = Manager

    @cherrypy.tools.json_out()
    @cherrypy.expose
    def status(self):
        self.manager.loadConfig()
        enabled_status = self.manager.enabled
        current_extension = self.manager.current_extension.Name
        current_extension_class = self.manager.current_extension_name
        with open("VERSION", "r") as f:
            current_version = f.read()
        tmpl = env.get_template("status.json")
        template_variables = {}
        template_variables["current_extension"] = current_extension
        template_variables["current_extension_class"] = current_extension_class
        template_variables["enabled"] = enabled_status
        template_variables["current_version"] = current_version
        return tmpl.render(template_variables)

    @cherrypy.expose
    def about(self):
        self.manager.loadConfig()
        current_version = "Cannot read version"
        with open("VERSION", "r") as f:
            current_version = f.read()
        github_version = "Cannot read from github"
        try:
            githubURL = (
                "https://raw.githubusercontent.com/AndrewMohawk/Aurora/master/VERSION"
            )
            github_page = urlopen(githubURL)
            github_version = github_page.read().decode("utf-8").strip()

        except Exception as e:
            self.manager.log("Exception trying to open github page:{}".format(str(e)))
            # we cant connect to github?
            pass

        tmpl = env.get_template("about.html")
        template_variables = {}
        template_variables["current_version"] = current_version
        template_variables["github_version"] = github_version
        template_variables["extensions_meta"] = self.manager.extensions
        template_variables[
            "current_extension_meta"
        ] = self.manager.current_extension_meta
        template_variables["config"] = {
            section: dict(self.manager.config[section])
            for section in self.manager.config.sections()
        }
        template_variables["page"] = "about"
        template_variables["msg"] = self.manager.messages
        self.manager.messages = []
        return tmpl.render(template_variables)

    @cherrypy.expose
    def index(self):
        if self.manager.current_extension_name == "Aurora_Configure":
            # process config file
            self.manager.loadConfig()
            # set/load the extension
            self.manager.setCurrentExtension(self.manager.current_extension_name)
            self.manager.setupExtension()

        self.manager.populateExtensions()
        tmpl = env.get_template("index.html")

        template_variables = {}

        template_variables["extensions_meta"] = self.manager.extensions
        template_variables[
            "current_extension_meta"
        ] = self.manager.current_extension_meta
        if self.manager.current_extension != False:
            template_variables["fps"] = self.manager.current_extension.FPS_avg
        else:
            template_variables["fps"] = 0
        template_variables["configured"] = self.manager.config.getboolean(
            "GENERAL", "configured"
        )
        template_variables["enabled"] = self.manager.config.getboolean(
            "GENERAL", "enabled"
        )
        template_variables["page"] = "home"
        template_variables["msg"] = self.manager.messages
        self.manager.messages = []
        return tmpl.render(template_variables)

    @cherrypy.expose
    def view(self):
        if self.manager.current_extension_name == "Aurora_Configure":
            # process config file
            self.manager.loadConfig()
            # set/load the extension
            self.manager.setCurrentExtension(self.manager.current_extension_name)
            self.manager.setupExtension()

        self.manager.populateExtensions()
        tmpl = env.get_template("view.html")
        self.screenshot()
        template_variables = {}

        template_variables["extensions_meta"] = self.manager.extensions
        template_variables[
            "current_extension_meta"
        ] = self.manager.current_extension_meta
        if self.manager.current_extension != False:
            template_variables["fps"] = self.manager.current_extension.FPS_avg
        else:
            template_variables["fps"] = 0
        template_variables["configured"] = self.manager.config.getboolean(
            "GENERAL", "configured"
        )
        template_variables["enabled"] = self.manager.config.getboolean(
            "GENERAL", "enabled"
        )
        template_variables["page"] = "view"
        template_variables["msg"] = self.manager.messages
        self.manager.messages = []
        return tmpl.render(template_variables)

    @cherrypy.expose
    def configure(self):
        if self.manager.enabled == False:  # Its turned off, we need it on to config
            self.manager.enabled = True
        self.manager.setCurrentExtension("Aurora_Configure")
        # self.manager.setCurrentExtension("Aurora_Ambient_AutoCrop")
        self.manager.extension_started = False  # so it doesnt loop visualise
        self.manager.current_extension.visualise()
        self.screenshot()

        tmpl = env.get_template("configure.html")
        template_variables = {}
        template_variables[
            "pixels_darkthreshold"
        ] = self.manager.current_extension.darkThreshhold
        template_variables["pixels_left"] = self.manager.current_extension.pixelsLeft
        template_variables["pixels_right"] = self.manager.current_extension.pixelsRight
        template_variables["pixels_top"] = self.manager.current_extension.pixelsTop
        template_variables[
            "pixels_bottom"
        ] = self.manager.current_extension.pixelsBottom
        # template_variables["hdmi_saturation"] = self.manager.config.getint(
        #     "HDMI", "HDMI_SATURATION"
        # )
        # template_variables["hdmi_brightness"] = self.manager.config.getint(
        #     "HDMI", "HDMI_BRIGHTNESS"
        # )
        # template_variables["hdmi_hue"] = self.manager.config.getint("HDMI", "HDMI_HUE")
        # template_variables["hdmi_contrast"] = self.manager.config.getint(
        #     "HDMI", "HDMI_CONTRAST"
        # )
        # template_variables["hdmi_brightness_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_BRIGHTNESS"])
        # template_variables["hdmi_saturation_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_SATURATION"])
        # template_variables["hdmi_contrast_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_CONTRAST"])
        # template_variables["hdmi_hue_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_HUE"])
        template_variables["hdmi_gamma"] = self.manager.current_extension.gamma
        template_variables["page"] = "configure"
        template_variables["msg"] = self.manager.messages
        self.manager.messages = []

        return tmpl.render(template_variables)

    @cherrypy.tools.json_out()
    @cherrypy.expose
    def toggleEnable(self):
        if self.manager.enabled:
            self.manager.tearDownExtension()
        else:
            self.manager.setupExtension()

        self.manager.enabled = not self.manager.enabled

        return {"status": self.manager.enabled}

    @cherrypy.tools.json_in()
    @cherrypy.tools.json_out()
    @cherrypy.expose
    def update_config(self):
        input_json = cherrypy.request.json
        if "enabled" in input_json:
            try:
                return_json = {"status": "ok"}
                enabled_status = input_json["enabled"]
                self.manager.enabled = enabled_status
                if enabled_status == False:
                    # we are turning it off, tear down the extension
                    self.manager.tearDownExtension()
                    return_json["message"] = "Aurora successfully turned off"
                elif enabled_status == True:
                    # we are turning it on, lets put everything back
                    self.manager.setupExtension()
                    return_json["message"] = "Aurora successfully turned on"

                self.manager.config.set("GENERAL", "enabled", str(enabled_status))
                self.manager.saveConfig()
                return return_json

            except Exception as e:
                return {"status": "error", "error": str(e)}
                pass
        else:
            return {"status": "error", "error": "No setting found in request"}

    @cherrypy.tools.json_out()
    @cherrypy.tools.json_in()
    @cherrypy.expose
    def update_HDMI_config(self):
        input_json = cherrypy.request.json
        # saturation = int(input_json["hdmi_saturation"])
        # hue = int(input_json["hdmi_hue"])
        # contrast = int(input_json["hdmi_contrast"])
        # brightness = int(input_json["hdmi_brightness"])

        errors = []
        try:
            gamma = float(input_json["hdmi_gamma"])
            self.manager.current_extension.gamma = gamma
            if "save" in input_json:
                self.manager.config.set("AURORA", "AURORA_GAMMA", str(gamma))
                self.manager.saveConfig()
                self.manager.addMessage("Saved config!")

            # self.manager.vid.set(cv2.CAP_PROP_SATURATION, saturation)
            # self.manager.vid.set(cv2.CAP_PROP_HUE, hue)
            # self.manager.vid.set(cv2.CAP_PROP_BRIGHTNESS, brightness)
            # self.manager.vid.set(cv2.CAP_PROP_CONTRAST, contrast)

            # self.manager.log(
            #     "Brightness:{} Saturation: {} Contrast: {} Hue: {}".format(
            #         int(self.manager.vid.get(cv2.CAP_PROP_BRIGHTNESS)),
            #         int(self.manager.vid.get(cv2.CAP_PROP_SATURATION)),
            #         int(self.manager.vid.get(cv2.CAP_PROP_CONTRAST)),
            #         int(self.manager.vid.get(cv2.CAP_PROP_HUE)),
            #     )
            # )

            # for i in range(5):
            #     self.manager.vid.read()

            self.manager.takeScreenshot()
        except Exception as e:
            errors.append(str(e))
            pass

        if len(errors) == 0:
            return {"status": "ok"}
        else:
            error_string = ",".join(errors)
            return {"status": "error", "error": error_string}

    @cherrypy.tools.json_out()
    @cherrypy.tools.json_in()
    @cherrypy.expose
    def update_LED_config(self):
        input_json = cherrypy.request.json
        pixelcount_left = self.manager.current_extension.pixelsLeft
        pixelcount_right = self.manager.current_extension.pixelsRight
        pixelcount_top = self.manager.current_extension.pixelsTop
        pixelcount_bottom = self.manager.current_extension.pixelsBottom
        pixel_darkthreshold = self.manager.current_extension.darkThreshhold

        configChange = False

        errors = []
        if "darkthreshhold" in input_json:
            try:

                dt = int(input_json["darkthreshhold"])
                if dt != pixel_darkthreshold:
                    configChange = True
                    pixel_darkthreshold = dt
            except Exception as e:
                errors.append(str(e))
                pass  # whatever, you are doing bad things with input

        if "pixelcount_left" in input_json:
            try:
                led_input_count = int(input_json["pixelcount_left"])
                if led_input_count != pixelcount_left:
                    configChange = True
                    pixelcount_left = led_input_count
            except Exception as e:
                errors.append(str(e))
                pass  # whatever, you are doing bad things with input

        if "pixelcount_right" in input_json:
            try:
                led_input_count = int(input_json["pixelcount_right"])
                if led_input_count != pixelcount_right:
                    configChange = True
                    pixelcount_right = led_input_count
            except Exception as e:
                errors.append(str(e))
                pass  # whatever, you are doing bad things with input

        if "pixelcount_top" in input_json:
            try:
                led_input_count = int(input_json["pixelcount_top"])
                if led_input_count != pixelcount_top:
                    configChange = True
                    pixelcount_top = led_input_count
            except Exception as e:
                errors.append(str(e))
                pass  # whatever, you are doing bad things with input

        if "pixelcount_bottom" in input_json:
            try:
                led_input_count = int(input_json["pixelcount_bottom"])
                if led_input_count != pixelcount_bottom:
                    configChange = True
                    pixelcount_bottom = led_input_count
            except Exception as e:
                errors.append(str(e))
                pass  # whatever, you are doing bad things with input

        pixelcount_total = (
            pixelcount_left + pixelcount_right + pixelcount_top + pixelcount_bottom
        )

        try:
            self.manager.current_extension.pixelsCount = pixelcount_total
            self.manager.current_extension.pixelsLeft = pixelcount_left
            self.manager.current_extension.pixelsRight = pixelcount_right
            self.manager.current_extension.pixelsTop = pixelcount_top
            self.manager.current_extension.pixelsBottom = pixelcount_bottom
            self.manager.current_extension.setup()
            self.manager.current_extension.visualise()
        except Exception as e:
            errors.append(str(e))

        if "save" in input_json:
            try:
                self.manager.config.set(
                    "AURORA", "AURORA_PIXELCOUNT_LEFT", str(pixelcount_left)
                )
                self.manager.config.set(
                    "AURORA", "AURORA_PIXELCOUNT_RIGHT", str(pixelcount_right)
                )
                self.manager.config.set(
                    "AURORA", "AURORA_PIXELCOUNT_TOP", str(pixelcount_top)
                )
                self.manager.config.set(
                    "AURORA", "AURORA_PIXELCOUNT_BOTTOM", str(pixelcount_bottom)
                )
                self.manager.config.set(
                    "AURORA", "AURORA_PIXELCOUNT_TOTAL", str(pixelcount_total)
                )
                self.manager.config.set(
                    "AURORA", "AURORA_DARKTHRESHOLD", str(pixel_darkthreshold)
                )
                self.manager.config.set("GENERAL", "configured", "True")
                self.manager.saveConfig()
                self.manager.addMessage("Saved config!")
            except Exception as e:
                logging.error(str(e))
                errors.append(str(e))

        if len(errors) == 0:
            return {"status": "ok"}
        else:
            error_string = ",".join(errors)
            return {"status": "error", "error": error_string}

    @cherrypy.tools.json_out()
    @cherrypy.tools.json_in()
    @cherrypy.expose
    def update_extension(self):
        input_json = cherrypy.request.json
        if "extension_name" in input_json:
            extension_name = input_json["extension_name"]
            self.manager.setCurrentExtension(extension_name)

        return {"status": "ok"}

    @cherrypy.tools.json_out()
    @cherrypy.expose
    def screenshot(self):
        self.manager.takeScreenshot()
        self.manager.makePixelImage()

        if self.manager.current_extension != False:
            return {"status": "ok"}
        else:
            return {
                "status": "error",
                "error": "Could not take screenshot and build pixel image",
            }

    @cherrypy.expose
    def load_screenshot(self, **params):
        screenshot_path = self.manager.screenshot_path
        # Its not enabled, it doesnt use HDMI or its got a 1x1 image (ie nothing on)
        if (
            self.manager.enabled == False
            or self.manager.current_extension.noHDMI == True
            or (
                self.manager.current_extension.vid_h == 1
                or self.manager.current_extension.vid_w == 1
            )
        ):
            screenshot_path = os.getcwd() + "/webserver/static/img/emptyimage.jpg"
        try:
            f = open(screenshot_path, "rb")
            contents = f.read()
            f.close()
            return contents
        except Exception as e:
            self.manager.log(
                "Error loading image {}: Err: {}".format(
                    self.manager.screenshot_path, str(e)
                )
            )
            return False

    @cherrypy.expose
    def load_pixel_image(self, **params):
        try:
            f = open(self.manager.pixel_image_path, "rb")
            contents = f.read()
            f.close()
            return contents
        except Exception as e:
            self.manager.log(
                "Error loading image {}: Err: {}".format(
                    self.manager.pixel_image_path, str(e)
                )
            )
            return False


if __name__ == "__main__":

    AuroraManager = AuroraManager()

    if AuroraManager.config.getboolean("WEBSERVER", "enabled") == True:

        conf = {
            "/": {
                "tools.sessions.on": True,
                "tools.staticdir.root": os.path.abspath(os.getcwd()),
            },
            "/assets": {
                "tools.staticdir.on": True,
                "tools.staticdir.dir": "./webserver/static",
            },
            "/favicon.ico": {
                "tools.staticfile.on": True,
                "tools.staticfile.filename": os.path.abspath(os.getcwd())
                + "/webserver/static/favicon/favicon.ico",
            },
        }

        cherrypy.config.update(
            {"log.screen": False, "log.access_file": "", "log.error_file": ""}
        )
        cherrypy.config.update(
            {
                "server.socket_port": AuroraManager.config.getint(
                    "WEBSERVER", "server_port"
                )
            }
        )
        cherrypy.config.update(
            {"server.socket_host": AuroraManager.config.get("WEBSERVER", "listen_host")}
        )
        cherrypy.config.update({"engine.autoreload.on": False})

        cherrypy.tree.mount(Aurora_Webserver(AuroraManager), "/", conf)

        cherrypy.engine.start()

    while True:
        AuroraManager.loop()
        time.sleep(0.001)

    # do other work


================================================
FILE: README.md
================================================
[![Slack](https://img.shields.io/badge/slack-chat-green.svg)](https://join.slack.com/t/auroraambientlighting/shared_invite/zt-sib46ode-0rE3GqXFEcHd_H_y_nG~oA) 

![Aurora Example](https://github.com/AndrewMohawk/Aurora/raw/master/github/Aurora_Ambient_Light_test_video.gif)

# Aurora
Aurora is an ambient light system built with an HDMI switch, and HDMI capture card, a Raspberry Pi and an LED strip. There is a full writeup of how this came to be at https://www.andrewmohawk.com/2021/05/25/aurora-ambient-lighting/ and a build guide at https://www.andrewmohawk.com/2021/05/24/aurora-how-to-build/

# Help
Feel free to submit PRs for the project to improve the code base or add your own visualisations. Please remember to update the VERSION when you are doing a new PR. If you *need* help with any part of the project feel free to email or you can join the gitter at https://gitter.im/AuroraAmbientLighting/community 

# Extending
While the documentation for extending and building your own hasnt been fully written up, the TL;DR is to copy the example extenion in the `extensions` folder, change the metadata and restart Aurora with `sudo service aurora restart`, once it has picked up the new file you can simply make changes and then click load extension in the main interface. 

Please note when changing your `visualise` function you need to make it non-locking or the interface will not be able to communicate with it. The interface will pause for 0.01s before running visualise() again between each run

# Install
The install for this requires a hardware and software setup. You can follow the install guide at https://www.andrewmohawk.com/2021/05/24/aurora-how-to-build/ 

The flow diagram looks as follows:
![Aurora Flow diagram](https://www.andrewmohawk.com/wp-content/uploads/2021/05/Aurora-Flow-Diagram.png)

For just grabbing the software you can use this one liner:
```
wget https://raw.githubusercontent.com/AndrewMohawk/Aurora/master/install.sh -O - | sudo /bin/bash
```




================================================
FILE: VERSION
================================================
0.42-catsdontlikefireworks


================================================
FILE: __init__.py
================================================


================================================
FILE: apt-requirements.txt
================================================


================================================
FILE: aurora.service
================================================
[Unit]
Description=Aurora light system
After=multi-user.target
StartLimitInterval=200
StartLimitBurst=5
[Service]
WorkingDirectory=/opt/Aurora/
User=root
Type=idle
ExecStart=/usr/bin/sudo /usr/bin/python3 Aurora.py 2>&1 > /var/log/Aurora.log 
Restart=always
RestartSec=30s
[Install]
WantedBy=multi-user.target


================================================
FILE: extensions/Aurora_Ambient_16x9.py
================================================
from lib.AuroraExtension import AuroraExtension
import time

import numpy as np
import pandas as pd
import cv2


class Aurora_Ambient_16x9(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension takes the HDMI input and crops the image to 16:9 (hopefully) removing the black borders"
        self.Name = "Aurora Ambient Lighting (16x9)"
        self.count = 0
        self.current_frame = False
        self.aspect_cropped_frame = False
        self.percent = 3
        self.aspectRatio = 16.0 / 9.0

    def aspectCrop(self, image, ratio):
        vid_h, vid_w, channels = image.shape
        # work out the size of the 'gaps'
        aspectRatioGap = vid_h - (vid_w / self.aspectRatio)
        aspectRatioGap = aspectRatioGap / 2  # since we want to put some top and bottom
        aspectRatioGap = int(aspectRatioGap)
        aspect_frame = image[aspectRatioGap : vid_h - aspectRatioGap, 0:vid_w]
        return aspect_frame

    def takeScreenShot(self, filepath):
        ret, self.current_frame = self.getFrame()
        vid_h, vid_w, channels = self.current_frame.shape
        aspectRatioGap = vid_h - (vid_w / self.aspectRatio)
        aspectRatioGap = aspectRatioGap / 2  # since we want to put some top and bottom
        aspectRatioGap = int(aspectRatioGap)
        borderEdges = [0, aspectRatioGap, 0, aspectRatioGap]
        return super().takeScreenShot(filepath, borderEdges=borderEdges)

    def visualise(self):
        # Capture the video frame
        ret, self.current_frame = self.getFrame()
        self.aspect_cropped_frame = self.aspectCrop(
            self.current_frame, self.aspectRatio
        )
        self.visualiseFrame(self.aspect_cropped_frame)


================================================
FILE: extensions/Aurora_Ambient_AutoCrop.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2


class Aurora_Ambient_AutoCrop(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension takes the HDMI input and calculates a border (including cropping for black borders) to work out ambient lighting for behind the display."
        self.Name = "Aurora Ambient Lighting ( AutoCrop )"
        self.count = 0
        self.current_frame = False
        self.cropped_frame = False
        self.percent = 3
        self.edgeDarkness = (
            5  # this defines the darkness that makes the edges for autocrop
        )

    def takeScreenShot(self, filepath, autocrop=False):
        return super().takeScreenShot(filepath, self.edgeDarkness)

    def autocrop(self, image, threshold=0):
        """Crops any edges below or equal to threshold
        Crops blank image to 1x1.
        Returns cropped image.
        """
        if len(image.shape) == 3:
            flatImage = np.max(image, 2)
        else:
            flatImage = image
        assert len(flatImage.shape) == 2

        rows = np.where(np.max(flatImage, 0) > threshold)[0]
        if rows.size:
            cols = np.where(np.max(flatImage, 1) > threshold)[0]
            image = image[cols[0] : cols[-1] + 1, rows[0] : rows[-1] + 1]
        else:
            image = image[:1, :1]

        return image

    def visualise(self):
        # Capture the video frame
        # stopwatchStartTime = datetime.datetime.now()
        # totalStartTime = stopwatchStartTime
        ret, self.current_frame = self.getFrame()
        self.vid_h, self.vid_w, self.channels = self.current_frame.shape
        # self.log(f"GetFrame: {datetime.datetime.now()-stopwatchStartTime}")

        # stopwatchStartTime = datetime.datetime.now()
        self.autocropped_frame = self.autocrop(self.current_frame, self.edgeDarkness)
        # self.log(f"AutoCropTime: {datetime.datetime.now()-stopwatchStartTime}")

        self.visualiseFrame(self.autocropped_frame)


================================================
FILE: extensions/Aurora_Ambient_NoCrop.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2


class Aurora_Ambient_NoCrop(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension takes the HDMI input and calculates a border (WITHOUT cropping) to work out ambient lighting for behind the display. This one is slightly faster as it doesnt need to do any of the maths to work out the cropping."
        self.Name = "Aurora Ambient Lighting ( No crop )"
        self.count = 0
        self.current_frame = False
        self.percent = 3

    def autocrop(self, image, threshold=0):
        """Crops any edges below or equal to threshold
        Crops blank image to 1x1.
        Returns cropped image.
        """
        if len(image.shape) == 3:
            flatImage = np.max(image, 2)
        else:
            flatImage = image
        assert len(flatImage.shape) == 2

        rows = np.where(np.max(flatImage, 0) > threshold)[0]
        if rows.size:
            cols = np.where(np.max(flatImage, 1) > threshold)[0]
            image = image[cols[0] : cols[-1] + 1, rows[0] : rows[-1] + 1]
        else:
            image = image[:1, :1]

        return image

    def takeScreenShot(self, filepath):
        screenshot_frame = self.current_frame
        widthPixels = int(self.vid_w * (self.percent / 100)) + 1
        heightPixels = int(self.vid_h * (self.percent / 100) * 2) + 1

        colour = (0, 0, 255)
        # top
        screenshot_frame = cv2.rectangle(
            screenshot_frame, (0, 0), (self.vid_w, heightPixels), (0, 0, 255), 1
        )
        # bottom
        screenshot_frame = cv2.rectangle(
            screenshot_frame,
            (0, self.vid_h - heightPixels),
            (self.vid_w, self.vid_h),
            (0, 0, 255),
            1,
        )
        # left
        screenshot_frame = cv2.rectangle(
            screenshot_frame, (0, 0), (widthPixels, self.vid_h), (0, 0, 255), 1
        )
        # right
        screenshot_frame = cv2.rectangle(
            screenshot_frame,
            (self.vid_w - widthPixels, 0),
            (self.vid_w, self.vid_h),
            (0, 0, 255),
            1,
        )

        cv2.imwrite(filepath, screenshot_frame)

        return True

    def visualise(self):
        # Capture the video frame
        ret, self.current_frame = self.getFrame()
        self.visualiseFrame(self.current_frame)


================================================
FILE: extensions/Aurora_AudioSpectogram.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import sounddevice as sd
from time import sleep
import sys
from collections import deque
import math


class Aurora_AudioSpectogram(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension creates a rainbow audio spectogram, based somewhat on the code found at https://python-sounddevice.readthedocs.io/en/0.3.3/examples.html"
        self.Name = "Aurora Audio Spectrogram"
        self.count = 0
        self.current_frame = False
        self.columns = 100
        self.gain = 10
        self.low = 10
        self.high = 2500
        # Lets find the right device
        self.deviceID = sd.default.device["input"]

        self.log("Starting Audio spectogram with Device {}".format(self.deviceID))
        self.samplerate = sd.query_devices(self.deviceID, "input")["default_samplerate"]
        self.delta_f = (self.high - self.low) / (self.columns - 1)
        self.fftsize = math.ceil(self.samplerate / self.delta_f)
        self.low_bin = math.floor(self.low / self.delta_f)
        self.threshhold = 40

        self.pixelCount_nobottom = self.pixelsLeft + self.pixelsRight + self.pixelsTop

        self.streamstarted = False
        self.noHDMI = True

    def takeScreenShot(self, filepath):
        # We have no screenshot since... well its just LEDs
        return True

    def startAudioStream(self):
        if self.streamstarted == True:
            while self.streamstarted == True:
                with sd.InputStream(
                    device=self.deviceID,
                    channels=1,
                    callback=self.visualiseAudio,
                    blocksize=int(self.samplerate * 50 / 1000),
                    samplerate=self.samplerate,
                ):
                    # running stream
                    sd.wait()

    def teardown(self):
        # incase things need to be broken down
        self.log("Tearing down {}".format(self.Name))
        self.fade_out_pixels()
        sd.stop()

    def fadeToBlack(self, pixelPos):
        fadeValue = 128
        b, g, r = self.pixels[pixelPos]

        r = 0 if r <= 10 else int(r - (r * fadeValue / 255))
        g = 0 if g <= 10 else int(g - (g * fadeValue / 255))
        b = 0 if b <= 10 else int(b - (b * fadeValue / 255))

        self.pixels[pixelPos] = (b, g, r)

    def wheel(self, pos):
        # Input a value 0 to 255 to get a color value.
        # The colours are a transition r - g - b - back to r.
        if pos < 0 or pos > 255:
            return (0, 0, 0)
        if pos < 85:
            return (255 - pos * 3, pos * 3, 0)
        if pos < 170:
            pos -= 85
            return (0, 255 - pos * 3, pos * 3)
        pos -= 170
        return (pos * 3, 0, 255 - pos * 3)

    def rainbow_cycle(self, j):
        for i in range(self.pixelCount_nobottom):
            rc_index = (i * 256 // self.pixelCount_nobottom) + j
            self.pixels[i] = self.wheel(rc_index & 255)
        self.getFrame(False)  # hack to have 'FPS'
        self.pixels.show()

    def visualiseAudio(self, indata, frames, time, status):
        if any(indata):

            magnitude = np.abs(np.fft.rfft(indata[:, 0], n=self.fftsize))
            magnitude *= self.gain / self.fftsize
            audio_channels = []

            for x in magnitude[self.low_bin : self.low_bin + self.columns]:
                audio_channel_val = int(np.clip(x, 0, 1) * (255))

                if audio_channel_val >= self.threshhold:
                    audio_channels.append(audio_channel_val)
                else:
                    audio_channels.append(0)

            d = deque(audio_channels)
            d.rotate(self.pixelsLeft)  # start top left? why not.
            audio_channels = list(d)

            chan_led_width = round(self.pixelCount_nobottom / len(audio_channels))

            for key, val in enumerate(audio_channels):

                first_pixel = key * chan_led_width
                last_pixel = first_pixel + chan_led_width - 1
                if last_pixel > self.pixelCount_nobottom:
                    last_pixel = self.pixelCount_nobottom - 1

                for pNum in range(first_pixel, last_pixel + 1):
                    col = self.wheel(pNum)
                    if val != 0:
                        self.pixels[pNum] = col
                    else:
                        self.fadeToBlack(pNum)

            count = 0
            for x in audio_channels:
                if x != 0:
                    count += 1

            testLine = ""
            for i in range(len(audio_channels)):
                testLine += str(audio_channels[i]).zfill(2) + "|"

            count = 0
            for x in self.pixels:
                if x != [0, 0, 0]:
                    count += 1

            self.pixels.show()
        else:
            self.log("No Audio data in")

    def visualise(self):
        with sd.InputStream(
            device=self.deviceID,
            channels=1,
            callback=self.visualiseAudio,
            blocksize=int(self.samplerate * 50 / 1000),
            samplerate=self.samplerate,
        ):
            # running stream
            sd.wait()

        time.sleep(0.01)


================================================
FILE: extensions/Aurora_Configure.py
================================================
from lib.AuroraExtension import AuroraExtension
import neopixel
import time
import datetime
import numpy as np
import pandas as pd
import board
import cv2
import sys


class Aurora_Configure(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension configures the LEDs"
        self.Name = "Aurora Ambient Lighting Configuration"
        self.count = 0
        self.current_frame = False
        self.percent = 3

    def setup(self):
        return True

    def visualise(self):
        # Capture the video frame
        ret, self.current_frame = self.getFrame()
        self.vid_h, self.vid_w, self.channels = self.current_frame.shape
        pos = 0

        colour = (255, 0, 0)  # red

        for i in range(pos, pos + self.pixelsLeft):
            self.pixels[i] = colour

        pos = pos + self.pixelsLeft

        colour = (0, 255, 0)  # green
        for x in range(pos, pos + self.pixelsTop):
            self.pixels[x] = colour

        pos = pos + self.pixelsTop
        colour = (0, 0, 255)  # blue
        for y in range(pos, pos + self.pixelsRight):
            self.pixels[y] = colour

        pos = pos + self.pixelsRight

        colour = (255, 255, 255)  # white
        for z in range(pos, pos + self.pixelsBottom):
            self.pixels[z] = colour

        self.pixels.show()


================================================
FILE: extensions/Aurora_Meteor.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2
from random import randint


class Aurora_Meteor(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = (
            "This has a 'meteor' effect in a random colour that shoots around the strip"
        )
        self.Name = "Aurora Meteor Display ( LED Only )"
        self.count = 0
        self.current_frame = False
        self.meteorSize = 10
        self.currentCol = (255, 0, 0)
        self.noHDMI = True
        self.startTime = time.time()

    def takeScreenShot(self, filepath):
        # We have no screenshot since... well its just LEDs
        return True

    def fadeToBlack(self, pixelPos):
        fadeValue = 64
        b, g, r = self.pixels[pixelPos]

        r = 0 if r <= 10 else int(r - (r * fadeValue / 255))
        g = 0 if g <= 10 else int(g - (g * fadeValue / 255))
        b = 0 if b <= 10 else int(b - (b * fadeValue / 255))

        self.pixels[pixelPos] = (b, g, r)

    def meteorRain(self, i, col):

        # Fade out
        for j in range(self.pixelsCount):
            if randint(0, 10) > 5:
                self.fadeToBlack(j)

        for j in range(self.meteorSize):
            if (i - j < self.pixelsCount) and (i - j >= 0):
                self.pixels[i - j] = col

        self.pixels.show()

    def visualise(self):

        # visualise!
        self.count += 5
        self.meteorRain(self.count, self.currentCol)
        if self.count >= self.pixelsCount + self.meteorSize:
            end = time.time()
            diff = end - self.startTime
            self.startTime = time.time()
            self.count = 0
            self.currentCol = (randint(0, 255), randint(0, 255), randint(0, 255))


================================================
FILE: extensions/Aurora_Rainbow.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2


class Aurora_Rainbow(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "This extension displays a rainbow pattern on the LEDs, a lot of this code is from https://learn.adafruit.com/circuitpython-essentials/circuitpython-neopixel"
        self.Name = "Aurora Rainbow Display ( LED Only )"
        self.count = 0
        self.current_frame = False
        self.noHDMI = True

    def takeScreenShot(self, filepath):
        # We have no screenshot since... well its just LEDs
        return True

    def wheel(self, pos):
        # Input a value 0 to 255 to get a color value.
        # The colours are a transition r - g - b - back to r.
        if pos < 0 or pos > 255:
            return (0, 0, 0)
        if pos < 85:
            return (255 - pos * 3, pos * 3, 0)
        if pos < 170:
            pos -= 85
            return (0, 255 - pos * 3, pos * 3)
        pos -= 170
        return (pos * 3, 0, 255 - pos * 3)

    def rainbow_cycle(self, j):
        for i in range(self.pixelsCount):
            rc_index = (i * 256 // self.pixelsCount) + j
            self.pixels[i] = self.wheel(rc_index & 255)
        self.getFrame(False)  # hack to have 'FPS'
        self.pixels.show()

    def visualise(self):

        # visualise!
        self.count += 1
        self.rainbow_cycle(self.count)
        if self.count == 255:
            self.count = 0

        time.sleep(0.01)


================================================
FILE: extensions/__init__.py
================================================


================================================
FILE: extensions/exampleExtension.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
from random import randint, choice
import numpy as np
import cv2


class exampleExtension(AuroraExtension):
    def __init__(self, NeoPixels, HDMI):
        super().__init__(NeoPixels, HDMI)
        self.Author = "Andrew MacPherson (@AndrewMohawk)"
        self.Description = "Extension Example"
        self.Name = "example extension"
        self.count = 0
        self.randomLED = 0
        self.randomSize = 0
        self.log("Extension {} Initiated".format(self.Name))

    # This doesnt need to be implemented, but can if you want
    # def takeScreenShot(self, filepath):

    # Fade LEDs to Black (so they dont instantly drop off)
    def fadeToBlack(self, pixelPos):
        fadeValue = 0.2
        b, g, r = self.pixels[pixelPos]

        r = 0 if r <= 10 else int(r - (r * fadeValue))
        g = 0 if g <= 10 else int(g - (g * fadeValue))
        b = 0 if b <= 10 else int(b - (b * fadeValue))
        self.pixels[pixelPos] = (b, g, r)

    # Fade LEDs to bright (or first block to 255) so they dont just spotlight
    def fadeToBright(self, pixelPos):

        fadeValue = 1.2
        b, g, r = self.pixels[pixelPos]

        if r == 255 or g == 255 or b == 255:
            return
        r = 255 if r >= 200 else int(r + (r * fadeValue))
        g = 255 if g >= 200 else int(g + (g * fadeValue))
        b = 255 if b >= 200 else int(b + (b * fadeValue))

        self.pixels[pixelPos] = (b, g, r)

    # This will 'visualise' whatever you want and will be called every ~0.01s
    def visualise(self):

        # Grab a 'frame' of the screen:
        ret, self.current_frame = self.getFrame()

        # Theres something on the screen
        if ret:
            self.count = self.count + 1

            if self.count <= 1:
                if self.count == 1:
                    self.log("Setting up colours")
                # Lets get a random size for our group of pixels between 5 and 50
                self.randomSize = randint(5, 50)

                # Lets pick a random LED to turn on
                self.randomLED = randint(0, self.pixelsCount - self.randomSize)

                # Lets get a random starting colour that we will scale up
                self.colour = (randint(0, 10), randint(0, 10), randint(0, 10))

                # Set the colour
                for x in range(self.randomLED, self.randomLED + self.randomSize):
                    self.pixels[x] = self.colour

            elif self.count < 20:
                if self.count == 2:
                    self.log("Fading up colours")
                # Fade it up
                for fade_up_pixel in range(
                    self.randomLED, (self.randomLED + self.randomSize)
                ):
                    self.fadeToBright(fade_up_pixel)

            elif self.count < 40:
                if self.count == 21:
                    self.log("Fading out colours")
                for fade_out_pixel in range(
                    self.randomLED, (self.randomLED + self.randomSize)
                ):
                    self.fadeToBlack(fade_out_pixel)

            else:
                if self.count == 40:
                    self.log("Resetting colours")
                self.count = 0

            # And show it
            self.pixels.show()


================================================
FILE: install.sh
================================================
#!/bin/bash
echo -e "
 ▄▄▄       █    ██  ██▀███   ▒█████   ██▀███   ▄▄▄      
▒████▄     ██  ▓██▒▓██ ▒ ██▒▒██▒  ██▒▓██ ▒ ██▒▒████▄    
▒██  ▀█▄  ▓██  ▒██░▓██ ░▄█ ▒▒██░  ██▒▓██ ░▄█ ▒▒██  ▀█▄  
░██▄▄▄▄██ ▓▓█  ░██░▒██▀▀█▄  ▒██   ██░▒██▀▀█▄  ░██▄▄▄▄██ 
 ▓█   ▓██▒▒▒█████▓ ░██▓ ▒██▒░ ████▓▒░░██▓ ▒██▒ ▓█   ▓██▒
 ▒▒   ▓▒█░░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░ ▒░▒░▒░ ░ ▒▓ ░▒▓░ ▒▒   ▓▒█░
  ▒   ▒▒ ░░░▒░ ░ ░   ░▒ ░ ▒░  ░ ▒ ▒░   ░▒ ░ ▒░  ▒   ▒▒ ░
  ░   ▒    ░░░ ░ ░   ░░   ░ ░ ░ ░ ▒    ░░   ░   ░   ▒   
      ░  ░   ░        ░         ░ ░     ░           ░  ░
                -Install Script-
                @AndrewMohawk                                                        
"
INSTALLDIR="/opt/Aurora"
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root, please use sudo" 
   exit 1
fi

if [ -d "$INSTALLDIR" ] 
then
    echo "Directory $INSTALLDIR exists. Please remove before performing a clean install" 
    exit 1
fi
echo "[+] Creating Install Directory"
echo "------------------------------"
mkdir $INSTALLDIR
cd $INSTALLDIR
echo "[+] Updating APT and installing dependencies"
echo "------------------------------"
apt update
apt-get install -y libatlas-base-dev libportaudio2 python3-pip git python3-opencv
echo "[+] Git Cloning Aurora base"
echo "------------------------------"
git clone https://github.com/AndrewMohawk/Aurora.git .
cp config.ini.bak config.ini
echo "[+] Installing Python Requirements"
echo "------------------------------"
pip3 install -r requirements.txt
echo "[+] Installing Service"
echo "------------------------------"
cp aurora.service /etc/systemd/system
systemctl enable aurora.service
systemctl start aurora.service
echo "[+] Service Status"
echo "------------------------------"
echo "Aurora status: `systemctl is-active aurora.service`"
echo "[+] Last 20 lines of aurora log"
echo "------------------------------"
journalctl -u aurora -n 20 --no-pager
echo -e "
 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 
(___|___|___|___|___|___|___|___|___|___|___|___)
        "
echo "Complete. You can now browse to the web interface to configure any changes you may need."
localip=$(hostname -I)
echo "This will likely be: http://$localip"


================================================
FILE: requirements.txt
================================================
numpy >= 1.16.5
pandas
opencv-python==4.5.1.48
cherrypy
configparser
adafruit-circuitpython-neopixel
blinker
jinja2
sounddevice
pyaudio


================================================
FILE: update.sh
================================================
#!/bin/bash
echo -e "
 ▄▄▄       █    ██  ██▀███   ▒█████   ██▀███   ▄▄▄      
▒████▄     ██  ▓██▒▓██ ▒ ██▒▒██▒  ██▒▓██ ▒ ██▒▒████▄    
▒██  ▀█▄  ▓██  ▒██░▓██ ░▄█ ▒▒██░  ██▒▓██ ░▄█ ▒▒██  ▀█▄  
░██▄▄▄▄██ ▓▓█  ░██░▒██▀▀█▄  ▒██   ██░▒██▀▀█▄  ░██▄▄▄▄██ 
 ▓█   ▓██▒▒▒█████▓ ░██▓ ▒██▒░ ████▓▒░░██▓ ▒██▒ ▓█   ▓██▒
 ▒▒   ▓▒█░░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░ ▒░▒░▒░ ░ ▒▓ ░▒▓░ ▒▒   ▓▒█░
  ▒   ▒▒ ░░░▒░ ░ ░   ░▒ ░ ▒░  ░ ▒ ▒░   ░▒ ░ ▒░  ▒   ▒▒ ░
  ░   ▒    ░░░ ░ ░   ░░   ░ ░ ░ ░ ▒    ░░   ░   ░   ▒   
      ░  ░   ░        ░         ░ ░     ░           ░  ░
                -Update Script-
                @AndrewMohawk                                                        
"
INSTALLDIR="/opt/Aurora"
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root, please use sudo" 
   exit 1
fi

if [ ! -d "$INSTALLDIR" ] 
then
    echo "Directory $INSTALLDIR Does not exist. Please run install first or update the install directory" 
    exit 1
fi
cd $INSTALLDIR
if [ -d .git ]; then
    echo "[+] Git Cloning Aurora base"
    echo "------------------------------"
    git pull 
    echo "[+] Stopping Aurora"
    echo "------------------------------"
    service aurora stop 
    echo "[+] Service Status"
    echo "------------------------------"
    echo "Aurora status: `systemctl is-active aurora.service`"
    echo "[+] Starting Aurora"
    echo "------------------------------"
    service aurora start 
    echo "[+] Service Status"
    echo "------------------------------"
    echo "Aurora status: `systemctl is-active aurora.service`"
    echo "[+] Last 20 lines of aurora log"
    echo "------------------------------"
    journalctl -u aurora -n 20 --no-pager
    echo -e "
 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 
(___|___|___|___|___|___|___|___|___|___|___|___)
        "
    echo "Complete. You can now browse to the web interface to configure any changes you may need."
    localip=$(hostname -I)
    echo "This will likely be: http://$localip"

else
    echo "This install directory ($INSTALLDIR) is *NOT* a git repo."
fi;


================================================
FILE: webserver/static/css/bootstrap.css
================================================
/*!
 * Bootstrap v4.5 (https://getbootstrap.com/)
 * Copyright 2011-2019 The Bootstrap Authors
 * Copyright 2011-2019 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */
html {
  box-sizing: border-box;
  -ms-overflow-style: scrollbar;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

:root {
  --blue: #007bff;
  --indigo: #6610f2;
  --purple: #6f42c1;
  --pink: #e83e8c;
  --red: #dc3545;
  --orange: #fd7e14;
  --yellow: #ffc107;
  --green: #28a745;
  --teal: #20c997;
  --cyan: #17a2b8;
  --white: #fff;
  --gray: #6c757d;
  --gray-dark: #343a40;
  --primary: #007bff;
  --secondary: #6c757d;
  --success: #28a745;
  --info: #17a2b8;
  --warning: #ffc107;
  --danger: #dc3545;
  --light: #f8f9fa;
  --dark: #343a40;
  --breakpoint-xs: 0;
  --breakpoint-sm: 576px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 992px;
  --breakpoint-xl: 1200px;
  --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-family: sans-serif;
  line-height: 1.15;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
  display: block;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: #212529;
  text-align: left;
  background-color: #fff;
}

[tabindex="-1"]:focus:not(:focus-visible) {
  outline: 0 !important;
}

hr {
  box-sizing: content-box;
  height: 0;
  overflow: visible;
}

h1, h2, h3, h4, h5, h6 {
  margin-top: 0;
  margin-bottom: 0.46875rem;
}

p {
  margin-top: 0;
  margin-bottom: 1rem;
}

abbr[title],
abbr[data-original-title] {
  text-decoration: underline;
  text-decoration: underline dotted;
  cursor: help;
  border-bottom: 0;
  text-decoration-skip-ink: none;
}

address {
  margin-bottom: 1rem;
  font-style: normal;
  line-height: inherit;
}

ol,
ul,
dl {
  margin-top: 0;
  margin-bottom: 1rem;
}

ol ol,
ul ul,
ol ul,
ul ol {
  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 {
  font-size: 80%;
}

sub,
sup {
  position: relative;
  font-size: 75%;
  line-height: 0;
  vertical-align: baseline;
}

sub {
  bottom: -.25em;
}

sup {
  top: -.5em;
}

a {
  color: #007bff;
  text-decoration: none;
  background-color: transparent;
}

a:hover {
  color: #0056b3;
  text-decoration: underline;
}

a:not([href]) {
  color: inherit;
  text-decoration: none;
}

a:not([href]):hover {
  color: inherit;
  text-decoration: none;
}

pre,
code,
kbd,
samp {
  font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  font-size: 1em;
}

pre {
  margin-top: 0;
  margin-bottom: 1rem;
  overflow: auto;
  -ms-overflow-style: scrollbar;
}

figure {
  margin: 0 0 1rem;
}

img {
  vertical-align: middle;
  border-style: none;
}

svg {
  overflow: hidden;
  vertical-align: middle;
}

table {
  border-collapse: collapse;
}

caption {
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  color: #6c757d;
  text-align: left;
  caption-side: bottom;
}

th {
  text-align: inherit;
}

label {
  display: inline-block;
  margin-bottom: 0.5rem;
}

button {
  border-radius: 0;
}

button:focus {
  outline: 1px dotted;
  outline: 5px auto -webkit-focus-ring-color;
}

input,
button,
select,
optgroup,
textarea {
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
}

button,
input {
  overflow: visible;
}

button,
select {
  text-transform: none;
}

[role="button"] {
  cursor: pointer;
}

select {
  word-wrap: normal;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: button;
}

button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
  cursor: pointer;
}

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
  padding: 0;
  border-style: none;
}

input[type="radio"],
input[type="checkbox"] {
  box-sizing: border-box;
  padding: 0;
}

textarea {
  overflow: auto;
  resize: vertical;
}

fieldset {
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
}

legend {
  display: block;
  width: 100%;
  max-width: 100%;
  padding: 0;
  margin-bottom: .5rem;
  font-size: 1.5rem;
  line-height: inherit;
  color: inherit;
  white-space: normal;
}

progress {
  vertical-align: baseline;
}

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

[type="search"] {
  outline-offset: -2px;
  -webkit-appearance: none;
}

[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

::-webkit-file-upload-button {
  font: inherit;
  -webkit-appearance: button;
}

output {
  display: inline-block;
}

summary {
  display: list-item;
  cursor: pointer;
}

template {
  display: none;
}

[hidden] {
  display: none !important;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  margin-bottom: 0.46875rem;
  font-weight: 500;
  line-height: 1.2;
}

h1, .h1 {
  font-size: 2.5rem;
}

h2, .h2 {
  font-size: 2rem;
}

h3, .h3 {
  font-size: 1.75rem;
}

h4, .h4 {
  font-size: 1.5rem;
}

h5, .h5 {
  font-size: 1.25rem;
}

h6, .h6 {
  font-size: 1rem;
}

.lead {
  font-size: 1.25rem;
  font-weight: 300;
}

.display-1 {
  font-size: 6rem;
  font-weight: 300;
  line-height: 1.2;
}

.display-2 {
  font-size: 5.5rem;
  font-weight: 300;
  line-height: 1.2;
}

.display-3 {
  font-size: 4.5rem;
  font-weight: 300;
  line-height: 1.2;
}

.display-4 {
  font-size: 3.5rem;
  font-weight: 300;
  line-height: 1.2;
}

hr {
  margin-top: 0.9375rem;
  margin-bottom: 0.9375rem;
  border: 0;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}

small,
.small {
  font-size: 80%;
  font-weight: 400;
}

mark,
.mark {
  padding: 0.2em;
  background-color: #fcf8e3;
}

.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: 0.5rem;
}

.initialism {
  font-size: 90%;
  text-transform: uppercase;
}

.blockquote {
  margin-bottom: 0.9375rem;
  font-size: 1.25rem;
}

.blockquote-footer {
  display: block;
  font-size: 80%;
  color: #6c757d;
}

.blockquote-footer::before {
  content: "\2014\00A0";
}

.img-fluid {
  max-width: 100%;
  height: auto;
}

.img-thumbnail {
  padding: 0.25rem;
  background-color: #fff;
  border: 1px solid #dee2e6;
  border-radius: 0.25rem;
  max-width: 100%;
  height: auto;
}

.figure {
  display: inline-block;
}

.figure-img {
  margin-bottom: 0.46875rem;
  line-height: 1;
}

.figure-caption {
  font-size: 90%;
  color: #6c757d;
}

.container {
  width: 100%;
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}

@media (min-width: 576px) {
  .container {
    max-width: 540px;
  }
}

@media (min-width: 768px) {
  .container {
    max-width: 720px;
  }
}

@media (min-width: 992px) {
  .container {
    max-width: 960px;
  }
}

@media (min-width: 1200px) {
  .container {
    max-width: 1140px;
  }
}

.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
  width: 100%;
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}

@media (min-width: 576px) {
  .container, .container-sm {
    max-width: 540px;
  }
}

@media (min-width: 768px) {
  .container, .container-sm, .container-md {
    max-width: 720px;
  }
}

@media (min-width: 992px) {
  .container, .container-sm, .container-md, .container-lg {
    max-width: 960px;
  }
}

@media (min-width: 1200px) {
  .container, .container-sm, .container-md, .container-lg, .container-xl {
    max-width: 1140px;
  }
}

.row {
  display: flex;
  flex-wrap: wrap;
  margin-right: -15px;
  margin-left: -15px;
}

.no-gutters {
  margin-right: 0;
  margin-left: 0;
}

.no-gutters > .col,
.no-gutters > [class*="col-"] {
  padding-right: 0;
  padding-left: 0;
}

.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,
.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,
.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,
.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,
.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,
.col-xl-auto {
  position: relative;
  width: 100%;
  padding-right: 15px;
  padding-left: 15px;
}

.col {
  flex-basis: 0;
  flex-grow: 1;
  min-width: 0;
  max-width: 100%;
}

.row-cols-1 > * {
  flex: 0 0 100%;
  max-width: 100%;
}

.row-cols-2 > * {
  flex: 0 0 50%;
  max-width: 50%;
}

.row-cols-3 > * {
  flex: 0 0 33.33333%;
  max-width: 33.33333%;
}

.row-cols-4 > * {
  flex: 0 0 25%;
  max-width: 25%;
}

.row-cols-5 > * {
  flex: 0 0 20%;
  max-width: 20%;
}

.row-cols-6 > * {
  flex: 0 0 16.66667%;
  max-width: 16.66667%;
}

.col-auto {
  flex: 0 0 auto;
  width: auto;
  max-width: 100%;
}

.col-1 {
  flex: 0 0 8.33333%;
  max-width: 8.33333%;
}

.col-2 {
  flex: 0 0 16.66667%;
  max-width: 16.66667%;
}

.col-3 {
  flex: 0 0 25%;
  max-width: 25%;
}

.col-4 {
  flex: 0 0 33.33333%;
  max-width: 33.33333%;
}

.col-5 {
  flex: 0 0 41.66667%;
  max-width: 41.66667%;
}

.col-6 {
  flex: 0 0 50%;
  max-width: 50%;
}

.col-7 {
  flex: 0 0 58.33333%;
  max-width: 58.33333%;
}

.col-8 {
  flex: 0 0 66.66667%;
  max-width: 66.66667%;
}

.col-9 {
  flex: 0 0 75%;
  max-width: 75%;
}

.col-10 {
  flex: 0 0 83.33333%;
  max-width: 83.33333%;
}

.col-11 {
  flex: 0 0 91.66667%;
  max-width: 91.66667%;
}

.col-12 {
  flex: 0 0 100%;
  max-width: 100%;
}

.order-first {
  order: -1;
}

.order-last {
  order: 13;
}

.order-0 {
  order: 0;
}

.order-1 {
  order: 1;
}

.order-2 {
  order: 2;
}

.order-3 {
  order: 3;
}

.order-4 {
  order: 4;
}

.order-5 {
  order: 5;
}

.order-6 {
  order: 6;
}

.order-7 {
  order: 7;
}

.order-8 {
  order: 8;
}

.order-9 {
  order: 9;
}

.order-10 {
  order: 10;
}

.order-11 {
  order: 11;
}

.order-12 {
  order: 12;
}

.offset-1 {
  margin-left: 8.33333%;
}

.offset-2 {
  margin-left: 16.66667%;
}

.offset-3 {
  margin-left: 25%;
}

.offset-4 {
  margin-left: 33.33333%;
}

.offset-5 {
  margin-left: 41.66667%;
}

.offset-6 {
  margin-left: 50%;
}

.offset-7 {
  margin-left: 58.33333%;
}

.offset-8 {
  margin-left: 66.66667%;
}

.offset-9 {
  margin-left: 75%;
}

.offset-10 {
  margin-left: 83.33333%;
}

.offset-11 {
  margin-left: 91.66667%;
}

@media (min-width: 576px) {
  .col-sm {
    flex-basis: 0;
    flex-grow: 1;
    min-width: 0;
    max-width: 100%;
  }
  .row-cols-sm-1 > * {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .row-cols-sm-2 > * {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .row-cols-sm-3 > * {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .row-cols-sm-4 > * {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .row-cols-sm-5 > * {
    flex: 0 0 20%;
    max-width: 20%;
  }
  .row-cols-sm-6 > * {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-sm-auto {
    flex: 0 0 auto;
    width: auto;
    max-width: 100%;
  }
  .col-sm-1 {
    flex: 0 0 8.33333%;
    max-width: 8.33333%;
  }
  .col-sm-2 {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-sm-3 {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .col-sm-4 {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .col-sm-5 {
    flex: 0 0 41.66667%;
    max-width: 41.66667%;
  }
  .col-sm-6 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .col-sm-7 {
    flex: 0 0 58.33333%;
    max-width: 58.33333%;
  }
  .col-sm-8 {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
  .col-sm-9 {
    flex: 0 0 75%;
    max-width: 75%;
  }
  .col-sm-10 {
    flex: 0 0 83.33333%;
    max-width: 83.33333%;
  }
  .col-sm-11 {
    flex: 0 0 91.66667%;
    max-width: 91.66667%;
  }
  .col-sm-12 {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .order-sm-first {
    order: -1;
  }
  .order-sm-last {
    order: 13;
  }
  .order-sm-0 {
    order: 0;
  }
  .order-sm-1 {
    order: 1;
  }
  .order-sm-2 {
    order: 2;
  }
  .order-sm-3 {
    order: 3;
  }
  .order-sm-4 {
    order: 4;
  }
  .order-sm-5 {
    order: 5;
  }
  .order-sm-6 {
    order: 6;
  }
  .order-sm-7 {
    order: 7;
  }
  .order-sm-8 {
    order: 8;
  }
  .order-sm-9 {
    order: 9;
  }
  .order-sm-10 {
    order: 10;
  }
  .order-sm-11 {
    order: 11;
  }
  .order-sm-12 {
    order: 12;
  }
  .offset-sm-0 {
    margin-left: 0;
  }
  .offset-sm-1 {
    margin-left: 8.33333%;
  }
  .offset-sm-2 {
    margin-left: 16.66667%;
  }
  .offset-sm-3 {
    margin-left: 25%;
  }
  .offset-sm-4 {
    margin-left: 33.33333%;
  }
  .offset-sm-5 {
    margin-left: 41.66667%;
  }
  .offset-sm-6 {
    margin-left: 50%;
  }
  .offset-sm-7 {
    margin-left: 58.33333%;
  }
  .offset-sm-8 {
    margin-left: 66.66667%;
  }
  .offset-sm-9 {
    margin-left: 75%;
  }
  .offset-sm-10 {
    margin-left: 83.33333%;
  }
  .offset-sm-11 {
    margin-left: 91.66667%;
  }
}

@media (min-width: 768px) {
  .col-md {
    flex-basis: 0;
    flex-grow: 1;
    min-width: 0;
    max-width: 100%;
  }
  .row-cols-md-1 > * {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .row-cols-md-2 > * {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .row-cols-md-3 > * {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .row-cols-md-4 > * {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .row-cols-md-5 > * {
    flex: 0 0 20%;
    max-width: 20%;
  }
  .row-cols-md-6 > * {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-md-auto {
    flex: 0 0 auto;
    width: auto;
    max-width: 100%;
  }
  .col-md-1 {
    flex: 0 0 8.33333%;
    max-width: 8.33333%;
  }
  .col-md-2 {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-md-3 {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .col-md-4 {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .col-md-5 {
    flex: 0 0 41.66667%;
    max-width: 41.66667%;
  }
  .col-md-6 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .col-md-7 {
    flex: 0 0 58.33333%;
    max-width: 58.33333%;
  }
  .col-md-8 {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
  .col-md-9 {
    flex: 0 0 75%;
    max-width: 75%;
  }
  .col-md-10 {
    flex: 0 0 83.33333%;
    max-width: 83.33333%;
  }
  .col-md-11 {
    flex: 0 0 91.66667%;
    max-width: 91.66667%;
  }
  .col-md-12 {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .order-md-first {
    order: -1;
  }
  .order-md-last {
    order: 13;
  }
  .order-md-0 {
    order: 0;
  }
  .order-md-1 {
    order: 1;
  }
  .order-md-2 {
    order: 2;
  }
  .order-md-3 {
    order: 3;
  }
  .order-md-4 {
    order: 4;
  }
  .order-md-5 {
    order: 5;
  }
  .order-md-6 {
    order: 6;
  }
  .order-md-7 {
    order: 7;
  }
  .order-md-8 {
    order: 8;
  }
  .order-md-9 {
    order: 9;
  }
  .order-md-10 {
    order: 10;
  }
  .order-md-11 {
    order: 11;
  }
  .order-md-12 {
    order: 12;
  }
  .offset-md-0 {
    margin-left: 0;
  }
  .offset-md-1 {
    margin-left: 8.33333%;
  }
  .offset-md-2 {
    margin-left: 16.66667%;
  }
  .offset-md-3 {
    margin-left: 25%;
  }
  .offset-md-4 {
    margin-left: 33.33333%;
  }
  .offset-md-5 {
    margin-left: 41.66667%;
  }
  .offset-md-6 {
    margin-left: 50%;
  }
  .offset-md-7 {
    margin-left: 58.33333%;
  }
  .offset-md-8 {
    margin-left: 66.66667%;
  }
  .offset-md-9 {
    margin-left: 75%;
  }
  .offset-md-10 {
    margin-left: 83.33333%;
  }
  .offset-md-11 {
    margin-left: 91.66667%;
  }
}

@media (min-width: 992px) {
  .col-lg {
    flex-basis: 0;
    flex-grow: 1;
    min-width: 0;
    max-width: 100%;
  }
  .row-cols-lg-1 > * {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .row-cols-lg-2 > * {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .row-cols-lg-3 > * {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .row-cols-lg-4 > * {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .row-cols-lg-5 > * {
    flex: 0 0 20%;
    max-width: 20%;
  }
  .row-cols-lg-6 > * {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-lg-auto {
    flex: 0 0 auto;
    width: auto;
    max-width: 100%;
  }
  .col-lg-1 {
    flex: 0 0 8.33333%;
    max-width: 8.33333%;
  }
  .col-lg-2 {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-lg-3 {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .col-lg-4 {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .col-lg-5 {
    flex: 0 0 41.66667%;
    max-width: 41.66667%;
  }
  .col-lg-6 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .col-lg-7 {
    flex: 0 0 58.33333%;
    max-width: 58.33333%;
  }
  .col-lg-8 {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
  .col-lg-9 {
    flex: 0 0 75%;
    max-width: 75%;
  }
  .col-lg-10 {
    flex: 0 0 83.33333%;
    max-width: 83.33333%;
  }
  .col-lg-11 {
    flex: 0 0 91.66667%;
    max-width: 91.66667%;
  }
  .col-lg-12 {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .order-lg-first {
    order: -1;
  }
  .order-lg-last {
    order: 13;
  }
  .order-lg-0 {
    order: 0;
  }
  .order-lg-1 {
    order: 1;
  }
  .order-lg-2 {
    order: 2;
  }
  .order-lg-3 {
    order: 3;
  }
  .order-lg-4 {
    order: 4;
  }
  .order-lg-5 {
    order: 5;
  }
  .order-lg-6 {
    order: 6;
  }
  .order-lg-7 {
    order: 7;
  }
  .order-lg-8 {
    order: 8;
  }
  .order-lg-9 {
    order: 9;
  }
  .order-lg-10 {
    order: 10;
  }
  .order-lg-11 {
    order: 11;
  }
  .order-lg-12 {
    order: 12;
  }
  .offset-lg-0 {
    margin-left: 0;
  }
  .offset-lg-1 {
    margin-left: 8.33333%;
  }
  .offset-lg-2 {
    margin-left: 16.66667%;
  }
  .offset-lg-3 {
    margin-left: 25%;
  }
  .offset-lg-4 {
    margin-left: 33.33333%;
  }
  .offset-lg-5 {
    margin-left: 41.66667%;
  }
  .offset-lg-6 {
    margin-left: 50%;
  }
  .offset-lg-7 {
    margin-left: 58.33333%;
  }
  .offset-lg-8 {
    margin-left: 66.66667%;
  }
  .offset-lg-9 {
    margin-left: 75%;
  }
  .offset-lg-10 {
    margin-left: 83.33333%;
  }
  .offset-lg-11 {
    margin-left: 91.66667%;
  }
}

@media (min-width: 1200px) {
  .col-xl {
    flex-basis: 0;
    flex-grow: 1;
    min-width: 0;
    max-width: 100%;
  }
  .row-cols-xl-1 > * {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .row-cols-xl-2 > * {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .row-cols-xl-3 > * {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .row-cols-xl-4 > * {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .row-cols-xl-5 > * {
    flex: 0 0 20%;
    max-width: 20%;
  }
  .row-cols-xl-6 > * {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-xl-auto {
    flex: 0 0 auto;
    width: auto;
    max-width: 100%;
  }
  .col-xl-1 {
    flex: 0 0 8.33333%;
    max-width: 8.33333%;
  }
  .col-xl-2 {
    flex: 0 0 16.66667%;
    max-width: 16.66667%;
  }
  .col-xl-3 {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .col-xl-4 {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .col-xl-5 {
    flex: 0 0 41.66667%;
    max-width: 41.66667%;
  }
  .col-xl-6 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .col-xl-7 {
    flex: 0 0 58.33333%;
    max-width: 58.33333%;
  }
  .col-xl-8 {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
  .col-xl-9 {
    flex: 0 0 75%;
    max-width: 75%;
  }
  .col-xl-10 {
    flex: 0 0 83.33333%;
    max-width: 83.33333%;
  }
  .col-xl-11 {
    flex: 0 0 91.66667%;
    max-width: 91.66667%;
  }
  .col-xl-12 {
    flex: 0 0 100%;
    max-width: 100%;
  }
  .order-xl-first {
    order: -1;
  }
  .order-xl-last {
    order: 13;
  }
  .order-xl-0 {
    order: 0;
  }
  .order-xl-1 {
    order: 1;
  }
  .order-xl-2 {
    order: 2;
  }
  .order-xl-3 {
    order: 3;
  }
  .order-xl-4 {
    order: 4;
  }
  .order-xl-5 {
    order: 5;
  }
  .order-xl-6 {
    order: 6;
  }
  .order-xl-7 {
    order: 7;
  }
  .order-xl-8 {
    order: 8;
  }
  .order-xl-9 {
    order: 9;
  }
  .order-xl-10 {
    order: 10;
  }
  .order-xl-11 {
    order: 11;
  }
  .order-xl-12 {
    order: 12;
  }
  .offset-xl-0 {
    margin-left: 0;
  }
  .offset-xl-1 {
    margin-left: 8.33333%;
  }
  .offset-xl-2 {
    margin-left: 16.66667%;
  }
  .offset-xl-3 {
    margin-left: 25%;
  }
  .offset-xl-4 {
    margin-left: 33.33333%;
  }
  .offset-xl-5 {
    margin-left: 41.66667%;
  }
  .offset-xl-6 {
    margin-left: 50%;
  }
  .offset-xl-7 {
    margin-left: 58.33333%;
  }
  .offset-xl-8 {
    margin-left: 66.66667%;
  }
  .offset-xl-9 {
    margin-left: 75%;
  }
  .offset-xl-10 {
    margin-left: 83.33333%;
  }
  .offset-xl-11 {
    margin-left: 91.66667%;
  }
}

.table {
  width: 100%;
  margin-bottom: 0.9375rem;
  color: #212529;
}

.table th,
.table td {
  padding: 0.75rem;
  vertical-align: top;
  border-top: 1px solid #dee2e6;
}

.table thead th {
  vertical-align: bottom;
  border-bottom: 2px solid #dee2e6;
}

.table tbody + tbody {
  border-top: 2px solid #dee2e6;
}

.table-sm th,
.table-sm td {
  padding: 0.3rem;
}

.table-bordered {
  border: 1px solid #dee2e6;
}

.table-bordered th,
.table-bordered td {
  border: 1px solid #dee2e6;
}

.table-bordered thead th,
.table-bordered thead td {
  border-bottom-width: 2px;
}

.table-borderless th,
.table-borderless td,
.table-borderless thead th,
.table-borderless tbody + tbody {
  border: 0;
}

.table-striped tbody tr:nth-of-type(odd) {
  background-color: rgba(0, 0, 0, 0.05);
}

.table-hover tbody tr:hover {
  color: #212529;
  background-color: rgba(0, 0, 0, 0.075);
}

.table-primary,
.table-primary > th,
.table-primary > td {
  background-color: #b8daff;
}

.table-primary th,
.table-primary td,
.table-primary thead th,
.table-primary tbody + tbody {
  border-color: #7abaff;
}

.table-hover .table-primary:hover {
  background-color: #9fcdff;
}

.table-hover .table-primary:hover > td,
.table-hover .table-primary:hover > th {
  background-color: #9fcdff;
}

.table-secondary,
.table-secondary > th,
.table-secondary > td {
  background-color: #d6d8db;
}

.table-secondary th,
.table-secondary td,
.table-secondary thead th,
.table-secondary tbody + tbody {
  border-color: #b3b7bb;
}

.table-hover .table-secondary:hover {
  background-color: #c8cbcf;
}

.table-hover .table-secondary:hover > td,
.table-hover .table-secondary:hover > th {
  background-color: #c8cbcf;
}

.table-success,
.table-success > th,
.table-success > td {
  background-color: #c3e6cb;
}

.table-success th,
.table-success td,
.table-success thead th,
.table-success tbody + tbody {
  border-color: #8fd19e;
}

.table-hover .table-success:hover {
  background-color: #b1dfbb;
}

.table-hover .table-success:hover > td,
.table-hover .table-success:hover > th {
  background-color: #b1dfbb;
}

.table-info,
.table-info > th,
.table-info > td {
  background-color: #bee5eb;
}

.table-info th,
.table-info td,
.table-info thead th,
.table-info tbody + tbody {
  border-color: #86cfda;
}

.table-hover .table-info:hover {
  background-color: #abdde5;
}

.table-hover .table-info:hover > td,
.table-hover .table-info:hover > th {
  background-color: #abdde5;
}

.table-warning,
.table-warning > th,
.table-warning > td {
  background-color: #ffeeba;
}

.table-warning th,
.table-warning td,
.table-warning thead th,
.table-warning tbody + tbody {
  border-color: #ffdf7e;
}

.table-hover .table-warning:hover {
  background-color: #ffe8a1;
}

.table-hover .table-warning:hover > td,
.table-hover .table-warning:hover > th {
  background-color: #ffe8a1;
}

.table-danger,
.table-danger > th,
.table-danger > td {
  background-color: #f5c6cb;
}

.table-danger th,
.table-danger td,
.table-danger thead th,
.table-danger tbody + tbody {
  border-color: #ed969e;
}

.table-hover .table-danger:hover {
  background-color: #f1b0b7;
}

.table-hover .table-danger:hover > td,
.table-hover .table-danger:hover > th {
  background-color: #f1b0b7;
}

.table-light,
.table-light > th,
.table-light > td {
  background-color: #fdfdfe;
}

.table-light th,
.table-light td,
.table-light thead th,
.table-light tbody + tbody {
  border-color: #fbfcfc;
}

.table-hover .table-light:hover {
  background-color: #ececf6;
}

.table-hover .table-light:hover > td,
.table-hover .table-light:hover > th {
  background-color: #ececf6;
}

.table-dark,
.table-dark > th,
.table-dark > td {
  background-color: #c6c8ca;
}

.table-dark th,
.table-dark td,
.table-dark thead th,
.table-dark tbody + tbody {
  border-color: #95999c;
}

.table-hover .table-dark:hover {
  background-color: #b9bbbe;
}

.table-hover .table-dark:hover > td,
.table-hover .table-dark:hover > th {
  background-color: #b9bbbe;
}

.table-active,
.table-active > th,
.table-active > td {
  background-color: rgba(0, 0, 0, 0.075);
}

.table-hover .table-active:hover {
  background-color: rgba(0, 0, 0, 0.075);
}

.table-hover .table-active:hover > td,
.table-hover .table-active:hover > th {
  background-color: rgba(0, 0, 0, 0.075);
}

.table .thead-dark th {
  color: #fff;
  background-color: #343a40;
  border-color: #454d55;
}

.table .thead-light th {
  color: #495057;
  background-color: #e9ecef;
  border-color: #dee2e6;
}

.table-dark {
  color: #fff;
  background-color: #343a40;
}

.table-dark th,
.table-dark td,
.table-dark thead th {
  border-color: #454d55;
}

.table-dark.table-bordered {
  border: 0;
}

.table-dark.table-striped tbody tr:nth-of-type(odd) {
  background-color: rgba(255, 255, 255, 0.05);
}

.table-dark.table-hover tbody tr:hover {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.075);
}

@media (max-width: 575.98px) {
  .table-responsive-sm {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-sm > .table-bordered {
    border: 0;
  }
}

@media (max-width: 767.98px) {
  .table-responsive-md {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-md > .table-bordered {
    border: 0;
  }
}

@media (max-width: 991.98px) {
  .table-responsive-lg {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-lg > .table-bordered {
    border: 0;
  }
}

@media (max-width: 1199.98px) {
  .table-responsive-xl {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .table-responsive-xl > .table-bordered {
    border: 0;
  }
}

.table-responsive {
  display: block;
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.table-responsive > .table-bordered {
  border: 0;
}

.form-control {
  display: block;
  width: 100%;
  height: calc(1.5em + 1.5rem + 2px);
  padding: 0.75rem 0.75rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: #495057;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid #ced4da;
  border-radius: 0.25rem;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
  .form-control {
    transition: none;
  }
}

.form-control::-ms-expand {
  background-color: transparent;
  border: 0;
}

.form-control:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 #495057;
}

.form-control:focus {
  color: #495057;
  background-color: #fff;
  border-color: #80bdff;
  outline: 0;
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}

.form-control::placeholder {
  color: #6c757d;
  opacity: 1;
}

.form-control:disabled, .form-control[readonly] {
  background-color: #e9ecef;
  opacity: 1;
}

input[type="date"].form-control,
input[type="time"].form-control,
input[type="datetime-local"].form-control,
input[type="month"].form-control {
  appearance: none;
}

select.form-control:focus::-ms-value {
  color: #495057;
  background-color: #fff;
}

.form-control-file,
.form-control-range {
  display: block;
  width: 100%;
}

.col-form-label {
  padding-top: calc(0.75rem + 1px);
  padding-bottom: calc(0.75rem + 1px);
  margin-bottom: 0;
  font-size: inherit;
  line-height: 1.5;
}

.col-form-label-lg {
  padding-top: calc(0.5rem + 1px);
  padding-bottom: calc(0.5rem + 1px);
  font-size: 1.25rem;
  line-height: 1.5;
}

.col-form-label-sm {
  padding-top: calc(0.25rem + 1px);
  padding-bottom: calc(0.25rem + 1px);
  font-size: 0.875rem;
  line-height: 1.5;
}

.form-control-plaintext {
  display: block;
  width: 100%;
  padding: 0.75rem 0;
  margin-bottom: 0;
  font-size: 1rem;
  line-height: 1.5;
  color: #212529;
  background-color: transparent;
  border: solid transparent;
  border-width: 1px 0;
}

.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {
  padding-right: 0;
  padding-left: 0;
}

.form-control-sm {
  height: calc(1.5em + 0.5rem + 2px);
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  line-height: 1.5;
  border-radius: 0.2rem;
}

.form-control-lg {
  height: calc(1.5em + 1rem + 2px);
  padding: 0.5rem 1rem;
  font-size: 1.25rem;
  line-height: 1.5;
  border-radius: 0.3rem;
}

select.form-control[size], select.form-control[multiple] {
  height: auto;
}

textarea.form-control {
  height: auto;
}

.form-group {
  margin-bottom: 1rem;
}

.form-text {
  display: block;
  margin-top: 0.25rem;
}

.form-row {
  display: flex;
  flex-wrap: wrap;
  margin-right: -5px;
  margin-left: -5px;
}

.form-row > .col,
.form-row > [class*="col-"] {
  padding-right: 5px;
  padding-left: 5px;
}

.form-check {
  position: relative;
  display: block;
  padding-left: 1.25rem;
}

.form-check-input {
  position: absolute;
  margin-top: 0.3rem;
  margin-left: -1.25rem;
}

.form-check-input[disabled] ~ .form-check-label,
.form-check-input:disabled ~ .form-check-label {
  color: #6c757d;
}

.form-check-label {
  margin-bottom: 0;
}

.form-check-inline {
  display: inline-flex;
  align-items: center;
  padding-left: 0;
  margin-right: 0.75rem;
}

.form-check-inline .form-check-input {
  position: static;
  margin-top: 0;
  margin-right: 0.3125rem;
  margin-left: 0;
}

.valid-feedback {
  display: none;
  width: 100%;
  margin-top: 0.25rem;
  font-size: 80%;
  color: #28a745;
}

.valid-tooltip {
  position: absolute;
  top: 100%;
  z-index: 5;
  display: none;
  max-width: 100%;
  padding: 0.25rem 0.5rem;
  margin-top: .1rem;
  font-size: 0.875rem;
  line-height: 1.5;
  color: #fff;
  background-color: rgba(40, 167, 69, 0.9);
  border-radius: 0.25rem;
}

.was-validated :valid ~ .valid-feedback,
.was-validated :valid ~ .valid-tooltip,
.is-valid ~ .valid-feedback,
.is-valid ~ .valid-tooltip {
  display: block;
}

.was-validated .form-control:valid, .form-control.is-valid {
  border-color: #28a745;
  padding-right: calc(1.5em + 1.5rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.375rem) center;
  background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}

.was-validated .form-control:valid:focus, .form-control.is-valid:focus {
  border-color: #28a745;
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}

.was-validated textarea.form-control:valid, textarea.form-control.is-valid {
  padding-right: calc(1.5em + 1.5rem);
  background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem);
}

.was-validated .custom-select:valid, .custom-select.is-valid {
  border-color: #28a745;
  padding-right: calc(0.75em + 2.875rem);
  background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}

.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {
  border-color: #28a745;
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}

.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {
  color: #28a745;
}

.was-validated .form-check-input:valid ~ .valid-feedback,
.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,
.form-check-input.is-valid ~ .valid-tooltip {
  display: block;
}

.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {
  color: #28a745;
}

.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {
  border-color: #28a745;
}

.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {
  border-color: #34ce57;
  background-color: #34ce57;
}

.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}

.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {
  border-color: #28a745;
}

.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {
  border-color: #28a745;
}

.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {
  border-color: #28a745;
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}

.invalid-feedback {
  display: none;
  width: 100%;
  margin-top: 0.25rem;
  font-size: 80%;
  color: #dc3545;
}

.invalid-tooltip {
  position: absolute;
  top: 100%;
  z-index: 5;
  display: none;
  max-width: 100%;
  padding: 0.25rem 0.5rem;
  margin-top: .1rem;
  font-size: 0.875rem;
  line-height: 1.5;
  color: #fff;
  background-color: rgba(220, 53, 69, 0.9);
  border-radius: 0.25rem;
}

.was-validated :invalid ~ .invalid-feedback,
.was-validated :invalid ~ .invalid-tooltip,
.is-invalid ~ .invalid-feedback,
.is-invalid ~ .invalid-tooltip {
  display: block;
}

.was-validated .form-control:invalid, .form-control.is-invalid {
  border-color: #dc3545;
  padding-right: calc(1.5em + 1.5rem);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.375rem) center;
  background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}

.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {
  border-color: #dc3545;
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}

.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {
  padding-right: calc(1.5em + 1.5rem);
  background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem);
}

.was-validated .custom-select:invalid, .custom-select.is-invalid {
  border-color: #dc3545;
  padding-right: calc(0.75em + 2.875rem);
  background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%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") #fff no-repeat center right 1.75rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}

.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {
  border-color: #dc3545;
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}

.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {
  color: #dc3545;
}

.was-validated .form-check-input:invalid ~ .invalid-feedback,
.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,
.form-check-input.is-invalid ~ .invalid-tooltip {
  display: block;
}

.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {
  color: #dc3545;
}

.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {
  border-color: #dc3545;
}

.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {
  border-color: #e4606d;
  background-color: #e4606d;
}

.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}

.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {
  border-color: #dc3545;
}

.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {
  border-color: #dc3545;
}

.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {
  border-color: #dc3545;
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}

.form-inline {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
}

.form-inline .form-check {
  width: 100%;
}

@media (min-width: 576px) {
  .form-inline label {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0;
  }
  .form-inline .form-group {
    display: flex;
    flex: 0 0 auto;
    flex-flow: row wrap;
    align-items: center;
    margin-bottom: 0;
  }
  .form-inline .form-control {
    display: inline-block;
    width: auto;
    vertical-align: middle;
  }
  .form-inline .form-control-plaintext {
    display: inline-block;
  }
  .form-inline .input-group,
  .form-inline .custom-select {
    width: auto;
  }
  .form-inline .form-check {
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto;
    padding-left: 0;
  }
  .form-inline .form-check-input {
    position: relative;
    flex-shrink: 0;
    margin-top: 0;
    margin-right: 0.25rem;
    margin-left: 0;
  }
  .form-inline .custom-control {
    align-items: center;
    justify-content: center;
  }
  .form-inline .custom-control-label {
    margin-bottom: 0;
  }
}

.btn {
  display: inline-block;
  font-weight: 400;
  color: #212529;
  text-align: center;
  vertical-align: middle;
  user-select: none;
  background-color: transparent;
  border: 1px solid transparent;
  padding: 0.75rem 0.75rem;
  font-size: 1rem;
  line-height: 1.5;
  border-radius: 0.25rem;
  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;
}

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
  }
}

.btn:hover {
  color: #212529;
  text-decoration: none;
}

.btn:focus, .btn.focus {
  outline: 0;
  box-shadow: 0;
}

.btn.disabled, .btn:disabled {
  opacity: 0.65;
}

.btn:not(:disabled):not(.disabled) {
  cursor: pointer;
}

a.btn.disabled,
fieldset:disabled a.btn {
  pointer-events: none;
}

.btn-primary {
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.btn-primary:hover {
  color: #fff;
  background-color: #0069d9;
  border-color: #0062cc;
}

.btn-primary:focus, .btn-primary.focus {
  color: #fff;
  background-color: #0069d9;
  border-color: #0062cc;
  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);
}

.btn-primary.disabled, .btn-primary:disabled {
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
  color: #fff;
  background-color: #0062cc;
  border-color: #005cbf;
}

.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-primary.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);
}

.btn-secondary {
  color: #fff;
  background-color: #6c757d;
  border-color: #6c757d;
}

.btn-secondary:hover {
  color: #fff;
  background-color: #5a6268;
  border-color: #545b62;
}

.btn-secondary:focus, .btn-secondary.focus {
  color: #fff;
  background-color: #5a6268;
  border-color: #545b62;
  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);
}

.btn-secondary.disabled, .btn-secondary:disabled {
  color: #fff;
  background-color: #6c757d;
  border-color: #6c757d;
}

.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,
.show > .btn-secondary.dropdown-toggle {
  color: #fff;
  background-color: #545b62;
  border-color: #4e555b;
}

.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,
.show > .btn-secondary.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);
}

.btn-success {
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
}

.btn-success:hover {
  color: #fff;
  background-color: #218838;
  border-color: #1e7e34;
}

.btn-success:focus, .btn-success.focus {
  color: #fff;
  background-color: #218838;
  border-color: #1e7e34;
  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);
}

.btn-success.disabled, .btn-success:disabled {
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
}

.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,
.show > .btn-success.dropdown-toggle {
  color: #fff;
  background-color: #1e7e34;
  border-color: #1c7430;
}

.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,
.show > .btn-success.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);
}

.btn-info {
  color: #fff;
  background-color: #17a2b8;
  border-color: #17a2b8;
}

.btn-info:hover {
  color: #fff;
  background-color: #138496;
  border-color: #117a8b;
}

.btn-info:focus, .btn-info.focus {
  color: #fff;
  background-color: #138496;
  border-color: #117a8b;
  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);
}

.btn-info.disabled, .btn-info:disabled {
  color: #fff;
  background-color: #17a2b8;
  border-color: #17a2b8;
}

.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,
.show > .btn-info.dropdown-toggle {
  color: #fff;
  background-color: #117a8b;
  border-color: #10707f;
}

.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,
.show > .btn-info.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);
}

.btn-warning {
  color: #212529;
  background-color: #ffc107;
  border-color: #ffc107;
}

.btn-warning:hover {
  color: #212529;
  background-color: #e0a800;
  border-color: #d39e00;
}

.btn-warning:focus, .btn-warning.focus {
  color: #212529;
  background-color: #e0a800;
  border-color: #d39e00;
  box-shadow: 0 0 0 0 rgba(222, 170, 12, 0.5);
}

.btn-warning.disabled, .btn-warning:disabled {
  color: #212529;
  background-color: #ffc107;
  border-color: #ffc107;
}

.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,
.show > .btn-warning.dropdown-toggle {
  color: #212529;
  background-color: #d39e00;
  border-color: #c69500;
}

.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,
.show > .btn-warning.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(222, 170, 12, 0.5);
}

.btn-danger {
  color: #fff;
  background-color: #dc3545;
  border-color: #dc3545;
}

.btn-danger:hover {
  color: #fff;
  background-color: #c82333;
  border-color: #bd2130;
}

.btn-danger:focus, .btn-danger.focus {
  color: #fff;
  background-color: #c82333;
  border-color: #bd2130;
  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);
}

.btn-danger.disabled, .btn-danger:disabled {
  color: #fff;
  background-color: #dc3545;
  border-color: #dc3545;
}

.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,
.show > .btn-danger.dropdown-toggle {
  color: #fff;
  background-color: #bd2130;
  border-color: #b21f2d;
}

.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,
.show > .btn-danger.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);
}

.btn-light {
  color: #212529;
  background-color: #f8f9fa;
  border-color: #f8f9fa;
}

.btn-light:hover {
  color: #212529;
  background-color: #e2e6ea;
  border-color: #dae0e5;
}

.btn-light:focus, .btn-light.focus {
  color: #212529;
  background-color: #e2e6ea;
  border-color: #dae0e5;
  box-shadow: 0 0 0 0 rgba(216, 217, 219, 0.5);
}

.btn-light.disabled, .btn-light:disabled {
  color: #212529;
  background-color: #f8f9fa;
  border-color: #f8f9fa;
}

.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,
.show > .btn-light.dropdown-toggle {
  color: #212529;
  background-color: #dae0e5;
  border-color: #d3d9df;
}

.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,
.show > .btn-light.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(216, 217, 219, 0.5);
}

.btn-dark {
  color: #fff;
  background-color: #343a40;
  border-color: #343a40;
}

.btn-dark:hover {
  color: #fff;
  background-color: #23272b;
  border-color: #1d2124;
}

.btn-dark:focus, .btn-dark.focus {
  color: #fff;
  background-color: #23272b;
  border-color: #1d2124;
  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);
}

.btn-dark.disabled, .btn-dark:disabled {
  color: #fff;
  background-color: #343a40;
  border-color: #343a40;
}

.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,
.show > .btn-dark.dropdown-toggle {
  color: #fff;
  background-color: #1d2124;
  border-color: #171a1d;
}

.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,
.show > .btn-dark.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);
}

.btn-outline-primary {
  color: #007bff;
  border-color: #007bff;
}

.btn-outline-primary:hover {
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.btn-outline-primary:focus, .btn-outline-primary.focus {
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}

.btn-outline-primary.disabled, .btn-outline-primary:disabled {
  color: #007bff;
  background-color: transparent;
}

.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
.show > .btn-outline-primary.dropdown-toggle {
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-primary.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}

.btn-outline-secondary {
  color: #6c757d;
  border-color: #6c757d;
}

.btn-outline-secondary:hover {
  color: #fff;
  background-color: #6c757d;
  border-color: #6c757d;
}

.btn-outline-secondary:focus, .btn-outline-secondary.focus {
  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}

.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {
  color: #6c757d;
  background-color: transparent;
}

.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,
.show > .btn-outline-secondary.dropdown-toggle {
  color: #fff;
  background-color: #6c757d;
  border-color: #6c757d;
}

.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-secondary.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}

.btn-outline-success {
  color: #28a745;
  border-color: #28a745;
}

.btn-outline-success:hover {
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
}

.btn-outline-success:focus, .btn-outline-success.focus {
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}

.btn-outline-success.disabled, .btn-outline-success:disabled {
  color: #28a745;
  background-color: transparent;
}

.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,
.show > .btn-outline-success.dropdown-toggle {
  color: #fff;
  background-color: #28a745;
  border-color: #28a745;
}

.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-success.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}

.btn-outline-info {
  color: #17a2b8;
  border-color: #17a2b8;
}

.btn-outline-info:hover {
  color: #fff;
  background-color: #17a2b8;
  border-color: #17a2b8;
}

.btn-outline-info:focus, .btn-outline-info.focus {
  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}

.btn-outline-info.disabled, .btn-outline-info:disabled {
  color: #17a2b8;
  background-color: transparent;
}

.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,
.show > .btn-outline-info.dropdown-toggle {
  color: #fff;
  background-color: #17a2b8;
  border-color: #17a2b8;
}

.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-info.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}

.btn-outline-warning {
  color: #ffc107;
  border-color: #ffc107;
}

.btn-outline-warning:hover {
  color: #212529;
  background-color: #ffc107;
  border-color: #ffc107;
}

.btn-outline-warning:focus, .btn-outline-warning.focus {
  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}

.btn-outline-warning.disabled, .btn-outline-warning:disabled {
  color: #ffc107;
  background-color: transparent;
}

.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,
.show > .btn-outline-warning.dropdown-toggle {
  color: #212529;
  background-color: #ffc107;
  border-color: #ffc107;
}

.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-warning.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}

.btn-outline-danger {
  color: #dc3545;
  border-color: #dc3545;
}

.btn-outline-danger:hover {
  color: #fff;
  background-color: #dc3545;
  border-color: #dc3545;
}

.btn-outline-danger:focus, .btn-outline-danger.focus {
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}

.btn-outline-danger.disabled, .btn-outline-danger:disabled {
  color: #dc3545;
  background-color: transparent;
}

.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,
.show > .btn-outline-danger.dropdown-toggle {
  color: #fff;
  background-color: #dc3545;
  border-color: #dc3545;
}

.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-danger.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}

.btn-outline-light {
  color: #f8f9fa;
  border-color: #f8f9fa;
}

.btn-outline-light:hover {
  color: #212529;
  background-color: #f8f9fa;
  border-color: #f8f9fa;
}

.btn-outline-light:focus, .btn-outline-light.focus {
  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}

.btn-outline-light.disabled, .btn-outline-light:disabled {
  color: #f8f9fa;
  background-color: transparent;
}

.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,
.show > .btn-outline-light.dropdown-toggle {
  color: #212529;
  background-color: #f8f9fa;
  border-color: #f8f9fa;
}

.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-light.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}

.btn-outline-dark {
  color: #343a40;
  border-color: #343a40;
}

.btn-outline-dark:hover {
  color: #fff;
  background-color: #343a40;
  border-color: #343a40;
}

.btn-outline-dark:focus, .btn-outline-dark.focus {
  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}

.btn-outline-dark.disabled, .btn-outline-dark:disabled {
  color: #343a40;
  background-color: transparent;
}

.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,
.show > .btn-outline-dark.dropdown-toggle {
  color: #fff;
  background-color: #343a40;
  border-color: #343a40;
}

.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-dark.dropdown-toggle:focus {
  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}

.btn-link {
  font-weight: 400;
  color: #007bff;
  text-decoration: none;
}

.btn-link:hover {
  color: #0056b3;
  text-decoration: underline;
}

.btn-link:focus, .btn-link.focus {
  text-decoration: underline;
}

.btn-link:disabled, .btn-link.disabled {
  color: #6c757d;
  pointer-events: none;
}

.btn-lg, .btn-group-lg > .btn {
  padding: 0.5rem 1rem;
  font-size: 1.25rem;
  line-height: 1.5;
  border-radius: 0.3rem;
}

.btn-sm, .btn-group-sm > .btn {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  line-height: 1.5;
  border-radius: 0.2rem;
}

.btn-block {
  display: block;
  width: 100%;
}

.btn-block + .btn-block {
  margin-top: 0.5rem;
}

input[type="submit"].btn-block,
input[type="reset"].btn-block,
input[type="button"].btn-block {
  width: 100%;
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  list-style: none;
  background-color: #e9ecef;
  border-radius: 0.25rem;
}

.breadcrumb-item {
  display: flex;
}

.breadcrumb-item + .breadcrumb-item {
  padding-left: 0.5rem;
}

.breadcrumb-item + .breadcrumb-item::before {
  display: inline-block;
  padding-right: 0.5rem;
  color: #6c757d;
  content: "/";
}

.breadcrumb-item + .breadcrumb-item:hover::before {
  text-decoration: underline;
}

.breadcrumb-item + .breadcrumb-item:hover::before {
  text-decoration: none;
}

.breadcrumb-item.active {
  color: #6c757d;
}

.fade {
  transition: opacity 0.15s linear;
}

@media (prefers-reduced-motion: reduce) {
  .fade {
    transition: none;
  }
}

.fade:not(.show) {
  opacity: 0;
}

.collapse:not(.show) {
  display: none;
}

.collapsing {
  position: relative;
  height: 0;
  overflow: hidden;
  transition: height 0.35s ease;
}

@media (prefers-reduced-motion: reduce) {
  .collapsing {
    transition: none;
  }
}

.dropup,
.dropright,
.dropdown,
.dropleft {
  position: relative;
}

.dropdown-toggle {
  white-space: nowrap;
}

.dropdown-toggle::after {
  display: inline-block;
  margin-left: 0.255em;
  vertical-align: 0.255em;
  content: "";
  border-top: 0.3em solid;
  border-right: 0.3em solid transparent;
  border-bottom: 0;
  border-left: 0.3em solid transparent;
}

.dropdown-toggle:empty::after {
  margin-left: 0;
}

.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1000;
  display: none;
  float: left;
  min-width: 10rem;
  padding: 0.5rem 0;
  margin: 0.125rem 0 0;
  font-size: 1rem;
  color: #212529;
  text-align: left;
  list-style: none;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 0.25rem;
}

.dropdown-menu-left {
  right: auto;
  left: 0;
}

.dropdown-menu-right {
  right: 0;
  left: auto;
}

@media (min-width: 576px) {
  .dropdown-menu-sm-left {
    right: auto;
    left: 0;
  }
  .dropdown-menu-sm-right {
    right: 0;
    left: auto;
  }
}

@media (min-width: 768px) {
  .dropdown-menu-md-left {
    right: auto;
    left: 0;
  }
  .dropdown-menu-md-right {
    right: 0;
    left: auto;
  }
}

@media (min-width: 992px) {
  .dropdown-menu-lg-left {
    right: auto;
    left: 0;
  }
  .dropdown-menu-lg-right {
    right: 0;
    left: auto;
  }
}

@media (min-width: 1200px) {
  .dropdown-menu-xl-left {
    right: auto;
    left: 0;
  }
  .dropdown-menu-xl-right {
    right: 0;
    left: auto;
  }
}

.dropup .dropdown-menu {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: 0.125rem;
}

.dropup .dropdown-toggle::after {
  display: inline-block;
  margin-left: 0.255em;
  vertical-align: 0.255em;
  content: "";
  border-top: 0;
  border-right: 0.3em solid transparent;
  border-bottom: 0.3em solid;
  border-left: 0.3em solid transparent;
}

.dropup .dropdown-toggle:empty::after {
  margin-left: 0;
}

.dropright .dropdown-menu {
  top: 0;
  right: auto;
  left: 100%;
  margin-top: 0;
  margin-left: 0.125rem;
}

.dropright .dropdown-toggle::after {
  display: inline-block;
  margin-left: 0.255em;
  vertical-align: 0.255em;
  content: "";
  border-top: 0.3em solid transparent;
  border-right: 0;
  border-bottom: 0.3em solid transparent;
  border-left: 0.3em solid;
}

.dropright .dropdown-toggle:empty::after {
  margin-left: 0;
}

.dropright .dropdown-toggle::after {
  vertical-align: 0;
}

.dropleft .dropdown-menu {
  top: 0;
  right: 100%;
  left: auto;
  margin-top: 0;
  margin-right: 0.125rem;
}

.dropleft .dropdown-toggle::after {
  display: inline-block;
  margin-left: 0.255em;
  vertical-align: 0.255em;
  content: "";
}

.dropleft .dropdown-toggle::after {
  display: none;
}

.dropleft .dropdown-toggle::before {
  display: inline-block;
  margin-right: 0.255em;
  vertical-align: 0.255em;
  content: "";
  border-top: 0.3em solid transparent;
  border-right: 0.3em solid;
  border-bottom: 0.3em solid transparent;
}

.dropleft .dropdown-toggle:empty::after {
  margin-left: 0;
}

.dropleft .dropdown-toggle::before {
  vertical-align: 0;
}

.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] {
  right: auto;
  bottom: auto;
}

.dropdown-divider {
  height: 0;
  margin: 0.46875rem 0;
  overflow: hidden;
  border-top: 1px solid #e9ecef;
}

.dropdown-item {
  display: block;
  width: 100%;
  padding: 0.25rem 1.5rem;
  clear: both;
  font-weight: 400;
  color: #212529;
  text-align: inherit;
  white-space: nowrap;
  background-color: transparent;
  border: 0;
}

.dropdown-item:hover, .dropdown-item:focus {
  color: #16181b;
  text-decoration: none;
  background-color: #f8f9fa;
}

.dropdown-item.active, .dropdown-item:active {
  color: #fff;
  text-decoration: none;
  background-color: #007bff;
}

.dropdown-item.disabled, .dropdown-item:disabled {
  color: #6c757d;
  pointer-events: none;
  background-color: transparent;
}

.dropdown-menu.show {
  display: block;
}

.dropdown-header {
  display: block;
  padding: 0.5rem 1.5rem;
  margin-bottom: 0;
  font-size: 0.875rem;
  color: #6c757d;
  white-space: nowrap;
}

.dropdown-item-text {
  display: block;
  padding: 0.25rem 1.5rem;
  color: #212529;
}

.btn-group,
.btn-group-vertical {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
}

.btn-group > .btn,
.btn-group-vertical > .btn {
  position: relative;
  flex: 1 1 auto;
}

.btn-group > .btn:hover,
.btn-group-vertical > .btn:hover {
  z-index: 1;
}

.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,
.btn-group-vertical > .btn:focus,
.btn-group-vertical > .btn:active,
.btn-group-vertical > .btn.active {
  z-index: 1;
}

.btn-toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
}

.btn-toolbar .input-group {
  width: auto;
}

.btn-group > .btn:not(:first-child),
.btn-group > .btn-group:not(:first-child) {
  margin-left: -1px;
}

.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
.btn-group > .btn-group:not(:last-child) > .btn {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.btn-group > .btn:not(:first-child),
.btn-group > .btn-group:not(:first-child) > .btn {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.dropdown-toggle-split {
  padding-right: 0.5625rem;
  padding-left: 0.5625rem;
}

.dropdown-toggle-split::after,
.dropup .dropdown-toggle-split::after,
.dropright .dropdown-toggle-split::after {
  margin-left: 0;
}

.dropleft .dropdown-toggle-split::before {
  margin-right: 0;
}

.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {
  padding-right: 0.375rem;
  padding-left: 0.375rem;
}

.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {
  padding-right: 0.75rem;
  padding-left: 0.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:not(:first-child),
.btn-group-vertical > .btn-group:not(:first-child) {
  margin-top: -1px;
}

.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),
.btn-group-vertical > .btn-group:not(:last-child) > .btn {
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}

.btn-group-vertical > .btn:not(:first-child),
.btn-group-vertical > .btn-group:not(:first-child) > .btn {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.btn-group-toggle > .btn,
.btn-group-toggle > .btn-group > .btn {
  margin-bottom: 0;
}

.btn-group-toggle > .btn input[type="radio"],
.btn-group-toggle > .btn input[type="checkbox"],
.btn-group-toggle > .btn-group > .btn input[type="radio"],
.btn-group-toggle > .btn-group > .btn input[type="checkbox"] {
  position: absolute;
  clip: rect(0, 0, 0, 0);
  pointer-events: none;
}

.input-group {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.input-group > .form-control,
.input-group > .form-control-plaintext,
.input-group > .custom-select,
.input-group > .custom-file {
  position: relative;
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
  margin-bottom: 0;
}

.input-group > .form-control + .form-control,
.input-group > .form-control + .custom-select,
.input-group > .form-control + .custom-file,
.input-group > .form-control-plaintext + .form-control,
.input-group > .form-control-plaintext + .custom-select,
.input-group > .form-control-plaintext + .custom-file,
.input-group > .custom-select + .form-control,
.input-group > .custom-select + .custom-select,
.input-group > .custom-select + .custom-file,
.input-group > .custom-file + .form-control,
.input-group > .custom-file + .custom-select,
.input-group > .custom-file + .custom-file {
  margin-left: -1px;
}

.input-group > .form-control:focus,
.input-group > .custom-select:focus,
.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {
  z-index: 3;
}

.input-group > .custom-file .custom-file-input:focus {
  z-index: 4;
}

.input-group > .form-control:not(:last-child),
.input-group > .custom-select:not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.input-group > .form-control:not(:first-child),
.input-group > .custom-select:not(:first-child) {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.input-group > .custom-file {
  display: flex;
  align-items: center;
}

.input-group > .custom-file:not(:last-child) .custom-file-label,
.input-group > .custom-file:not(:last-child) .custom-file-label::after {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.input-group > .custom-file:not(:first-child) .custom-file-label {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.input-group-prepend,
.input-group-append {
  display: flex;
}

.input-group-prepend .btn,
.input-group-append .btn {
  position: relative;
  z-index: 2;
}

.input-group-prepend .btn:focus,
.input-group-append .btn:focus {
  z-index: 3;
}

.input-group-prepend .btn + .btn,
.input-group-prepend .btn + .input-group-text,
.input-group-prepend .input-group-text + .input-group-text,
.input-group-prepend .input-group-text + .btn,
.input-group-append .btn + .btn,
.input-group-append .btn + .input-group-text,
.input-group-append .input-group-text + .input-group-text,
.input-group-append .input-group-text + .btn {
  margin-left: -1px;
}

.input-group-prepend {
  margin-right: -1px;
}

.input-group-append {
  margin-left: -1px;
}

.input-group-text {
  display: flex;
  align-items: center;
  padding: 0.75rem 0.75rem;
  margin-bottom: 0;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: #495057;
  text-align: center;
  white-space: nowrap;
  background-color: #e9ecef;
  border: 1px solid #ced4da;
  border-radius: 0.25rem;
}

.input-group-text input[type="radio"],
.input-group-text input[type="checkbox"] {
  margin-top: 0;
}

.input-group-lg > .form-control:not(textarea),
.input-group-lg > .custom-select {
  height: calc(1.5em + 1rem + 2px);
}

.input-group-lg > .form-control,
.input-group-lg > .custom-select,
.input-group-lg > .input-group-prepend > .input-group-text,
.input-group-lg > .input-group-append > .input-group-text,
.input-group-lg > .input-group-prepend > .btn,
.input-group-lg > .input-group-append > .btn {
  padding: 0.5rem 1rem;
  font-size: 1.25rem;
  line-height: 1.5;
  border-radius: 0.3rem;
}

.input-group-sm > .form-control:not(textarea),
.input-group-sm > .custom-select {
  height: calc(1.5em + 0.5rem + 2px);
}

.input-group-sm > .form-control,
.input-group-sm > .custom-select,
.input-group-sm > .input-group-prepend > .input-group-text,
.input-group-sm > .input-group-append > .input-group-text,
.input-group-sm > .input-group-prepend > .btn,
.input-group-sm > .input-group-append > .btn {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  line-height: 1.5;
  border-radius: 0.2rem;
}

.input-group-lg > .custom-select,
.input-group-sm > .custom-select {
  padding-right: 1.75rem;
}

.input-group > .input-group-prepend > .btn,
.input-group > .input-group-prepend > .input-group-text,
.input-group > .input-group-append:not(:last-child) > .btn,
.input-group > .input-group-append:not(:last-child) > .input-group-text,
.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.input-group > .input-group-append > .btn,
.input-group > .input-group-append > .input-group-text,
.input-group > .input-group-prepend:not(:first-child) > .btn,
.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.custom-control {
  position: relative;
  display: block;
  min-height: 1.5rem;
  padding-left: 1.5rem;
}

.custom-control-inline {
  display: inline-flex;
  margin-right: 1rem;
}

.custom-control-input {
  position: absolute;
  left: 0;
  z-index: -1;
  width: 1rem;
  height: 1.25rem;
  opacity: 0;
}

.custom-control-input:checked ~ .custom-control-label::before {
  color: #fff;
  border-color: #007bff;
  background-color: #007bff;
}

.custom-control-input:focus ~ .custom-control-label::before {
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {
  border-color: #80bdff;
}

.custom-control-input:not(:disabled):active ~ .custom-control-label::before {
  color: #fff;
  background-color: #b3d7ff;
  border-color: #b3d7ff;
}

.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {
  color: #6c757d;
}

.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {
  background-color: #e9ecef;
}

.custom-control-label {
  position: relative;
  margin-bottom: 0;
  vertical-align: top;
}

.custom-control-label::before {
  position: absolute;
  top: 0.25rem;
  left: -1.5rem;
  display: block;
  width: 1rem;
  height: 1rem;
  pointer-events: none;
  content: "";
  background-color: #fff;
  border: #adb5bd solid 1px;
}

.custom-control-label::after {
  position: absolute;
  top: 0.25rem;
  left: -1.5rem;
  display: block;
  width: 1rem;
  height: 1rem;
  content: "";
  background: no-repeat 50% / 50% 50%;
}

.custom-checkbox .custom-control-label::before {
  border-radius: 0.25rem;
}

.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
}

.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {
  border-color: #007bff;
  background-color: #007bff;
}

.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e");
}

.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {
  background-color: rgba(0, 123, 255, 0.5);
}

.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {
  background-color: rgba(0, 123, 255, 0.5);
}

.custom-radio .custom-control-label::before {
  border-radius: 50%;
}

.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}

.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {
  background-color: rgba(0, 123, 255, 0.5);
}

.custom-switch {
  padding-left: 2.25rem;
}

.custom-switch .custom-control-label::before {
  left: -2.25rem;
  width: 1.75rem;
  pointer-events: all;
  border-radius: 0.5rem;
}

.custom-switch .custom-control-label::after {
  top: calc(0.25rem + 2px);
  left: calc(-2.25rem + 2px);
  width: calc(1rem - 4px);
  height: calc(1rem - 4px);
  background-color: #adb5bd;
  border-radius: 0.5rem;
  transition: transform 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;
}

@media (prefers-reduced-motion: reduce) {
  .custom-switch .custom-control-label::after {
    transition: none;
  }
}

.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
  background-color: #fff;
  transform: translateX(0.75rem);
}

.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {
  background-color: rgba(0, 123, 255, 0.5);
}

.custom-select {
  display: inline-block;
  width: 100%;
  height: calc(1.5em + 1.5rem + 2px);
  padding: 0.75rem 1.75rem 0.75rem 0.75rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: #495057;
  vertical-align: middle;
  background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;
  border: 1px solid #ced4da;
  border-radius: 0.25rem;
  appearance: none;
}

.custom-select:focus {
  border-color: #80bdff;
  outline: 0;
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-select:focus::-ms-value {
  color: #495057;
  background-color: #fff;
}

.custom-select[multiple], .custom-select[size]:not([size="1"]) {
  height: auto;
  padding-right: 0.75rem;
  background-image: none;
}

.custom-select:disabled {
  color: #6c757d;
  background-color: #e9ecef;
}

.custom-select::-ms-expand {
  display: none;
}

.custom-select:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 #495057;
}

.custom-select-sm {
  height: calc(1.5em + 0.5rem + 2px);
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  padding-left: 0.5rem;
  font-size: 0.875rem;
}

.custom-select-lg {
  height: calc(1.5em + 1rem + 2px);
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  padding-left: 1rem;
  font-size: 1.25rem;
}

.custom-file {
  position: relative;
  display: inline-block;
  width: 100%;
  height: calc(1.5em + 1.5rem + 2px);
  margin-bottom: 0;
}

.custom-file-input {
  position: relative;
  z-index: 2;
  width: 100%;
  height: calc(1.5em + 1.5rem + 2px);
  margin: 0;
  opacity: 0;
}

.custom-file-input:focus ~ .custom-file-label {
  border-color: #80bdff;
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-file-input[disabled] ~ .custom-file-label,
.custom-file-input:disabled ~ .custom-file-label {
  background-color: #e9ecef;
}

.custom-file-input:lang(en) ~ .custom-file-label::after {
  content: "Browse";
}

.custom-file-input ~ .custom-file-label[data-browse]::after {
  content: attr(data-browse);
}

.custom-file-label {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 1;
  height: calc(1.5em + 1.5rem + 2px);
  padding: 0.75rem 0.75rem;
  font-weight: 400;
  line-height: 1.5;
  color: #495057;
  background-color: #fff;
  border: 1px solid #ced4da;
  border-radius: 0.25rem;
}

.custom-file-label::after {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: block;
  height: calc(1.5em + 1.5rem);
  padding: 0.75rem 0.75rem;
  line-height: 1.5;
  color: #495057;
  content: "Browse";
  background-color: #e9ecef;
  border-left: inherit;
  border-radius: 0 0.25rem 0.25rem 0;
}

.custom-range {
  width: 100%;
  height: 1rem;
  padding: 0;
  background-color: transparent;
  appearance: none;
}

.custom-range:focus {
  outline: none;
}

.custom-range:focus::-webkit-slider-thumb {
  box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-range:focus::-moz-range-thumb {
  box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-range:focus::-ms-thumb {
  box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}

.custom-range::-moz-focus-outer {
  border: 0;
}

.custom-range::-webkit-slider-thumb {
  width: 1rem;
  height: 1rem;
  margin-top: -0.25rem;
  background-color: #007bff;
  border: 0;
  border-radius: 1rem;
  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  appearance: none;
}

@media (prefers-reduced-motion: reduce) {
  .custom-range::-webkit-slider-thumb {
    transition: none;
  }
}

.custom-range::-webkit-slider-thumb:active {
  background-color: #b3d7ff;
}

.custom-range::-webkit-slider-runnable-track {
  width: 100%;
  height: 0.5rem;
  color: transparent;
  cursor: pointer;
  background-color: #dee2e6;
  border-color: transparent;
  border-radius: 1rem;
}

.custom-range::-moz-range-thumb {
  width: 1rem;
  height: 1rem;
  background-color: #007bff;
  border: 0;
  border-radius: 1rem;
  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  appearance: none;
}

@media (prefers-reduced-motion: reduce) {
  .custom-range::-moz-range-thumb {
    transition: none;
  }
}

.custom-range::-moz-range-thumb:active {
  background-color: #b3d7ff;
}

.custom-range::-moz-range-track {
  width: 100%;
  height: 0.5rem;
  color: transparent;
  cursor: pointer;
  background-color: #dee2e6;
  border-color: transparent;
  border-radius: 1rem;
}

.custom-range::-ms-thumb {
  width: 1rem;
  height: 1rem;
  margin-top: 0;
  margin-right: 0;
  margin-left: 0;
  background-color: #007bff;
  border: 0;
  border-radius: 1rem;
  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  appearance: none;
}

@media (prefers-reduced-motion: reduce) {
  .custom-range::-ms-thumb {
    transition: none;
  }
}

.custom-range::-ms-thumb:active {
  background-color: #b3d7ff;
}

.custom-range::-ms-track {
  width: 100%;
  height: 0.5rem;
  color: transparent;
  cursor: pointer;
  background-color: transparent;
  border-color: transparent;
  border-width: 0.5rem;
}

.custom-range::-ms-fill-lower {
  background-color: #dee2e6;
  border-radius: 1rem;
}

.custom-range::-ms-fill-upper {
  margin-right: 15px;
  background-color: #dee2e6;
  border-radius: 1rem;
}

.custom-range:disabled::-webkit-slider-thumb {
  background-color: #adb5bd;
}

.custom-range:disabled::-webkit-slider-runnable-track {
  cursor: default;
}

.custom-range:disabled::-moz-range-thumb {
  background-color: #adb5bd;
}

.custom-range:disabled::-moz-range-track {
  cursor: default;
}

.custom-range:disabled::-ms-thumb {
  background-color: #adb5bd;
}

.custom-control-label::before,
.custom-file-label,
.custom-select {
  transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
  .custom-control-label::before,
  .custom-file-label,
  .custom-select {
    transition: none;
  }
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;
  background-color: #fff;
  background-clip: border-box;
  border: 1px solid rgba(0, 0, 0, 0.125);
  border-radius: 0.25rem;
}

.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: calc(0.25rem - 1px);
  border-top-right-radius: calc(0.25rem - 1px);
}

.card > .list-group:last-child {
  border-bottom-width: 0;
  border-bottom-right-radius: calc(0.25rem - 1px);
  border-bottom-left-radius: calc(0.25rem - 1px);
}

.card-body {
  flex: 1 1 auto;
  min-height: 1px;
  padding: 1.25rem;
}

.card-title {
  margin-bottom: 0.75rem;
}

.card-subtitle {
  margin-top: -0.375rem;
  margin-bottom: 0;
}

.card-text:last-child {
  margin-bottom: 0;
}

.card-link:hover {
  text-decoration: none;
}

.card-link + .card-link {
  margin-left: 1.25rem;
}

.card-header {
  padding: 0.75rem 1.25rem;
  margin-bottom: 0;
  background-color: rgba(0, 0, 0, 0.03);
  border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}

.card-header:first-child {
  border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;
}

.card-header + .list-group .list-group-item:first-child {
  border-top: 0;
}

.card-footer {
  padding: 0.75rem 1.25rem;
  background-color: rgba(0, 0, 0, 0.03);
  border-top: 1px solid rgba(0, 0, 0, 0.125);
}

.card-footer:last-child {
  border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);
}

.card-header-tabs {
  margin-right: -0.625rem;
  margin-bottom: -0.75rem;
  margin-left: -0.625rem;
  border-bottom: 0;
}

.card-header-pills {
  margin-right: -0.625rem;
  margin-left: -0.625rem;
}

.card-img-overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 1.25rem;
}

.card-img,
.card-img-top,
.card-img-bottom {
  flex-shrink: 0;
  width: 100%;
}

.card-img,
.card-img-top {
  border-top-left-radius: calc(0.25rem - 1px);
  border-top-right-radius: calc(0.25rem - 1px);
}

.card-img,
.card-img-bottom {
  border-bottom-right-radius: calc(0.25rem - 1px);
  border-bottom-left-radius: calc(0.25rem - 1px);
}

.card-deck .card {
  margin-bottom: 15px;
}

@media (min-width: 576px) {
  .card-deck {
    display: flex;
    flex-flow: row wrap;
    margin-right: -15px;
    margin-left: -15px;
  }
  .card-deck .card {
    flex: 1 0 0%;
    margin-right: 15px;
    margin-bottom: 0;
    margin-left: 15px;
  }
}

.card-group > .card {
  margin-bottom: 15px;
}

@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-img-top,
  .card-group > .card:not(:last-child) .card-header {
    border-top-right-radius: 0;
  }
  .card-group > .card:not(:last-child) .card-img-bottom,
  .card-group > .card:not(:last-child) .card-footer {
    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-img-top,
  .card-group > .card:not(:first-child) .card-header {
    border-top-left-radius: 0;
  }
  .card-group > .card:not(:first-child) .card-img-bottom,
  .card-group > .card:not(:first-child) .card-footer {
    border-bottom-left-radius: 0;
  }
}

.card-columns .card {
  margin-bottom: 0.75rem;
}

@media (min-width: 576px) {
  .card-columns {
    column-count: 3;
    column-gap: 1.25rem;
    orphans: 1;
    widows: 1;
  }
  .card-columns .card {
    display: inline-block;
    width: 100%;
  }
}

.accordion > .card {
  overflow: hidden;
}

.accordion > .card:not(:last-of-type) {
  border-bottom: 0;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}

.accordion > .card:not(:first-of-type) {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.accordion > .card > .card-header {
  border-radius: 0;
  margin-bottom: -1px;
}

.pagination {
  display: flex;
  padding-left: 0;
  list-style: none;
  border-radius: 0.25rem;
}

.page-link {
  position: relative;
  display: block;
  padding: 0.5rem 0.75rem;
  margin-left: -1px;
  line-height: 1.25;
  color: #007bff;
  background-color: #fff;
  border: 1px solid #dee2e6;
}

.page-link:hover {
  z-index: 2;
  color: #0056b3;
  text-decoration: none;
  background-color: #e9ecef;
  border-color: #dee2e6;
}

.page-link:focus {
  z-index: 3;
  outline: 0;
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}

.page-item:first-child .page-link {
  margin-left: 0;
  border-top-left-radius: 0.25rem;
  border-bottom-left-radius: 0.25rem;
}

.page-item:last-child .page-link {
  border-top-right-radius: 0.25rem;
  border-bottom-right-radius: 0.25rem;
}

.page-item.active .page-link {
  z-index: 3;
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.page-item.disabled .page-link {
  color: #6c757d;
  pointer-events: none;
  cursor: auto;
  background-color: #fff;
  border-color: #dee2e6;
}

.pagination-lg .page-link {
  padding: 0.75rem 1.5rem;
  font-size: 1.25rem;
  line-height: 1.5;
}

.pagination-lg .page-item:first-child .page-link {
  border-top-left-radius: 0.3rem;
  border-bottom-left-radius: 0.3rem;
}

.pagination-lg .page-item:last-child .page-link {
  border-top-right-radius: 0.3rem;
  border-bottom-right-radius: 0.3rem;
}

.pagination-sm .page-link {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  line-height: 1.5;
}

.pagination-sm .page-item:first-child .page-link {
  border-top-left-radius: 0.2rem;
  border-bottom-left-radius: 0.2rem;
}

.pagination-sm .page-item:last-child .page-link {
  border-top-right-radius: 0.2rem;
  border-bottom-right-radius: 0.2rem;
}

.badge {
  display: inline-block;
  padding: 0.25em 0.4em;
  font-size: 75%;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
  vertical-align: baseline;
  border-radius: 0.25rem;
  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;
}

@media (prefers-reduced-motion: reduce) {
  .badge {
    transition: none;
  }
}

a.badge:hover, a.badge:focus {
  text-decoration: none;
}

.badge:empty {
  display: none;
}

.btn .badge {
  position: relative;
  top: -1px;
}

.badge-pill {
  padding-right: 0.6em;
  padding-left: 0.6em;
  border-radius: 10rem;
}

.badge-primary {
  color: #fff;
  background-color: #007bff;
}

a.badge-primary:hover, a.badge-primary:focus {
  color: #fff;
  background-color: #0062cc;
}

a.badge-primary:focus, a.badge-primary.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}

.badge-secondary {
  color: #fff;
  background-color: #6c757d;
}

a.badge-secondary:hover, a.badge-secondary:focus {
  color: #fff;
  background-color: #545b62;
}

a.badge-secondary:focus, a.badge-secondary.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}

.badge-success {
  color: #fff;
  background-color: #28a745;
}

a.badge-success:hover, a.badge-success:focus {
  color: #fff;
  background-color: #1e7e34;
}

a.badge-success:focus, a.badge-success.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}

.badge-info {
  color: #fff;
  background-color: #17a2b8;
}

a.badge-info:hover, a.badge-info:focus {
  color: #fff;
  background-color: #117a8b;
}

a.badge-info:focus, a.badge-info.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}

.badge-warning {
  color: #212529;
  background-color: #ffc107;
}

a.badge-warning:hover, a.badge-warning:focus {
  color: #212529;
  background-color: #d39e00;
}

a.badge-warning:focus, a.badge-warning.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}

.badge-danger {
  color: #fff;
  background-color: #dc3545;
}

a.badge-danger:hover, a.badge-danger:focus {
  color: #fff;
  background-color: #bd2130;
}

a.badge-danger:focus, a.badge-danger.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}

.badge-light {
  color: #212529;
  background-color: #f8f9fa;
}

a.badge-light:hover, a.badge-light:focus {
  color: #212529;
  background-color: #dae0e5;
}

a.badge-light:focus, a.badge-light.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}

.badge-dark {
  color: #fff;
  background-color: #343a40;
}

a.badge-dark:hover, a.badge-dark:focus {
  color: #fff;
  background-color: #1d2124;
}

a.badge-dark:focus, a.badge-dark.focus {
  outline: 0;
  box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}

.tooltip {
  position: absolute;
  z-index: 1070;
  display: block;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  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;
  word-spacing: normal;
  white-space: normal;
  line-break: auto;
  font-size: 0.875rem;
  word-wrap: break-word;
  opacity: 0;
}

.tooltip.show {
  opacity: 0.9;
}

.tooltip .arrow {
  position: absolute;
  display: block;
  width: 0.8rem;
  height: 0.4rem;
}

.tooltip .arrow::before {
  position: absolute;
  content: "";
  border-color: transparent;
  border-style: solid;
}

.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] {
  padding: 0.4rem 0;
}

.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow {
  bottom: 0;
}

.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before {
  top: 0;
  border-width: 0.4rem 0.4rem 0;
  border-top-color: #000;
}

.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] {
  padding: 0 0.4rem;
}

.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow {
  left: 0;
  width: 0.4rem;
  height: 0.8rem;
}

.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before {
  right: 0;
  border-width: 0.4rem 0.4rem 0.4rem 0;
  border-right-color: #000;
}

.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] {
  padding: 0.4rem 0;
}

.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow {
  top: 0;
}

.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before {
  bottom: 0;
  border-width: 0 0.4rem 0.4rem;
  border-bottom-color: #000;
}

.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] {
  padding: 0 0.4rem;
}

.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow {
  right: 0;
  width: 0.4rem;
  height: 0.8rem;
}

.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before {
  left: 0;
  border-width: 0.4rem 0 0.4rem 0.4rem;
  border-left-color: #000;
}

.tooltip-inner {
  max-width: 200px;
  padding: 0.25rem 0.5rem;
  color: #fff;
  text-align: center;
  background-color: #000;
  border-radius: 0.25rem;
}

.alert {
  position: relative;
  padding: 0.75rem 1.25rem;
  margin-bottom: 1rem;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

.alert-heading {
  color: inherit;
}

.alert-link {
  font-weight: 700;
}

.alert-dismissible {
  padding-right: 4rem;
}

.alert-dismissible .close {
  position: absolute;
  top: 0;
  right: 0;
  padding: 0.75rem 1.25rem;
  color: inherit;
}

.alert-primary {
  color: #004085;
  background-color: #cce5ff;
  border-color: #b8daff;
}

.alert-primary hr {
  border-top-color: #9fcdff;
}

.alert-primary .alert-link {
  color: #002752;
}

.alert-secondary {
  color: #383d41;
  background-color: #e2e3e5;
  border-color: #d6d8db;
}

.alert-secondary hr {
  border-top-color: #c8cbcf;
}

.alert-secondary .alert-link {
  color: #202326;
}

.alert-success {
  color: #155724;
  background-color: #d4edda;
  border-color: #c3e6cb;
}

.alert-success hr {
  border-top-color: #b1dfbb;
}

.alert-success .alert-link {
  color: #0b2e13;
}

.alert-info {
  color: #0c5460;
  background-color: #d1ecf1;
  border-color: #bee5eb;
}

.alert-info hr {
  border-top-color: #abdde5;
}

.alert-info .alert-link {
  color: #062c33;
}

.alert-warning {
  color: #856404;
  background-color: #fff3cd;
  border-color: #ffeeba;
}

.alert-warning hr {
  border-top-color: #ffe8a1;
}

.alert-warning .alert-link {
  color: #533f03;
}

.alert-danger {
  color: #721c24;
  background-color: #f8d7da;
  border-color: #f5c6cb;
}

.alert-danger hr {
  border-top-color: #f1b0b7;
}

.alert-danger .alert-link {
  color: #491217;
}

.alert-light {
  color: #818182;
  background-color: #fefefe;
  border-color: #fdfdfe;
}

.alert-light hr {
  border-top-color: #ececf6;
}

.alert-light .alert-link {
  color: #686868;
}

.alert-dark {
  color: #1b1e21;
  background-color: #d6d8d9;
  border-color: #c6c8ca;
}

.alert-dark hr {
  border-top-color: #b9bbbe;
}

.alert-dark .alert-link {
  color: #040505;
}

@keyframes progress-bar-stripes {
  from {
    background-position: 1rem 0;
  }
  to {
    background-position: 0 0;
  }
}

.progress {
  display: flex;
  height: 1rem;
  overflow: hidden;
  line-height: 0;
  font-size: 0.75rem;
  background-color: #e9ecef;
  border-radius: 0.25rem;
}

.progress-bar {
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  color: #fff;
  text-align: center;
  white-space: nowrap;
  background-color: #007bff;
  transition: width 0.6s ease;
}

@media (prefers-reduced-motion: reduce) {
  .progress-bar {
    transition: none;
  }
}

.progress-bar-striped {
  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
  background-size: 1rem 1rem;
}

.progress-bar-animated {
  animation: progress-bar-stripes 1s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .progress-bar-animated {
    animation: none;
  }
}

.media {
  display: flex;
  align-items: flex-start;
}

.media-body {
  flex: 1;
}

.list-group {
  display: flex;
  flex-direction: column;
  padding-left: 0;
  margin-bottom: 0;
  border-radius: 0.25rem;
}

.list-group-item-action {
  width: 100%;
  color: #495057;
  text-align: inherit;
}

.list-group-item-action:hover, .list-group-item-action:focus {
  z-index: 1;
  color: #495057;
  text-decoration: none;
  background-color: #f8f9fa;
}

.list-group-item-action:active {
  color: #212529;
  background-color: #e9ecef;
}

.list-group-item {
  position: relative;
  display: block;
  padding: 0.75rem 1.25rem;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, 0.125);
}

.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: #6c757d;
  pointer-events: none;
  background-color: #fff;
}

.list-group-item.active {
  z-index: 2;
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

.list-group-item + .list-group-item {
  border-top-width: 0;
}

.list-group-item + .list-group-item.active {
  margin-top: -1px;
  border-top-width: 1px;
}

.list-group-horizontal {
  flex-direction: row;
}

.list-group-horizontal > .list-group-item:first-child {
  border-bottom-left-radius: 0.25rem;
  border-top-right-radius: 0;
}

.list-group-horizontal > .list-group-item:last-child {
  border-top-right-radius: 0.25rem;
  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: 1px;
  border-left-width: 0;
}

.list-group-horizontal > .list-group-item + .list-group-item.active {
  margin-left: -1px;
  border-left-width: 1px;
}

@media (min-width: 576px) {
  .list-group-horizontal-sm {
    flex-direction: row;
  }
  .list-group-horizontal-sm > .list-group-item:first-child {
    border-bottom-left-radius: 0.25rem;
    border-top-right-radius: 0;
  }
  .list-group-horizontal-sm > .list-group-item:last-child {
    border-top-right-radius: 0.25rem;
    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: 1px;
    border-left-width: 0;
  }
  .list-group-horizontal-sm > .list-group-item + .list-group-item.active {
    margin-left: -1px;
    border-left-width: 1px;
  }
}

@media (min-width: 768px) {
  .list-group-horizontal-md {
    flex-direction: row;
  }
  .list-group-horizontal-md > .list-group-item:first-child {
    border-bottom-left-radius: 0.25rem;
    border-top-right-radius: 0;
  }
  .list-group-horizontal-md > .list-group-item:last-child {
    border-top-right-radius: 0.25rem;
    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: 1px;
    border-left-width: 0;
  }
  .list-group-horizontal-md > .list-group-item + .list-group-item.active {
    margin-left: -1px;
    border-left-width: 1px;
  }
}

@media (min-width: 992px) {
  .list-group-horizontal-lg {
    flex-direction: row;
  }
  .list-group-horizontal-lg > .list-group-item:first-child {
    border-bottom-left-radius: 0.25rem;
    border-top-right-radius: 0;
  }
  .list-group-horizontal-lg > .list-group-item:last-child {
    border-top-right-radius: 0.25rem;
    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: 1px;
    border-left-width: 0;
  }
  .list-group-horizontal-lg > .list-group-item + .list-group-item.active {
    margin-left: -1px;
    border-left-width: 1px;
  }
}

@media (min-width: 1200px) {
  .list-group-horizontal-xl {
    flex-direction: row;
  }
  .list-group-horizontal-xl > .list-group-item:first-child {
    border-bottom-left-radius: 0.25rem;
    border-top-right-radius: 0;
  }
  .list-group-horizontal-xl > .list-group-item:last-child {
    border-top-right-radius: 0.25rem;
    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: 1px;
    border-left-width: 0;
  }
  .list-group-horizontal-xl > .list-group-item + .list-group-item.active {
    margin-left: -1px;
    border-left-width: 1px;
  }
}

.list-group-flush {
  border-radius: 0;
}

.list-group-flush > .list-group-item {
  border-width: 0 0 1px;
}

.list-group-flush > .list-group-item:last-child {
  border-bottom-width: 0;
}

.list-group-item-primary {
  color: #004085;
  background-color: #b8daff;
}

.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {
  color: #004085;
  background-color: #9fcdff;
}

.list-group-item-primary.list-group-item-action.active {
  color: #fff;
  background-color: #004085;
  border-color: #004085;
}

.list-group-item-secondary {
  color: #383d41;
  background-color: #d6d8db;
}

.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {
  color: #383d41;
  background-color: #c8cbcf;
}

.list-group-item-secondary.list-group-item-action.active {
  color: #fff;
  background-color: #383d41;
  border-color: #383d41;
}

.list-group-item-success {
  color: #155724;
  background-color: #c3e6cb;
}

.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {
  color: #155724;
  background-color: #b1dfbb;
}

.list-group-item-success.list-group-item-action.active {
  color: #fff;
  background-color: #155724;
  border-color: #155724;
}

.list-group-item-info {
  color: #0c5460;
  background-color: #bee5eb;
}

.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {
  color: #0c5460;
  background-color: #abdde5;
}

.list-group-item-info.list-group-item-action.active {
  color: #fff;
  background-color: #0c5460;
  border-color: #0c5460;
}

.list-group-item-warning {
  color: #856404;
  background-color: #ffeeba;
}

.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {
  color: #856404;
  background-color: #ffe8a1;
}

.list-group-item-warning.list-group-item-action.active {
  color: #fff;
  background-color: #856404;
  border-color: #856404;
}

.list-group-item-danger {
  color: #721c24;
  background-color: #f5c6cb;
}

.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {
  color: #721c24;
  background-color: #f1b0b7;
}

.list-group-item-danger.list-group-item-action.active {
  color: #fff;
  background-color: #721c24;
  border-color: #721c24;
}

.list-group-item-light {
  color: #818182;
  background-color: #fdfdfe;
}

.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {
  color: #818182;
  background-color: #ececf6;
}

.list-group-item-light.list-group-item-action.active {
  color: #fff;
  background-color: #818182;
  border-color: #818182;
}

.list-group-item-dark {
  color: #1b1e21;
  background-color: #c6c8ca;
}

.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {
  color: #1b1e21;
  background-color: #b9bbbe;
}

.list-group-item-dark.list-group-item-action.active {
  color: #fff;
  background-color: #1b1e21;
  border-color: #1b1e21;
}

.close {
  float: right;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: #000;
  text-shadow: 0 1px 0 #fff;
  opacity: .5;
}

.close:hover {
  color: #000;
  text-decoration: none;
}

.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {
  opacity: .75;
}

button.close {
  padding: 0;
  background-color: transparent;
  border: 0;
}

a.close.disabled {
  pointer-events: none;
}

.toast {
  max-width: 350px;
  overflow: hidden;
  font-size: 0.875rem;
  background-color: rgba(255, 255, 255, 0.85);
  background-clip: padding-box;
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(10px);
  opacity: 0;
  border-radius: 0.25rem;
}

.toast:not(:last-child) {
  margin-bottom: 0.75rem;
}

.toast.showing {
  opacity: 1;
}

.toast.show {
  display: block;
  opacity: 1;
}

.toast.hide {
  display: none;
}

.toast-header {
  display: flex;
  align-items: center;
  padding: 0.25rem 0.75rem;
  color: #6c757d;
  background-color: rgba(255, 255, 255, 0.85);
  background-clip: padding-box;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.toast-body {
  padding: 0.75rem;
}

.modal-open {
  overflow: hidden;
}

.modal-open .modal {
  overflow-x: hidden;
  overflow-y: auto;
}

.modal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1050;
  display: none;
  width: 100%;
  height: 100%;
  overflow: hidden;
  outline: 0;
}

.modal-dialog {
  position: relative;
  width: auto;
  margin: 0.5rem;
  pointer-events: none;
}

.modal.fade .modal-dialog {
  transition: transform 0.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 {
  display: flex;
  max-height: calc(100% - 1rem);
}

.modal-dialog-scrollable .modal-content {
  max-height: calc(100vh - 1rem);
  overflow: hidden;
}

.modal-dialog-scrollable .modal-header,
.modal-dialog-scrollable .modal-footer {
  flex-shrink: 0;
}

.modal-dialog-scrollable .modal-body {
  overflow-y: auto;
}

.modal-dialog-centered {
  display: flex;
  align-items: center;
  min-height: calc(100% - 1rem);
}

.modal-dialog-centered::before {
  display: block;
  height: calc(100vh - 1rem);
  height: min-content;
  content: "";
}

.modal-dialog-centered.modal-dialog-scrollable {
  flex-direction: column;
  justify-content: center;
  height: 100%;
}

.modal-dialog-centered.modal-dialog-scrollable .modal-content {
  max-height: none;
}

.modal-dialog-centered.modal-dialog-scrollable::before {
  content: none;
}

.modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  pointer-events: auto;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 0.3rem;
  outline: 0;
}

.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1040;
  width: 100vw;
  height: 100vh;
  background-color: #000;
}

.modal-backdrop.fade {
  opacity: 0;
}

.modal-backdrop.show {
  opacity: 0.5;
}

.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1rem 1rem;
  border-bottom: 1px solid #dee2e6;
  border-top-left-radius: calc(0.3rem - 1px);
  border-top-right-radius: calc(0.3rem - 1px);
}

.modal-header .close {
  padding: 1rem 1rem;
  margin: -1rem -1rem -1rem auto;
}

.modal-title {
  margin-bottom: 0;
  line-height: 1.5;
}

.modal-body {
  position: relative;
  flex: 1 1 auto;
  padding: 1rem;
}

.modal-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  padding: 0.75rem;
  border-top: 1px solid #dee2e6;
  border-bottom-right-radius: calc(0.3rem - 1px);
  border-bottom-left-radius: calc(0.3rem - 1px);
}

.modal-footer > * {
  margin: 0.25rem;
}

.modal-scrollbar-measure {
  position: absolute;
  top: -9999px;
  width: 50px;
  height: 50px;
  overflow: scroll;
}

@media (min-width: 576px) {
  .modal-dialog {
    max-width: 500px;
    margin: 1.75rem auto;
  }
  .modal-dialog-scrollable {
    max-height: calc(100% - 3.5rem);
  }
  .modal-dialog-scrollable .modal-content {
    max-height: calc(100vh - 3.5rem);
  }
  .modal-dialog-centered {
    min-height: calc(100% - 3.5rem);
  }
  .modal-dialog-centered::before {
    height: calc(100vh - 3.5rem);
    height: min-content;
  }
  .modal-sm {
    max-width: 300px;
  }
}

@media (min-width: 992px) {
  .modal-lg,
  .modal-xl {
    max-width: 800px;
  }
}

@media (min-width: 1200px) {
  .modal-xl {
    max-width: 1140px;
  }
}

@keyframes spinner-border {
  to {
    transform: rotate(360deg);
  }
}

.spinner-border {
  display: inline-block;
  width: 2rem;
  height: 2rem;
  vertical-align: text-bottom;
  border: 0.25em solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spinner-border .75s linear infinite;
}

.spinner-border-sm {
  width: 1rem;
  height: 1rem;
  border-width: 0.2em;
}

@keyframes spinner-grow {
  0% {
    transform: scale(0);
  }
  50% {
    opacity: 1;
    transform: none;
  }
}

.spinner-grow {
  display: inline-block;
  width: 2rem;
  height: 2rem;
  vertical-align: text-bottom;
  background-color: currentColor;
  border-radius: 50%;
  opacity: 0;
  animation: spinner-grow .75s linear infinite;
}

.spinner-grow-sm {
  width: 1rem;
  height: 1rem;
}

.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;
}

.bg-primary {
  background-color: #007bff !important;
}

a.bg-primary:hover, a.bg-primary:focus,
button.bg-primary:hover,
button.bg-primary:focus {
  background-color: #0062cc !important;
}

.bg-secondary {
  background-color: #6c757d !important;
}

a.bg-secondary:hover, a.bg-secondary:focus,
button.bg-secondary:hover,
button.bg-secondary:focus {
  background-color: #545b62 !important;
}

.bg-success {
  background-color: #28a745 !important;
}

a.bg-success:hover, a.bg-success:focus,
button.bg-success:hover,
button.bg-success:focus {
  background-color: #1e7e34 !important;
}

.bg-info {
  background-color: #17a2b8 !important;
}

a.bg-info:hover, a.bg-info:focus,
button.bg-info:hover,
button.bg-info:focus {
  background-color: #117a8b !important;
}

.bg-warning {
  background-color: #ffc107 !important;
}

a.bg-warning:hover, a.bg-warning:focus,
button.bg-warning:hover,
button.bg-warning:focus {
  background-color: #d39e00 !important;
}

.bg-danger {
  background-color: #dc3545 !important;
}

a.bg-danger:hover, a.bg-danger:focus,
button.bg-danger:hover,
button.bg-danger:focus {
  background-color: #bd2130 !important;
}

.bg-light {
  background-color: #f8f9fa !important;
}

a.bg-light:hover, a.bg-light:focus,
button.bg-light:hover,
button.bg-light:focus {
  background-color: #dae0e5 !important;
}

.bg-dark {
  background-color: #343a40 !important;
}

a.bg-dark:hover, a.bg-dark:focus,
button.bg-dark:hover,
button.bg-dark:focus {
  background-color: #1d2124 !important;
}

.bg-white {
  background-color: #fff !important;
}

.bg-transparent {
  background-color: transparent !important;
}

.border {
  border: 1px solid #dee2e6 !important;
}

.border-top {
  border-top: 1px solid #dee2e6 !important;
}

.border-right {
  border-right: 1px solid #dee2e6 !important;
}

.border-bottom {
  border-bottom: 1px solid #dee2e6 !important;
}

.border-left {
  border-left: 1px solid #dee2e6 !important;
}

.border-0 {
  border: 0 !important;
}

.border-top-0 {
  border-top: 0 !important;
}

.border-right-0 {
  border-right: 0 !important;
}

.border-bottom-0 {
  border-bottom: 0 !important;
}

.border-left-0 {
  border-left: 0 !important;
}

.border-primary {
  border-color: #007bff !important;
}

.border-secondary {
  border-color: #6c757d !important;
}

.border-success {
  border-color: #28a745 !important;
}

.border-info {
  border-color: #17a2b8 !important;
}

.border-warning {
  border-color: #ffc107 !important;
}

.border-danger {
  border-color: #dc3545 !important;
}

.border-light {
  border-color: #f8f9fa !important;
}

.border-dark {
  border-color: #343a40 !important;
}

.border-white {
  border-color: #fff !important;
}

.rounded-sm {
  border-radius: 0.2rem !important;
}

.rounded {
  border-radius: 0.25rem !important;
}

.rounded-top {
  border-top-left-radius: 0.25rem !important;
  border-top-right-radius: 0.25rem !important;
}

.rounded-right {
  border-top-right-radius: 0.25rem !important;
  border-bottom-right-radius: 0.25rem !important;
}

.rounded-bottom {
  border-bottom-right-radius: 0.25rem !important;
  border-bottom-left-radius: 0.25rem !important;
}

.rounded-left {
  border-top-left-radius: 0.25rem !important;
  border-bottom-left-radius: 0.25rem !important;
}

.rounded-lg {
  border-radius: 0.3rem !important;
}

.rounded-circle {
  border-radius: 50% !important;
}

.rounded-pill {
  border-radius: 50rem !important;
}

.rounded-0 {
  border-radius: 0 !important;
}

.clearfix::after {
  display: block;
  clear: both;
  content: "";
}

.d-none {
  display: none !important;
}

.d-inline {
  display: inline !important;
}

.d-inline-block {
  display: inline-block !important;
}

.d-block {
  display: block !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;
}

@media (min-width: 576px) {
  .d-sm-none {
    display: none !important;
  }
  .d-sm-inline {
    display: inline !important;
  }
  .d-sm-inline-block {
    display: inline-block !important;
  }
  .d-sm-block {
    display: block !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;
  }
}

@media (min-width: 768px) {
  .d-md-none {
    display: none !important;
  }
  .d-md-inline {
    display: inline !important;
  }
  .d-md-inline-block {
    display: inline-block !important;
  }
  .d-md-block {
    display: block !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;
  }
}

@media (min-width: 992px) {
  .d-lg-none {
    display: none !important;
  }
  .d-lg-inline {
    display: inline !important;
  }
  .d-lg-inline-block {
    display: inline-block !important;
  }
  .d-lg-block {
    display: block !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;
  }
}

@media (min-width: 1200px) {
  .d-xl-none {
    display: none !important;
  }
  .d-xl-inline {
    display: inline !important;
  }
  .d-xl-inline-block {
    display: inline-block !important;
  }
  .d-xl-block {
    display: block !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;
  }
}

@media print {
  .d-print-none {
    display: none !important;
  }
  .d-print-inline {
    display: inline !important;
  }
  .d-print-inline-block {
    display: inline-block !important;
  }
  .d-print-block {
    display: block !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;
  }
}

.embed-responsive {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
}

.embed-responsive::before {
  display: block;
  content: "";
}

.embed-responsive .embed-responsive-item,
.embed-responsive iframe,
.embed-responsive embed,
.embed-responsive object,
.embed-responsive video {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.embed-responsive-21by9::before {
  padding-top: 42.85714%;
}

.embed-responsive-16by9::before {
  padding-top: 56.25%;
}

.embed-responsive-4by3::before {
  padding-top: 75%;
}

.embed-responsive-1by1::before {
  padding-top: 100%;
}

.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-wrap {
  flex-wrap: wrap !important;
}

.flex-nowrap {
  flex-wrap: nowrap !important;
}

.flex-wrap-reverse {
  flex-wrap: wrap-reverse !important;
}

.flex-fill {
  flex: 1 1 auto !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;
}

.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;
}

.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;
}

@media (min-width: 576px) {
  .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-wrap {
    flex-wrap: wrap !important;
  }
  .flex-sm-nowrap {
    flex-wrap: nowrap !important;
  }
  .flex-sm-wrap-reverse {
    flex-wrap: wrap-reverse !important;
  }
  .flex-sm-fill {
    flex: 1 1 auto !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;
  }
  .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;
  }
  .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;
  }
}

@media (min-width: 768px) {
  .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-wrap {
    flex-wrap: wrap !important;
  }
  .flex-md-nowrap {
    flex-wrap: nowrap !important;
  }
  .flex-md-wrap-reverse {
    flex-wrap: wrap-reverse !important;
  }
  .flex-md-fill {
    flex: 1 1 auto !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;
  }
  .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;
  }
  .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;
  }
}

@media (min-width: 992px) {
  .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-wrap {
    flex-wrap: wrap !important;
  }
  .flex-lg-nowrap {
    flex-wrap: nowrap !important;
  }
  .flex-lg-wrap-reverse {
    flex-wrap: wrap-reverse !important;
  }
  .flex-lg-fill {
    flex: 1 1 auto !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;
  }
  .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;
  }
  .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;
  }
}

@media (min-width: 1200px) {
  .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-wrap {
    flex-wrap: wrap !important;
  }
  .flex-xl-nowrap {
    flex-wrap: nowrap !important;
  }
  .flex-xl-wrap-reverse {
    flex-wrap: wrap-reverse !important;
  }
  .flex-xl-fill {
    flex: 1 1 auto !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;
  }
  .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;
  }
  .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;
  }
}

.float-left {
  float: left !important;
}

.float-right {
  float: right !important;
}

.float-none {
  float: none !important;
}

@media (min-width: 576px) {
  .float-sm-left {
    float: left !important;
  }
  .float-sm-right {
    float: right !important;
  }
  .float-sm-none {
    float: none !important;
  }
}

@media (min-width: 768px) {
  .float-md-left {
    float: left !important;
  }
  .float-md-right {
    float: right !important;
  }
  .float-md-none {
    float: none !important;
  }
}

@media (min-width: 992px) {
  .float-lg-left {
    float: left !important;
  }
  .float-lg-right {
    float: right !important;
  }
  .float-lg-none {
    float: none !important;
  }
}

@media (min-width: 1200px) {
  .float-xl-left {
    float: left !important;
  }
  .float-xl-right {
    float: right !important;
  }
  .float-xl-none {
    float: none !important;
  }
}

.user-select-all {
  user-select: all !important;
}

.user-select-auto {
  user-select: auto !important;
}

.user-select-none {
  user-select: none !important;
}

.overflow-auto {
  overflow: auto !important;
}

.overflow-hidden {
  overflow: hidden !important;
}

.position-static {
  position: static !important;
}

.position-relative {
  position: relative !important;
}

.position-absolute {
  position: absolute !important;
}

.position-fixed {
  position: fixed !important;
}

.position-sticky {
  position: sticky !important;
}

.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;
}

@supports (position: sticky) {
  .sticky-top {
    position: sticky;
    top: 0;
    z-index: 1020;
  }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.sr-only-focusable:active, .sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
}

.shadow-sm {
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
}

.shadow {
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}

.shadow-lg {
  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
}

.shadow-none {
  box-shadow: none !important;
}

.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;
}

.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;
}

.mw-100 {
  max-width: 100% !important;
}

.mh-100 {
  max-height: 100% !important;
}

.min-vw-100 {
  min-width: 100vw !important;
}

.min-vh-100 {
  min-height: 100vh !important;
}

.vw-100 {
  width: 100vw !important;
}

.vh-100 {
  height: 100vh !important;
}

.m-0 {
  margin: 0 !important;
}

.mt-0,
.my-0 {
  margin-top: 0 !important;
}

.mr-0,
.mx-0 {
  margin-right: 0 !important;
}

.mb-0,
.my-0 {
  margin-bottom: 0 !important;
}

.ml-0,
.mx-0 {
  margin-left: 0 !important;
}

.m-1 {
  margin: 0.3125rem !important;
}

.mt-1,
.my-1 {
  margin-top: 0.3125rem !important;
}

.mr-1,
.mx-1 {
  margin-right: 0.3125rem !important;
}

.mb-1,
.my-1 {
  margin-bottom: 0.3125rem !important;
}

.ml-1,
.mx-1 {
  margin-left: 0.3125rem !important;
}

.m-2 {
  margin: 0.625rem !important;
}

.mt-2,
.my-2 {
  margin-top: 0.625rem !important;
}

.mr-2,
.mx-2 {
  margin-right: 0.625rem !important;
}

.mb-2,
.my-2 {
  margin-bottom: 0.625rem !important;
}

.ml-2,
.mx-2 {
  margin-left: 0.625rem !important;
}

.m-3 {
  margin: 0.9375rem !important;
}

.mt-3,
.my-3 {
  margin-top: 0.9375rem !important;
}

.mr-3,
.mx-3 {
  margin-right: 0.9375rem !important;
}

.mb-3,
.my-3 {
  margin-bottom: 0.9375rem !important;
}

.ml-3,
.mx-3 {
  margin-left: 0.9375rem !important;
}

.m-4 {
  margin: 1.875rem !important;
}

.mt-4,
.my-4 {
  margin-top: 1.875rem !important;
}

.mr-4,
.mx-4 {
  margin-right: 1.875rem !important;
}

.mb-4,
.my-4 {
  margin-bottom: 1.875rem !important;
}

.ml-4,
.mx-4 {
  margin-left: 1.875rem !important;
}

.m-5 {
  margin: 2.8125rem !important;
}

.mt-5,
.my-5 {
  margin-top: 2.8125rem !important;
}

.mr-5,
.mx-5 {
  margin-right: 2.8125rem !important;
}

.mb-5,
.my-5 {
  margin-bottom: 2.8125rem !important;
}

.ml-5,
.mx-5 {
  margin-left: 2.8125rem !important;
}

.p-0 {
  padding: 0 !important;
}

.pt-0,
.py-0 {
  padding-top: 0 !important;
}

.pr-0,
.px-0 {
  padding-right: 0 !important;
}

.pb-0,
.py-0 {
  padding-bottom: 0 !important;
}

.pl-0,
.px-0 {
  padding-left: 0 !important;
}

.p-1 {
  padding: 0.3125rem !important;
}

.pt-1,
.py-1 {
  padding-top: 0.3125rem !important;
}

.pr-1,
.px-1 {
  padding-right: 0.3125rem !important;
}

.pb-1,
.py-1 {
  padding-bottom: 0.3125rem !important;
}

.pl-1,
.px-1 {
  padding-left: 0.3125rem !important;
}

.p-2 {
  padding: 0.625rem !important;
}

.pt-2,
.py-2 {
  padding-top: 0.625rem !important;
}

.pr-2,
.px-2 {
  padding-right: 0.625rem !important;
}

.pb-2,
.py-2 {
  padding-bottom: 0.625rem !important;
}

.pl-2,
.px-2 {
  padding-left: 0.625rem !important;
}

.p-3 {
  padding: 0.9375rem !important;
}

.pt-3,
.py-3 {
  padding-top: 0.9375rem !important;
}

.pr-3,
.px-3 {
  padding-right: 0.9375rem !important;
}

.pb-3,
.py-3 {
  padding-bottom: 0.9375rem !important;
}

.pl-3,
.px-3 {
  padding-left: 0.9375rem !important;
}

.p-4 {
  padding: 1.875rem !important;
}

.pt-4,
.py-4 {
  padding-top: 1.875rem !important;
}

.pr-4,
.px-4 {
  padding-right: 1.875rem !important;
}

.pb-4,
.py-4 {
  padding-bottom: 1.875rem !important;
}

.pl-4,
.px-4 {
  padding-left: 1.875rem !important;
}

.p-5 {
  padding: 2.8125rem !important;
}

.pt-5,
.py-5 {
  padding-top: 2.8125rem !important;
}

.pr-5,
.px-5 {
  padding-right: 2.8125rem !important;
}

.pb-5,
.py-5 {
  padding-bottom: 2.8125rem !important;
}

.pl-5,
.px-5 {
  padding-left: 2.8125rem !important;
}

.m-n1 {
  margin: -0.3125rem !important;
}

.mt-n1,
.my-n1 {
  margin-top: -0.3125rem !important;
}

.mr-n1,
.mx-n1 {
  margin-right: -0.3125rem !important;
}

.mb-n1,
.my-n1 {
  margin-bottom: -0.3125rem !important;
}

.ml-n1,
.mx-n1 {
  margin-left: -0.3125rem !important;
}

.m-n2 {
  margin: -0.625rem !important;
}

.mt-n2,
.my-n2 {
  margin-top: -0.625rem !important;
}

.mr-n2,
.mx-n2 {
  margin-right: -0.625rem !important;
}

.mb-n2,
.my-n2 {
  margin-bottom: -0.625rem !important;
}

.ml-n2,
.mx-n2 {
  margin-left: -0.625rem !important;
}

.m-n3 {
  margin: -0.9375rem !important;
}

.mt-n3,
.my-n3 {
  margin-top: -0.9375rem !important;
}

.mr-n3,
.mx-n3 {
  margin-right: -0.9375rem !important;
}

.mb-n3,
.my-n3 {
  margin-bottom: -0.9375rem !important;
}

.ml-n3,
.mx-n3 {
  margin-left: -0.9375rem !important;
}

.m-n4 {
  margin: -1.875rem !important;
}

.mt-n4,
.my-n4 {
  margin-top: -1.875rem !important;
}

.mr-n4,
.mx-n4 {
  margin-right: -1.875rem !important;
}

.mb-n4,
.my-n4 {
  margin-bottom: -1.875rem !important;
}

.ml-n4,
.mx-n4 {
  margin-left: -1.875rem !important;
}

.m-n5 {
  margin: -2.8125rem !important;
}

.mt-n5,
.my-n5 {
  margin-top: -2.8125rem !important;
}

.mr-n5,
.mx-n5 {
  margin-right: -2.8125rem !important;
}

.mb-n5,
.my-n5 {
  margin-bottom: -2.8125rem !important;
}

.ml-n5,
.mx-n5 {
  margin-left: -2.8125rem !important;
}

.m-auto {
  margin: auto !important;
}

.mt-auto,
.my-auto {
  margin-top: auto !important;
}

.mr-auto,
.mx-auto {
  margin-right: auto !important;
}

.mb-auto,
.my-auto {
  margin-bottom: auto !important;
}

.ml-auto,
.mx-auto {
  margin-left: auto !important;
}

@media (min-width: 576px) {
  .m-sm-0 {
    margin: 0 !important;
  }
  .mt-sm-0,
  .my-sm-0 {
    margin-top: 0 !important;
  }
  .mr-sm-0,
  .mx-sm-0 {
    margin-right: 0 !important;
  }
  .mb-sm-0,
  .my-sm-0 {
    margin-bottom: 0 !important;
  }
  .ml-sm-0,
  .mx-sm-0 {
    margin-left: 0 !important;
  }
  .m-sm-1 {
    margin: 0.3125rem !important;
  }
  .mt-sm-1,
  .my-sm-1 {
    margin-top: 0.3125rem !important;
  }
  .mr-sm-1,
  .mx-sm-1 {
    margin-right: 0.3125rem !important;
  }
  .mb-sm-1,
  .my-sm-1 {
    margin-bottom: 0.3125rem !important;
  }
  .ml-sm-1,
  .mx-sm-1 {
    margin-left: 0.3125rem !important;
  }
  .m-sm-2 {
    margin: 0.625rem !important;
  }
  .mt-sm-2,
  .my-sm-2 {
    margin-top: 0.625rem !important;
  }
  .mr-sm-2,
  .mx-sm-2 {
    margin-right: 0.625rem !important;
  }
  .mb-sm-2,
  .my-sm-2 {
    margin-bottom: 0.625rem !important;
  }
  .ml-sm-2,
  .mx-sm-2 {
    margin-left: 0.625rem !important;
  }
  .m-sm-3 {
    margin: 0.9375rem !important;
  }
  .mt-sm-3,
  .my-sm-3 {
    margin-top: 0.9375rem !important;
  }
  .mr-sm-3,
  .mx-sm-3 {
    margin-right: 0.9375rem !important;
  }
  .mb-sm-3,
  .my-sm-3 {
    margin-bottom: 0.9375rem !important;
  }
  .ml-sm-3,
  .mx-sm-3 {
    margin-left: 0.9375rem !important;
  }
  .m-sm-4 {
    margin: 1.875rem !important;
  }
  .mt-sm-4,
  .my-sm-4 {
    margin-top: 1.875rem !important;
  }
  .mr-sm-4,
  .mx-sm-4 {
    margin-right: 1.875rem !important;
  }
  .mb-sm-4,
  .my-sm-4 {
    margin-bottom: 1.875rem !important;
  }
  .ml-sm-4,
  .mx-sm-4 {
    margin-left: 1.875rem !important;
  }
  .m-sm-5 {
    margin: 2.8125rem !important;
  }
  .mt-sm-5,
  .my-sm-5 {
    margin-top: 2.8125rem !important;
  }
  .mr-sm-5,
  .mx-sm-5 {
    margin-right: 2.8125rem !important;
  }
  .mb-sm-5,
  .my-sm-5 {
    margin-bottom: 2.8125rem !important;
  }
  .ml-sm-5,
  .mx-sm-5 {
    margin-left: 2.8125rem !important;
  }
  .p-sm-0 {
    padding: 0 !important;
  }
  .pt-sm-0,
  .py-sm-0 {
    padding-top: 0 !important;
  }
  .pr-sm-0,
  .px-sm-0 {
    padding-right: 0 !important;
  }
  .pb-sm-0,
  .py-sm-0 {
    padding-bottom: 0 !important;
  }
  .pl-sm-0,
  .px-sm-0 {
    padding-left: 0 !important;
  }
  .p-sm-1 {
    padding: 0.3125rem !important;
  }
  .pt-sm-1,
  .py-sm-1 {
    padding-top: 0.3125rem !important;
  }
  .pr-sm-1,
  .px-sm-1 {
    padding-right: 0.3125rem !important;
  }
  .pb-sm-1,
  .py-sm-1 {
    padding-bottom: 0.3125rem !important;
  }
  .pl-sm-1,
  .px-sm-1 {
    padding-left: 0.3125rem !important;
  }
  .p-sm-2 {
    padding: 0.625rem !important;
  }
  .pt-sm-2,
  .py-sm-2 {
    padding-top: 0.625rem !important;
  }
  .pr-sm-2,
  .px-sm-2 {
    padding-right: 0.625rem !important;
  }
  .pb-sm-2,
  .py-sm-2 {
    padding-bottom: 0.625rem !important;
  }
  .pl-sm-2,
  .px-sm-2 {
    padding-left: 0.625rem !important;
  }
  .p-sm-3 {
    padding: 0.9375rem !important;
  }
  .pt-sm-3,
  .py-sm-3 {
    padding-top: 0.9375rem !important;
  }
  .pr-sm-3,
  .px-sm-3 {
    padding-right: 0.9375rem !important;
  }
  .pb-sm-3,
  .py-sm-3 {
    padding-bottom: 0.9375rem !important;
  }
  .pl-sm-3,
  .px-sm-3 {
    padding-left: 0.9375rem !important;
  }
  .p-sm-4 {
    padding: 1.875rem !important;
  }
  .pt-sm-4,
  .py-sm-4 {
    padding-top: 1.875rem !important;
  }
  .pr-sm-4,
  .px-sm-4 {
    padding-right: 1.875rem !important;
  }
  .pb-sm-4,
  .py-sm-4 {
    padding-bottom: 1.875rem !important;
  }
  .pl-sm-4,
  .px-sm-4 {
    padding-left: 1.875rem !important;
  }
  .p-sm-5 {
    padding: 2.8125rem !important;
  }
  .pt-sm-5,
  .py-sm-5 {
    padding-top: 2.8125rem !important;
  }
  .pr-sm-5,
  .px-sm-5 {
    padding-right: 2.8125rem !important;
  }
  .pb-sm-5,
  .py-sm-5 {
    padding-bottom: 2.8125rem !important;
  }
  .pl-sm-5,
  .px-sm-5 {
    padding-left: 2.8125rem !important;
  }
  .m-sm-n1 {
    margin: -0.3125rem !important;
  }
  .mt-sm-n1,
  .my-sm-n1 {
    margin-top: -0.3125rem !important;
  }
  .mr-sm-n1,
  .mx-sm-n1 {
    margin-right: -0.3125rem !important;
  }
  .mb-sm-n1,
  .my-sm-n1 {
    margin-bottom: -0.3125rem !important;
  }
  .ml-sm-n1,
  .mx-sm-n1 {
    margin-left: -0.3125rem !important;
  }
  .m-sm-n2 {
    margin: -0.625rem !important;
  }
  .mt-sm-n2,
  .my-sm-n2 {
    margin-top: -0.625rem !important;
  }
  .mr-sm-n2,
  .mx-sm-n2 {
    margin-right: -0.625rem !important;
  }
  .mb-sm-n2,
  .my-sm-n2 {
    margin-bottom: -0.625rem !important;
  }
  .ml-sm-n2,
  .mx-sm-n2 {
    margin-left: -0.625rem !important;
  }
  .m-sm-n3 {
    margin: -0.9375rem !important;
  }
  .mt-sm-n3,
  .my-sm-n3 {
    margin-top: -0.9375rem !important;
  }
  .mr-sm-n3,
  .mx-sm-n3 {
    margin-right: -0.9375rem !important;
  }
  .mb-sm-n3,
  .my-sm-n3 {
    margin-bottom: -0.9375rem !important;
  }
  .ml-sm-n3,
  .mx-sm-n3 {
    margin-left: -0.9375rem !important;
  }
  .m-sm-n4 {
    margin: -1.875rem !important;
  }
  .mt-sm-n4,
  .my-sm-n4 {
    margin-top: -1.875rem !important;
  }
  .mr-sm-n4,
  .mx-sm-n4 {
    margin-right: -1.875rem !important;
  }
  .mb-sm-n4,
  .my-sm-n4 {
    margin-bottom: -1.875rem !important;
  }
  .ml-sm-n4,
  .mx-sm-n4 {
    margin-left: -1.875rem !important;
  }
  .m-sm-n5 {
    margin: -2.8125rem !important;
  }
  .mt-sm-n5,
  .my-sm-n5 {
    margin-top: -2.8125rem !important;
  }
  .mr-sm-n5,
  .mx-sm-n5 {
    margin-right: -2.8125rem !important;
  }
  .mb-sm-n5,
  .my-sm-n5 {
    margin-bottom: -2.8125rem !important;
  }
  .ml-sm-n5,
  .mx-sm-n5 {
    margin-left: -2.8125rem !important;
  }
  .m-sm-auto {
    margin: auto !important;
  }
  .mt-s
Download .txt
gitextract_hr3ssai2/

├── .gitignore
├── Aurora.py
├── README.md
├── VERSION
├── __init__.py
├── apt-requirements.txt
├── aurora.service
├── extensions/
│   ├── Aurora_Ambient_16x9.py
│   ├── Aurora_Ambient_AutoCrop.py
│   ├── Aurora_Ambient_NoCrop.py
│   ├── Aurora_AudioSpectogram.py
│   ├── Aurora_Configure.py
│   ├── Aurora_Meteor.py
│   ├── Aurora_Rainbow.py
│   ├── __init__.py
│   └── exampleExtension.py
├── install.sh
├── requirements.txt
├── update.sh
└── webserver/
    ├── static/
    │   ├── css/
    │   │   ├── bootstrap.css
    │   │   └── style.css
    │   ├── images/
    │   │   ├── icons/
    │   │   │   └── license.txt
    │   │   └── undraw/
    │   │       └── _license_and_link.rtf
    │   ├── js/
    │   │   ├── aurora-configure.js
    │   │   ├── aurora-generic.js
    │   │   ├── aurora-index.js
    │   │   ├── aurora-view.js
    │   │   ├── custom.js
    │   │   └── jquery.js
    │   └── menu/
    │       └── menu-main.html
    └── templates/
        ├── about.html
        ├── configure.html
        ├── footer.html
        ├── header.html
        ├── index.html
        ├── menu-colors.html
        ├── menu-footer.html
        ├── menu-share.html
        ├── status.json
        └── view.html
Download .txt
SYMBOL INDEX (179 symbols across 15 files)

FILE: Aurora.py
  class AuroraManager (line 27) | class AuroraManager:
    method __init__ (line 28) | def __init__(self):
    method setupNeoPixels (line 79) | def setupNeoPixels(self):
    method setupHDMI (line 92) | def setupHDMI(self):
    method saveConfig (line 164) | def saveConfig(self):
    method log (line 169) | def log(self, message):
    method loadConfig (line 173) | def loadConfig(self):
    method getExtensionClass (line 199) | def getExtensionClass(self, extension_name, extension_dir):
    method fetchMeta (line 227) | def fetchMeta(self, extension, filename):
    method populateExtensions (line 238) | def populateExtensions(self):
    method addMessage (line 254) | def addMessage(self, msg):
    method getCurrentExtension (line 260) | def getCurrentExtension(self):
    method setCurrentExtension (line 268) | def setCurrentExtension(self, new_current_extension):
    method takeScreenshot (line 296) | def takeScreenshot(self):
    method makePixelImage (line 299) | def makePixelImage(self):
    method setupExtension (line 302) | def setupExtension(self):
    method tearDownExtension (line 306) | def tearDownExtension(self):
    method loop (line 310) | def loop(self):
  class Aurora_Webserver (line 322) | class Aurora_Webserver(object):
    method __init__ (line 323) | def __init__(self, Manager):
    method status (line 328) | def status(self):
    method about (line 344) | def about(self):
    method index (line 380) | def index(self):
    method view (line 413) | def view(self):
    method configure (line 446) | def configure(self):
    method toggleEnable (line 489) | def toggleEnable(self):
    method update_config (line 502) | def update_config(self):
    method update_HDMI_config (line 531) | def update_HDMI_config(self):
    method update_LED_config (line 578) | def update_LED_config(self):
    method update_extension (line 691) | def update_extension(self):
    method screenshot (line 701) | def screenshot(self):
    method load_screenshot (line 714) | def load_screenshot(self, **params):
    method load_pixel_image (line 740) | def load_pixel_image(self, **params):

FILE: extensions/Aurora_Ambient_16x9.py
  class Aurora_Ambient_16x9 (line 9) | class Aurora_Ambient_16x9(AuroraExtension):
    method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
    method aspectCrop (line 21) | def aspectCrop(self, image, ratio):
    method takeScreenShot (line 30) | def takeScreenShot(self, filepath):
    method visualise (line 39) | def visualise(self):

FILE: extensions/Aurora_Ambient_AutoCrop.py
  class Aurora_Ambient_AutoCrop (line 9) | class Aurora_Ambient_AutoCrop(AuroraExtension):
    method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
    method takeScreenShot (line 23) | def takeScreenShot(self, filepath, autocrop=False):
    method autocrop (line 26) | def autocrop(self, image, threshold=0):
    method visualise (line 46) | def visualise(self):

FILE: extensions/Aurora_Ambient_NoCrop.py
  class Aurora_Ambient_NoCrop (line 9) | class Aurora_Ambient_NoCrop(AuroraExtension):
    method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
    method autocrop (line 19) | def autocrop(self, image, threshold=0):
    method takeScreenShot (line 39) | def takeScreenShot(self, filepath):
    method visualise (line 74) | def visualise(self):

FILE: extensions/Aurora_AudioSpectogram.py
  class Aurora_AudioSpectogram (line 13) | class Aurora_AudioSpectogram(AuroraExtension):
    method __init__ (line 14) | def __init__(self, NeoPixels, HDMI):
    method takeScreenShot (line 40) | def takeScreenShot(self, filepath):
    method startAudioStream (line 44) | def startAudioStream(self):
    method teardown (line 57) | def teardown(self):
    method fadeToBlack (line 63) | def fadeToBlack(self, pixelPos):
    method wheel (line 73) | def wheel(self, pos):
    method rainbow_cycle (line 86) | def rainbow_cycle(self, j):
    method visualiseAudio (line 93) | def visualiseAudio(self, indata, frames, time, status):
    method visualise (line 146) | def visualise(self):

FILE: extensions/Aurora_Configure.py
  class Aurora_Configure (line 12) | class Aurora_Configure(AuroraExtension):
    method __init__ (line 13) | def __init__(self, NeoPixels, HDMI):
    method setup (line 22) | def setup(self):
    method visualise (line 25) | def visualise(self):

FILE: extensions/Aurora_Meteor.py
  class Aurora_Meteor (line 10) | class Aurora_Meteor(AuroraExtension):
    method __init__ (line 11) | def __init__(self, NeoPixels, HDMI):
    method takeScreenShot (line 25) | def takeScreenShot(self, filepath):
    method fadeToBlack (line 29) | def fadeToBlack(self, pixelPos):
    method meteorRain (line 39) | def meteorRain(self, i, col):
    method visualise (line 52) | def visualise(self):

FILE: extensions/Aurora_Rainbow.py
  class Aurora_Rainbow (line 9) | class Aurora_Rainbow(AuroraExtension):
    method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
    method takeScreenShot (line 19) | def takeScreenShot(self, filepath):
    method wheel (line 23) | def wheel(self, pos):
    method rainbow_cycle (line 36) | def rainbow_cycle(self, j):
    method visualise (line 43) | def visualise(self):

FILE: extensions/exampleExtension.py
  class exampleExtension (line 8) | class exampleExtension(AuroraExtension):
    method __init__ (line 9) | def __init__(self, NeoPixels, HDMI):
    method fadeToBlack (line 23) | def fadeToBlack(self, pixelPos):
    method fadeToBright (line 33) | def fadeToBright(self, pixelPos):
    method visualise (line 47) | def visualise(self):

FILE: webserver/static/js/aurora-configure.js
  function fetch_config_data (line 1) | function fetch_config_data() {
  function save_hdmi_image (line 51) | function save_hdmi_image()
  function reloadImages (line 62) | function reloadImages() {
  function setHDMIValues (line 70) | function setHDMIValues()
  function reset_gamma (line 79) | function reset_gamma(hue_gamma)

FILE: webserver/static/js/aurora-generic.js
  function togglePower (line 3) | function togglePower()
  function make_AJAX_Call (line 10) | function make_AJAX_Call(url, data_dict, callback_function = false) {
  function create_snackbar (line 51) | function create_snackbar(heading, message, type) {
  function showExtensionDetails (line 73) | function showExtensionDetails(name) {
  function loadExtension (line 86) | function loadExtension(extension_name = false) {
  function reload_pixel_image (line 100) | function reload_pixel_image() {
  function reload_hdmi_image (line 113) | function reload_hdmi_image() {

FILE: webserver/static/js/aurora-index.js
  function change_system_status (line 1) | function change_system_status()
  function toast_system_status (line 16) | function toast_system_status(system_status_call)

FILE: webserver/static/js/aurora-view.js
  function change_system_status (line 1) | function change_system_status() {
  function toast_system_status (line 15) | function toast_system_status(system_status_call) {
  function reloadImages (line 30) | function reloadImages() {
  function reloadCounter (line 37) | function reloadCounter() {

FILE: webserver/static/js/custom.js
  function init_template (line 29) | function init_template(){

FILE: webserver/static/js/jquery.js
  function m (line 2) | function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n...
  function x (line 2) | function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof...
  function C (line 2) | function C(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!y(e...
  function oe (line 2) | function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeTy...
  function ae (line 2) | function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLengt...
  function se (line 2) | function se(e){return e[b]=!0,e}
  function ue (line 2) | function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(...
  function le (line 2) | function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[...
  function ce (line 2) | function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourc...
  function fe (line 2) | function fe(e){return function(t){return"input"===t.nodeName.toLowerCase...
  function pe (line 2) | function pe(e){return function(t){var n=t.nodeName.toLowerCase();return(...
  function de (line 2) | function de(e){return function(t){return"form"in t?t.parentNode&&!1===t....
  function he (line 2) | function he(e){return se(function(t){return t=+t,se(function(n,r){var i,...
  function ge (line 2) | function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}
  function ye (line 2) | function ye(){}
  function ve (line 2) | function ve(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}
  function me (line 2) | function me(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=C...
  function xe (line 2) | function xe(e){return e.length>1?function(t,n,r){var i=e.length;while(i-...
  function be (line 2) | function be(e,t,n){for(var r=0,i=t.length;r<i;r++)oe(e,t[r],n);return n}
  function we (line 2) | function we(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(...
  function Te (line 2) | function Te(e,t,n,r,i,o){return r&&!r[b]&&(r=Te(r)),i&&!i[b]&&(i=Te(i,o)...
  function Ce (line 2) | function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.r...
  function Ee (line 2) | function Ee(e,t){var n=t.length>0,i=e.length>0,o=function(o,a,s,u,c){var...
  function N (line 2) | function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerC...
  function j (line 2) | function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,...
  function P (line 2) | function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}
  function R (line 2) | function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!...
  function I (line 2) | function I(e){return e}
  function W (line 2) | function W(e){throw e}
  function $ (line 2) | function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n...
  function a (line 2) | function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(...
  function _ (line 2) | function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventLi...
  function V (line 2) | function V(e,t){return t.toUpperCase()}
  function G (line 2) | function G(e){return e.replace(X,"ms-").replace(U,V)}
  function Q (line 2) | function Q(){this.expando=w.expando+Q.uid++}
  function te (line 2) | function te(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""...
  function ne (line 2) | function ne(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.re...
  function ue (line 2) | function ue(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:functio...
  function ce (line 2) | function ce(e){var t,n=e.ownerDocument,r=e.nodeName,i=le[r];return i||(t...
  function fe (line 2) | function fe(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)(r=e[o]).style&...
  function ye (line 2) | function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagNa...
  function ve (line 2) | function ve(e,t){for(var n=0,r=e.length;n<r;n++)J.set(e[n],"globalEval",...
  function xe (line 2) | function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),...
  function Ee (line 2) | function Ee(){return!0}
  function ke (line 2) | function ke(){return!1}
  function Se (line 2) | function Se(){try{return r.activeElement}catch(e){}}
  function De (line 2) | function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof...
  function Le (line 2) | function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"...
  function He (line 2) | function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}
  function Oe (line 2) | function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.sli...
  function Pe (line 2) | function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&...
  function Me (line 2) | function Me(e,t){var n=t.nodeName.toLowerCase();"input"===n&&pe.test(e.t...
  function Re (line 2) | function Re(e,t,n,r){t=a.apply([],t);var i,o,s,u,l,c,f=0,p=e.length,d=p-...
  function Ie (line 2) | function Ie(e,t,n){for(var r,i=t?w.filter(t,e):e,o=0;null!=(r=i[o]);o++)...
  function t (line 2) | function t(){if(c){l.style.cssText="position:absolute;left:-11111px;widt...
  function n (line 2) | function n(e){return Math.round(parseFloat(e))}
  function Fe (line 2) | function Fe(e,t,n){var r,i,o,a,s=e.style;return(n=n||$e(e))&&(""!==(a=n....
  function _e (line 2) | function _e(e,t){return{get:function(){if(!e())return(this.get=t).apply(...
  function Qe (line 2) | function Qe(e){if(e in Ye)return e;var t=e[0].toUpperCase()+e.slice(1),n...
  function Je (line 2) | function Je(e){var t=w.cssProps[e];return t||(t=w.cssProps[e]=Qe(e)||e),t}
  function Ke (line 2) | function Ke(e,t,n){var r=ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[...
  function Ze (line 2) | function Ze(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border...
  function et (line 2) | function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"box...
  function tt (line 2) | function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}
  function at (line 2) | function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnima...
  function st (line 2) | function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}
  function ut (line 2) | function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin...
  function lt (line 2) | function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["...
  function ct (line 2) | function ct(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=thi...
  function ft (line 2) | function ft(e,t){var n,r,i,o,a;for(n in e)if(r=G(n),i=t[r],o=e[n],Array....
  function pt (line 2) | function pt(e,t,n){var r,i,o=0,a=pt.prefilters.length,s=w.Deferred().alw...
  function vt (line 2) | function vt(e){return(e.match(M)||[]).join(" ")}
  function mt (line 2) | function mt(e){return e.getAttribute&&e.getAttribute("class")||""}
  function xt (line 2) | function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||...
  function jt (line 2) | function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n|...
  function Ft (line 2) | function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var ...
  function _t (line 2) | function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!...
  function zt (line 2) | function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)voi...
  function Xt (line 2) | function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[...
  function Ut (line 2) | function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])fo...
  function k (line 2) | function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=vo...
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (626K chars).
[
  {
    "path": ".gitignore",
    "chars": 2191,
    "preview": "#Aurora test things\ncolourtest_from_youtube.mp4\n*.jpg\n*.jpeg\nenv/*\n.vscode/\naudio.py\nauroraspec.py\nspec.py\nnohup.out\ncon"
  },
  {
    "path": "Aurora.py",
    "chars": 29997,
    "preview": "# Main Aurora python file, runs the webserver and the Aurora client, configs are loaded from extensions directory\nimport"
  },
  {
    "path": "README.md",
    "chars": 1988,
    "preview": "[![Slack](https://img.shields.io/badge/slack-chat-green.svg)](https://join.slack.com/t/auroraambientlighting/shared_invi"
  },
  {
    "path": "VERSION",
    "chars": 27,
    "preview": "0.42-catsdontlikefireworks\n"
  },
  {
    "path": "__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "apt-requirements.txt",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "aurora.service",
    "chars": 310,
    "preview": "[Unit]\nDescription=Aurora light system\nAfter=multi-user.target\nStartLimitInterval=200\nStartLimitBurst=5\n[Service]\nWorkin"
  },
  {
    "path": "extensions/Aurora_Ambient_16x9.py",
    "chars": 1827,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\n\nimport numpy as np\nimport pandas as pd\nimport cv2\n\n\nclass A"
  },
  {
    "path": "extensions/Aurora_Ambient_AutoCrop.py",
    "chars": 2185,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
  },
  {
    "path": "extensions/Aurora_Ambient_NoCrop.py",
    "chars": 2556,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
  },
  {
    "path": "extensions/Aurora_AudioSpectogram.py",
    "chars": 5390,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
  },
  {
    "path": "extensions/Aurora_Configure.py",
    "chars": 1448,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport neopixel\nimport time\nimport datetime\nimport numpy as np\nimport pa"
  },
  {
    "path": "extensions/Aurora_Meteor.py",
    "chars": 1902,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
  },
  {
    "path": "extensions/Aurora_Rainbow.py",
    "chars": 1645,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
  },
  {
    "path": "extensions/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "extensions/exampleExtension.py",
    "chars": 3312,
    "preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nfrom random import randint, choice\nimport numpy as np\nimport"
  },
  {
    "path": "install.sh",
    "chars": 2168,
    "preview": "#!/bin/bash\necho -e \"\n ▄▄▄       █    ██  ██▀███   ▒█████   ██▀███   ▄▄▄      \n▒████▄     ██  ▓██▒▓██ ▒ ██▒▒██▒  ██▒▓██ "
  },
  {
    "path": "requirements.txt",
    "chars": 136,
    "preview": "numpy >= 1.16.5\npandas\nopencv-python==4.5.1.48\ncherrypy\nconfigparser\nadafruit-circuitpython-neopixel\nblinker\njinja2\nsoun"
  },
  {
    "path": "update.sh",
    "chars": 2037,
    "preview": "#!/bin/bash\necho -e \"\n ▄▄▄       █    ██  ██▀███   ▒█████   ██▀███   ▄▄▄      \n▒████▄     ██  ▓██▒▓██ ▒ ██▒▒██▒  ██▒▓██ "
  },
  {
    "path": "webserver/static/css/bootstrap.css",
    "chars": 202432,
    "preview": "/*!\n * Bootstrap v4.5 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Tw"
  },
  {
    "path": "webserver/static/css/style.css",
    "chars": 119218,
    "preview": "/*\n\n1.  Table of Contents \n2.  Typography\n3.  Structure\n4.  Page Logos\n5.  Highlight Colors\n6.  Header and Page Title\n7."
  },
  {
    "path": "webserver/static/images/icons/license.txt",
    "chars": 140,
    "preview": "https://www.iconfinder.com/iconsets/round-varieties\r\n\r\n19 PNG icons by Creaticca Ltd  - License: Creative Commons (Attri"
  },
  {
    "path": "webserver/static/images/undraw/_license_and_link.rtf",
    "chars": 2594,
    "preview": "{\\rtf1\\ansi\\ansicpg1252\\cocoartf2512\n\\cocoatextscaling0\\cocoaplatform0{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;\\f1\\froman"
  },
  {
    "path": "webserver/static/js/aurora-configure.js",
    "chars": 2563,
    "preview": "function fetch_config_data() {\n  ajax_send_data = {}\n  ajax_send_data[\"pixelcount_left\"] = $('#aurora_configure_left').v"
  },
  {
    "path": "webserver/static/js/aurora-generic.js",
    "chars": 3794,
    "preview": "currentAjaxRequest = null; // Stores our current ajax request so we can cancel it if we do another\n\nfunction togglePower"
  },
  {
    "path": "webserver/static/js/aurora-index.js",
    "chars": 932,
    "preview": "function change_system_status()\n{\n    \n    $('#toggle_aurora_enabled').prop(\"disabled\",true)\n    enabled_stats = $('#tog"
  },
  {
    "path": "webserver/static/js/aurora-view.js",
    "chars": 1350,
    "preview": "function change_system_status() {\n\n    $('#toggle_aurora_enabled').prop(\"disabled\", true)\n    enabled_stats = $('#toggle"
  },
  {
    "path": "webserver/static/js/custom.js",
    "chars": 82064,
    "preview": "$(window).on('load',function(){\r\n    $('.menu').css('display','block');\r\n    $('#preloader').addClass('preloader-hide');"
  },
  {
    "path": "webserver/static/js/jquery.js",
    "chars": 86926,
    "preview": "/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object"
  },
  {
    "path": "webserver/static/menu/menu-main.html",
    "chars": 2532,
    "preview": "<div class=\"card rounded-0 bg-aurora\" data-card-height=\"150\">\r\n    <div class=\"card-top\">\r\n        <a href=\"#\" class=\"cl"
  },
  {
    "path": "webserver/templates/about.html",
    "chars": 2837,
    "preview": "{% include 'header.html' %}\r\n        {% if configured == False %}\r\n        <div class=\"alert mr-3 ml-3 rounded-s bg-yell"
  },
  {
    "path": "webserver/templates/configure.html",
    "chars": 7206,
    "preview": "{% include 'header.html' %}\r\n<div class=\"card card-style\" style=\"max-width:40rem;\">\r\n    <div class=\"content mb-0\">\r\n   "
  },
  {
    "path": "webserver/templates/footer.html",
    "chars": 1389,
    "preview": "</div>\n<!-- Page content ends here-->\n\n\n\n<!-- Main Menu-->\n<div id=\"menu-main\" class=\"menu menu-box-left rounded-0\" data"
  },
  {
    "path": "webserver/templates/header.html",
    "chars": 4321,
    "preview": "<!DOCTYPE HTML>\n<html lang=\"en\">\n\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <"
  },
  {
    "path": "webserver/templates/index.html",
    "chars": 5049,
    "preview": "{% include 'header.html' %}\r\n        {% if configured == False %}\r\n        <div class=\"alert mr-3 ml-3 rounded-s bg-yell"
  },
  {
    "path": "webserver/templates/menu-colors.html",
    "chars": 3091,
    "preview": "<div class=\"menu-title\">\r\n    <p class=\"color-highlight font-600\">Choose your Favorite</p>\r\n    <h1>Highlight</h1>\r\n    "
  },
  {
    "path": "webserver/templates/menu-footer.html",
    "chars": 1206,
    "preview": "<div class=\"card card-style\">\r\n    <h4 class=\"font-28 text-center color-theme font-800 pt-3 mt-3\">AppKit</h4>\r\n    <p cl"
  },
  {
    "path": "webserver/templates/menu-share.html",
    "chars": 1556,
    "preview": "<div class=\"menu-title\">\r\n    <p class=\"color-highlight\">Tap a link to</p>\r\n    <h1>Share</h1>\r\n    <a href=\"#\" class=\"c"
  },
  {
    "path": "webserver/templates/status.json",
    "chars": 188,
    "preview": "{\n    \"enabled\": {{enabled}},\n    \"current_extension\": \"{{current_extension}}\",\n    \"current_extension_class\": \"{{curren"
  },
  {
    "path": "webserver/templates/view.html",
    "chars": 1969,
    "preview": "    {% include 'header.html' %}\r\n\r\n    \r\n\r\n    <div class=\"row mb-1 text-center d-flex justify-content-center\">\r\n       "
  }
]

About this extraction

This page contains the full source code of the AndrewMohawk/Aurora GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (578.6 KB), approximately 179.9k tokens, and a symbol index with 179 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!