diff --git a/.github/workflows/main.yml b/.github/workflows/bake_to_latest.yml similarity index 69% rename from .github/workflows/main.yml rename to .github/workflows/bake_to_latest.yml index f1b995e..0d5c8c6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/bake_to_latest.yml @@ -1,16 +1,13 @@ -name: Build Image On Release +name: Build release-tags on: workflow_dispatch: push: branches: - master - pull_request: - branches: - - master jobs: - bake: + bake-latest: runs-on: ubuntu-latest permissions: packages: write @@ -24,11 +21,6 @@ jobs: platforms: all - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1.6.0 - - name: Login to DockerHub - uses: docker/login-action@v1.10.0 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GitHub Container Registry uses: docker/login-action@v1.10.0 with: @@ -42,7 +34,10 @@ jobs: file: ./docker/docker-py3-kms/Dockerfile platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 push: true - tags: pykmsorg/py-kms:python3,ghcr.io/py-kms-organization/py-kms:python3 + tags: ghcr.io/py-kms-organization/py-kms:python3 + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} - name: Build uses: docker/build-push-action@v2 with: @@ -50,4 +45,7 @@ jobs: file: ./docker/docker-py3-kms-minimal/Dockerfile platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 push: true - tags: pykmsorg/py-kms:latest,ghcr.io/py-kms-organization/py-kms:latest,pykmsorg/py-kms:minimal,ghcr.io/py-kms-organization/py-kms:minimal + tags: ghcr.io/py-kms-organization/py-kms:latest,ghcr.io/py-kms-organization/py-kms:minimal + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} diff --git a/.github/workflows/bake_to_next.yml b/.github/workflows/bake_to_next.yml new file mode 100644 index 0000000..5fc1b6c --- /dev/null +++ b/.github/workflows/bake_to_next.yml @@ -0,0 +1,51 @@ +name: Build next-tags + +on: + workflow_dispatch: + push: + branches: + - next + +jobs: + bake-next: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1.6.0 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1.10.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: ./docker/docker-py3-kms/Dockerfile + platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 + push: true + tags: ghcr.io/py-kms-organization/py-kms:python3-next + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: ./docker/docker-py3-kms-minimal/Dockerfile + platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 + push: true + tags: ghcr.io/py-kms-organization/py-kms:latest-next,ghcr.io/py-kms-organization/py-kms:minimal-next + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} diff --git a/.github/workflows/bake_to_test.yml b/.github/workflows/bake_to_test.yml new file mode 100644 index 0000000..2790ba4 --- /dev/null +++ b/.github/workflows/bake_to_test.yml @@ -0,0 +1,38 @@ +name: Test-Build Docker Image + +on: + workflow_dispatch: + push: + +jobs: + bake-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1.6.0 + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: ./docker/docker-py3-kms/Dockerfile + platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 + push: false + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} + - name: Build + uses: docker/build-push-action@v2 + with: + context: . + file: ./docker/docker-py3-kms-minimal/Dockerfile + platforms: linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6 + push: false + build-args: | + BUILD_COMMIT=${{ github.sha }} + BUILD_BRANCH=${{ github.ref_name }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 874f86a..6f4050f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ pykms_logserver.log* pykms_logclient.log* pykms_database.db* -etrigan.log* # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c16912..c306839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +### py-kms_2022-12-16 +- Added support for new web-gui into Docker +- Implemented whole-new web-based GUI with Flask +- Removed old GUI (Etrigan) from code and resources +- Removed sqliteweb +- Removed Etrigan (GUI) + +### py-kms_2022-12-07 +- Added warning about Etrigan (GUI) being deprecated +- More docs (do not run on same machine as client) +- Added Docker support for multiple listen IPs +- Added graceful Docker shutdowns + +### py-kms_2021-12-23 +- More Windows 10/11 keys +- Fixed some deprecation warnings +- Fixed SO_REUSEPORT platform checks +- Fixed loglevel "MININFO" with Docker +- Added Docker healthcheck +- Added UID/GID change support for Docker +- Dependabot alerts + +### py-kms_2021-10-22 +- Integrated Office 2021 GLVK keys & database +- Docker entrypoint fixes +- Updated docs to include SQLite stuff +- Fix for undefined timezones +- Removed LOGFILE extension checks +- Added support for Windows 11 + +### py-kms_2021-10-07 +- Helm charts for Kubernetes deployment +- Windows 2022 updates +- Faster Github Action builds + +### py-kms_2021-11-12 +- Addded GHCR support +- Docs table reformatted +- Updated GUI +- Windows Sandbox fix +- Added contribution guidelines +- Docker multiarch +- Reshot screenshots in docs + ### py-kms_2020-10-01 - Sql database path customizable. - Sql database file keeps different AppId. diff --git a/LICENSE.gui.md b/LICENSE.gui.md deleted file mode 100644 index 1d06073..0000000 --- a/LICENSE.gui.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Matteo ℱan - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 4a765c6..abf294c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ ![repo-size](https://img.shields.io/github/repo-size/Py-KMS-Organization/py-kms) ![open-issues](https://img.shields.io/github/issues/Py-KMS-Organization/py-kms) ![last-commit](https://img.shields.io/github/last-commit/Py-KMS-Organization/py-kms/master) -![docker-pulls](https://img.shields.io/docker/pulls/pykmsorg/py-kms) ![read-the-docs](https://img.shields.io/readthedocs/py-kms) *** @@ -42,10 +41,9 @@ This version of _py-kms_ is for itself a fork of the original implementation by The wiki has been completly reworked and is now available on [readthedocs.com](https://py-kms.readthedocs.io/en/latest/). It should you provide all necessary information how to setup and to use _py-kms_ , all without clumping this readme. The documentation also houses more details about activation with _py-kms_ and how to get GVLK keys. ## Quick start -- To start the server, execute `python3 pykms_Server.py [IPADDRESS] [PORT]`, the default _IPADDRESS_ is `0.0.0.0` ( all interfaces ) and the default _PORT_ is `1688`. Note that both the address and port are optional. It's allowed to use IPv4 and IPv6 addresses. If you have a IPv6-capable dual-stack OS, a dual-stack socket is created when using a IPv6 address. +- To start the server, execute `python3 pykms_Server.py [IPADDRESS] [PORT]`, the default _IPADDRESS_ is `::` ( all interfaces ) and the default _PORT_ is `1688`. Note that both the address and port are optional. It's allowed to use IPv4 and IPv6 addresses. If you have a IPv6-capable dual-stack OS, a dual-stack socket is created when using a IPv6 address. **In case your OS does not support IPv6, make sure to explicitly specify the legacy IPv4 of `0.0.0.0`!** - To start the server automatically using Docker, execute `docker run -d --name py-kms --restart always -p 1688:1688 ghcr.io/py-kms-organization/py-kms`. - To show the help pages type: `python3 pykms_Server.py -h` and `python3 pykms_Client.py -h`. ## License - - _py-kms_ is [![Unlicense](https://img.shields.io/badge/license-unlicense-lightgray.svg)](https://github.com/SystemRage/py-kms/blob/master/LICENSE) - - _py-kms GUI_ is [![MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/SystemRage/py-kms/blob/master/LICENSE.gui.md) © Matteo ℱan + - _py-kms_ is [![Unlicense](https://img.shields.io/badge/license-unlicense-lightgray.svg)](https://github.com/SystemRage/py-kms/blob/master/LICENSE) \ No newline at end of file diff --git a/charts/py-kms/README.md b/charts/py-kms/README.md index 00fd65d..6c676f4 100644 --- a/charts/py-kms/README.md +++ b/charts/py-kms/README.md @@ -29,7 +29,7 @@ For more information please refer to the Helm Install command documentation loca | autoscaling.targetCPUUtilizationPercentage | int | `80` | | | fullnameOverride | string | `""` | | | image.pullPolicy | string | `"IfNotPresent"` | | -| image.repository | string | `"pykmsorg/py-kms"` | | +| image.repository | string | `"ghcr.io/py-kms-organization/py-kms"` | | | image.tag | string | `"python3"` | | | imagePullSecrets | list | `[]` | | | ingress.annotations | object | `{}` | | @@ -44,10 +44,9 @@ For more information please refer to the Helm Install command documentation loca | podAnnotations | object | `{}` | | | podSecurityContext | object | `{}` | | | py-kms.environment.HWID | string | `"RANDOM"` | | -| py-kms.environment.IP | string | `"0.0.0.0"` | | +| py-kms.environment.IP | string | `"::"` | | | py-kms.environment.LOGLEVEL | string | `"INFO"` | | | py-kms.environment.LOGSIZE | int | `2` | | -| py-kms.environment.SQLITE | bool | `true` | | | replicaCount | int | `1` | | | resources | object | `{}` | | | securityContext | object | `{}` | | diff --git a/charts/py-kms/templates/deployment.yaml b/charts/py-kms/templates/deployment.yaml index a4e46b4..e0c150b 100644 --- a/charts/py-kms/templates/deployment.yaml +++ b/charts/py-kms/templates/deployment.yaml @@ -45,14 +45,17 @@ spec: - name: http containerPort: 8080 protocol: TCP + startupProbe: + httpGet: + port: http + path: /readyz + failureThreshold: 30 # 30 seconds seem to be enough under heavy workloads + periodSeconds: 1 livenessProbe: httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / + path: /livez port: http + periodSeconds: 20 resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/py-kms/values.yaml b/charts/py-kms/values.yaml index 4b03894..ae6728a 100644 --- a/charts/py-kms/values.yaml +++ b/charts/py-kms/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - repository: pykmsorg/py-kms + repository: ghcr.io/py-kms-organization/py-kms pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: python3 @@ -20,8 +20,7 @@ py-kms: LOGSIZE: 2 LOGFILE: /var/log/py-kms.log HWID: RANDOM - SQLITE: true - IP: 0.0.0.0 + IP: '::' serviceAccount: {} # # Specifies whether a service account should be created diff --git a/docker/docker-py3-kms-minimal/Dockerfile b/docker/docker-py3-kms-minimal/Dockerfile index 7000606..883d12e 100644 --- a/docker/docker-py3-kms-minimal/Dockerfile +++ b/docker/docker-py3-kms-minimal/Dockerfile @@ -1,7 +1,7 @@ # This is a minimized version from docker/docker-py3-kms/Dockerfile without SQLite support to further reduce image size FROM alpine:3.15 -ENV IP 0.0.0.0 +ENV IP :: ENV PORT 1688 ENV EPID "" ENV LCID 1033 @@ -12,15 +12,12 @@ ENV HWID RANDOM ENV LOGLEVEL INFO ENV LOGFILE STDOUT ENV LOGSIZE "" -ENV TYPE MINIMAL -COPY ./py-kms /home/py-kms -COPY docker/requirements_minimal.txt /home/py-kms/requirements.txt +COPY docker/docker-py3-kms-minimal/requirements.txt /home/py-kms/requirements.txt RUN apk add --no-cache --update \ bash \ python3 \ py3-pip \ - python3-tkinter \ ca-certificates \ shadow \ tzdata \ @@ -31,6 +28,7 @@ bash \ # Fix undefined timezone, in case the user did not mount the /etc/localtime && ln -sf /usr/share/zoneinfo/UTC /etc/localtime +COPY ./py-kms /home/py-kms COPY docker/entrypoint.py /usr/bin/entrypoint.py COPY docker/start.py /usr/bin/start.py diff --git a/docker/docker-py3-kms-minimal/requirements.txt b/docker/docker-py3-kms-minimal/requirements.txt new file mode 100644 index 0000000..4159f71 --- /dev/null +++ b/docker/docker-py3-kms-minimal/requirements.txt @@ -0,0 +1,2 @@ +dnspython==2.2.1 +tzlocal==4.2 \ No newline at end of file diff --git a/docker/docker-py3-kms/Dockerfile b/docker/docker-py3-kms/Dockerfile index a2f4c27..996da01 100644 --- a/docker/docker-py3-kms/Dockerfile +++ b/docker/docker-py3-kms/Dockerfile @@ -1,28 +1,27 @@ # Switch to the target image FROM alpine:3.15 -ENV IP 0.0.0.0 +ARG BUILD_COMMIT=unknown +ARG BUILD_BRANCH=unknown + +ENV IP :: ENV PORT 1688 ENV EPID "" ENV LCID 1033 ENV CLIENT_COUNT 26 ENV ACTIVATION_INTERVAL 120 ENV RENEWAL_INTERVAL 10080 -ENV SQLITE true -ENV SQLITE_PORT 8080 ENV HWID RANDOM ENV LOGLEVEL INFO ENV LOGFILE STDOUT ENV LOGSIZE "" ENV TZ America/Chicago -COPY py-kms /home/py-kms/ -COPY docker/requirements.txt /home/py-kms/ +COPY docker/docker-py3-kms/requirements.txt /home/py-kms/ RUN apk add --no-cache --update \ bash \ python3 \ py3-pip \ - python3-tkinter \ sqlite-libs \ ca-certificates \ tzdata \ @@ -36,15 +35,20 @@ RUN apk add --no-cache --update \ # Fix undefined timezone, in case the user did not mount the /etc/localtime && ln -sf /usr/share/zoneinfo/UTC /etc/localtime +COPY py-kms /home/py-kms/ COPY docker/entrypoint.py /usr/bin/entrypoint.py COPY docker/start.py /usr/bin/start.py +# Web-interface specifics +COPY LICENSE /LICENSE +RUN echo "$BUILD_COMMIT" > /VERSION && echo "$BUILD_BRANCH" >> /VERSION + RUN chmod 755 /usr/bin/entrypoint.py WORKDIR /home/py-kms EXPOSE ${PORT}/tcp -EXPOSE 8080 +EXPOSE 8080/tcp HEALTHCHECK --interval=5m --timeout=3s --start-period=10s --retries=4 CMD echo | nc -z ${IP%% *} ${PORT} || exit 1 diff --git a/docker/requirements.txt b/docker/docker-py3-kms/requirements.txt similarity index 55% rename from docker/requirements.txt rename to docker/docker-py3-kms/requirements.txt index bad41ef..7bdfb22 100644 --- a/docker/requirements.txt +++ b/docker/docker-py3-kms/requirements.txt @@ -1,5 +1,5 @@ -Flask==2.1.2 -Pygments==2.12.0 dnspython==2.2.1 tzlocal==4.2 -sqlite-web==0.4.0 \ No newline at end of file + +Flask==2.1.2 +gunicorn==20.1.0 \ No newline at end of file diff --git a/docker/requirements_minimal.txt b/docker/requirements_minimal.txt deleted file mode 100644 index 509c137..0000000 --- a/docker/requirements_minimal.txt +++ /dev/null @@ -1,4 +0,0 @@ -Flask==2.1.2 -Pygments==2.12.0 -dnspython==2.2.1 -tzlocal==4.2 \ No newline at end of file diff --git a/docker/start.py b/docker/start.py index 591ed02..fd8a546 100644 --- a/docker/start.py +++ b/docker/start.py @@ -20,35 +20,20 @@ argumentVariableMapping = { '-e': 'EPID' } -sqliteWebPath = '/home/sqlite_web/sqlite_web.py' -enableSQLITE = os.environ.get('SQLITE', 'false').lower() == 'true' and os.environ.get('TYPE') != 'MINIMAL' -dbPath = os.path.join(os.sep, 'home', 'py-kms', 'db', 'pykms_database.db') +db_path = os.path.join(os.sep, 'home', 'py-kms', 'db', 'pykms_database.db') log_level_bootstrap = log_level = os.environ.get('LOGLEVEL', 'INFO') if log_level_bootstrap == "MININFO": log_level_bootstrap = "INFO" log_file = os.environ.get('LOGFILE', 'STDOUT') -listen_ip = os.environ.get('IP', '0.0.0.0').split() +listen_ip = os.environ.get('IP', '::').split() listen_port = os.environ.get('PORT', '1688') -sqlite_port = os.environ.get('SQLITE_PORT', '8080') - - -def start_kms_client(): - if not os.path.isfile(dbPath): - # Start a dummy activation to ensure the database file is created - client_cmd = [PYTHON3, '-u', 'pykms_Client.py', listen_ip[0], listen_port, - '-m', 'Windows10', '-n', 'DummyClient', '-c', 'ae3a27d1-b73a-4734-9878-70c949815218', - '-V', log_level, '-F', log_file] - if os.environ.get('LOGSIZE', '') != "": - client_cmd.append('-S') - client_cmd.append(os.environ.get('LOGSIZE')) - loggersrv.info("Starting a dummy activation to ensure the database file is created") - loggersrv.debug("client_cmd: %s" % (" ".join(str(x) for x in client_cmd).strip())) - - subprocess.run(client_cmd) - +want_webui = os.environ.get('WEBUI', '0') def start_kms(): - sqlite_process = None + # Make sure the full path to the db exists + if want_webui and not os.path.exists(os.path.dirname(db_path)): + os.makedirs(os.path.dirname(db_path), exist_ok=True) + # Build the command to execute command = [PYTHON3, '-u', 'pykms_Server.py', listen_ip[0], listen_port] for (arg, env) in argumentVariableMapping.items(): @@ -60,25 +45,25 @@ def start_kms(): for i in range(1, len(listen_ip)): command.append("-n") command.append(listen_ip[i] + "," + listen_port) - - if enableSQLITE: - loggersrv.info("Storing database file to %s" % dbPath) + if want_webui: command.append('-s') - command.append(dbPath) - os.makedirs(os.path.dirname(dbPath), exist_ok=True) + command.append(db_path) loggersrv.debug("server_cmd: %s" % (" ".join(str(x) for x in command).strip())) pykms_process = subprocess.Popen(command) + pykms_webui_process = None - # In case SQLITE is defined: Start the web interface - if enableSQLITE: - time.sleep(5) # The server may take a while to start - start_kms_client() - sqlite_cmd = ['sqlite_web', '-H', listen_ip[0], '--read-only', '-x', - dbPath, '-p', sqlite_port] - - loggersrv.debug("sqlite_cmd: %s" % (" ".join(str(x) for x in sqlite_cmd).strip())) - sqlite_process = subprocess.Popen(sqlite_cmd) + try: + if want_webui: + time.sleep(2) # Wait for the server to start up + pykms_webui_env = os.environ.copy() + pykms_webui_env['PYKMS_SQLITE_DB_PATH'] = db_path + pykms_webui_env['PORT'] = '8080' + pykms_webui_env['PYKMS_LICENSE_PATH'] = '/LICENSE' + pykms_webui_env['PYKMS_VERSION_PATH'] = '/VERSION' + pykms_webui_process = subprocess.Popen(['gunicorn', '--log-level', os.environ.get('LOGLEVEL'), 'pykms_WebUI:app'], env=pykms_webui_env) + except Exception as e: + loggersrv.error("Failed to start webui: %s" % e) try: pykms_process.wait() @@ -88,9 +73,9 @@ def start_kms(): except KeyboardInterrupt: pass - if enableSQLITE: - if None != sqlite_process: sqlite_process.terminate() - pykms_process.terminate() + if pykms_webui_process: + pykms_webui_process.terminate() + pykms_process.terminate() # Main diff --git a/docs/Getting Started.md b/docs/Getting Started.md index 1a5db8a..d66a051 100644 --- a/docs/Getting Started.md +++ b/docs/Getting Started.md @@ -6,23 +6,23 @@ What follows are some guides how to start the `pykms_Server.py` script, which pr You can simply manage a daemon that runs as a background process. This can be achieved by using any of the notes below or by writing your own solution. ### Docker -![docker-pulls](https://img.shields.io/docker/pulls/pykmsorg/py-kms) -![docker-size](https://img.shields.io/docker/image-size/pykmsorg/py-kms) - If you wish to get _py-kms_ just up and running without installing any dependencies or writing own scripts: Just use Docker ! Docker also solves problems regarding the explicit IPv4 and IPv6 usage (it just supports both). The following command will download, "install" and start _py-kms_ and also keep it alive after any service disruption. ```bash docker run -d --name py-kms --restart always -p 1688:1688 -v /etc/localtime:/etc/localtime:ro ghcr.io/py-kms-organization/py-kms ``` -If you just want to use the image and don't want to build them yourself, you can always use the official image at the [Docker Hub](https://hub.docker.com/r/pykmsorg/py-kms) (`pykmsorg/py-kms`) or [GitHub Container Registry](https://github.com/Py-KMS-Organization/py-kms/pkgs/container/py-kms) (`ghcr.io/py-kms-organization/py-kms`). To ensure that you are using always the -latest version you should check something like [watchtower](https://github.com/containrrr/watchtower) out! +If you just want to use the image and don't want to build them yourself, you can always use the official image at the [GitHub Container Registry](https://github.com/Py-KMS-Organization/py-kms/pkgs/container/py-kms) (`ghcr.io/py-kms-organization/py-kms`). To ensure that you are using always the latest version you should check something like [watchtower](https://github.com/containrrr/watchtower) out! #### Tags There are currently three tags of the image available (select one just by appending `:` to the image from above): * `latest`, currently the same like `minimal`. * `minimal`, which is based on the python3 minimal configuration of py-kms. _This tag does NOT include `sqlite` support !_ -* `python3`, which is fully configurable and equipped with `sqlite` support and a web interface (make sure to expose port 8080) for management. +* `python3`, which is fully configurable and equipped with `sqlite` support and a web-interface (make sure to expose port `8080`) for management. + +Wait... Web-interface? Yes! `py-kms` now comes with a simple web-ui to let you browse the known clients or its supported products. In case you wonder, here is a screenshot of the web-ui (*note that this screenshot may not reflect the current state of the ui*): + +![web-ui](img/webinterface.png) #### Architectures There are currently the following architectures available (if you need an other, feel free to open an issue): @@ -46,8 +46,7 @@ services: - 1688:1688 - 8080:8080 environment: - - IP=0.0.0.0 - - SQLITE=true + - IP='::' - HWID=RANDOM - LOGLEVEL=INFO restart: always @@ -62,11 +61,10 @@ Below is a little bit more extended run command, detailing all the different sup docker run -it -d --name py3-kms \ -p 8080:8080 \ -p 1688:1688 \ - -e SQLITE=true \ -v /etc/localtime:/etc/localtime:ro \ --restart unless-stopped ghcr.io/py-kms-organization/py-kms:[TAG] ``` -You can omit the `-e SQLITE=...` and `-p 8080:8080` option if you plan to use the `minimal` or `latest` image, which does not include the respective module support. +You can omit the `-p 8080:8080` option if you plan to use the `minimal` or `latest` image, which does not include the `sqlite` module support. ### Systemd If you are running a Linux distro using `systemd`, create the file: `sudo nano /etc/systemd/system/py3-kms.service`, then add the following (change it where needed) and save: @@ -82,7 +80,7 @@ Restart=always RestartSec=1 KillMode=process User=root -ExecStart=/usr/bin/python3 /py-kms/pykms_Server.py 0.0.0.0 1688 -V DEBUG -F /pykms_logserver.log +ExecStart=/usr/bin/python3 /py-kms/pykms_Server.py :: 1688 -V DEBUG -F /pykms_logserver.log [Install] WantedBy=multi-user.target @@ -91,10 +89,6 @@ Check syntax with `sudo systemd-analyze verify py3-kms.service`, correct file pe start the daemon `sudo systemctl start py3-kms.service` and view its status `sudo systemctl status py3-kms.service`. Check if daemon is correctly running with `cat /pykms_logserver.log`. Finally a few generic commands useful for interact with your daemon [here](https://linoxide.com/linux-how-to/enable-disable-services-ubuntu-systemd-upstart/). -### Etrigan (deprecated) -You can run py-kms daemonized (via [Etrigan](https://github.com/SystemRage/Etrigan)) using a command like `python3 pykms_Server.py etrigan start` and stop it with `python3 pykms_Server.py etrigan stop`. With Etrigan you have another -way to launch py-kms GUI (specially suitable if you're using a virtualenv), so `python3 pykms_Server.py etrigan start -g` and stop the GUI with `python3 pykms_Server.py etrigan stop` (or interact with the `EXIT` button). - ### Upstart (deprecated) If you are running a Linux distro using `upstart` (deprecated), create the file: `sudo nano /etc/init/py3-kms.conf`, then add the following (change it where needed) and save: ``` @@ -105,7 +99,7 @@ env PYKMSPATH=/py-kms env LOGPATH=/pykms_logserver.log start on runlevel [2345] stop on runlevel [016] -exec $PYTHONPATH/python3 $PYKMSPATH/pykms_Server.py 0.0.0.0 1688 -V DEBUG -F $LOGPATH +exec $PYTHONPATH/python3 $PYKMSPATH/pykms_Server.py :: 1688 -V DEBUG -F $LOGPATH respawn ``` Check syntax with `sudo init-checkconf -d /etc/init/py3-kms.conf`, then reload upstart to recognise this process `sudo initctl reload-configuration`. Now start the service `sudo start py3-kms`, and you can see the logfile @@ -125,7 +119,7 @@ class AppServerSvc (win32serviceutil.ServiceFramework): _svc_name_ = "py-kms" _svc_display_name_ = "py-kms" _proc = None - _cmd = ["C:\Windows\Python27\python.exe", "C:\Windows\Python27\py-kms\pykms_Server.py"] + _cmd = ["C:\Windows\Python27\python.exe", "C:\Windows\Python27\py-kms\pykms_Server.py"] # UPDATE THIS - because Python 2.7 is end of life and you will use other parameters anyway def __init__(self,args): win32serviceutil.ServiceFramework.__init__(self,args) @@ -168,10 +162,10 @@ They might be useful to you: - Python 3.x. - If the `tzlocal` module is installed, the "Request Time" in the verbose output will be converted into local time. Otherwise, it will be in UTC. - It can use the `sqlite3` module, storing activation data in a database so it can be recalled again. -- Installation example on Ubuntu / Mint: +- Installation example on Ubuntu / Mint (`requirements.txt` is from the sources): - `sudo apt-get update` - - `sudo apt-get install python3-tk python3-pip` - - `sudo pip3 install tzlocal pysqlite3` (on Ubuntu Server 22, you'll need `pysqlite3-binary` - see [this issue](https://github.com/Py-KMS-Organization/py-kms/issues/76)) + - `sudo apt-get install python3-pip` + - `pip3 install -r requirements.txt` (on Ubuntu Server 22, you'll need `pysqlite3-binary` - see [this issue](https://github.com/Py-KMS-Organization/py-kms/issues/76)) ### Startup A Linux user with `ip addr` command can get his KMS IP (Windows users can try `ipconfig /all`). diff --git a/docs/Usage.md b/docs/Usage.md index 0a14598..6586386 100644 --- a/docs/Usage.md +++ b/docs/Usage.md @@ -7,7 +7,7 @@ Follows a list of usable parameters: ip -> Instructs py-kms to listen on _IPADDRESS_ (can be an hostname too). If this option is not specified, _IPADDRESS_ 0.0.0.0 is used. +> Instructs py-kms to listen on _IPADDRESS_ (can be an hostname too). If this option is not specified, _IPADDRESS_ `::` is used. port > Define TCP _PORT_ the KMS service is listening on. Default is 1688. @@ -53,7 +53,6 @@ e.g. because it could not reach the server. The default is 120 minutes (2 hours) -s or --sqlite [] > Use this option to store request information from unique clients in an SQLite database. Deactivated by default. -If enabled the default database file is _pykms_database.db_. You can also provide a specific location. -t0 or --timeout-idle > Maximum inactivity time (in seconds) after which the connection with the client is closed. @@ -75,7 +74,7 @@ user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py -V INFO ``` creates _pykms_logserver.log_ with these initial messages: ``` -Mon, 12 Jun 2017 22:09:00 INFO TCP server listening at 0.0.0.0 on port 1688. +Mon, 12 Jun 2017 22:09:00 INFO TCP server listening at :: on port 1688. Mon, 12 Jun 2017 22:09:00 INFO HWID: 364F463A8863D35F ``` @@ -83,11 +82,11 @@ Mon, 12 Jun 2017 22:09:00 INFO HWID: 364F463A8863D35F > Creates a _LOGFILE.log_ logging file. The default is named _pykms_logserver.log_. example: ``` -user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 8080 -F ~/path/to/folder/py-kms/newlogfile.log -V INFO -w RANDOM +user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 1688 -F ~/path/to/folder/py-kms/newlogfile.log -V INFO -w RANDOM ``` creates _newlogfile.log_ with these initial messages: ``` -Mon, 12 Jun 2017 22:09:00 INFO TCP server listening at 192.168.1.102 on port 8080. +Mon, 12 Jun 2017 22:09:00 INFO TCP server listening at 192.168.1.102 on port 1688. Mon, 12 Jun 2017 22:09:00 INFO HWID: 58C4F4E53AE14224 ``` @@ -125,14 +124,14 @@ examples (with fictitious addresses and ports): | command | address (main) | backlog (main) | reuse port (main) | address (listen) | backlog (listen) | reuse port (listen) | dualstack (main / listen) | | --- | --- | --- | --- | --- | --- | --- | --- | -| `python3 pykms_Server.py connect -b 12` | ('0.0.0.0', 1688) | 12 | True | [] | [] | [] | False | +| `python3 pykms_Server.py connect -b 12` | ('::', 1688) | 12 | True | [] | [] | [] | False | | `python3 pykms_Server.py :: connect -b 12 -u -d` | ('::', 1688) | 12 | False | [] | [] | [] | True | -| `python3 pykms_Server.py connect -n 1.1.1.1,1699 -b 10` | ('0.0.0.0', 1688) | 5 | True | [('1.1.1.1', 1699)] | [10] | [True] | False | +| `python3 pykms_Server.py connect -n 1.1.1.1,1699 -b 10` | ('::', 1688) | 5 | True | [('1.1.1.1', 1699)] | [10] | [True] | False | | `python3 pykms_Server.py :: 1655 connect -n 2001:db8:0:200::7,1699 -d -b 10 -n 2.2.2.2,1677 -u` | ('::', 1655) | 5 | True | [('2001:db8:0:200::7', 1699), ('2.2.2.2', 1677)] | [10, 5] | [True, False] | True | -| `python3 pykms_Server.py connect -b 12 -u -n 1.1.1.1,1699 -b 10 -n 2.2.2.2,1677 -b 15` | ('0.0.0.0', 1688) | 12 | False | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [10, 15] | [False, False] | False | -| `python3 pykms_Server.py connect -b 12 -n 1.1.1.1,1699 -u -n 2.2.2.2,1677` | ('0.0.0.0', 1688) | 12 | True | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [12, 12] | [False, True] | False | -| `python3 pykms_Server.py connect -d -u -b 8 -n 1.1.1.1,1699 -n 2.2.2.2,1677 -b 12` | ('0.0.0.0', 1688) | 8 | False | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [8, 12] | [False, False] | True | -| `python3 pykms_Server.py connect -b 11 -u -n ::,1699 -n 2.2.2.2,1677` | ('0.0.0.0', 1688) | 11 | False | [('::', 1699), ('2.2.2.2', 1677)] | [11, 11] | [False, False] | False | +| `python3 pykms_Server.py connect -b 12 -u -n 1.1.1.1,1699 -b 10 -n 2.2.2.2,1677 -b 15` | ('::', 1688) | 12 | False | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [10, 15] | [False, False] | False | +| `python3 pykms_Server.py connect -b 12 -n 1.1.1.1,1699 -u -n 2.2.2.2,1677` | ('::', 1688) | 12 | True | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [12, 12] | [False, True] | False | +| `python3 pykms_Server.py connect -d -u -b 8 -n 1.1.1.1,1699 -n 2.2.2.2,1677 -b 12` | ('::', 1688) | 8 | False | [('1.1.1.1', 1699), ('2.2.2.2', 1677)] | [8, 12] | [False, False] | True | +| `python3 pykms_Server.py connect -b 11 -u -n ::,1699 -n 2.2.2.2,1677` | ('::', 1688) | 11 | False | [('::', 1699), ('2.2.2.2', 1677)] | [11, 11] | [False, False] | False | ### pykms_Client.py If _py-kms_ server doesn't works correctly, you can test it with the KMS client `pykms_Client.py`, running on the same machine where you started `pykms_Server.py`. @@ -202,8 +201,8 @@ You can enable same _pykms_Server.py_ suboptions of `-F`. This are the currently used `ENV` statements from the Dockerfile(s). For further references what exactly the parameters mean, please see the start parameters for the [server](Usage.html#pykms-server-py). ``` # IP-address -# The IP address to listen on. The default is "0.0.0.0" (all interfaces). -ENV IP 0.0.0.0 +# The IP address to listen on. The default is "::" (all interfaces). +ENV IP :: # TCP-port # The network port to listen on. The default is "1688". @@ -230,14 +229,6 @@ ENV ACTIVATION_INTERVAL 120 # Use this flag to specify the renewal interval (in minutes). Default is 10080 minutes (7 days). ENV RENEWAL_INTERVAL 10080 -# Use SQLITE -# Use this flag to store request information from unique clients in an SQLite database. -ENV SQLITE false - -# TCP-port -# The network port to listen with the web interface on. The default is "8080". -ENV SQLITE_PORT 8080 - # hwid # Use this flag to specify a HWID. # The HWID must be an 16-character string of hex characters. diff --git a/docs/img/webinterface.png b/docs/img/webinterface.png new file mode 100644 index 0000000..8a0b36b Binary files /dev/null and b/docs/img/webinterface.png differ diff --git a/py-kms/Etrigan.py b/py-kms/Etrigan.py deleted file mode 100644 index c9cf193..0000000 --- a/py-kms/Etrigan.py +++ /dev/null @@ -1,609 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import atexit -import errno -import os -import sys -import time -import signal -import logging -import argparse -from collections.abc import Sequence - -__version__ = "0.1" -__license__ = "MIT License" -__author__ = u"Matteo ℱan " -__copyright__ = "© Copyright 2020" -__url__ = "https://github.com/SystemRage/Etrigan" -__description__ = "Etrigan: a python daemonizer that rocks." - - -class Etrigan(object): - """ - Daemonizer based on double-fork method - -------------------------------------- - Each option can be passed as a keyword argument or modified by assigning - to an attribute on the instance: - - jasonblood = Etrigan(pidfile, - argument_example_1 = foo, - argument_example_2 = bar) - - that is equivalent to: - - jasonblood = Etrigan(pidfile) - jasonblood.argument_example_1 = foo - jasonblood.argument_example_2 = bar - - Object constructor expects always `pidfile` argument. - `pidfile` - Path to the pidfile. - - The following other options are defined: - `stdin` - `stdout` - `stderr` - :Default: `os.devnull` - File objects used as the new file for the standard I/O streams - `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. - - `funcs_to_daemonize` - :Default: `[]` - Define a list of your custom functions - which will be executed after daemonization. - If None, you have to subclass Etrigan `run` method. - Note that these functions can return elements that will be - added to Etrigan object (`etrigan_add` list) so the other subsequent - ones can reuse them for further processing. - You only have to provide indexes of `etrigan_add` list, - (an int (example: 2) for single index or a string (example: '1:4') for slices) - as first returning element. - - `want_quit` - :Default: `False` - If `True`, runs Etrigan `quit_on_start` or `quit_on_stop` - lists of your custom functions at the end of `start` or `stop` operations. - These can return elements as `funcs_to_daemonize`. - - `logfile` - :Default: `None` - Path to the output log file. - - `loglevel` - :Default: `None` - Set the log level of logging messages. - - `mute` - :Default: `False` - Disable all stdout and stderr messages (before double forking). - - `pause_loop` - :Default: `None` - Seconds of pause between the calling, in an infinite loop, - of every function in `funcs_to_daemonize` list. - If `-1`, no pause between the calling, in an infinite loop, - of every function in `funcs_to_daemonize` list. - If `None`, only one run (no infinite loop) of functions in - `funcs_to_daemonize` list, without pause. - """ - - def __init__(self, pidfile, - stdin = os.devnull, stdout = os.devnull, stderr = os.devnull, - funcs_to_daemonize = [], want_quit = False, - logfile = None, loglevel = None, - mute = False, pause_loop = None): - - self.pidfile = pidfile - self.funcs_to_daemonize = funcs_to_daemonize - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - self.logfile = logfile - self.loglevel = loglevel - self.mute = mute - self.want_quit = want_quit - self.pause_loop = pause_loop - # internal only. - self.homedir = '/' - self.umask = 0o22 - self.etrigan_restart, self.etrigan_reload = (False for _ in range(2)) - self.etrigan_alive = True - self.etrigan_add = [] - self.etrigan_index = None - # seconds of pause between stop and start during the restart of the daemon. - self.pause_restart = 5 - # when terminate a process, seconds to wait until kill the process with signal. - # self.pause_kill = 3 - - # create logfile. - self.setup_files() - - def handle_terminate(self, signum, frame): - if os.path.exists(self.pidfile): - self.etrigan_alive = False - # eventually run quit (on stop) function/s. - if self.want_quit: - if not isinstance(self.quit_on_stop, (list, tuple)): - self.quit_on_stop = [self.quit_on_stop] - self.execute(self.quit_on_stop) - # then always run quit standard. - self.quit_standard() - else: - self.view(self.logdaemon.error, self.emit_error, "Failed to stop the daemon process: can't find PIDFILE '%s'" %self.pidfile) - sys.exit(0) - - def handle_reload(self, signum, frame): - self.etrigan_reload = True - - def setup_files(self): - self.pidfile = os.path.abspath(self.pidfile) - - if self.logfile is not None: - self.logdaemon = logging.getLogger('logdaemon') - self.logdaemon.setLevel(self.loglevel) - - filehandler = logging.FileHandler(self.logfile) - filehandler.setLevel(self.loglevel) - formatter = logging.Formatter(fmt = '[%(asctime)s] [%(levelname)8s] --- %(message)s', - datefmt = '%Y-%m-%d %H:%M:%S') - filehandler.setFormatter(formatter) - self.logdaemon.addHandler(filehandler) - else: - nullhandler = logging.NullHandler() - self.logdaemon.addHandler(nullhandler) - - def emit_error(self, message, to_exit = True): - """ Print an error message to STDERR. """ - if not self.mute: - sys.stderr.write(message + '\n') - sys.stderr.flush() - if to_exit: - sys.exit(1) - - def emit_message(self, message, to_exit = False): - """ Print a message to STDOUT. """ - if not self.mute: - sys.stdout.write(message + '\n') - sys.stdout.flush() - if to_exit: - sys.exit(0) - - def view(self, logobj, emitobj, msg, **kwargs): - options = {'to_exit' : False, - 'silent' : False - } - options.update(kwargs) - - if logobj: - logobj(msg) - if emitobj: - if not options['silent']: - emitobj(msg, to_exit = options['to_exit']) - - def daemonize(self): - """ - Double-forks the process to daemonize the script. - see Stevens' "Advanced Programming in the UNIX Environment" for details (ISBN 0201563177) - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - """ - self.view(self.logdaemon.debug, None, "Attempting to daemonize the process...") - - # First fork. - self.fork(msg = "First fork") - # Decouple from parent environment. - self.detach() - # Second fork. - self.fork(msg = "Second fork") - # Write the PID file. - self.create_pidfile() - self.view(self.logdaemon.info, self.emit_message, "The daemon process has started.") - # Redirect standard file descriptors. - sys.stdout.flush() - sys.stderr.flush() - self.attach('stdin', mode = 'r') - self.attach('stdout', mode = 'a+') - - try: - self.attach('stderr', mode = 'a+', buffering = 0) - except ValueError: - # Python 3 can't have unbuffered text I/O. - self.attach('stderr', mode = 'a+', buffering = 1) - - # Handle signals. - signal.signal(signal.SIGINT, self.handle_terminate) - signal.signal(signal.SIGTERM, self.handle_terminate) - signal.signal(signal.SIGHUP, self.handle_reload) - #signal.signal(signal.SIGKILL....) - - def fork(self, msg): - try: - pid = os.fork() - if pid > 0: - self.view(self.logdaemon.debug, None, msg + " success with PID %d." %pid) - # Exit from parent. - sys.exit(0) - except Exception as e: - msg += " failed: %s." %str(e) - self.view(self.logdaemon.error, self.emit_error, msg) - - def detach(self): - # cd to root for a guarenteed working dir. - try: - os.chdir(self.homedir) - except Exception as e: - msg = "Unable to change working directory: %s." %str(e) - self.view(self.logdaemon.error, self.emit_error, msg) - - # clear the session id to clear the controlling tty. - pid = os.setsid() - if pid == -1: - sys.exit(1) - - # set the umask so we have access to all files created by the daemon. - try: - os.umask(self.umask) - except Exception as e: - msg = "Unable to change file creation mask: %s." %str(e) - self.view(self.logdaemon.error, self.emit_error, msg) - - def attach(self, name, mode, buffering = -1): - with open(getattr(self, name), mode, buffering) as stream: - os.dup2(stream.fileno(), getattr(sys, name).fileno()) - - def checkfile(self, path, typearg, typefile): - filename = os.path.basename(path) - pathname = os.path.dirname(path) - if not os.path.isdir(pathname): - msg = "argument %s: invalid directory: '%s'. Exiting..." %(typearg, pathname) - self.view(self.logdaemon.error, self.emit_error, msg) - elif not filename.lower().endswith(typefile): - msg = "argument %s: not a %s file, invalid extension: '%s'. Exiting..." %(typearg, typefile, filename) - self.view(self.logdaemon.error, self.emit_error, msg) - - def create_pidfile(self): - atexit.register(self.delete_pidfile) - pid = os.getpid() - try: - with open(self.pidfile, 'w+') as pf: - pf.write("%s\n" %pid) - self.view(self.logdaemon.debug, None, "PID %d written to '%s'." %(pid, self.pidfile)) - except Exception as e: - msg = "Unable to write PID to PIDFILE '%s': %s" %(self.pidfile, str(e)) - self.view(self.logdaemon.error, self.emit_error, msg) - - def delete_pidfile(self, pid): - # Remove the PID file. - try: - os.remove(self.pidfile) - self.view(self.logdaemon.debug, None, "Removing PIDFILE '%s' with PID %d." %(self.pidfile, pid)) - except Exception as e: - if e.errno != errno.ENOENT: - self.view(self.logdaemon.error, self.emit_error, str(e)) - - def get_pidfile(self): - # Get the PID from the PID file. - if self.pidfile is None: - return None - if not os.path.isfile(self.pidfile): - return None - - try: - with open(self.pidfile, 'r') as pf: - pid = int(pf.read().strip()) - self.view(self.logdaemon.debug, None, "Found PID %d in PIDFILE '%s'" %(pid, self.pidfile)) - except Exception as e: - self.view(self.logdaemon.warning, None, "Empty or broken PIDFILE") - pid = None - - def pid_exists(pid): - # psutil _psposix.py. - if pid == 0: - return True - try: - os.kill(pid, 0) - except OSError as e: - if e.errno == errno.ESRCH: - return False - elif e.errno == errno.EPERM: - return True - else: - self.view(self.logdaemon.error, self.emit_error, str(e)) - else: - return True - - if pid is not None and pid_exists(pid): - return pid - else: - # Remove the stale PID file. - self.delete_pidfile(pid) - return None - - def start(self): - """ Start the daemon. """ - self.view(self.logdaemon.info, self.emit_message, "Starting the daemon process...", silent = self.etrigan_restart) - - # Check for a PID file to see if the Daemon is already running. - pid = self.get_pidfile() - if pid is not None: - msg = "A previous daemon process with PIDFILE '%s' already exists. Daemon already running ?" %self.pidfile - self.view(self.logdaemon.warning, self.emit_error, msg, to_exit = False) - return - - # Daemonize the main process. - self.daemonize() - # Start a infinitive loop that periodically runs `funcs_to_daemonize`. - self.loop() - # eventualy run quit (on start) function/s. - if self.want_quit: - if not isinstance(self.quit_on_start, (list, tuple)): - self.quit_on_start = [self.quit_on_start] - self.execute(self.quit_on_start) - - def stop(self): - """ Stop the daemon. """ - self.view(None, self.emit_message, "Stopping the daemon process...", silent = self.etrigan_restart) - - self.logdaemon.disabled = True - pid = self.get_pidfile() - self.logdaemon.disabled = False - if not pid: - # Just to be sure. A ValueError might occur - # if the PIDFILE is empty but does actually exist. - if os.path.exists(self.pidfile): - self.delete_pidfile(pid) - - msg = "Can't find the daemon process with PIDFILE '%s'. Daemon not running ?" %self.pidfile - self.view(self.logdaemon.warning, self.emit_error, msg, to_exit = False) - return - - # Try to kill the daemon process. - try: - while True: - os.kill(pid, signal.SIGTERM) - time.sleep(0.1) - except Exception as e: - if (e.errno != errno.ESRCH): - self.view(self.logdaemon.error, self.emit_error, "Failed to stop the daemon process: %s" %str(e)) - else: - self.view(None, self.emit_message, "The daemon process has ended correctly.", silent = self.etrigan_restart) - - def restart(self): - """ Restart the daemon. """ - self.view(self.logdaemon.info, self.emit_message, "Restarting the daemon process...") - self.etrigan_restart = True - self.stop() - if self.pause_restart: - time.sleep(self.pause_restart) - self.etrigan_alive = True - self.start() - - def reload(self): - pass - - def status(self): - """ Get status of the daemon. """ - self.view(self.logdaemon.info, self.emit_message, "Viewing the daemon process status...") - - if self.pidfile is None: - self.view(self.logdaemon.error, self.emit_error, "Cannot get the status of daemon without PIDFILE.") - - pid = self.get_pidfile() - if pid is None: - self.view(self.logdaemon.info, self.emit_message, "The daemon process is not running.", to_exit = True) - else: - try: - with open("/proc/%d/status" %pid, 'r') as pf: - pass - self.view(self.logdaemon.info, self.emit_message, "The daemon process is running.", to_exit = True) - except Exception as e: - msg = "There is not a process with the PIDFILE '%s': %s" %(self.pidfile, str(e)) - self.view(self.logdaemon.error, self.emit_error, msg) - - def flatten(self, alistoflists, ltypes = Sequence): - # https://stackoverflow.com/questions/2158395/flatten-an-irregular-list-of-lists/2158532#2158532 - alistoflists = list(alistoflists) - while alistoflists: - while alistoflists and isinstance(alistoflists[0], ltypes): - alistoflists[0:1] = alistoflists[0] - if alistoflists: yield alistoflists.pop(0) - - def exclude(self, func): - from inspect import getargspec - args = getargspec(func) - if callable(func): - try: - args[0].pop(0) - except IndexError: - pass - return args - else: - self.view(self.logdaemon.error, self.emit_error, "Not a function.") - return - - def execute(self, some_functions): - returned = None - if isinstance(some_functions, (list, tuple)): - for func in some_functions: - l_req = len(self.exclude(func)[0]) - - if l_req == 0: - returned = func() - else: - l_add = len(self.etrigan_add) - if l_req > l_add: - self.view(self.logdaemon.error, self.emit_error, - "Can't evaluate function: given %s, required %s." %(l_add, l_req)) - return - else: - arguments = self.etrigan_add[self.etrigan_index] - l_args = (len(arguments) if isinstance(arguments, list) else 1) - if (l_args > l_req) or (l_args < l_req): - self.view(self.logdaemon.error, self.emit_error, - "Can't evaluate function: given %s, required %s." %(l_args, l_req)) - return - else: - if isinstance(arguments, list): - returned = func(*arguments) - else: - returned = func(arguments) - - if returned: - if isinstance(returned, (list, tuple)): - if isinstance(returned[0], int): - self.etrigan_index = returned[0] - else: - self.etrigan_index = slice(*map(int, returned[0].split(':'))) - if returned[1:] != []: - self.etrigan_add.append(returned[1:]) - self.etrigan_add = list(self.flatten(self.etrigan_add)) - else: - self.view(self.logdaemon.error, self.emit_error, "Function should return list or tuple.") - returned = None - else: - if some_functions is None: - self.run() - - def loop(self): - try: - if self.pause_loop is None: - # one-shot. - self.execute(self.funcs_to_daemonize) - else: - if self.pause_loop >= 0: - # infinite with pause. - time.sleep(self.pause_loop) - while self.etrigan_alive: - self.execute(self.funcs_to_daemonize) - time.sleep(self.pause_loop) - elif self.pause_loop == -1: - # infinite without pause. - while self.etrigan_alive: - self.execute(self.funcs_to_daemonize) - except Exception as e: - msg = "The daemon process start method failed: %s" %str(e) - self.view(self.logdaemon.error, self.emit_error, msg) - - def quit_standard(self): - self.view(self.logdaemon.info, None, "Stopping the daemon process...") - self.delete_pidfile(self.get_pidfile()) - self.view(self.logdaemon.info, None, "The daemon process has ended correctly.") - - def quit_on_start(self): - """ - Override this method when you subclass Daemon. - """ - self.quit_standard() - - def quit_on_stop(self): - """ - Override this method when you subclass Daemon. - """ - pass - - def run(self): - """ - Override this method when you subclass Daemon. - It will be called after the process has been - daemonized by start() or restart(). - """ - pass - -#----------------------------------------------------------------------------------------------------------------------------------------------------------- - -class JasonBlood(Etrigan): - def run(self): - jasonblood_func() - -def jasonblood_func(): - with open(os.path.join('.', 'etrigan_test.txt'), 'a') as file: - file.write("Yarva Demonicus Etrigan " + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) + '\n') - -def Etrigan_parser(parser = None): - if parser is None: - # create a new parser. - parser = argparse.ArgumentParser(description = __description__, epilog = __version__) - if not parser.add_help: - # create help argument. - parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit") - - # attach to an existent parser. - parser.add_argument("operation", action = "store", choices = ["start", "stop", "restart", "status", "reload"], - help = "Select an operation for daemon.", type = str) - parser.add_argument("--etrigan-pid", - action = "store", dest = "etriganpid", default = "/tmp/etrigan.pid", - help = "Choose a pidfile path. Default is \"/tmp/etrigan.pid\".", type = str) #'/var/run/etrigan.pid' - parser.add_argument("--etrigan-log", - action = "store", dest = "etriganlog", default = os.path.join('.', "etrigan.log"), - help = "Use this option to choose an output log file; for not logging don't select it. Default is \"etrigan.log\".", type = str) - parser.add_argument("--etrigan-lev", - action = "store", dest = "etriganlev", default = "DEBUG", - choices = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], - help = "Use this option to set a log level. Default is \"DEBUG\".", type = str) - parser.add_argument("--etrigan-mute", - action = "store_const", dest = 'etriganmute', const = True, default = False, - help = "Disable all stdout and stderr messages.") - return parser - -class Etrigan_check(object): - def emit_opt_err(self, msg): - print(msg) - sys.exit(1) - - def checkfile(self, path, typearg, typefile): - filename, extension = os.path.splitext(path) - pathname = os.path.dirname(path) - if not os.path.isdir(pathname): - msg = "argument `%s`: invalid directory: '%s'. Exiting..." %(typearg, pathname) - self.emit_opt_err(msg) - elif not extension == typefile: - msg = "argument `%s`: not a %s file, invalid extension: '%s'. Exiting..." %(typearg, typefile, extension) - self.emit_opt_err(msg) - - def checkfunction(self, funcs, booleans): - if not isinstance(funcs, (list, tuple)): - if funcs is not None: - msg = "argument `funcs_to_daemonize`: provide list, tuple or None" - self.emit_opt_err(msg) - - for elem in booleans: - if not type(elem) == bool: - msg = "argument `want_quit`: not a boolean." - self.emit_opt_err(msg) - -def Etrigan_job(type_oper, daemon_obj): - Etrigan_check().checkfunction(daemon_obj.funcs_to_daemonize, - [daemon_obj.want_quit]) - if type_oper == "start": - daemon_obj.start() - elif type_oper == "stop": - daemon_obj.stop() - elif type_oper == "restart": - daemon_obj.restart() - elif type_oper == "status": - daemon_obj.status() - elif type_oper == "reload": - daemon_obj.reload() - sys.exit(0) - -def main(): - # Parse arguments. - parser = Etrigan_parser() - args = vars(parser.parse_args()) - # Check arguments. - Etrigan_check().checkfile(args['etriganpid'], '--etrigan-pid', '.pid') - Etrigan_check().checkfile(args['etriganlog'], '--etrigan-log', '.log') - - # Setup daemon. - jasonblood_1 = Etrigan(pidfile = args['etriganpid'], logfile = args['etriganlog'], loglevel = args['etriganlev'], - mute = args['etriganmute'], - funcs_to_daemonize = [jasonblood_func], pause_loop = 5) - -## jasonblood_2 = JasonBlood(pidfile = args['etriganpid'], logfile = args['etriganlog'], loglevel = args['etriganlev'], -## mute = args['etriganmute'], -## funcs_to_daemonize = None, pause_loop = 5) - # Do job. - Etrigan_job(args['operation'], jasonblood_1) - -if __name__ == '__main__': - main() diff --git a/py-kms/graphics/pykms_Arrow_Left.gif b/py-kms/graphics/pykms_Arrow_Left.gif deleted file mode 100644 index 731405d..0000000 Binary files a/py-kms/graphics/pykms_Arrow_Left.gif and /dev/null differ diff --git a/py-kms/graphics/pykms_Arrow_Right.gif b/py-kms/graphics/pykms_Arrow_Right.gif deleted file mode 100644 index 422583f..0000000 Binary files a/py-kms/graphics/pykms_Arrow_Right.gif and /dev/null differ diff --git a/py-kms/graphics/pykms_Keyhole_Left.gif b/py-kms/graphics/pykms_Keyhole_Left.gif deleted file mode 100644 index 5d2933c..0000000 Binary files a/py-kms/graphics/pykms_Keyhole_Left.gif and /dev/null differ diff --git a/py-kms/graphics/pykms_Keyhole_Right.gif b/py-kms/graphics/pykms_Keyhole_Right.gif deleted file mode 100644 index d7499bb..0000000 Binary files a/py-kms/graphics/pykms_Keyhole_Right.gif and /dev/null differ diff --git a/py-kms/graphics/pykms_Keys.gif b/py-kms/graphics/pykms_Keys.gif deleted file mode 100644 index 540eadc..0000000 Binary files a/py-kms/graphics/pykms_Keys.gif and /dev/null differ diff --git a/py-kms/pykms_Base.py b/py-kms/pykms_Base.py index 1f38ba9..e0e9a6b 100644 --- a/py-kms/pykms_Base.py +++ b/py-kms/pykms_Base.py @@ -4,13 +4,12 @@ import binascii import logging import time import uuid -import socket from pykms_Structure import Structure from pykms_DB2Dict import kmsDB2Dict from pykms_PidGenerator import epidGenerator from pykms_Filetimes import filetime_to_dt -from pykms_Sql import sql_initialize, sql_update, sql_update_epid +from pykms_Sql import sql_update, sql_update_epid from pykms_Format import justify, byterize, enco, deco, pretty_printer #-------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -214,7 +213,6 @@ could be detected as not genuine !{end}" %currentClientCount) 'product' : infoDict["skuId"]}) # Create database. if self.srv_config['sqlite']: - sql_initialize(self.srv_config['sqlite']) sql_update(self.srv_config['sqlite'], infoDict) return self.createKmsResponse(kmsRequest, currentClientCount, appName) diff --git a/py-kms/pykms_Client.py b/py-kms/pykms_Client.py index 109f83a..d2fe1b5 100644 --- a/py-kms/pykms_Client.py +++ b/py-kms/pykms_Client.py @@ -45,10 +45,9 @@ class client_thread(threading.Thread): def __init__(self, name): threading.Thread.__init__(self) self.name = name - self.with_gui = False def run(self): - clt_main(with_gui = self.with_gui) + clt_main() #--------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -56,7 +55,7 @@ loggerclt = logging.getLogger('logclt') # 'help' string - 'default' value - 'dest' string. clt_options = { - 'ip' : {'help' : 'The IP address or hostname of the KMS server.', 'def' : "0.0.0.0", 'des' : "ip"}, + 'ip' : {'help' : 'The IP address or hostname of the KMS server.', 'def' : "::", 'des' : "ip"}, 'port' : {'help' : 'The port the KMS service is listening on. The default is \"1688\".', 'def' : 1688, 'des' : "port"}, 'mode' : {'help' : 'Use this flag to manually specify a Microsoft product for testing the server. The default is \"Windows81\"', 'def' : "Windows8.1", 'des' : "mode", @@ -297,11 +296,10 @@ def client_create(clt_sock): pretty_printer(log_obj = loggerclt.warning, to_exit = True, where = "clt", put_text = "{reverse}{magenta}{bold}Something went wrong. Exiting...{end}") -def clt_main(with_gui = False): +def clt_main(): try: - if not with_gui: - # Parse options. - client_options() + # Parse options. + client_options() # Check options. client_check() @@ -393,4 +391,4 @@ def readKmsResponseV6(data): return message if __name__ == "__main__": - clt_main(with_gui = False) + clt_main() diff --git a/py-kms/pykms_Format.py b/py-kms/pykms_Format.py index dd968c0..ee37363 100644 --- a/py-kms/pykms_Format.py +++ b/py-kms/pykms_Format.py @@ -274,9 +274,7 @@ class ShellMessage(object): ShellMessage.indx += 1 def print_logging_setup(self, logger, async_flag, formatter = logging.Formatter('%(name)s %(message)s')): - from pykms_GuiBase import gui_redirector - stream = gui_redirector(StringIO()) - handler = logging.StreamHandler(stream) + handler = logging.StreamHandler(StringIO()) handler.name = 'LogStream' handler.setLevel(logging.INFO) handler.setFormatter(formatter) @@ -293,9 +291,6 @@ class ShellMessage(object): def print_logging(self, toprint): if (self.nshell and ((0 in self.nshell) or (2 in self.nshell and not ShellMessage.viewclt))) or ShellMessage.indx == 0: - from pykms_GuiBase import gui_redirector_setup, gui_redirector_clear - gui_redirector_setup() - gui_redirector_clear() self.print_logging_setup(ShellMessage.loggersrv_pty, ShellMessage.asyncmsgsrv) self.print_logging_setup(ShellMessage.loggerclt_pty, ShellMessage.asyncmsgclt) @@ -405,7 +400,6 @@ def pretty_printer(**kwargs): if None `put_text` must be defined for printing process. `to_exit ` --> if True system exit is called. `where` --> specifies if message is server-side or client-side - (useful for GUI redirect). """ # Set defaults for not defined options. options = {'log_obj' : None, diff --git a/py-kms/pykms_GuiBase.py b/py-kms/pykms_GuiBase.py deleted file mode 100644 index 30f5dec..0000000 --- a/py-kms/pykms_GuiBase.py +++ /dev/null @@ -1,948 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -import sys -import threading -from time import sleep -import tkinter as tk -from tkinter import ttk -from tkinter import messagebox -from tkinter import filedialog -import tkinter.font as tkFont - -from pykms_Server import srv_options, srv_version, srv_config, server_terminate, serverqueue, serverthread -from pykms_GuiMisc import ToolTip, TextDoubleScroll, TextRedirect, ListboxOfRadiobuttons -from pykms_GuiMisc import custom_background, custom_pages -from pykms_Client import clt_options, clt_version, clt_config, client_thread - -gui_version = "py-kms_gui_v3.0" -__license__ = "MIT License" -__author__ = u"Matteo ℱan " -__copyright__ = "© Copyright 2020" -__url__ = "https://github.com/SystemRage/py-kms" -gui_description = "A GUI for py-kms." - -##--------------------------------------------------------------------------------------------------------------------------------------------------------- -def get_ip_address(): - if os.name == 'posix': - import subprocess - ip = subprocess.getoutput("hostname -I") - elif os.name == 'nt': - import socket - ip = socket.gethostbyname(socket.gethostname()) - else: - ip = 'Unknown' - return ip - -def gui_redirector(stream, redirect_to = TextRedirect.Pretty, redirect_conditio = True, stderr_side = "srv"): - global txsrv, txclt, txcol - if redirect_conditio: - if stream == 'stdout': - sys.stdout = redirect_to(txsrv, txclt, txcol) - elif stream == 'stderr': - sys.stderr = redirect_to(txsrv, txclt, txcol, stderr_side) - else: - stream = redirect_to(txsrv, txclt, txcol) - return stream - -def gui_redirector_setup(): - TextRedirect.Pretty.tag_num = 0 - TextRedirect.Pretty.newlinecut = [-1, -2, -4, -5] - -def gui_redirector_clear(): - global txsrv, oysrv - try: - if oysrv: - txsrv.configure(state = 'normal') - txsrv.delete('1.0', 'end') - txsrv.configure(state = 'disabled') - except: - # self.onlysrv not defined (menu not used) - pass - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- - -class KmsGui(tk.Tk): - def __init__(self, *args, **kwargs): - tk.Tk.__init__(self, *args, **kwargs) - self.wraplength = 200 - serverthread.with_gui = True - self.validation_int = (self.register(self.validate_int), "%S") - self.validation_float = (self.register(self.validate_float), "%P") - - ## Define fonts and colors. - self.customfonts = {'btn' : tkFont.Font(family = 'Fixedsys', size = 11, weight = 'bold'), - 'oth' : tkFont.Font(family = 'Times', size = 9, weight = 'bold'), - 'opt' : tkFont.Font(family = 'Fixedsys', size = 9, weight = 'bold'), - 'lst' : tkFont.Font(family = 'Fixedsys', size = 8, weight = 'bold', slant = 'italic'), - 'msg' : tkFont.Font(family = 'Monospace', size = 6), # need a monospaced type (like courier, etc..). - } - - self.customcolors = { 'black' : '#000000', - 'white' : '#FFFFFF', - 'green' : '#00EE76', - 'yellow' : '#FFFF00', - 'magenta' : '#CD00CD', - 'orange' : '#FFA500', - 'red' : '#FF4500', - 'blue' : '#1E90FF', - 'cyan' : '#AFEEEE', - 'lavender': '#E6E6FA', - 'brown' : '#A52A2A', - } - - self.option_add('*TCombobox*Listbox.font', self.customfonts['lst']) - - self.gui_create() - - def invert(self, widgets = []): - for widget in widgets: - if widget['state'] == 'normal': - widget.configure(state = 'disabled') - elif widget['state'] == 'disabled': - widget.configure(state = 'normal') - - def gui_menu(self): - self.onlysrv, self.onlyclt = (False for _ in range(2)) - menubar = tk.Menu(self) - prefmenu = tk.Menu(menubar, tearoff = 0, font = ("Noto Sans Regular", 10), borderwidth = 3, relief = 'ridge') - menubar.add_cascade(label = 'Preferences', menu = prefmenu) - prefmenu.add_command(label = 'Enable server-side mode', command = lambda: self.pref_onlysrv(prefmenu)) - prefmenu.add_command(label = 'Enable client-side mode', command = lambda: self.pref_onlyclt(prefmenu)) - self.config(menu = menubar) - - def pref_onlysrv(self, menu): - global oysrv - - if self.onlyclt or serverthread.is_running_server: - return - self.onlysrv = not self.onlysrv - if self.onlysrv: - menu.entryconfigure(0, label = 'Disable server-side mode') - self.clt_on_show(force_remove = True) - else: - menu.entryconfigure(0, label = 'Enable server-side mode') - self.invert(widgets = [self.shbtnclt]) - oysrv = self.onlysrv - - def pref_onlyclt(self, menu): - if self.onlysrv or serverthread.is_running_server: - return - self.onlyclt = not self.onlyclt - if self.onlyclt: - menu.entryconfigure(1, label = 'Disable client-side mode') - if self.shbtnclt['text'] == 'SHOW\nCLIENT': - self.clt_on_show(force_view = True) - self.optsrvwin.grid_remove() - self.msgsrvwin.grid_remove() - gui_redirector('stderr', redirect_to = TextRedirect.Stderr, stderr_side = "clt") - else: - menu.entryconfigure(1, label = 'Enable client-side mode') - self.optsrvwin.grid() - self.msgsrvwin.grid() - gui_redirector('stderr', redirect_to = TextRedirect.Stderr) - - self.invert(widgets = [self.runbtnsrv, self.shbtnclt, self.runbtnclt]) - - def gui_create(self): - ## Create server gui - self.gui_srv() - ## Create client gui + other operations. - self.gui_complete() - ## Create menu. - self.gui_menu() - ## Create globals for printing process (redirect stdout). - global txsrv, txclt, txcol - txsrv = self.textboxsrv.get() - txclt = self.textboxclt.get() - txcol = self.customcolors - ## Redirect stderr. - gui_redirector('stderr', redirect_to = TextRedirect.Stderr) - - def gui_pages_show(self, pagename, side): - # https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter - # https://www.reddit.com/r/learnpython/comments/7xxtsy/trying_to_understand_tkinter_and_how_to_switch/ - pageside = self.pagewidgets[side] - tk.Misc.lift(pageside["PageWin"][pagename], aboveThis = None) - keylist = list(pageside["PageWin"].keys()) - - for elem in [pageside["BtnAni"], pageside["LblAni"]]: - if pagename == "PageStart": - elem["Left"].config(state = "disabled") - if len(keylist) == 2: - elem["Right"].config(state = "normal") - elif pagename == "PageEnd": - elem["Right"].config(state = "disabled") - if len(keylist) == 2: - elem["Left"].config(state = "normal") - else: - for where in ["Left", "Right"]: - elem[where].config(state = "normal") - - if pagename != "PageStart": - page_l = keylist[keylist.index(pagename) - 1] - pageside["BtnAni"]["Left"]['command'] = lambda pag=page_l, pos=side: self.gui_pages_show(pag, pos) - if pagename != "PageEnd": - page_r = keylist[keylist.index(pagename) + 1] - pageside["BtnAni"]["Right"]['command'] = lambda pag=page_r, pos=side: self.gui_pages_show(pag, pos) - - def gui_pages_buttons(self, parent, side): - btnwin = tk.Canvas(parent, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - btnwin.grid(row = 14, column = 2, padx = 2, pady = 2, sticky = 'nsew') - btnwin.grid_columnconfigure(1, weight = 1) - self.pagewidgets[side]["BtnWin"] = btnwin - - for position in ["Left", "Right"]: - if position == "Left": - col = [0, 0, 1] - stick = 'e' - elif position == "Right": - col = [2, 1, 0] - stick = 'w' - - aniwin = tk.Canvas(btnwin, background = self.customcolors['white'], borderwidth = 0, relief = 'ridge') - aniwin.grid(row = 0, column = col[0], padx = 5, pady = 5, sticky = 'nsew') - self.pagewidgets[side]["AniWin"][position] = aniwin - - lblani = tk.Label(aniwin, width = 1, height = 1) - lblani.grid(row = 0, column = col[1], padx = 2, pady = 2, sticky = stick) - self.pagewidgets[side]["LblAni"][position] = lblani - - btnani = tk.Button(aniwin) - btnani.grid(row = 0, column = col[2], padx = 2, pady = 2, sticky = stick) - self.pagewidgets[side]["BtnAni"][position] = btnani - ## Customize buttons. - custom_pages(self, side) - - def gui_pages_create(self, parent, side, create = {}): - self.pagewidgets.update({side : {"PageWin" : create, - "BtnWin" : None, - "BtnAni" : {"Left" : None, - "Right" : None}, - "AniWin" : {"Left" : None, - "Right" : None}, - "LblAni" : {"Left" : None, - "Right" : None}, - } - }) - - for pagename in self.pagewidgets[side]["PageWin"].keys(): - page = tk.Canvas(parent, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - self.pagewidgets[side]["PageWin"][pagename] = page - page.grid(row = 0, column = 2, padx = 2, pady = 2, sticky = "nsew") - page.grid_columnconfigure(1, weight = 1) - self.gui_pages_buttons(parent = parent, side = side) - self.gui_pages_show("PageStart", side = side) - - def gui_store(self, side, typewidgets): - stored = [] - for pagename in self.pagewidgets[side]["PageWin"].keys(): - for widget in self.pagewidgets[side]["PageWin"][pagename].winfo_children(): - if widget.winfo_class() in typewidgets: - stored.append(widget) - return stored - - def gui_srv(self): - ## Create main containers. ------------------------------------------------------------------------------------------------------------------ - self.masterwin = tk.Canvas(self, borderwidth = 3, relief = tk.RIDGE) - self.btnsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - self.optsrvwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - self.msgsrvwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200) - - ## Layout main containers. - self.masterwin.grid(row = 0, column = 0, sticky = 'nsew') - self.btnsrvwin.grid(row = 0, column = 1, padx = 2, pady = 2, sticky = 'nw') - self.optsrvwin.grid(row = 0, column = 2, padx = 2, pady = 2, sticky = 'nsew') - self.optsrvwin.grid_rowconfigure(0, weight = 1) - self.optsrvwin.grid_columnconfigure(1, weight = 1) - - self.pagewidgets = {} - - ## Subpages of "optsrvwin". - self.gui_pages_create(parent = self.optsrvwin, side = "Srv", create = {"PageStart": None, - "PageEnd": None}) - - ## Continue to grid. - self.msgsrvwin.grid(row = 1, column = 2, padx = 1, pady = 1, sticky = 'nsew') - self.msgsrvwin.grid_propagate(False) - self.msgsrvwin.grid_columnconfigure(0, weight = 1) - self.msgsrvwin.grid_rowconfigure(0, weight = 1) - - ## Create widgets (btnsrvwin) --------------------------------------------------------------------------------------------------------------- - self.statesrv = tk.Label(self.btnsrvwin, text = 'Server\nState:\nStopped', font = self.customfonts['oth'], - foreground = self.customcolors['red']) - self.runbtnsrv = tk.Button(self.btnsrvwin, text = 'START\nSERVER', background = self.customcolors['green'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - command = self.srv_on_start) - self.shbtnclt = tk.Button(self.btnsrvwin, text = 'SHOW\nCLIENT', background = self.customcolors['magenta'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - command = self.clt_on_show) - self.defaubtnsrv = tk.Button(self.btnsrvwin, text = 'DEFAULTS', background = self.customcolors['brown'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - command = self.on_defaults) - self.clearbtnsrv = tk.Button(self.btnsrvwin, text = 'CLEAR', background = self.customcolors['orange'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - command = lambda: self.on_clear([txsrv, txclt])) - self.exitbtnsrv = tk.Button(self.btnsrvwin, text = 'EXIT', background = self.customcolors['black'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - command = self.on_exit) - - ## Layout widgets (btnsrvwin) - self.statesrv.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew') - self.runbtnsrv.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = 'ew') - self.shbtnclt.grid(row = 2, column = 0, padx = 2, pady = 2, sticky = 'ew') - self.defaubtnsrv.grid(row = 3, column = 0, padx = 2, pady = 2, sticky = 'ew') - self.clearbtnsrv.grid(row = 4, column = 0, padx = 2, pady = 2, sticky = 'ew') - self.exitbtnsrv.grid(row = 5, column = 0, padx = 2, pady = 2, sticky = 'ew') - - ## Create widgets (optsrvwin:Srv:PageWin:PageStart) ----------------------------------------------------------------------------------------- - # Version. - ver = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], - text = 'You are running server version: ' + srv_version, font = self.customfonts['oth'], - foreground = self.customcolors['red']) - # Ip Address. - srvipaddlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.customfonts['opt']) - self.srvipadd = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'ip') - self.srvipadd.insert('end', srv_options['ip']['def']) - ToolTip(self.srvipadd, text = srv_options['ip']['help'], wraplength = self.wraplength) - myipadd = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Your IP address is: {}'.format(get_ip_address()), - font = self.customfonts['oth'], foreground = self.customcolors['red']) - # Port. - srvportlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Port: ', font = self.customfonts['opt']) - self.srvport = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'port', - validate = "key", validatecommand = self.validation_int) - self.srvport.insert('end', str(srv_options['port']['def'])) - ToolTip(self.srvport, text = srv_options['port']['help'], wraplength = self.wraplength) - # EPID. - epidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'EPID: ', font = self.customfonts['opt']) - self.epid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'epid') - self.epid.insert('end', str(srv_options['epid']['def'])) - ToolTip(self.epid, text = srv_options['epid']['help'], wraplength = self.wraplength) - # LCID. - lcidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'LCID: ', font = self.customfonts['opt']) - self.lcid = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lcid', - validate = "key", validatecommand = self.validation_int) - self.lcid.insert('end', str(srv_options['lcid']['def'])) - ToolTip(self.lcid, text = srv_options['lcid']['help'], wraplength = self.wraplength) - # HWID. - hwidlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'HWID: ', font = self.customfonts['opt']) - self.hwid = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = (str(srv_options['hwid']['def']), 'RANDOM'), - width = 17, height = 10, font = self.customfonts['lst'], name = 'hwid') - self.hwid.set(str(srv_options['hwid']['def'])) - ToolTip(self.hwid, text = srv_options['hwid']['help'], wraplength = self.wraplength) - # Client Count - countlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Client Count: ', font = self.customfonts['opt']) - self.count = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'count') - self.count.insert('end', str(srv_options['count']['def'])) - ToolTip(self.count, text = srv_options['count']['help'], wraplength = self.wraplength) - # Activation Interval. - activlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Activation Interval: ', font = self.customfonts['opt']) - self.activ = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'activation', - validate = "key", validatecommand = self.validation_int) - self.activ.insert('end', str(srv_options['activation']['def'])) - ToolTip(self.activ, text = srv_options['activation']['help'], wraplength = self.wraplength) - # Renewal Interval. - renewlbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Renewal Interval: ', font = self.customfonts['opt']) - self.renew = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'renewal', - validate = "key", validatecommand = self.validation_int) - self.renew.insert('end', str(srv_options['renewal']['def'])) - ToolTip(self.renew, text = srv_options['renewal']['help'], wraplength = self.wraplength) - # Logfile. - srvfilelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.customfonts['opt']) - self.srvfile = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lfile') - self.srvfile.insert('end', srv_options['lfile']['def']) - self.srvfile.xview_moveto(1) - ToolTip(self.srvfile, text = srv_options['lfile']['help'], wraplength = self.wraplength) - srvfilebtnwin = tk.Button(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Browse', font = self.customfonts['opt'], - command = lambda: self.on_browse(self.srvfile, srv_options)) - # Loglevel. - srvlevellbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.customfonts['opt']) - self.srvlevel = ttk.Combobox(self.pagewidgets["Srv"]["PageWin"]["PageStart"], values = tuple(srv_options['llevel']['choi']), - width = 10, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'llevel') - self.srvlevel.set(srv_options['llevel']['def']) - ToolTip(self.srvlevel, text = srv_options['llevel']['help'], wraplength = self.wraplength) - # Logsize. - srvsizelbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.customfonts['opt']) - self.srvsize = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lsize', - validate = "key", validatecommand = self.validation_float) - self.srvsize.insert('end', srv_options['lsize']['def']) - ToolTip(self.srvsize, text = srv_options['lsize']['help'], wraplength = self.wraplength) - # Asynchronous messages. - self.chkvalsrvasy = tk.BooleanVar() - self.chkvalsrvasy.set(srv_options['asyncmsg']['def']) - chksrvasy = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageStart"], text = 'Async\nMsg', - font = self.customfonts['opt'], var = self.chkvalsrvasy, relief = 'groove', name = 'asyncmsg') - ToolTip(chksrvasy, text = srv_options['asyncmsg']['help'], wraplength = self.wraplength) - - # Listbox radiobuttons server. - self.chksrvfile = ListboxOfRadiobuttons(self.pagewidgets["Srv"]["PageWin"]["PageStart"], - ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], - self.customfonts['lst'], - changed = [(self.srvfile, srv_options['lfile']['def']), - (srvfilebtnwin, ''), - (self.srvsize, srv_options['lsize']['def']), - (self.srvlevel, srv_options['llevel']['def'])], - width = 10, height = 1, borderwidth = 2, relief = 'ridge') - - ## Layout widgets (optsrvwin:Srv:PageWin:PageStart) - ver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew') - srvipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew') - myipadd.grid(row = 2, column = 1, columnspan = 2, padx = 5, pady = 5, sticky = 'ew') - srvportlbl.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvport.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'ew') - epidlbl.grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e') - self.epid.grid(row = 4, column = 1, padx = 5, pady = 5, sticky = 'ew') - lcidlbl.grid(row = 5, column = 0, padx = 5, pady = 5, sticky = 'e') - self.lcid.grid(row = 5, column = 1, padx = 5, pady = 5, sticky = 'ew') - hwidlbl.grid(row = 6, column = 0, padx = 5, pady = 5, sticky = 'e') - self.hwid.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew') - countlbl.grid(row = 7, column = 0, padx = 5, pady = 5, sticky = 'e') - self.count.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew') - activlbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e') - self.activ.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew') - renewlbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e') - self.renew.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew') - srvfilelbl.grid(row = 10, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvfile.grid(row = 10, column = 1, padx = 5, pady = 5, sticky = 'ew') - srvfilebtnwin.grid(row = 10, column = 2, padx = 5, pady = 5, sticky = 'ew') - self.chksrvfile.grid(row = 11, column = 1, padx = 5, pady = 5, sticky = 'ew') - chksrvasy.grid(row = 11, column = 2, padx = 5, pady = 5, sticky = 'ew') - srvlevellbl.grid(row = 12, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvlevel.grid(row = 12, column = 1, padx = 5, pady = 5, sticky = 'ew') - srvsizelbl.grid(row = 13, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvsize.grid(row = 13, column = 1, padx = 5, pady = 5, sticky = 'ew') - - ## Create widgets (optsrvwin:Srv:PageWin:PageEnd)------------------------------------------------------------------------------------------- - # Timeout connection. - srvtimeout0lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.customfonts['opt']) - self.srvtimeout0 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time0') - self.srvtimeout0.insert('end', str(srv_options['time0']['def'])) - ToolTip(self.srvtimeout0, text = srv_options['time0']['help'], wraplength = self.wraplength) - # Timeout send/recv. - srvtimeout1lbl = tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Timeout send-recv: ', font = self.customfonts['opt']) - self.srvtimeout1 = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time1') - self.srvtimeout1.insert('end', str(srv_options['time1']['def'])) - ToolTip(self.srvtimeout1, text = srv_options['time1']['help'], wraplength = self.wraplength) - # Sqlite database. - self.chkvalsql = tk.BooleanVar() - self.chkvalsql.set(srv_options['sql']['def']) - self.chkfilesql = tk.Entry(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'sql') - self.chkfilesql.insert('end', srv_options['sql']['file']) - self.chkfilesql.xview_moveto(1) - self.chkfilesql.configure(state = 'disabled') - - chksql = tk.Checkbutton(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], text = 'Create Sqlite\nDatabase', - font = self.customfonts['opt'], var = self.chkvalsql, relief = 'groove', - command = lambda: self.sql_status()) - ToolTip(chksql, text = srv_options['sql']['help'], wraplength = self.wraplength) - - ## Layout widgets (optsrvwin:Srv:PageWin:PageEnd) - # a label for vertical aligning with PageStart - tk.Label(self.pagewidgets["Srv"]["PageWin"]["PageEnd"], width = 0, - height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') - srvtimeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvtimeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w') - srvtimeout1lbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e') - self.srvtimeout1.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w') - chksql.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e') - self.chkfilesql.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'w') - - # Store server-side widgets. - self.storewidgets_srv = self.gui_store(side = "Srv", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton']) - self.storewidgets_srv.append(self.chksrvfile) - - ## Create widgets and layout (msgsrvwin) --------------------------------------------------------------------------------------------------- - self.textboxsrv = TextDoubleScroll(self.msgsrvwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled', - relief = 'ridge', font = self.customfonts['msg']) - self.textboxsrv.put() - - def sql_status(self): - if self.chkvalsql.get(): - self.chkfilesql.configure(state = 'normal') - else: - self.chkfilesql.insert('end', srv_options['sql']['file']) - self.chkfilesql.xview_moveto(1) - self.chkfilesql.configure(state = 'disabled') - - def always_centered(self, geo, centered, refs): - x = (self.winfo_screenwidth() // 2) - (self.winfo_width() // 2) - y = (self.winfo_screenheight() // 2) - (self.winfo_height() // 2) - w, h, dx, dy = geo.split('+')[0].split('x') + geo.split('+')[1:] - - if w == refs[1]: - if centered: - self.geometry('+%d+%d' %(x, y)) - centered = False - elif w == refs[0]: - if not centered: - self.geometry('+%d+%d' %(x, y)) - centered = True - - if dx != str(x) or dy != str(y): - self.geometry('+%d+%d' %(x, 0)) - - self.after(200, self.always_centered, self.geometry(), centered, refs) - - def gui_complete(self): - ## Create client widgets (optcltwin, msgcltwin, btncltwin) - self.update_idletasks() # update Gui to get btnsrvwin values --> btncltwin. - minw, minh = self.winfo_width(), self.winfo_height() - self.iconify() - self.gui_clt() - maxw, minh = self.winfo_width(), self.winfo_height() - ## Main window custom background. - self.update_idletasks() # update Gui for custom background - self.iconify() - custom_background(self) - ## Main window other modifications. - self.eval('tk::PlaceWindow %s center' %self.winfo_pathname(self.winfo_id())) - self.wm_attributes("-topmost", True) - self.protocol("WM_DELETE_WINDOW", lambda: 0) - ## Disable maximize button. - self.resizable(False, False) - ## Centered window. - self.always_centered(self.geometry(), False, [minw, maxw]) - - def get_position(self, widget): - x, y = (widget.winfo_x(), widget.winfo_y()) - w, h = (widget.winfo_width(), widget.winfo_height()) - return x, y, w, h - - def gui_clt(self): - self.count_clear, self.keep_clear = (0, '0.0') - self.optcltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - self.msgcltwin = tk.Frame(self.masterwin, background = self.customcolors['black'], relief = 'ridge', width = 300, height = 200) - self.btncltwin = tk.Canvas(self.masterwin, background = self.customcolors['white'], borderwidth = 3, relief = 'ridge') - - xb, yb, wb, hb = self.get_position(self.btnsrvwin) - self.btncltwin_X = xb - self.btncltwin_Y = yb + hb + 6 - self.btncltwin.place(x = self.btncltwin_X, y = self.btncltwin_Y, bordermode = 'outside', anchor = 'center') - - self.optcltwin.grid(row = 0, column = 4, padx = 2, pady = 2, sticky = 'nsew') - self.optcltwin.grid_rowconfigure(0, weight = 1) - self.optcltwin.grid_columnconfigure(1, weight = 1) - - ## Subpages of "optcltwin". - self.gui_pages_create(parent = self.optcltwin, side = "Clt", create = {"PageStart": None, - "PageEnd": None}) - - ## Continue to grid. - self.msgcltwin.grid(row = 1, column = 4, padx = 1, pady = 1, sticky = 'nsew') - self.msgcltwin.grid_propagate(False) - self.msgcltwin.grid_columnconfigure(0, weight = 1) - self.msgcltwin.grid_rowconfigure(0, weight = 1) - - ## Create widgets (btncltwin) ---------------------------------------------------------------------------------------------------------------- - self.runbtnclt = tk.Button(self.btncltwin, text = 'START\nCLIENT', background = self.customcolors['blue'], - foreground = self.customcolors['white'], relief = 'raised', font = self.customfonts['btn'], - state = 'disabled', command = self.clt_on_start, width = 8, height = 2) - - ## Layout widgets (btncltwin) - self.runbtnclt.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'ew') - - ## Create widgets (optcltwin:Clt:PageWin:PageStart) ------------------------------------------------------------------------------------------ - # Version. - cltver = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'You are running client version: ' + clt_version, - font = self.customfonts['oth'], foreground = self.customcolors['red']) - # Ip Address. - cltipaddlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'IP Address: ', font = self.customfonts['opt']) - self.cltipadd = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'ip') - self.cltipadd.insert('end', clt_options['ip']['def']) - ToolTip(self.cltipadd, text = clt_options['ip']['help'], wraplength = self.wraplength) - # Port. - cltportlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Port: ', font = self.customfonts['opt']) - self.cltport = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'port', - validate = "key", validatecommand = self.validation_int) - self.cltport.insert('end', str(clt_options['port']['def'])) - ToolTip(self.cltport, text = clt_options['port']['help'], wraplength = self.wraplength) - # Mode. - cltmodelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Mode: ', font = self.customfonts['opt']) - self.cltmode = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['mode']['choi']), - width = 17, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'mode') - self.cltmode.set(clt_options['mode']['def']) - ToolTip(self.cltmode, text = clt_options['mode']['help'], wraplength = self.wraplength) - # CMID. - cltcmidlbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'CMID: ', font = self.customfonts['opt']) - self.cltcmid = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'cmid') - self.cltcmid.insert('end', str(clt_options['cmid']['def'])) - ToolTip(self.cltcmid, text = clt_options['cmid']['help'], wraplength = self.wraplength) - # Machine Name. - cltnamelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Machine Name: ', font = self.customfonts['opt']) - self.cltname = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'name') - self.cltname.insert('end', str(clt_options['name']['def'])) - ToolTip(self.cltname, text = clt_options['name']['help'], wraplength = self.wraplength) - # Logfile. - cltfilelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logfile Path / Name: ', font = self.customfonts['opt']) - self.cltfile = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lfile') - self.cltfile.insert('end', clt_options['lfile']['def']) - self.cltfile.xview_moveto(1) - ToolTip(self.cltfile, text = clt_options['lfile']['help'], wraplength = self.wraplength) - cltfilebtnwin = tk.Button(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Browse', font = self.customfonts['opt'], - command = lambda: self.on_browse(self.cltfile, clt_options)) - # Loglevel. - cltlevellbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Loglevel: ', font = self.customfonts['opt']) - self.cltlevel = ttk.Combobox(self.pagewidgets["Clt"]["PageWin"]["PageStart"], values = tuple(clt_options['llevel']['choi']), - width = 10, height = 10, font = self.customfonts['lst'], state = "readonly", name = 'llevel') - self.cltlevel.set(clt_options['llevel']['def']) - ToolTip(self.cltlevel, text = clt_options['llevel']['help'], wraplength = self.wraplength) - # Logsize. - cltsizelbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Logsize: ', font = self.customfonts['opt']) - self.cltsize = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageStart"], width = 10, font = self.customfonts['opt'], name = 'lsize', - validate = "key", validatecommand = self.validation_float) - self.cltsize.insert('end', clt_options['lsize']['def']) - ToolTip(self.cltsize, text = clt_options['lsize']['help'], wraplength = self.wraplength) - # Asynchronous messages. - self.chkvalcltasy = tk.BooleanVar() - self.chkvalcltasy.set(clt_options['asyncmsg']['def']) - chkcltasy = tk.Checkbutton(self.pagewidgets["Clt"]["PageWin"]["PageStart"], text = 'Async\nMsg', - font = self.customfonts['opt'], var = self.chkvalcltasy, relief = 'groove', name = 'asyncmsg') - ToolTip(chkcltasy, text = clt_options['asyncmsg']['help'], wraplength = self.wraplength) - - # Listbox radiobuttons client. - self.chkcltfile = ListboxOfRadiobuttons(self.pagewidgets["Clt"]["PageWin"]["PageStart"], - ['FILE', 'FILEOFF', 'STDOUT', 'STDOUTOFF', 'FILESTDOUT'], - self.customfonts['lst'], - changed = [(self.cltfile, clt_options['lfile']['def']), - (cltfilebtnwin, ''), - (self.cltsize, clt_options['lsize']['def']), - (self.cltlevel, clt_options['llevel']['def'])], - width = 10, height = 1, borderwidth = 2, relief = 'ridge') - - ## Layout widgets (optcltwin:Clt:PageWin:PageStart) - cltver.grid(row = 0, column = 0, columnspan = 3, padx = 5, pady = 5, sticky = 'ew') - cltipaddlbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltipadd.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltportlbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltport.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltmodelbl.grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltmode.grid(row = 3, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltcmidlbl.grid(row = 4, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltcmid.grid(row = 4, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltnamelbl.grid(row = 5, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltname.grid(row = 5, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltfilelbl.grid(row = 6, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltfile.grid(row = 6, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltfilebtnwin.grid(row = 6, column = 2, padx = 5, pady = 5, sticky = 'ew') - self.chkcltfile.grid(row = 7, column = 1, padx = 5, pady = 5, sticky = 'ew') - chkcltasy.grid(row = 7, column = 2, padx = 5, pady = 5, sticky = 'ew') - cltlevellbl.grid(row = 8, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltlevel.grid(row = 8, column = 1, padx = 5, pady = 5, sticky = 'ew') - cltsizelbl.grid(row = 9, column = 0, padx = 5, pady = 5, sticky = 'e') - self.cltsize.grid(row = 9, column = 1, padx = 5, pady = 5, sticky = 'ew') - - # ugly fix when client-side mode is activated. - templbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageStart"], - bg = self.customcolors['lavender']).grid(row = 10, column = 0, - padx = 35, pady = 54, sticky = 'e') - - ## Create widgets (optcltwin:Clt:PageWin:PageEnd) ------------------------------------------------------------------------------------------- - # Timeout connection. - clttimeout0lbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], text = 'Timeout connection: ', font = self.customfonts['opt']) - self.clttimeout0 = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time0') - self.clttimeout0.insert('end', str(clt_options['time0']['def'])) - ToolTip(self.clttimeout0, text = clt_options['time0']['help'], wraplength = self.wraplength) - # Timeout send/recv. - clttimeout1lbl = tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], text = 'Timeout send-recv: ', font = self.customfonts['opt']) - self.clttimeout1 = tk.Entry(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 16, font = self.customfonts['opt'], name = 'time1') - self.clttimeout1.insert('end', str(clt_options['time1']['def'])) - ToolTip(self.clttimeout1, text = clt_options['time1']['help'], wraplength = self.wraplength) - - ## Layout widgets (optcltwin:Clt:PageWin:PageEnd) - # a label for vertical aligning with PageStart - tk.Label(self.pagewidgets["Clt"]["PageWin"]["PageEnd"], width = 0, - height = 0, bg = self.customcolors['lavender']).grid(row = 0, column = 0, padx = 5, pady = 5, sticky = 'nw') - clttimeout0lbl.grid(row = 1, column = 0, padx = 5, pady = 5, sticky = 'e') - self.clttimeout0.grid(row = 1, column = 1, padx = 5, pady = 5, sticky = 'w') - clttimeout1lbl.grid(row = 2, column = 0, padx = 5, pady = 5, sticky = 'e') - self.clttimeout1.grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'w') - - ## Store client-side widgets. - self.storewidgets_clt = self.gui_store(side = "Clt", typewidgets = ['Button', 'Entry', 'TCombobox', 'Checkbutton']) - self.storewidgets_clt.append(self.chkcltfile) - - ## Create widgets and layout (msgcltwin) ----------------------------------------------------------------------------------------------------- - self.textboxclt = TextDoubleScroll(self.msgcltwin, background = self.customcolors['black'], wrap = 'none', state = 'disabled', - relief = 'ridge', font = self.customfonts['msg']) - self.textboxclt.put() - - def prep_option(self, value): - try: - # is an INT - return int(value) - except (TypeError, ValueError): - try: - # is a FLOAT - return float(value) - except (TypeError, ValueError): - # is a STRING. - return value - - def prep_logfile(self, filepath, status): - # FILE (pretty on, log view off, logfile yes) - # FILEOFF (pretty on, log view off, no logfile) - # STDOUT (pretty off, log view on, no logfile) - # STDOUTOFF (pretty off, log view off, logfile yes) - # FILESTDOUT (pretty off, log view on, logfile yes) - - if status == 'FILE': - return filepath - elif status in ['FILESTDOUT', 'STDOUTOFF']: - return [status, filepath] - elif status in ['STDOUT', 'FILEOFF']: - return status - - def validate_int(self, value): - return value == "" or value.isdigit() - - def validate_float(self, value): - if value == "": - return True - try: - float(value) - return True - except ValueError: - return False - - def clt_on_show(self, force_remove = False, force_view = False): - if self.optcltwin.winfo_ismapped() or force_remove: - self.shbtnclt.configure(text = 'SHOW\nCLIENT', relief = 'raised') - self.optcltwin.grid_remove() - self.msgcltwin.grid_remove() - self.btncltwin.place_forget() - elif not self.optcltwin.winfo_ismapped() or force_view: - self.shbtnclt.configure(text = 'HIDE\nCLIENT', relief = 'sunken') - self.optcltwin.grid() - self.msgcltwin.grid() - self.btncltwin.place(x = self.btncltwin_X, y = self.btncltwin_Y, bordermode = 'inside', anchor = 'nw') - - def srv_on_start(self): - if self.runbtnsrv['text'] == 'START\nSERVER': - self.on_clear([txsrv, txclt]) - self.srv_actions_start() - # wait for switch. - while not serverthread.is_running_server: - pass - - self.srv_toggle_all(on_start = True) - # run thread for interrupting server when an error happens. - self.srv_eject_thread = threading.Thread(target = self.srv_eject, name = "Thread-SrvEjt") - self.srv_eject_thread.setDaemon(True) - self.srv_eject_thread.start() - - elif self.runbtnsrv['text'] == 'STOP\nSERVER': - serverthread.terminate_eject() - - def srv_eject(self): - while not serverthread.eject: - sleep(0.1) - self.srv_actions_stop() - - def srv_actions_start(self): - srv_config[srv_options['ip']['des']] = self.srvipadd.get() - srv_config[srv_options['port']['des']] = self.prep_option(self.srvport.get()) - srv_config[srv_options['epid']['des']] = self.epid.get() - srv_config[srv_options['lcid']['des']] = self.prep_option(self.lcid.get()) - srv_config[srv_options['hwid']['des']] = self.hwid.get() - srv_config[srv_options['count']['des']] = self.prep_option(self.count.get()) - srv_config[srv_options['activation']['des']] = self.prep_option(self.activ.get()) - srv_config[srv_options['renewal']['des']] = self.prep_option(self.renew.get()) - srv_config[srv_options['lfile']['des']] = self.prep_logfile(self.srvfile.get(), self.chksrvfile.state()) - srv_config[srv_options['asyncmsg']['des']] = self.chkvalsrvasy.get() - srv_config[srv_options['llevel']['des']] = self.srvlevel.get() - srv_config[srv_options['lsize']['des']] = self.prep_option(self.srvsize.get()) - - srv_config[srv_options['time0']['des']] = self.prep_option(self.srvtimeout0.get()) - srv_config[srv_options['time1']['des']] = self.prep_option(self.srvtimeout1.get()) - srv_config[srv_options['sql']['des']] = (self.chkfilesql.get() if self.chkvalsql.get() else self.chkvalsql.get()) - - ## Redirect stdout. - gui_redirector('stdout', redirect_to = TextRedirect.Log, - redirect_conditio = (srv_config[srv_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT'])) - serverqueue.put('start') - - def srv_actions_stop(self): - if serverthread.is_running_server: - if serverthread.server is not None: - server_terminate(serverthread, exit_server = True) - # wait for switch. - while serverthread.is_running_server: - pass - else: - serverthread.is_running_server = False - self.srv_toggle_all(on_start = False) - self.count_clear, self.keep_clear = (0, '0.0') - - def srv_toggle_all(self, on_start = True): - self.srv_toggle_state() - if on_start: - self.runbtnsrv.configure(text = 'STOP\nSERVER', background = self.customcolors['red'], - foreground = self.customcolors['white'], relief = 'sunken') - for widget in self.storewidgets_srv: - widget.configure(state = 'disabled') - self.runbtnclt.configure(state = 'normal') - else: - self.runbtnsrv.configure(text = 'START\nSERVER', background = self.customcolors['green'], - foreground = self.customcolors['white'], relief = 'raised') - for widget in self.storewidgets_srv: - widget.configure(state = 'normal') - if isinstance(widget, ListboxOfRadiobuttons): - widget.change() - self.runbtnclt.configure(state = 'disabled') - - def srv_toggle_state(self): - if serverthread.is_running_server: - txt, color = ('Server\nState:\nServing', self.customcolors['green']) - else: - txt, color = ('Server\nState:\nStopped', self.customcolors['red']) - - self.statesrv.configure(text = txt, foreground = color) - - def clt_on_start(self): - if self.onlyclt: - self.on_clear([txclt]) - else: - rng, add_newline = self.on_clear_setup() - self.on_clear([txsrv, txclt], clear_range = [rng, None], newline_list = [add_newline, False]) - - self.runbtnclt.configure(relief = 'sunken') - self.clt_actions_start() - # run thread for disabling interrupt server and client, when client running. - self.clt_eject_thread = threading.Thread(target = self.clt_eject, name = "Thread-CltEjt") - self.clt_eject_thread.setDaemon(True) - self.clt_eject_thread.start() - - for widget in self.storewidgets_clt + [self.runbtnsrv, self.runbtnclt, self.defaubtnsrv]: - widget.configure(state = 'disabled') - self.runbtnclt.configure(relief = 'raised') - - def clt_actions_start(self): - clt_config[clt_options['ip']['des']] = self.cltipadd.get() - clt_config[clt_options['port']['des']] = self.prep_option(self.cltport.get()) - clt_config[clt_options['mode']['des']] = self.cltmode.get() - clt_config[clt_options['cmid']['des']] = self.cltcmid.get() - clt_config[clt_options['name']['des']] = self.cltname.get() - clt_config[clt_options['lfile']['des']] = self.prep_logfile(self.cltfile.get(), self.chkcltfile.state()) - clt_config[clt_options['asyncmsg']['des']] = self.chkvalcltasy.get() - clt_config[clt_options['llevel']['des']] = self.cltlevel.get() - clt_config[clt_options['lsize']['des']] = self.prep_option(self.cltsize.get()) - - clt_config[clt_options['time0']['des']] = self.prep_option(self.clttimeout0.get()) - clt_config[clt_options['time1']['des']] = self.prep_option(self.clttimeout1.get()) - - ## Redirect stdout. - gui_redirector('stdout', redirect_to = TextRedirect.Log, - redirect_conditio = (clt_config[clt_options['lfile']['des']] in ['STDOUT', 'FILESTDOUT'])) - - # run client (in a thread). - self.clientthread = client_thread(name = "Thread-Clt") - self.clientthread.setDaemon(True) - self.clientthread.with_gui = True - self.clientthread.start() - - def clt_eject(self): - while self.clientthread.is_alive(): - sleep(0.1) - - widgets = self.storewidgets_clt + [self.runbtnclt] + [self.defaubtnsrv] - if not self.onlyclt: - widgets += [self.runbtnsrv] - - for widget in widgets: - if isinstance(widget, ttk.Combobox): - widget.configure(state = 'readonly') - else: - widget.configure(state = 'normal') - if isinstance(widget, ListboxOfRadiobuttons): - widget.change() - - def on_browse(self, entrywidget, options): - path = filedialog.askdirectory() - if os.path.isdir(path): - entrywidget.delete('0', 'end') - entrywidget.insert('end', path + os.sep + os.path.basename(options['lfile']['def'])) - - def on_exit(self): - if serverthread.is_running_server: - if serverthread.server is not None: - server_terminate(serverthread, exit_server = True) - else: - serverthread.is_running_server = False - server_terminate(serverthread, exit_thread = True) - self.destroy() - - def on_clear_setup(self): - if any(opt in ['STDOUT', 'FILESTDOUT'] for opt in srv_config[srv_options['lfile']['des']]): - add_newline = True - if self.count_clear == 0: - self.keep_clear = txsrv.index('end-1c') - else: - add_newline = False - if self.count_clear == 0: - self.keep_clear = txsrv.index('end') - - rng = [self.keep_clear, 'end'] - self.count_clear += 1 - - return rng, add_newline - - def on_clear(self, widget_list, clear_range = None, newline_list = []): - if newline_list == []: - newline_list = len(widget_list) * [False] - - for num, couple in enumerate(zip(widget_list, newline_list)): - widget, add_n = couple - try: - ini, fin = clear_range[num] - except TypeError: - ini, fin = '1.0', 'end' - - widget.configure(state = 'normal') - widget.delete(ini, fin) - if add_n: - widget.insert('end', '\n') - widget.configure(state = 'disabled') - - def on_defaults(self): - - def put_defaults(widgets, chkasy, listofradio, options): - for widget in widgets: - wclass, wname = widget.winfo_class(), widget.winfo_name() - if wname == '!checkbutton': - continue - - opt = options[wname]['def'] - if wclass == 'Entry': - widget.delete(0, 'end') - if wname == 'sql': - self.chkvalsql.set(opt) - self.sql_status() - else: - widget.insert('end', (opt if isinstance(opt, str) else str(opt))) - elif wclass == 'Checkbutton': - if wname == 'asyncmsg': - chkasy.set(opt) - elif wclass == 'TCombobox': - widget.set(str(opt)) - - # ListboxOfRadiobuttons default. - listofradio.radiovar.set('FILE') - listofradio.textbox.yview_moveto(0) - listofradio.change() - - if self.runbtnsrv['text'] == 'START\nSERVER': - apply_default = zip(["Srv", "Clt"], - [self.chkvalsrvasy, self.chkvalcltasy], - [self.chksrvfile, self.chkcltfile], - [srv_options, clt_options]) - elif self.runbtnsrv['text'] == 'STOP\nSERVER': - apply_default = zip(*[("Clt",), - (self.chkvalcltasy,), - (self.chkcltfile,), - (clt_options,)]) - - for side, chkasy, listofradio, options in apply_default: - widgets = self.gui_store(side = side, typewidgets = ['Entry', 'TCombobox', 'Checkbutton']) - put_defaults(widgets, chkasy, listofradio, options) diff --git a/py-kms/pykms_GuiMisc.py b/py-kms/pykms_GuiMisc.py deleted file mode 100644 index 97b1cc1..0000000 --- a/py-kms/pykms_GuiMisc.py +++ /dev/null @@ -1,517 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import sys -from collections import Counter -from time import sleep -import threading -import tkinter as tk -from tkinter import ttk -import tkinter.font as tkFont - -from pykms_Format import MsgMap, unshell_message, unformat_message - -#------------------------------------------------------------------------------------------------------------------------------------------------------------ - -# https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter -class ToolTip(object): - """ Create a tooltip for a given widget """ - def __init__(self, widget, bg = '#FFFFEA', pad = (5, 3, 5, 3), text = 'widget info', waittime = 400, wraplength = 250): - self.waittime = waittime # ms - self.wraplength = wraplength # pixels - self.widget = widget - self.text = text - self.widget.bind("", self.onEnter) - self.widget.bind("", self.onLeave) - self.widget.bind("", self.onLeave) - self.bg = bg - self.pad = pad - self.id = None - self.tw = None - - def onEnter(self, event = None): - self.schedule() - - def onLeave(self, event = None): - self.unschedule() - self.hide() - - def schedule(self): - self.unschedule() - self.id = self.widget.after(self.waittime, self.show) - - def unschedule(self): - id_ = self.id - self.id = None - if id_: - self.widget.after_cancel(id_) - - def show(self): - def tip_pos_calculator(widget, label, tip_delta = (10, 5), pad = (5, 3, 5, 3)): - w = widget - s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight() - width, height = (pad[0] + label.winfo_reqwidth() + pad[2], - pad[1] + label.winfo_reqheight() + pad[3]) - mouse_x, mouse_y = w.winfo_pointerxy() - x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1] - x2, y2 = x1 + width, y1 + height - - x_delta = x2 - s_width - if x_delta < 0: - x_delta = 0 - y_delta = y2 - s_height - if y_delta < 0: - y_delta = 0 - - offscreen = (x_delta, y_delta) != (0, 0) - - if offscreen: - if x_delta: - x1 = mouse_x - tip_delta[0] - width - if y_delta: - y1 = mouse_y - tip_delta[1] - height - - offscreen_again = y1 < 0 # out on the top - - if offscreen_again: - # No further checks will be done. - - # TIP: - # A further mod might automagically augment the - # wraplength when the tooltip is too high to be - # kept inside the screen. - y1 = 0 - - return x1, y1 - - bg = self.bg - pad = self.pad - widget = self.widget - - # creates a toplevel window - self.tw = tk.Toplevel(widget) - - # leaves only the label and removes the app window - self.tw.wm_overrideredirect(True) - - win = tk.Frame(self.tw, background = bg, borderwidth = 0) - label = ttk.Label(win, text = self.text, justify = tk.LEFT, background = bg, relief = tk.SOLID, borderwidth = 0, - wraplength = self.wraplength) - label.grid(padx = (pad[0], pad[2]), pady = (pad[1], pad[3]), sticky=tk.NSEW) - win.grid() - - x, y = tip_pos_calculator(widget, label) - - self.tw.wm_geometry("+%d+%d" % (x, y)) - - def hide(self): - tw = self.tw - if tw: - tw.destroy() - self.tw = None - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- - -class TextRedirect(object): - class Pretty(object): - grpmsg = unformat_message([MsgMap[1], MsgMap[7], MsgMap[12], MsgMap[20]]) - arrows = [ item[0] for item in grpmsg ] - clt_msg_nonewline = [ item[1] for item in grpmsg ] - arrows = list(set(arrows)) - lenarrow = len(arrows[0]) - srv_msg_nonewline = [ item[0] for item in unformat_message([MsgMap[2], MsgMap[5], MsgMap[13], MsgMap[18]]) ] - msg_align = [ msg[0].replace('\t', '').replace('\n', '') for msg in unformat_message([MsgMap[-2], MsgMap[-4]]) ] - - def __init__(self, srv_text_space, clt_text_space, customcolors): - self.srv_text_space = srv_text_space - self.clt_text_space = clt_text_space - self.customcolors = customcolors - - def textbox_write(self, tag, message, color, extras): - widget = self.textbox_choose(message) - self.w_maxpix, self.h_maxpix = widget.winfo_width(), widget.winfo_height() - self.xfont = tkFont.Font(font = widget['font']) - widget.configure(state = 'normal') - widget.insert('end', self.textbox_format(message), tag) - self.textbox_color(tag, widget, color, self.customcolors['black'], extras) - widget.after(100, widget.see('end')) - widget.configure(state = 'disabled') - - def textbox_choose(self, message): - if any(item.startswith('logsrv') for item in [message, self.str_to_print]): - self.srv_text_space.focus_set() - self.where = "srv" - return self.srv_text_space - elif any(item.startswith('logclt') for item in [message, self.str_to_print]): - self.clt_text_space.focus_set() - self.where = "clt" - return self.clt_text_space - - def textbox_color(self, tag, widget, forecolor = 'white', backcolor = 'black', extras = []): - for extra in extras: - if extra == 'bold': - self.xfont.configure(weight = "bold") - elif extra == 'italic': - self.xfont.configure(slant = "italic") - elif extra == 'underlined': - self.xfont.text_font.configure(underline = True) - elif extra == 'strike': - self.xfont.configure(overstrike = True) - elif extra == 'reverse': - forecolor, backcolor = backcolor, forecolor - - widget.tag_configure(tag, foreground = forecolor, background = backcolor, font = self.xfont) - widget.tag_add(tag, "insert linestart", "insert lineend") - - def textbox_newline(self, message): - if not message.endswith('\n'): - return message + '\n' - else: - return message - - def textbox_format(self, message): - # vertical align. - self.w_maxpix = self.w_maxpix - 5 # pixel reduction for distance from border. - w_fontpix, h_fontpix = (self.xfont.measure('0'), self.xfont.metrics('linespace')) - msg_unformat = message.replace('\t', '').replace('\n', '') - lenfixed_chars = int((self.w_maxpix / w_fontpix) - len(msg_unformat)) - - if message in self.srv_msg_nonewline + self.clt_msg_nonewline: - lung = lenfixed_chars - self.lenarrow - if message in self.clt_msg_nonewline: - message = self.textbox_newline(message) - else: - lung = lenfixed_chars - if (self.where == "srv") or (self.where == "clt" and message not in self.arrows): - message = self.textbox_newline(message) - # horizontal align. - if msg_unformat in self.msg_align: - msg_strip = message.lstrip('\n') - message = '\n' * (len(message) - len(msg_strip) + TextRedirect.Pretty.newlinecut[0]) + msg_strip - TextRedirect.Pretty.newlinecut.pop(0) - - count = Counter(message) - countab = (count['\t'] if count['\t'] != 0 else 1) - message = message.replace('\t' * countab, ' ' * lung) - return message - - def textbox_do(self): - msgs, TextRedirect.Pretty.tag_num = unshell_message(self.str_to_print, TextRedirect.Pretty.tag_num) - for tag in msgs: - self.textbox_write(tag, msgs[tag]['text'], self.customcolors[msgs[tag]['color']], msgs[tag]['extra']) - - def flush(self): - pass - - def write(self, string): - if string != '\n': - self.str_to_print = string - self.textbox_do() - - class Stderr(Pretty): - def __init__(self, srv_text_space, clt_text_space, customcolors, side): - self.srv_text_space = srv_text_space - self.clt_text_space = clt_text_space - self.customcolors = customcolors - self.side = side - self.tag_err = 'STDERR' - self.xfont = tkFont.Font(font = self.srv_text_space['font']) - - def textbox_choose(self, message): - if self.side == "srv": - return self.srv_text_space - elif self.side == "clt": - return self.clt_text_space - - def write(self, string): - widget = self.textbox_choose(string) - self.textbox_color(self.tag_err, widget, self.customcolors['red'], self.customcolors['black']) - self.srv_text_space.configure(state = 'normal') - self.srv_text_space.insert('end', string, self.tag_err) - self.srv_text_space.see('end') - self.srv_text_space.configure(state = 'disabled') - - class Log(Pretty): - def textbox_format(self, message): - if message.startswith('logsrv'): - message = message.replace('logsrv ', '') - if message.startswith('logclt'): - message = message.replace('logclt ', '') - return message + '\n' - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- -class TextDoubleScroll(tk.Frame): - def __init__(self, master, **kwargs): - """ Initialize. - - horizontal scrollbar - - vertical scrollbar - - text widget - """ - tk.Frame.__init__(self, master) - self.master = master - - self.textbox = tk.Text(self.master, **kwargs) - self.sizegrip = ttk.Sizegrip(self.master) - self.hs = ttk.Scrollbar(self.master, orient = "horizontal", command = self.on_scrollbar_x) - self.vs = ttk.Scrollbar(self.master, orient = "vertical", command = self.on_scrollbar_y) - self.textbox.configure(yscrollcommand = self.on_textscroll, xscrollcommand = self.hs.set) - - def on_scrollbar_x(self, *args): - """ Horizontally scrolls text widget. """ - self.textbox.xview(*args) - - def on_scrollbar_y(self, *args): - """ Vertically scrolls text widget. """ - self.textbox.yview(*args) - - def on_textscroll(self, *args): - """ Moves the scrollbar and scrolls text widget when the mousewheel is moved on a text widget. """ - self.vs.set(*args) - self.on_scrollbar_y('moveto', args[0]) - - def put(self, **kwargs): - """ Grid the scrollbars and textbox correctly. """ - self.textbox.grid(row = 0, column = 0, padx = 3, pady = 3, sticky = "nsew") - self.vs.grid(row = 0, column = 1, sticky = "ns") - self.hs.grid(row = 1, column = 0, sticky = "we") - self.sizegrip.grid(row = 1, column = 1, sticky = "news") - - def get(self): - """ Return the "frame" useful to place inner controls. """ - return self.textbox - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- -def custom_background(window): - # first level canvas. - allwidgets = window.grid_slaves(0,0)[0].grid_slaves() + window.grid_slaves(0,0)[0].place_slaves() - widgets_alphalow = [ widget for widget in allwidgets if widget.winfo_class() == 'Canvas'] - widgets_alphahigh = [] - # sub-level canvas. - for side in ["Srv", "Clt"]: - widgets_alphahigh.append(window.pagewidgets[side]["BtnWin"]) - for position in ["Left", "Right"]: - widgets_alphahigh.append(window.pagewidgets[side]["AniWin"][position]) - for pagename in window.pagewidgets[side]["PageWin"].keys(): - widgets_alphalow.append(window.pagewidgets[side]["PageWin"][pagename]) - - try: - from PIL import Image, ImageTk - - # Open Image. - img = Image.open(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Keys.gif") - img = img.convert('RGBA') - # Resize image. - img.resize((window.winfo_width(), window.winfo_height()), Image.ANTIALIAS) - # Put semi-transparent background chunks. - window.backcrops_alphalow, window.backcrops_alphahigh = ([] for _ in range(2)) - - def cutter(master, image, widgets, crops, alpha): - for widget in widgets: - x, y, w, h = master.get_position(widget) - cropped = image.crop((x, y, x + w, y + h)) - cropped.putalpha(alpha) - crops.append(ImageTk.PhotoImage(cropped)) - # Not in same loop to prevent reference garbage. - for crop, widget in zip(crops, widgets): - widget.create_image(1, 1, image = crop, anchor = 'nw') - - cutter(window, img, widgets_alphalow, window.backcrops_alphalow, 36) - cutter(window, img, widgets_alphahigh, window.backcrops_alphahigh, 96) - - # Put semi-transparent background overall. - img.putalpha(128) - window.backimg = ImageTk.PhotoImage(img) - window.masterwin.create_image(1, 1, image = window.backimg, anchor = 'nw') - - except ImportError: - for widget in widgets_alphalow + widgets_alphahigh: - widget.configure(background = window.customcolors['lavender']) - - # Hide client. - window.clt_on_show(force_remove = True) - # Show Gui. - window.deiconify() - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- -class Animation(object): - def __init__(self, gifpath, master, widget, loop = False): - from PIL import Image, ImageTk, ImageSequence - - self.master = master - self.widget = widget - self.loop = loop - self.cancelid = None - self.flagstop = False - self.index = 0 - self.frames = [] - - img = Image.open(gifpath) - size = img.size - for frame in ImageSequence.Iterator(img): - static_img = ImageTk.PhotoImage(frame.convert('RGBA')) - try: - static_img.delay = int(frame.info['duration']) - except KeyError: - static_img.delay = 100 - self.frames.append(static_img) - - self.widget.configure(width = size[0], height = size[1]) - self.initialize() - - def initialize(self): - self.widget.configure(image = self.frames[0]) - self.widget.image = self.frames[0] - - def deanimate(self): - while not self.flagstop: - pass - self.flagstop = False - self.index = 0 - self.widget.configure(relief = "raised") - - def animate(self): - frame = self.frames[self.index] - self.widget.configure(image = frame, relief = "sunken") - self.index += 1 - self.cancelid = self.master.after(frame.delay, self.animate) - if self.index == len(self.frames): - if self.loop: - self.index = 0 - else: - self.stop() - - def start(self, event = None): - if str(self.widget['state']) != 'disabled': - if self.cancelid is None: - if not self.loop: - self.btnani_thread = threading.Thread(target = self.deanimate, name = "Thread-BtnAni") - self.btnani_thread.setDaemon(True) - self.btnani_thread.start() - self.cancelid = self.master.after(self.frames[0].delay, self.animate) - - def stop(self, event = None): - if self.cancelid: - self.master.after_cancel(self.cancelid) - self.cancelid = None - self.flagstop = True - self.initialize() - - -def custom_pages(window, side): - buttons = window.pagewidgets[side]["BtnAni"] - labels = window.pagewidgets[side]["LblAni"] - - for position in buttons.keys(): - buttons[position].config(anchor = "center", - font = window.customfonts['btn'], - background = window.customcolors['white'], - activebackground = window.customcolors['white'], - borderwidth = 2) - - try: - anibtn = Animation(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Keyhole_%s.gif" %position, - window, buttons[position], loop = False) - anilbl = Animation(os.path.dirname(os.path.abspath( __file__ )) + "/graphics/pykms_Arrow_%s.gif" %position, - window, labels[position], loop = True) - - def animationwait(master, button, btn_animation, lbl_animation): - while btn_animation.cancelid: - pass - sleep(1) - x, y = master.winfo_pointerxy() - if master.winfo_containing(x, y) == button: - lbl_animation.start() - - def animationcombo(master, button, btn_animation, lbl_animation): - wait_thread = threading.Thread(target = animationwait, - args = (master, button, btn_animation, lbl_animation), - name = "Thread-WaitAni") - wait_thread.setDaemon(True) - wait_thread.start() - lbl_animation.stop() - btn_animation.start() - - buttons[position].bind("", lambda event, anim1 = anibtn, anim2 = anilbl, - bt = buttons[position], win = window: - animationcombo(win, bt, anim1, anim2)) - buttons[position].bind("", anilbl.start) - buttons[position].bind("", anilbl.stop) - - except ImportError: - buttons[position].config(activebackground = window.customcolors['blue'], - foreground = window.customcolors['blue']) - labels[position].config(background = window.customcolors['lavender']) - - if position == "Left": - buttons[position].config(text = '<<') - elif position == "Right": - buttons[position].config(text = '>>') - -##----------------------------------------------------------------------------------------------------------------------------------------------------------- -class ListboxOfRadiobuttons(tk.Frame): - def __init__(self, master, radios, font, changed, **kwargs): - tk.Frame.__init__(self, master) - - self.master = master - self.radios = radios - self.font = font - self.changed = changed - - self.scrollv = tk.Scrollbar(self, orient = "vertical") - self.textbox = tk.Text(self, yscrollcommand = self.scrollv.set, **kwargs) - self.scrollv.config(command = self.textbox.yview) - # layout. - self.scrollv.pack(side = "right", fill = "y") - self.textbox.pack(side = "left", fill = "both", expand = True) - # create radiobuttons. - self.radiovar = tk.StringVar() - self.radiovar.set('FILE') - self.create() - - def create(self): - self.rdbtns = [] - for n, nameradio in enumerate(self.radios): - rdbtn = tk.Radiobutton(self, text = nameradio, value = nameradio, variable = self.radiovar, - font = self.font, indicatoron = 0, width = 15, - borderwidth = 3, selectcolor = 'yellow', command = self.change) - self.textbox.window_create("end", window = rdbtn) - # to force one checkbox per line - if n != len(self.radios) - 1: - self.textbox.insert("end", "\n") - self.rdbtns.append(rdbtn) - self.textbox.configure(state = "disabled") - - def change(self): - st = self.state() - for widget, default in self.changed: - wclass = widget.winfo_class() - if st in ['STDOUT', 'FILEOFF']: - if wclass == 'Entry': - widget.delete(0, 'end') - widget.configure(state = "disabled") - elif wclass == 'TCombobox': - if st == 'STDOUT': - widget.set(default) - widget.configure(state = "readonly") - elif st == 'FILEOFF': - widget.set('') - widget.configure(state = "disabled") - elif st in ['FILE', 'FILESTDOUT', 'STDOUTOFF']: - if wclass == 'Entry': - widget.configure(state = "normal") - widget.delete(0, 'end') - widget.insert('end', default) - widget.xview_moveto(1) - elif wclass == 'TCombobox': - widget.configure(state = "readonly") - widget.set(default) - elif wclass == 'Button': - widget.configure(state = "normal") - - def configure(self, state): - for rb in self.rdbtns: - rb.configure(state = state) - - def state(self): - return self.radiovar.get() diff --git a/py-kms/pykms_Misc.py b/py-kms/pykms_Misc.py index f948d44..c96f094 100644 --- a/py-kms/pykms_Misc.py +++ b/py-kms/pykms_Misc.py @@ -194,9 +194,6 @@ def logger_create(log_obj, config, mode = 'a'): frmt_name = '%(name)s ' from pykms_Server import serverthread - if serverthread.with_gui: - frmt_std = frmt_name + frmt_std - frmt_min = frmt_name + frmt_min def apply_formatter(levelnum, formats, handler, color = False): levelformdict = {} @@ -521,7 +518,7 @@ def check_setup(config, options, logger, where): # Check logfile. config['logfile'] = check_logfile(config['logfile'], options['lfile']['def'], where = where) - # Check logsize (py-kms Gui). + # Check logsize if config['logsize'] == "": if any(opt in ['STDOUT', 'FILEOFF'] for opt in config['logfile']): # set a recognized size never used. @@ -530,7 +527,7 @@ def check_setup(config, options, logger, where): pretty_printer(put_text = "{reverse}{red}{bold}argument `-S/--logsize`: invalid with: '%s'. Exiting...{end}" %config['logsize'], where = where, to_exit = True) - # Check loglevel (py-kms Gui). + # Check loglevel if config['loglevel'] == "": # set a recognized level never used. config['loglevel'] = 'ERROR' diff --git a/py-kms/pykms_Server.py b/py-kms/pykms_Server.py index ab65e0a..f506455 100755 --- a/py-kms/pykms_Server.py +++ b/py-kms/pykms_Server.py @@ -9,22 +9,20 @@ import uuid import logging import os import threading -import pickle import socketserver import queue as Queue import selectors -from tempfile import gettempdir from time import monotonic as time import pykms_RpcBind, pykms_RpcRequest from pykms_RpcBase import rpcBase from pykms_Dcerpc import MSRPCHeader -from pykms_Misc import check_setup, check_lcid, check_dir, check_other +from pykms_Misc import check_setup, check_lcid, check_other from pykms_Misc import KmsParser, KmsParserException, KmsParserHelp from pykms_Misc import kms_parser_get, kms_parser_check_optionals, kms_parser_check_positionals, kms_parser_check_connect from pykms_Format import enco, deco, pretty_printer, justify -from Etrigan import Etrigan, Etrigan_parser, Etrigan_check, Etrigan_job from pykms_Connect import MultipleListener +from pykms_Sql import sql_initialize srv_version = "py-kms_2020-10-01" __license__ = "The Unlicense" @@ -135,7 +133,8 @@ class server_thread(threading.Thread): self.name = name self.queue = queue self.server = None - self.is_running_server, self.with_gui, self.checked = [False for _ in range(3)] + self.is_running_server = False + self.checked = False self.is_running_thread = threading.Event() def terminate_serve(self): @@ -170,13 +169,7 @@ class server_thread(threading.Thread): self.server.pykms_serve() except (SystemExit, Exception) as e: self.eject = True - if not self.with_gui: - raise - else: - if isinstance(e, SystemExit): - continue - else: - raise + raise ##--------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -184,7 +177,7 @@ loggersrv = logging.getLogger('logsrv') # 'help' string - 'default' value - 'dest' string. srv_options = { - 'ip' : {'help' : 'The IP address (IPv4 or IPv6) to listen on. The default is \"0.0.0.0\" (all interfaces).', 'def' : "0.0.0.0", 'des' : "ip"}, + 'ip' : {'help' : 'The IP address (IPv4 or IPv6) to listen on. The default is \"::\" (all interfaces).', 'def' : "::", 'des' : "ip"}, 'port' : {'help' : 'The network port to listen on. The default is \"1688\".', 'def' : 1688, 'des' : "port"}, 'epid' : {'help' : 'Use this option to manually specify an ePID to use. If no ePID is specified, a random ePID will be auto generated.', 'def' : None, 'des' : "epid"}, @@ -196,8 +189,7 @@ for server OSes and Office >=5', 'def' : None, 'des' : "clientcount"}, 'def' : 120, 'des': "activation"}, 'renewal' : {'help' : 'Use this option to specify the renewal interval (in minutes). Default is \"10080\" minutes (7 days).', 'def' : 1440 * 7, 'des' : "renewal"}, - 'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Deactivated by default. \ -If enabled the default .db file is \"pykms_database.db\". You can also provide a specific location.', 'def' : False, + 'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Deactivated by default.', 'def' : False, 'file': os.path.join('.', 'pykms_database.db'), 'des' : "sqlite"}, 'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \ The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', @@ -220,7 +212,7 @@ Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to 'reuse' : {'help' : 'Do not allows binding / listening to the same address and port. Reusing port is activated by default.', 'def' : True, 'des': "reuse"}, 'dual' : {'help' : 'Allows listening to an IPv6 address also accepting connections via IPv4. Deactivated by default.', - 'def' : False, 'des': "dual"} + 'def' : True, 'des': "dual"} } def server_options(): @@ -256,15 +248,6 @@ def server_options(): server_parser.add_argument("-h", "--help", action = "help", help = "show this help message and exit") - ## Daemon (Etrigan) parsing. - daemon_parser = KmsParser(description = "daemon options inherited from Etrigan", add_help = False) - daemon_subparser = daemon_parser.add_subparsers(dest = "mode") - - etrigan_parser = daemon_subparser.add_parser("etrigan", add_help = False) - etrigan_parser.add_argument("-g", "--gui", action = "store_const", dest = 'gui', const = True, default = False, - help = "Enable py-kms GUI usage.") - etrigan_parser = Etrigan_parser(parser = etrigan_parser) - ## Connection parsing. connection_parser = KmsParser(description = "connect options", add_help = False) connection_subparser = connection_parser.add_subparsers(dest = "mode") @@ -284,16 +267,14 @@ def server_options(): # Run help. if any(arg in ["-h", "--help"] for arg in userarg): - KmsParserHelp().printer(parsers = [server_parser, (daemon_parser, etrigan_parser), - (connection_parser, connect_parser)]) + KmsParserHelp().printer(parsers = [server_parser, (connection_parser, connect_parser)]) # Get stored arguments. pykmssrv_zeroarg, pykmssrv_onearg = kms_parser_get(server_parser) - etrigan_zeroarg, etrigan_onearg = kms_parser_get(etrigan_parser) connect_zeroarg, connect_onearg = kms_parser_get(connect_parser) - subdict = {'etrigan' : (etrigan_zeroarg, etrigan_onearg, daemon_parser.parse_args), - 'connect' : (connect_zeroarg, connect_onearg, connection_parser.parse_args) - } + subdict = { + 'connect' : (connect_zeroarg, connect_onearg, connection_parser.parse_args) + } subpars = list(subdict.keys()) pykmssrv_zeroarg += subpars # add subparsers @@ -309,14 +290,7 @@ def server_options(): if subindx: # Set `daemon options` and/or `connect options` for server dict config. # example cases: - # 1 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] etrigan daemon_positional [--daemon_optionals] \ - # connect [--connect_optionals] - # - # 2 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] connect [--connect_optionals] etrigan \ - # daemon_positional [--daemon_optionals] - # - # 3 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] etrigan daemon_positional [--daemon_optionals] - # 4 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] connect [--connect_optionals] + # 1 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] connect [--connect_optionals] first = subindx[0][0] # initial. kms_parser_check_optionals(userarg[0 : first], pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = exclude_kms) @@ -338,7 +312,7 @@ def server_options(): else: # Update `pykms options` for server dict config. # example case: - # 5 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] + # 2 python3 pykms_Server.py [1.2.3.4] [1234] [--pykms_optionals] kms_parser_check_optionals(userarg, pykmssrv_zeroarg, pykmssrv_onearg, exclude_opt_len = exclude_kms) kms_parser_check_positionals(srv_config, server_parser.parse_args) @@ -347,63 +321,6 @@ def server_options(): except KmsParserException as e: pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) -class Etrigan_Check(Etrigan_check): - def emit_opt_err(self, msg): - pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %msg, to_exit = True) - -class Etrigan(Etrigan): - def emit_message(self, message, to_exit = False): - if not self.mute: - pretty_printer(put_text = "{reverse}{green}{bold}%s{end}" %message) - if to_exit: - sys.exit(0) - - def emit_error(self, message, to_exit = True): - if not self.mute: - pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %message, to_exit = True) - -def server_daemon(): - if 'etrigan' in srv_config.values(): - path = os.path.join(gettempdir(), 'pykms_config.pickle') - - if srv_config['operation'] in ['stop', 'restart', 'status'] and len(sys.argv[1:]) > 2: - pretty_printer(put_text = "{reverse}{red}{bold}too much arguments with etrigan '%s'. Exiting...{end}" %srv_config['operation'], - to_exit = True) - - # Check file arguments. - Etrigan_Check().checkfile(srv_config['etriganpid'], '--etrigan-pid', '.pid') - Etrigan_Check().checkfile(srv_config['etriganlog'], '--etrigan-log', '.log') - - if srv_config['gui']: - pass - else: - if srv_config['operation'] == 'start': - with open(path, 'wb') as file: - pickle.dump(srv_config, file, protocol = pickle.HIGHEST_PROTOCOL) - elif srv_config['operation'] in ['stop', 'status', 'restart']: - with open(path, 'rb') as file: - old_srv_config = pickle.load(file) - old_srv_config = {x: old_srv_config[x] for x in old_srv_config if x not in ['operation']} - srv_config.update(old_srv_config) - - serverdaemon = Etrigan(srv_config['etriganpid'], - logfile = srv_config['etriganlog'], loglevel = srv_config['etriganlev'], - mute = srv_config['etriganmute'], pause_loop = None) - - if srv_config['operation'] in ['start', 'restart']: - serverdaemon.want_quit = True - if srv_config['gui']: - serverdaemon.funcs_to_daemonize = [server_with_gui] - else: - server_without_gui = ServerWithoutGui() - serverdaemon.funcs_to_daemonize = [server_without_gui.start, server_without_gui.join] - indx_for_clean = lambda: (0, ) - serverdaemon.quit_on_stop = [indx_for_clean, server_without_gui.clean] - elif srv_config['operation'] == 'stop': - os.remove(path) - - Etrigan_job(srv_config['operation'], serverdaemon) - def server_check(): # Setup and some checks. check_setup(srv_config, srv_options, loggersrv, where = "srv") @@ -445,25 +362,25 @@ def server_check(): # Check sqlite. if srv_config['sqlite']: - if isinstance(srv_config['sqlite'], str): - check_dir(srv_config['sqlite'], 'srv', log_obj = loggersrv.error, argument = '-s/--sqlite') - elif srv_config['sqlite'] is True: + if srv_config['sqlite'] is True: # Resolve bool to the default path srv_config['sqlite'] = srv_options['sql']['file'] + if os.path.isdir(srv_config['sqlite']): + pretty_printer(log_obj = loggersrv.warning, + put_text = "{reverse}{yellow}{bold}You specified a folder instead of a database file! This behavior is not officially supported anymore, please change your start parameters soon.{end}") + srv_config['sqlite'] = os.path.join(srv_config['sqlite'], 'pykms_database.db') try: import sqlite3 + sql_initialize(srv_config['sqlite']) except ImportError: pretty_printer(log_obj = loggersrv.warning, - put_text = "{reverse}{yellow}{bold}Module 'sqlite3' not installed, database support disabled.{end}") + put_text = "{reverse}{yellow}{bold}Module 'sqlite3' not installed, database support disabled.{end}") srv_config['sqlite'] = False # Check other specific server options. opts = [('clientcount', '-c/--client-count'), ('timeoutidle', '-t0/--timeout-idle'), ('timeoutsndrcv', '-t1/--timeout-sndrcv')] - if serverthread.with_gui: - opts += [('activation', '-a/--activation-interval'), - ('renewal', '-r/--renewal-interval')] check_other(srv_config, opts, loggersrv, where = 'srv') # Check further addresses / ports. @@ -543,35 +460,14 @@ def server_main_terminal(): server_check() serverthread.checked = True - if 'etrigan' not in srv_config.values(): - # (without GUI) and (without daemon). - # Run threaded server. - serverqueue.put('start') - # Wait to finish. - try: - while serverthread.is_alive(): - serverthread.join(timeout = 0.5) - except (KeyboardInterrupt, SystemExit): - server_terminate(serverthread, exit_server = True, exit_thread = True) - else: - # (with or without GUI) and (with daemon) - # Setup daemon (eventually). - pretty_printer(log_obj = loggersrv.warning, put_text = "{reverse}{yellow}{bold}Etrigan support is deprecated and will be removed in the future!{end}") - server_daemon() - -def server_with_gui(): - import pykms_GuiBase - - pretty_printer(log_obj = loggersrv.warning, put_text = "{reverse}{yellow}{bold}Etrigan GUI support is deprecated and will be removed in the future!{end}") - - root = pykms_GuiBase.KmsGui() - root.title(pykms_GuiBase.gui_description + ' (' + pykms_GuiBase.gui_version + ')') - root.mainloop() - -def server_main_no_terminal(): - # Run tkinter GUI. - # (with GUI) and (without daemon). - server_with_gui() + # Run threaded server. + serverqueue.put('start') + # Wait to finish. + try: + while serverthread.is_alive(): + serverthread.join(timeout = 0.5) + except (KeyboardInterrupt, SystemExit): + server_terminate(serverthread, exit_server = True, exit_thread = True) class kmsServerHandler(socketserver.BaseRequestHandler): def setup(self): @@ -636,10 +532,4 @@ serverthread.daemon = True serverthread.start() if __name__ == "__main__": - if sys.stdout.isatty(): - server_main_terminal() - else: - try: - server_main_no_terminal() - except: - server_main_terminal() + server_main_terminal() diff --git a/py-kms/pykms_Sql.py b/py-kms/pykms_Sql.py index 6afa889..841c557 100644 --- a/py-kms/pykms_Sql.py +++ b/py-kms/pykms_Sql.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import datetime import os import logging @@ -23,17 +24,35 @@ def sql_initialize(dbName): try: con = sqlite3.connect(dbName) cur = con.cursor() - cur.execute("CREATE TABLE clients(clientMachineId TEXT, machineName TEXT, applicationId TEXT, skuId TEXT, \ -licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER)") + cur.execute("CREATE TABLE clients(clientMachineId TEXT , machineName TEXT, applicationId TEXT, skuId TEXT, licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER, PRIMARY KEY(clientMachineId, applicationId))") except sqlite3.Error as e: - pretty_printer(log_obj = loggersrv.error, to_exit = True, - put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) + pretty_printer(log_obj = loggersrv.error, to_exit = True, put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) finally: if con: con.commit() con.close() +def sql_get_all(dbName): + if not os.path.isfile(dbName): + return None + with sqlite3.connect(dbName) as con: + cur = con.cursor() + cur.execute("SELECT * FROM clients") + clients = [] + for row in cur.fetchall(): + clients.append({ + 'clientMachineId': row[0], + 'machineName': row[1], + 'applicationId': row[2], + 'skuId': row[3], + 'licenseStatus': row[4], + 'lastRequestTime': datetime.datetime.fromtimestamp(row[5]).isoformat(), + 'kmsEpid': row[6], + 'requestCount': row[7] + }) + return clients + def sql_update(dbName, infoDict): con = None try: diff --git a/py-kms/pykms_WebUI.py b/py-kms/pykms_WebUI.py new file mode 100644 index 0000000..43285a5 --- /dev/null +++ b/py-kms/pykms_WebUI.py @@ -0,0 +1,141 @@ +import os, uuid, datetime +from flask import Flask, render_template +from pykms_Sql import sql_get_all +from pykms_DB2Dict import kmsDB2Dict + +def _random_uuid(): + return str(uuid.uuid4()).replace('-', '_') + +_serve_count = 0 +def _increase_serve_count(): + global _serve_count + _serve_count += 1 + +def _get_serve_count(): + return _serve_count + +_kms_items = None +_kms_items_ignored = None +def _get_kms_items_cache(): + global _kms_items, _kms_items_ignored + if _kms_items is None: + _kms_items = {} + _kms_items_ignored = 0 + queue = [kmsDB2Dict()] + while len(queue): + item = queue.pop(0) + if isinstance(item, list): + for i in item: + queue.append(i) + elif isinstance(item, dict): + if 'KmsItems' in item: + queue.append(item['KmsItems']) + elif 'SkuItems' in item: + queue.append(item['SkuItems']) + elif 'Gvlk' in item: + if len(item['Gvlk']): + _kms_items[item['DisplayName']] = item['Gvlk'] + else: + _kms_items_ignored += 1 + #else: + # print(item) + else: + raise NotImplementedError(f'Unknown type: {type(item)}') + return _kms_items, _kms_items_ignored + +app = Flask('pykms_webui') +app.jinja_env.globals['start_time'] = datetime.datetime.now() +app.jinja_env.globals['get_serve_count'] = _get_serve_count +app.jinja_env.globals['random_uuid'] = _random_uuid +app.jinja_env.globals['version_info'] = None + +_version_info_path = os.environ.get('PYKMS_VERSION_PATH', '../VERSION') +if os.path.exists(_version_info_path): + with open(_version_info_path, 'r') as f: + app.jinja_env.globals['version_info'] = { + 'hash': f.readline().strip(), + 'branch': f.readline().strip() + } + +_dbEnvVarName = 'PYKMS_SQLITE_DB_PATH' +def _env_check(): + if _dbEnvVarName not in os.environ: + raise Exception(f'Environment variable is not set: {_dbEnvVarName}') + +@app.route('/') +def root(): + _increase_serve_count() + error = None + # Get the db name / path + dbPath = None + if _dbEnvVarName in os.environ: + dbPath = os.environ.get(_dbEnvVarName) + else: + error = f'Environment variable is not set: {_dbEnvVarName}' + # Fetch all clients from the database. + clients = None + try: + if dbPath: + clients = sql_get_all(dbPath) + except Exception as e: + error = f'Error while loading database: {e}' + countClients = len(clients) if clients else 0 + countClientsWindows = len([c for c in clients if c['applicationId'] == 'Windows']) if clients else 0 + countClientsOffice = countClients - countClientsWindows + return render_template( + 'clients.html', + path='/', + error=error, + clients=clients, + count_clients=countClients, + count_clients_windows=countClientsWindows, + count_clients_office=countClientsOffice, + count_projects=len(_get_kms_items_cache()[0]) + ), 200 if error is None else 500 + +@app.route('/readyz') +def readyz(): + try: + _env_check() + except Exception as e: + return f'Whooops! {e}', 503 + if (datetime.datetime.now() - app.jinja_env.globals['start_time']).seconds > 10: # Wait 10 seconds before accepting requests + return 'OK', 200 + else: + return 'Not ready', 503 + +@app.route('/livez') +def livez(): + try: + _env_check() + return 'OK', 200 # There are no checks for liveness, so we just return OK + except Exception as e: + return f'Whooops! {e}', 503 + +@app.route('/license') +def license(): + _increase_serve_count() + with open(os.environ.get('PYKMS_LICENSE_PATH', '../LICENSE'), 'r') as f: + return render_template( + 'license.html', + path='/license/', + license=f.read() + ) + +@app.route('/products') +def products(): + _increase_serve_count() + items, ignored = _get_kms_items_cache() + countProducts = len(items) + countProductsWindows = len([i for i in items if 'windows' in i.lower()]) + countProductsOffice = len([i for i in items if 'office' in i.lower()]) + return render_template( + 'products.html', + path='/products/', + products=items, + filtered=ignored, + count_products=countProducts, + count_products_windows=countProductsWindows, + count_products_office=countProductsOffice + ) + \ No newline at end of file diff --git a/py-kms/static/css/bulma.min.css b/py-kms/static/css/bulma.min.css new file mode 100644 index 0000000..86ad2ff --- /dev/null +++ b/py-kms/static/css/bulma.min.css @@ -0,0 +1 @@ +/*! bulma.io v0.9.4 | MIT License | github.com/jgthms/bulma */.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.5em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.5em - 1px);padding-left:calc(.75em - 1px);padding-right:calc(.75em - 1px);padding-top:calc(.5em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{outline:0}.button[disabled],.file-cta[disabled],.file-name[disabled],.input[disabled],.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .button,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .input,fieldset[disabled] .pagination-ellipsis,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-previous,fieldset[disabled] .select select,fieldset[disabled] .textarea{cursor:not-allowed}.breadcrumb,.button,.file,.is-unselectable,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:not(.is-arrowless)::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.pagination:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:9999px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.delete::after,.delete::before,.modal-close::after,.modal-close::before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.delete::before,.modal-close::before{height:2px;width:50%}.delete::after,.modal-close::after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.delete:active,.modal-close:active{background-color:rgba(10,10,10,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.control.is-loading::after,.loader,.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:9999px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img,.is-overlay,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.navbar-burger{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:0 0;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0}/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:inherit}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,optgroup,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1em;font-weight:400;line-height:1.5}a{color:#485fc7;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#da1039;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{vertical-align:top}table td:not([align]),table th:not([align]){text-align:inherit}table th{color:#363636}@-webkit-keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}@keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}.box{background-color:#fff;border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px #485fc7}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #485fc7}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(.5em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.5em - 1px);margin-right:.25em}.button .icon:last-child:not(:first-child){margin-left:.25em;margin-right:calc(-.5em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.5em - 1px);margin-right:calc(-.5em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#485fc7;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-ghost{background:0 0;border-color:transparent;color:#485fc7;text-decoration:none}.button.is-ghost.is-hovered,.button.is-ghost:hover{color:#485fc7;text-decoration:underline}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:#fff;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-hovered,.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined.is-focused,.button.is-white.is-outlined.is-hovered,.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined.is-loading.is-focused::after,.button.is-white.is-outlined.is-loading.is-hovered::after,.button.is-white.is-outlined.is-loading:focus::after,.button.is-white.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined.is-focused,.button.is-white.is-inverted.is-outlined.is-hovered,.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-white.is-inverted.is-outlined.is-loading:focus::after,.button.is-white.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:#0a0a0a;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-hovered,.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined.is-focused,.button.is-black.is-outlined.is-hovered,.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined.is-loading.is-focused::after,.button.is-black.is-outlined.is-loading.is-hovered::after,.button.is-black.is-outlined.is-loading:focus::after,.button.is-black.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined.is-focused,.button.is-black.is-inverted.is-outlined.is-hovered,.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-black.is-inverted.is-outlined.is-loading:focus::after,.button.is-black.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none}.button.is-light.is-inverted{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-hovered,.button.is-light.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined.is-focused,.button.is-light.is-outlined.is-hovered,.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined.is-loading.is-focused::after,.button.is-light.is-outlined.is-loading.is-hovered::after,.button.is-light.is-outlined.is-loading:focus::after,.button.is-light.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-light.is-inverted.is-outlined.is-focused,.button.is-light.is-inverted.is-outlined.is-hovered,.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-light.is-inverted.is-outlined.is-loading:focus::after,.button.is-light.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-dark{background-color:#363636;border-color:transparent;color:#fff}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#fff}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#fff}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#fff}.button.is-dark[disabled],fieldset[disabled] .button.is-dark{background-color:#363636;border-color:#363636;box-shadow:none}.button.is-dark.is-inverted{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-hovered,.button.is-dark.is-inverted:hover{background-color:#f2f2f2}.button.is-dark.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined.is-focused,.button.is-dark.is-outlined.is-hovered,.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#fff}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined.is-loading.is-focused::after,.button.is-dark.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-outlined.is-loading:focus::after,.button.is-dark.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-dark.is-inverted.is-outlined.is-focused,.button.is-dark.is-inverted.is-outlined.is-hovered,.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-inverted.is-outlined.is-loading:focus::after,.button.is-dark.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled],fieldset[disabled] .button.is-primary{background-color:#00d1b2;border-color:#00d1b2;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-hovered,.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],fieldset[disabled] .button.is-primary.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined.is-focused,.button.is-primary.is-outlined.is-hovered,.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined.is-loading.is-focused::after,.button.is-primary.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-outlined.is-loading:focus::after,.button.is-primary.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined.is-focused,.button.is-primary.is-inverted.is-outlined.is-hovered,.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-inverted.is-outlined.is-loading:focus::after,.button.is-primary.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary.is-light{background-color:#ebfffc;color:#00947e}.button.is-primary.is-light.is-hovered,.button.is-primary.is-light:hover{background-color:#defffa;border-color:transparent;color:#00947e}.button.is-primary.is-light.is-active,.button.is-primary.is-light:active{background-color:#d1fff8;border-color:transparent;color:#00947e}.button.is-link{background-color:#485fc7;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#3e56c4;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#3a51bb;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#485fc7;border-color:#485fc7;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#485fc7}.button.is-link.is-inverted.is-hovered,.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#485fc7}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#485fc7;color:#485fc7}.button.is-link.is-outlined.is-focused,.button.is-link.is-outlined.is-hovered,.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#485fc7;border-color:#485fc7;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #485fc7 #485fc7!important}.button.is-link.is-outlined.is-loading.is-focused::after,.button.is-link.is-outlined.is-loading.is-hovered::after,.button.is-link.is-outlined.is-loading:focus::after,.button.is-link.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#485fc7;box-shadow:none;color:#485fc7}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined.is-focused,.button.is-link.is-inverted.is-outlined.is-hovered,.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#485fc7}.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-link.is-inverted.is-outlined.is-loading:focus::after,.button.is-link.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #485fc7 #485fc7!important}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link.is-light{background-color:#eff1fa;color:#3850b7}.button.is-link.is-light.is-hovered,.button.is-link.is-light:hover{background-color:#e6e9f7;border-color:transparent;color:#3850b7}.button.is-link.is-light.is-active,.button.is-link.is-light:active{background-color:#dce0f4;border-color:transparent;color:#3850b7}.button.is-info{background-color:#3e8ed0;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#3488ce;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#3082c5;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#3e8ed0;border-color:#3e8ed0;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#3e8ed0}.button.is-info.is-inverted.is-hovered,.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3e8ed0}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#3e8ed0;color:#3e8ed0}.button.is-info.is-outlined.is-focused,.button.is-info.is-outlined.is-hovered,.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#3e8ed0;border-color:#3e8ed0;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #3e8ed0 #3e8ed0!important}.button.is-info.is-outlined.is-loading.is-focused::after,.button.is-info.is-outlined.is-loading.is-hovered::after,.button.is-info.is-outlined.is-loading:focus::after,.button.is-info.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#3e8ed0;box-shadow:none;color:#3e8ed0}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined.is-focused,.button.is-info.is-inverted.is-outlined.is-hovered,.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#3e8ed0}.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-info.is-inverted.is-outlined.is-loading:focus::after,.button.is-info.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #3e8ed0 #3e8ed0!important}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info.is-light{background-color:#eff5fb;color:#296fa8}.button.is-info.is-light.is-hovered,.button.is-info.is-light:hover{background-color:#e4eff9;border-color:transparent;color:#296fa8}.button.is-info.is-light.is-active,.button.is-info.is-light:active{background-color:#dae9f6;border-color:transparent;color:#296fa8}.button.is-success{background-color:#48c78e;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#3ec487;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#3abb81;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#48c78e;border-color:#48c78e;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#48c78e}.button.is-success.is-inverted.is-hovered,.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#48c78e}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#48c78e;color:#48c78e}.button.is-success.is-outlined.is-focused,.button.is-success.is-outlined.is-hovered,.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#48c78e;border-color:#48c78e;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #48c78e #48c78e!important}.button.is-success.is-outlined.is-loading.is-focused::after,.button.is-success.is-outlined.is-loading.is-hovered::after,.button.is-success.is-outlined.is-loading:focus::after,.button.is-success.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#48c78e;box-shadow:none;color:#48c78e}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined.is-focused,.button.is-success.is-inverted.is-outlined.is-hovered,.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#48c78e}.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-success.is-inverted.is-outlined.is-loading:focus::after,.button.is-success.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #48c78e #48c78e!important}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success.is-light{background-color:#effaf5;color:#257953}.button.is-success.is-light.is-hovered,.button.is-success.is-light:hover{background-color:#e6f7ef;border-color:transparent;color:#257953}.button.is-success.is-light.is-active,.button.is-success.is-light:active{background-color:#dcf4e9;border-color:transparent;color:#257953}.button.is-warning{background-color:#ffe08a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdc7d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd970;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffe08a;border-color:#ffe08a;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffe08a}.button.is-warning.is-inverted.is-hovered,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffe08a}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffe08a;color:#ffe08a}.button.is-warning.is-outlined.is-focused,.button.is-warning.is-outlined.is-hovered,.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffe08a;border-color:#ffe08a;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffe08a #ffe08a!important}.button.is-warning.is-outlined.is-loading.is-focused::after,.button.is-warning.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-outlined.is-loading:focus::after,.button.is-warning.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffe08a;box-shadow:none;color:#ffe08a}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined.is-focused,.button.is-warning.is-inverted.is-outlined.is-hovered,.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffe08a}.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-inverted.is-outlined.is-loading:focus::after,.button.is-warning.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #ffe08a #ffe08a!important}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-warning.is-light{background-color:#fffaeb;color:#946c00}.button.is-warning.is-light.is-hovered,.button.is-warning.is-light:hover{background-color:#fff6de;border-color:transparent;color:#946c00}.button.is-warning.is-light.is-active,.button.is-warning.is-light:active{background-color:#fff3d1;border-color:transparent;color:#946c00}.button.is-danger{background-color:#f14668;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#f03a5f;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ef2e55;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#f14668;border-color:#f14668;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-hovered,.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#f14668}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;color:#f14668}.button.is-danger.is-outlined.is-focused,.button.is-danger.is-outlined.is-hovered,.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#f14668;border-color:#f14668;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-outlined.is-loading.is-focused::after,.button.is-danger.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-outlined.is-loading:focus::after,.button.is-danger.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;box-shadow:none;color:#f14668}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined.is-focused,.button.is-danger.is-inverted.is-outlined.is-hovered,.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-inverted.is-outlined.is-loading:focus::after,.button.is-danger.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.button.is-danger.is-light.is-hovered,.button.is-danger.is-light:hover{background-color:#fde0e6;border-color:transparent;color:#cc0f35}.button.is-danger.is-light.is-active,.button.is-danger.is-light:active{background-color:#fcd4dc;border-color:transparent;color:#cc0f35}.button.is-small{font-size:.75rem}.button.is-small:not(.is-rounded){border-radius:2px}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em * .5));top:calc(50% - (1em * .5));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:9999px;padding-left:calc(1em + .25em);padding-right:calc(1em + .25em)}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){font-size:.75rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large):not(.is-rounded){border-radius:2px}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}.buttons.is-centered{justify-content:center}.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}.buttons.is-right{justify-content:flex-end}.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}@media screen and (max-width:768px){.button.is-responsive.is-small{font-size:.5625rem}.button.is-responsive,.button.is-responsive.is-normal{font-size:.65625rem}.button.is-responsive.is-medium{font-size:.75rem}.button.is-responsive.is-large{font-size:1rem}}@media screen and (min-width:769px) and (max-width:1023px){.button.is-responsive.is-small{font-size:.65625rem}.button.is-responsive,.button.is-responsive.is-normal{font-size:.75rem}.button.is-responsive.is-medium{font-size:1rem}.button.is-responsive.is-large{font-size:1.25rem}}.container{flex-grow:1;margin:0 auto;position:relative;width:auto}.container.is-fluid{max-width:none!important;padding-left:32px;padding-right:32px;width:100%}@media screen and (min-width:1024px){.container{max-width:960px}}@media screen and (max-width:1215px){.container.is-widescreen:not(.is-max-desktop){max-width:1152px}}@media screen and (max-width:1407px){.container.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}@media screen and (min-width:1216px){.container:not(.is-max-desktop){max-width:1152px}}@media screen and (min-width:1408px){.container:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol:not([type]).is-lower-alpha{list-style-type:lower-alpha}.content ol:not([type]).is-lower-roman{list-style-type:lower-roman}.content ol:not([type]).is-upper-alpha{list-style-type:upper-alpha}.content ol:not([type]).is-upper-roman{list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636}.content table th:not([align]){text-align:inherit}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content .tabs li+li{margin-top:0}.content.is-small{font-size:.75rem}.content.is-normal{font-size:1rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.icon-text{align-items:flex-start;color:inherit;display:inline-flex;flex-wrap:wrap;line-height:1.5rem;vertical-align:top}.icon-text .icon{flex-grow:0;flex-shrink:0}.icon-text .icon:not(:last-child){margin-right:.25em}.icon-text .icon:not(:first-child){margin-left:.25em}div.icon-text{display:flex}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:9999px}.image.is-fullwidth{width:100%}.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;position:relative;padding:1.25rem 2.5rem 1.25rem 1.5rem}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{right:.5rem;position:absolute;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.notification.is-dark{background-color:#363636;color:#fff}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-primary.is-light{background-color:#ebfffc;color:#00947e}.notification.is-link{background-color:#485fc7;color:#fff}.notification.is-link.is-light{background-color:#eff1fa;color:#3850b7}.notification.is-info{background-color:#3e8ed0;color:#fff}.notification.is-info.is-light{background-color:#eff5fb;color:#296fa8}.notification.is-success{background-color:#48c78e;color:#fff}.notification.is-success.is-light{background-color:#effaf5;color:#257953}.notification.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.notification.is-warning.is-light{background-color:#fffaeb;color:#946c00}.notification.is-danger{background-color:#f14668;color:#fff}.notification.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:9999px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#ededed}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right,#fff 30%,#ededed 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right,#0a0a0a 30%,#ededed 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right,#f5f5f5 30%,#ededed 30%)}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate{background-image:linear-gradient(to right,#363636 30%,#ededed 30%)}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-primary:indeterminate{background-image:linear-gradient(to right,#00d1b2 30%,#ededed 30%)}.progress.is-link::-webkit-progress-value{background-color:#485fc7}.progress.is-link::-moz-progress-bar{background-color:#485fc7}.progress.is-link::-ms-fill{background-color:#485fc7}.progress.is-link:indeterminate{background-image:linear-gradient(to right,#485fc7 30%,#ededed 30%)}.progress.is-info::-webkit-progress-value{background-color:#3e8ed0}.progress.is-info::-moz-progress-bar{background-color:#3e8ed0}.progress.is-info::-ms-fill{background-color:#3e8ed0}.progress.is-info:indeterminate{background-image:linear-gradient(to right,#3e8ed0 30%,#ededed 30%)}.progress.is-success::-webkit-progress-value{background-color:#48c78e}.progress.is-success::-moz-progress-bar{background-color:#48c78e}.progress.is-success::-ms-fill{background-color:#48c78e}.progress.is-success:indeterminate{background-image:linear-gradient(to right,#48c78e 30%,#ededed 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffe08a}.progress.is-warning::-moz-progress-bar{background-color:#ffe08a}.progress.is-warning::-ms-fill{background-color:#ffe08a}.progress.is-warning:indeterminate{background-image:linear-gradient(to right,#ffe08a 30%,#ededed 30%)}.progress.is-danger::-webkit-progress-value{background-color:#f14668}.progress.is-danger::-moz-progress-bar{background-color:#f14668}.progress.is-danger::-ms-fill{background-color:#f14668}.progress.is-danger:indeterminate{background-image:linear-gradient(to right,#f14668 30%,#ededed 30%)}.progress:indeterminate{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:moveIndeterminate;animation-name:moveIndeterminate;-webkit-animation-timing-function:linear;animation-timing-function:linear;background-color:#ededed;background-image:linear-gradient(to right,#4a4a4a 30%,#ededed 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress:indeterminate::-ms-fill{animation-name:none}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@-webkit-keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#fff}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#485fc7;border-color:#485fc7;color:#fff}.table td.is-info,.table th.is-info{background-color:#3e8ed0;border-color:#3e8ed0;color:#fff}.table td.is-success,.table th.is-success{background-color:#48c78e;border-color:#48c78e;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffe08a;border-color:#ffe08a;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#f14668;border-color:#f14668;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table td.is-vcentered,.table th.is-vcentered{vertical-align:middle}.table th{color:#363636}.table th:not([align]){text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:transparent}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot{background-color:transparent}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody{background-color:transparent}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(2n){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(2n){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.tags.has-addons .tag:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.tag:not(body).is-dark{background-color:#363636;color:#fff}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-primary.is-light{background-color:#ebfffc;color:#00947e}.tag:not(body).is-link{background-color:#485fc7;color:#fff}.tag:not(body).is-link.is-light{background-color:#eff1fa;color:#3850b7}.tag:not(body).is-info{background-color:#3e8ed0;color:#fff}.tag:not(body).is-info.is-light{background-color:#eff5fb;color:#296fa8}.tag:not(body).is-success{background-color:#48c78e;color:#fff}.tag:not(body).is-success.is-light{background-color:#effaf5;color:#257953}.tag:not(body).is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.tag:not(body).is-warning.is-light{background-color:#fffaeb;color:#946c00}.tag:not(body).is-danger{background-color:#f14668;color:#fff}.tag:not(body).is-danger.is-light{background-color:#feecf0;color:#cc0f35}.tag:not(body).is-normal{font-size:.75rem}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::after,.tag:not(body).is-delete::before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:9999px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.number{align-items:center;background-color:#f5f5f5;border-radius:9999px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.input,.select select,.textarea{background-color:#fff;border-color:#dbdbdb;border-radius:4px;color:#363636}.input::-moz-placeholder,.select select::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.select select::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.select select:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.select select:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input:hover,.is-hovered.input,.is-hovered.textarea,.select select.is-hovered,.select select:hover,.textarea:hover{border-color:#b5b5b5}.input:active,.input:focus,.is-active.input,.is-active.textarea,.is-focused.input,.is-focused.textarea,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{border-color:#485fc7;box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.input[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .input,fieldset[disabled] .select select,fieldset[disabled] .textarea{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.select fieldset[disabled] select::-moz-placeholder,.select select[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.select fieldset[disabled] select::-webkit-input-placeholder,.select select[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.select fieldset[disabled] select:-moz-placeholder,.select select[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.select fieldset[disabled] select:-ms-input-placeholder,.select select[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input,.textarea{box-shadow:inset 0 .0625em .125em rgba(10,10,10,.05);max-width:100%;width:100%}.input[readonly],.textarea[readonly]{box-shadow:none}.is-white.input,.is-white.textarea{border-color:#fff}.is-white.input:active,.is-white.input:focus,.is-white.is-active.input,.is-white.is-active.textarea,.is-white.is-focused.input,.is-white.is-focused.textarea,.is-white.textarea:active,.is-white.textarea:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.is-black.input,.is-black.textarea{border-color:#0a0a0a}.is-black.input:active,.is-black.input:focus,.is-black.is-active.input,.is-black.is-active.textarea,.is-black.is-focused.input,.is-black.is-focused.textarea,.is-black.textarea:active,.is-black.textarea:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.is-light.input,.is-light.textarea{border-color:#f5f5f5}.is-light.input:active,.is-light.input:focus,.is-light.is-active.input,.is-light.is-active.textarea,.is-light.is-focused.input,.is-light.is-focused.textarea,.is-light.textarea:active,.is-light.textarea:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.is-dark.input,.is-dark.textarea{border-color:#363636}.is-dark.input:active,.is-dark.input:focus,.is-dark.is-active.input,.is-dark.is-active.textarea,.is-dark.is-focused.input,.is-dark.is-focused.textarea,.is-dark.textarea:active,.is-dark.textarea:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.is-primary.input,.is-primary.textarea{border-color:#00d1b2}.is-primary.input:active,.is-primary.input:focus,.is-primary.is-active.input,.is-primary.is-active.textarea,.is-primary.is-focused.input,.is-primary.is-focused.textarea,.is-primary.textarea:active,.is-primary.textarea:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.is-link.input,.is-link.textarea{border-color:#485fc7}.is-link.input:active,.is-link.input:focus,.is-link.is-active.input,.is-link.is-active.textarea,.is-link.is-focused.input,.is-link.is-focused.textarea,.is-link.textarea:active,.is-link.textarea:focus{box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.is-info.input,.is-info.textarea{border-color:#3e8ed0}.is-info.input:active,.is-info.input:focus,.is-info.is-active.input,.is-info.is-active.textarea,.is-info.is-focused.input,.is-info.is-focused.textarea,.is-info.textarea:active,.is-info.textarea:focus{box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.is-success.input,.is-success.textarea{border-color:#48c78e}.is-success.input:active,.is-success.input:focus,.is-success.is-active.input,.is-success.is-active.textarea,.is-success.is-focused.input,.is-success.is-focused.textarea,.is-success.textarea:active,.is-success.textarea:focus{box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.is-warning.input,.is-warning.textarea{border-color:#ffe08a}.is-warning.input:active,.is-warning.input:focus,.is-warning.is-active.input,.is-warning.is-active.textarea,.is-warning.is-focused.input,.is-warning.is-focused.textarea,.is-warning.textarea:active,.is-warning.textarea:focus{box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.is-danger.input,.is-danger.textarea{border-color:#f14668}.is-danger.input:active,.is-danger.input:focus,.is-danger.is-active.input,.is-danger.is-active.textarea,.is-danger.is-focused.input,.is-danger.is-focused.textarea,.is-danger.textarea:active,.is-danger.textarea:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.is-small.input,.is-small.textarea{border-radius:2px;font-size:.75rem}.is-medium.input,.is-medium.textarea{font-size:1.25rem}.is-large.input,.is-large.textarea{font-size:1.5rem}.is-fullwidth.input,.is-fullwidth.textarea{display:block;width:100%}.is-inline.input,.is-inline.textarea{display:inline;width:auto}.input.is-rounded{border-radius:9999px;padding-left:calc(calc(.75em - 1px) + .375em);padding-right:calc(calc(.75em - 1px) + .375em)}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:calc(.75em - 1px);resize:vertical}.textarea:not([rows]){max-height:40em;min-height:8em}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox input[disabled],.checkbox[disabled],.radio input[disabled],.radio[disabled],fieldset[disabled] .checkbox,fieldset[disabled] .radio{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.5em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#485fc7;right:1.125em;z-index:4}.select.is-rounded select{border-radius:9999px;padding-left:1em}.select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover)::after{border-color:#485fc7}.select.is-link select{border-color:#485fc7}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#3a51bb}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(72,95,199,.25)}.select.is-info:not(:hover)::after{border-color:#3e8ed0}.select.is-info select{border-color:#3e8ed0}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#3082c5}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(62,142,208,.25)}.select.is-success:not(:hover)::after{border-color:#48c78e}.select.is-success select{border-color:#48c78e}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#3abb81}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(72,199,142,.25)}.select.is-warning:not(:hover)::after{border-color:#ffe08a}.select.is-warning select{border-color:#ffe08a}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd970}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,224,138,.25)}.select.is-danger:not(:hover)::after{border-color:#f14668}.select.is-danger select{border-color:#f14668}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ef2e55}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a!important;opacity:.5}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(245,245,245,.25);color:rgba(0,0,0,.7)}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#fff}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#fff}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#fff}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#fff}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#485fc7;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#3e56c4;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(72,95,199,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#3a51bb;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#3e8ed0;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#3488ce;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(62,142,208,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#3082c5;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#48c78e;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#3ec487;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(72,199,142,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#3abb81;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffe08a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdc7d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,224,138,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd970;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#f14668;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#f03a5f;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(241,70,104,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ef2e55;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-normal{font-size:1rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:0;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:inherit;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#485fc7}.help.is-info{color:#3e8ed0}.help.is-success{color:#48c78e}.help.is-warning{color:#ffe08a}.help.is-danger{color:#f14668}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]).is-hovered,.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .input:not([disabled]).is-hovered,.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]).is-hovered,.field.has-addons .control .select select:not([disabled]):hover{z-index:2}.field.has-addons .control .button:not([disabled]).is-active,.field.has-addons .control .button:not([disabled]).is-focused,.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .input:not([disabled]).is-active,.field.has-addons .control .input:not([disabled]).is-focused,.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control .select select:not([disabled]).is-active,.field.has-addons .control .select select:not([disabled]).is-focused,.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select:not([disabled]):focus{z-index:3}.field.has-addons .control .button:not([disabled]).is-active:hover,.field.has-addons .control .button:not([disabled]).is-focused:hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .input:not([disabled]).is-active:hover,.field.has-addons .control .input:not([disabled]).is-focused:hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control .select select:not([disabled]).is-active:hover,.field.has-addons .control .select select:not([disabled]).is-focused:hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select:not([disabled]):focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:inherit}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#4a4a4a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.5em;pointer-events:none;position:absolute;top:0;width:2.5em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.5em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.5em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#485fc7;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;border-radius:.25rem;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;max-width:100%;position:relative}.card-content:first-child,.card-footer:first-child,.card-header:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-content:last-child,.card-footer:last-child,.card-header:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-header{background-color:transparent;align-items:stretch;box-shadow:0 .125em .25em rgba(10,10,10,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem 1rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:0 0;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0;align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem 1rem}.card-image{display:block;position:relative}.card-image:first-child img{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-image:last-child img{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-content{background-color:transparent;padding:1.5rem}.card-footer{background-color:transparent;border-top:1px solid #ededed;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #ededed}.card .media:not(:last-child){margin-bottom:1.5rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:inherit;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#485fc7;color:#fff}.dropdown-divider{background-color:#ededed;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width:769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:inherit}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:inherit}@media screen and (max-width:768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#485fc7;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.message.is-light .message-body{border-color:#f5f5f5}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#fff}.message.is-dark .message-body{border-color:#363636}.message.is-primary{background-color:#ebfffc}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#00947e}.message.is-link{background-color:#eff1fa}.message.is-link .message-header{background-color:#485fc7;color:#fff}.message.is-link .message-body{border-color:#485fc7;color:#3850b7}.message.is-info{background-color:#eff5fb}.message.is-info .message-header{background-color:#3e8ed0;color:#fff}.message.is-info .message-body{border-color:#3e8ed0;color:#296fa8}.message.is-success{background-color:#effaf5}.message.is-success .message-header{background-color:#48c78e;color:#fff}.message.is-success .message-body{border-color:#48c78e;color:#257953}.message.is-warning{background-color:#fffaeb}.message.is-warning .message-header{background-color:#ffe08a;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffe08a;color:#946c00}.message.is-danger{background-color:#feecf0}.message.is-danger .message-header{background-color:#f14668;color:#fff}.message.is-danger .message-body{border-color:#f14668;color:#cc0f35}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:.5em}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:focus,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:focus,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width:1024px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:focus,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:focus,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:focus,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:focus,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:focus,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:focus,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:focus,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:focus,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:focus,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:focus,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:focus,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:focus,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:focus,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:focus,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:focus,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:focus,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:rgba(0,0,0,.7)}}.navbar.is-dark{background-color:#363636;color:#fff}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#fff}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:focus,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:focus,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#fff}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:focus,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:focus,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:focus,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:focus,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#fff}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#fff}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:focus,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:focus,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:focus,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:focus,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:focus,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:focus,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#485fc7;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:focus,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:focus,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:focus,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:focus,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:focus,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:focus,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#3a51bb;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#485fc7;color:#fff}}.navbar.is-info{background-color:#3e8ed0;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:focus,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:focus,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:focus,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:focus,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:focus,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:focus,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#3082c5;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#3e8ed0;color:#fff}}.navbar.is-success{background-color:#48c78e;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:focus,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:focus,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:focus,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:focus,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:focus,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:focus,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#3abb81;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#48c78e;color:#fff}}.navbar.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:focus,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:focus,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:focus,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:focus,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:focus,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:focus,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd970;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffe08a;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#f14668;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:focus,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:focus,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:focus,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:focus,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:focus,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:focus,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#f14668;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:focus,.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#4a4a4a;-moz-appearance:none;-webkit-appearance:none;appearance:none;background:0 0;border:none;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:first-child{top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:first-child{transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:focus,.navbar-link:focus-within,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:focus,a.navbar-item:focus-within,a.navbar-item:hover{background-color:#fafafa;color:#485fc7}.navbar-item{flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:focus,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#485fc7}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#485fc7;border-bottom-style:solid;border-bottom-width:3px;color:#485fc7;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#485fc7;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1023px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1024px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:focus,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:focus,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:focus,.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#485fc7}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:focus,.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#485fc7}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-.75rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:focus):not(:hover),a.navbar-item.is-active:not(:focus):not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:focus .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:9999px}.pagination.is-rounded .pagination-link{border-radius:9999px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.5em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#485fc7}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link.is-disabled,.pagination-link[disabled],.pagination-next.is-disabled,.pagination-next[disabled],.pagination-previous.is-disabled,.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#485fc7;border-color:#485fc7;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}.pagination-list li{list-style:none}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{margin-bottom:0;margin-top:0}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between;margin-bottom:0;margin-top:0}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel.is-white .panel-heading{background-color:#fff;color:#0a0a0a}.panel.is-white .panel-tabs a.is-active{border-bottom-color:#fff}.panel.is-white .panel-block.is-active .panel-icon{color:#fff}.panel.is-black .panel-heading{background-color:#0a0a0a;color:#fff}.panel.is-black .panel-tabs a.is-active{border-bottom-color:#0a0a0a}.panel.is-black .panel-block.is-active .panel-icon{color:#0a0a0a}.panel.is-light .panel-heading{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.panel.is-light .panel-tabs a.is-active{border-bottom-color:#f5f5f5}.panel.is-light .panel-block.is-active .panel-icon{color:#f5f5f5}.panel.is-dark .panel-heading{background-color:#363636;color:#fff}.panel.is-dark .panel-tabs a.is-active{border-bottom-color:#363636}.panel.is-dark .panel-block.is-active .panel-icon{color:#363636}.panel.is-primary .panel-heading{background-color:#00d1b2;color:#fff}.panel.is-primary .panel-tabs a.is-active{border-bottom-color:#00d1b2}.panel.is-primary .panel-block.is-active .panel-icon{color:#00d1b2}.panel.is-link .panel-heading{background-color:#485fc7;color:#fff}.panel.is-link .panel-tabs a.is-active{border-bottom-color:#485fc7}.panel.is-link .panel-block.is-active .panel-icon{color:#485fc7}.panel.is-info .panel-heading{background-color:#3e8ed0;color:#fff}.panel.is-info .panel-tabs a.is-active{border-bottom-color:#3e8ed0}.panel.is-info .panel-block.is-active .panel-icon{color:#3e8ed0}.panel.is-success .panel-heading{background-color:#48c78e;color:#fff}.panel.is-success .panel-tabs a.is-active{border-bottom-color:#48c78e}.panel.is-success .panel-block.is-active .panel-icon{color:#48c78e}.panel.is-warning .panel-heading{background-color:#ffe08a;color:rgba(0,0,0,.7)}.panel.is-warning .panel-tabs a.is-active{border-bottom-color:#ffe08a}.panel.is-warning .panel-block.is-active .panel-icon{color:#ffe08a}.panel.is-danger .panel-heading{background-color:#f14668;color:#fff}.panel.is-danger .panel-tabs a.is-active{border-bottom-color:#f14668}.panel.is-danger .panel-block.is-active .panel-icon{color:#f14668}.panel-block:not(:last-child),.panel-tabs:not(:last-child){border-bottom:1px solid #ededed}.panel-heading{background-color:#ededed;border-radius:6px 6px 0 0;color:#363636;font-size:1.25em;font-weight:700;line-height:1.25;padding:.75em 1em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#485fc7}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#485fc7;color:#363636}.panel-block.is-active .panel-icon{color:#485fc7}.panel-block:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#485fc7;color:#485fc7}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-top-left-radius:4px;border-bottom-left-radius:4px}.tabs.is-toggle li:last-child a{border-top-right-radius:4px;border-bottom-right-radius:4px}.tabs.is-toggle li.is-active a{background-color:#485fc7;border-color:#485fc7;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:9999px;border-top-left-radius:9999px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:9999px;border-top-right-radius:9999px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none;width:unset}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-0{flex:none;width:0%}.columns.is-mobile>.column.is-offset-0{margin-left:0}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none;width:unset}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-0-mobile{flex:none;width:0%}.column.is-offset-0-mobile{margin-left:0}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none;width:unset}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-0,.column.is-0-tablet{flex:none;width:0%}.column.is-offset-0,.column.is-offset-0-tablet{margin-left:0}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1023px){.column.is-narrow-touch{flex:none;width:unset}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-0-touch{flex:none;width:0%}.column.is-offset-0-touch{margin-left:0}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1024px){.column.is-narrow-desktop{flex:none;width:unset}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-0-desktop{flex:none;width:0%}.column.is-offset-0-desktop{margin-left:0}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1216px){.column.is-narrow-widescreen{flex:none;width:unset}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-0-widescreen{flex:none;width:0%}.column.is-offset-0-widescreen{margin-left:0}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1408px){.column.is-narrow-fullhd{flex:none;width:unset}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-0-fullhd{flex:none;width:0%}.column.is-offset-0-fullhd{margin-left:0}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1024px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable>.column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}@media screen and (max-width:768px){.columns.is-variable.is-0-mobile{--columnGap:0rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-0-tablet{--columnGap:0rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-0-tablet-only{--columnGap:0rem}}@media screen and (max-width:1023px){.columns.is-variable.is-0-touch{--columnGap:0rem}}@media screen and (min-width:1024px){.columns.is-variable.is-0-desktop{--columnGap:0rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-0-desktop-only{--columnGap:0rem}}@media screen and (min-width:1216px){.columns.is-variable.is-0-widescreen{--columnGap:0rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-0-widescreen-only{--columnGap:0rem}}@media screen and (min-width:1408px){.columns.is-variable.is-0-fullhd{--columnGap:0rem}}.columns.is-variable.is-1{--columnGap:0.25rem}@media screen and (max-width:768px){.columns.is-variable.is-1-mobile{--columnGap:0.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-1-tablet{--columnGap:0.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-1-tablet-only{--columnGap:0.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-1-touch{--columnGap:0.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-1-desktop{--columnGap:0.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-1-desktop-only{--columnGap:0.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-1-widescreen{--columnGap:0.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-1-widescreen-only{--columnGap:0.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-1-fullhd{--columnGap:0.25rem}}.columns.is-variable.is-2{--columnGap:0.5rem}@media screen and (max-width:768px){.columns.is-variable.is-2-mobile{--columnGap:0.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-2-tablet{--columnGap:0.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-2-tablet-only{--columnGap:0.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-2-touch{--columnGap:0.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-2-desktop{--columnGap:0.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-2-desktop-only{--columnGap:0.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-2-widescreen{--columnGap:0.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-2-widescreen-only{--columnGap:0.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-2-fullhd{--columnGap:0.5rem}}.columns.is-variable.is-3{--columnGap:0.75rem}@media screen and (max-width:768px){.columns.is-variable.is-3-mobile{--columnGap:0.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-3-tablet{--columnGap:0.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-3-tablet-only{--columnGap:0.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-3-touch{--columnGap:0.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-3-desktop{--columnGap:0.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-3-desktop-only{--columnGap:0.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-3-widescreen{--columnGap:0.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-3-widescreen-only{--columnGap:0.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-3-fullhd{--columnGap:0.75rem}}.columns.is-variable.is-4{--columnGap:1rem}@media screen and (max-width:768px){.columns.is-variable.is-4-mobile{--columnGap:1rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-4-tablet{--columnGap:1rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-4-tablet-only{--columnGap:1rem}}@media screen and (max-width:1023px){.columns.is-variable.is-4-touch{--columnGap:1rem}}@media screen and (min-width:1024px){.columns.is-variable.is-4-desktop{--columnGap:1rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-4-desktop-only{--columnGap:1rem}}@media screen and (min-width:1216px){.columns.is-variable.is-4-widescreen{--columnGap:1rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-4-widescreen-only{--columnGap:1rem}}@media screen and (min-width:1408px){.columns.is-variable.is-4-fullhd{--columnGap:1rem}}.columns.is-variable.is-5{--columnGap:1.25rem}@media screen and (max-width:768px){.columns.is-variable.is-5-mobile{--columnGap:1.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-5-tablet{--columnGap:1.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-5-tablet-only{--columnGap:1.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-5-touch{--columnGap:1.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-5-desktop{--columnGap:1.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-5-desktop-only{--columnGap:1.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-5-widescreen{--columnGap:1.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-5-widescreen-only{--columnGap:1.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-5-fullhd{--columnGap:1.25rem}}.columns.is-variable.is-6{--columnGap:1.5rem}@media screen and (max-width:768px){.columns.is-variable.is-6-mobile{--columnGap:1.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-6-tablet{--columnGap:1.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-6-tablet-only{--columnGap:1.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-6-touch{--columnGap:1.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-6-desktop{--columnGap:1.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-6-desktop-only{--columnGap:1.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-6-widescreen{--columnGap:1.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-6-widescreen-only{--columnGap:1.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-6-fullhd{--columnGap:1.5rem}}.columns.is-variable.is-7{--columnGap:1.75rem}@media screen and (max-width:768px){.columns.is-variable.is-7-mobile{--columnGap:1.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-7-tablet{--columnGap:1.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-7-tablet-only{--columnGap:1.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-7-touch{--columnGap:1.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-7-desktop{--columnGap:1.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-7-desktop-only{--columnGap:1.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-7-widescreen{--columnGap:1.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-7-widescreen-only{--columnGap:1.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-7-fullhd{--columnGap:1.75rem}}.columns.is-variable.is-8{--columnGap:2rem}@media screen and (max-width:768px){.columns.is-variable.is-8-mobile{--columnGap:2rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-8-tablet{--columnGap:2rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-8-tablet-only{--columnGap:2rem}}@media screen and (max-width:1023px){.columns.is-variable.is-8-touch{--columnGap:2rem}}@media screen and (min-width:1024px){.columns.is-variable.is-8-desktop{--columnGap:2rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-8-desktop-only{--columnGap:2rem}}@media screen and (min-width:1216px){.columns.is-variable.is-8-widescreen{--columnGap:2rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-8-widescreen-only{--columnGap:2rem}}@media screen and (min-width:1408px){.columns.is-variable.is-8-fullhd{--columnGap:2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-primary-light{color:#ebfffc!important}a.has-text-primary-light:focus,a.has-text-primary-light:hover{color:#b8fff4!important}.has-background-primary-light{background-color:#ebfffc!important}.has-text-primary-dark{color:#00947e!important}a.has-text-primary-dark:focus,a.has-text-primary-dark:hover{color:#00c7a9!important}.has-background-primary-dark{background-color:#00947e!important}.has-text-link{color:#485fc7!important}a.has-text-link:focus,a.has-text-link:hover{color:#3449a8!important}.has-background-link{background-color:#485fc7!important}.has-text-link-light{color:#eff1fa!important}a.has-text-link-light:focus,a.has-text-link-light:hover{color:#c8cfee!important}.has-background-link-light{background-color:#eff1fa!important}.has-text-link-dark{color:#3850b7!important}a.has-text-link-dark:focus,a.has-text-link-dark:hover{color:#576dcb!important}.has-background-link-dark{background-color:#3850b7!important}.has-text-info{color:#3e8ed0!important}a.has-text-info:focus,a.has-text-info:hover{color:#2b74b1!important}.has-background-info{background-color:#3e8ed0!important}.has-text-info-light{color:#eff5fb!important}a.has-text-info-light:focus,a.has-text-info-light:hover{color:#c6ddf1!important}.has-background-info-light{background-color:#eff5fb!important}.has-text-info-dark{color:#296fa8!important}a.has-text-info-dark:focus,a.has-text-info-dark:hover{color:#368ace!important}.has-background-info-dark{background-color:#296fa8!important}.has-text-success{color:#48c78e!important}a.has-text-success:focus,a.has-text-success:hover{color:#34a873!important}.has-background-success{background-color:#48c78e!important}.has-text-success-light{color:#effaf5!important}a.has-text-success-light:focus,a.has-text-success-light:hover{color:#c8eedd!important}.has-background-success-light{background-color:#effaf5!important}.has-text-success-dark{color:#257953!important}a.has-text-success-dark:focus,a.has-text-success-dark:hover{color:#31a06e!important}.has-background-success-dark{background-color:#257953!important}.has-text-warning{color:#ffe08a!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd257!important}.has-background-warning{background-color:#ffe08a!important}.has-text-warning-light{color:#fffaeb!important}a.has-text-warning-light:focus,a.has-text-warning-light:hover{color:#ffecb8!important}.has-background-warning-light{background-color:#fffaeb!important}.has-text-warning-dark{color:#946c00!important}a.has-text-warning-dark:focus,a.has-text-warning-dark:hover{color:#c79200!important}.has-background-warning-dark{background-color:#946c00!important}.has-text-danger{color:#f14668!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ee1742!important}.has-background-danger{background-color:#f14668!important}.has-text-danger-light{color:#feecf0!important}a.has-text-danger-light:focus,a.has-text-danger-light:hover{color:#fabdc9!important}.has-background-danger-light{background-color:#feecf0!important}.has-text-danger-dark{color:#cc0f35!important}a.has-text-danger-dark:focus,a.has-text-danger-dark:hover{color:#ee2049!important}.has-background-danger-dark{background-color:#cc0f35!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.is-flex-direction-row{flex-direction:row!important}.is-flex-direction-row-reverse{flex-direction:row-reverse!important}.is-flex-direction-column{flex-direction:column!important}.is-flex-direction-column-reverse{flex-direction:column-reverse!important}.is-flex-wrap-nowrap{flex-wrap:nowrap!important}.is-flex-wrap-wrap{flex-wrap:wrap!important}.is-flex-wrap-wrap-reverse{flex-wrap:wrap-reverse!important}.is-justify-content-flex-start{justify-content:flex-start!important}.is-justify-content-flex-end{justify-content:flex-end!important}.is-justify-content-center{justify-content:center!important}.is-justify-content-space-between{justify-content:space-between!important}.is-justify-content-space-around{justify-content:space-around!important}.is-justify-content-space-evenly{justify-content:space-evenly!important}.is-justify-content-start{justify-content:start!important}.is-justify-content-end{justify-content:end!important}.is-justify-content-left{justify-content:left!important}.is-justify-content-right{justify-content:right!important}.is-align-content-flex-start{align-content:flex-start!important}.is-align-content-flex-end{align-content:flex-end!important}.is-align-content-center{align-content:center!important}.is-align-content-space-between{align-content:space-between!important}.is-align-content-space-around{align-content:space-around!important}.is-align-content-space-evenly{align-content:space-evenly!important}.is-align-content-stretch{align-content:stretch!important}.is-align-content-start{align-content:start!important}.is-align-content-end{align-content:end!important}.is-align-content-baseline{align-content:baseline!important}.is-align-items-stretch{align-items:stretch!important}.is-align-items-flex-start{align-items:flex-start!important}.is-align-items-flex-end{align-items:flex-end!important}.is-align-items-center{align-items:center!important}.is-align-items-baseline{align-items:baseline!important}.is-align-items-start{align-items:start!important}.is-align-items-end{align-items:end!important}.is-align-items-self-start{align-items:self-start!important}.is-align-items-self-end{align-items:self-end!important}.is-align-self-auto{align-self:auto!important}.is-align-self-flex-start{align-self:flex-start!important}.is-align-self-flex-end{align-self:flex-end!important}.is-align-self-center{align-self:center!important}.is-align-self-baseline{align-self:baseline!important}.is-align-self-stretch{align-self:stretch!important}.is-flex-grow-0{flex-grow:0!important}.is-flex-grow-1{flex-grow:1!important}.is-flex-grow-2{flex-grow:2!important}.is-flex-grow-3{flex-grow:3!important}.is-flex-grow-4{flex-grow:4!important}.is-flex-grow-5{flex-grow:5!important}.is-flex-shrink-0{flex-shrink:0!important}.is-flex-shrink-1{flex-shrink:1!important}.is-flex-shrink-2{flex-shrink:2!important}.is-flex-shrink-3{flex-shrink:3!important}.is-flex-shrink-4{flex-shrink:4!important}.is-flex-shrink-5{flex-shrink:5!important}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.is-clickable{cursor:pointer!important;pointer-events:all!important}.is-clipped{overflow:hidden!important}.is-relative{position:relative!important}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.m-0{margin:0!important}.mt-0{margin-top:0!important}.mr-0{margin-right:0!important}.mb-0{margin-bottom:0!important}.ml-0{margin-left:0!important}.mx-0{margin-left:0!important;margin-right:0!important}.my-0{margin-top:0!important;margin-bottom:0!important}.m-1{margin:.25rem!important}.mt-1{margin-top:.25rem!important}.mr-1{margin-right:.25rem!important}.mb-1{margin-bottom:.25rem!important}.ml-1{margin-left:.25rem!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.m-2{margin:.5rem!important}.mt-2{margin-top:.5rem!important}.mr-2{margin-right:.5rem!important}.mb-2{margin-bottom:.5rem!important}.ml-2{margin-left:.5rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.m-3{margin:.75rem!important}.mt-3{margin-top:.75rem!important}.mr-3{margin-right:.75rem!important}.mb-3{margin-bottom:.75rem!important}.ml-3{margin-left:.75rem!important}.mx-3{margin-left:.75rem!important;margin-right:.75rem!important}.my-3{margin-top:.75rem!important;margin-bottom:.75rem!important}.m-4{margin:1rem!important}.mt-4{margin-top:1rem!important}.mr-4{margin-right:1rem!important}.mb-4{margin-bottom:1rem!important}.ml-4{margin-left:1rem!important}.mx-4{margin-left:1rem!important;margin-right:1rem!important}.my-4{margin-top:1rem!important;margin-bottom:1rem!important}.m-5{margin:1.5rem!important}.mt-5{margin-top:1.5rem!important}.mr-5{margin-right:1.5rem!important}.mb-5{margin-bottom:1.5rem!important}.ml-5{margin-left:1.5rem!important}.mx-5{margin-left:1.5rem!important;margin-right:1.5rem!important}.my-5{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-6{margin:3rem!important}.mt-6{margin-top:3rem!important}.mr-6{margin-right:3rem!important}.mb-6{margin-bottom:3rem!important}.ml-6{margin-left:3rem!important}.mx-6{margin-left:3rem!important;margin-right:3rem!important}.my-6{margin-top:3rem!important;margin-bottom:3rem!important}.m-auto{margin:auto!important}.mt-auto{margin-top:auto!important}.mr-auto{margin-right:auto!important}.mb-auto{margin-bottom:auto!important}.ml-auto{margin-left:auto!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.p-0{padding:0!important}.pt-0{padding-top:0!important}.pr-0{padding-right:0!important}.pb-0{padding-bottom:0!important}.pl-0{padding-left:0!important}.px-0{padding-left:0!important;padding-right:0!important}.py-0{padding-top:0!important;padding-bottom:0!important}.p-1{padding:.25rem!important}.pt-1{padding-top:.25rem!important}.pr-1{padding-right:.25rem!important}.pb-1{padding-bottom:.25rem!important}.pl-1{padding-left:.25rem!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.p-2{padding:.5rem!important}.pt-2{padding-top:.5rem!important}.pr-2{padding-right:.5rem!important}.pb-2{padding-bottom:.5rem!important}.pl-2{padding-left:.5rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.p-3{padding:.75rem!important}.pt-3{padding-top:.75rem!important}.pr-3{padding-right:.75rem!important}.pb-3{padding-bottom:.75rem!important}.pl-3{padding-left:.75rem!important}.px-3{padding-left:.75rem!important;padding-right:.75rem!important}.py-3{padding-top:.75rem!important;padding-bottom:.75rem!important}.p-4{padding:1rem!important}.pt-4{padding-top:1rem!important}.pr-4{padding-right:1rem!important}.pb-4{padding-bottom:1rem!important}.pl-4{padding-left:1rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.p-5{padding:1.5rem!important}.pt-5{padding-top:1.5rem!important}.pr-5{padding-right:1.5rem!important}.pb-5{padding-bottom:1.5rem!important}.pl-5{padding-left:1.5rem!important}.px-5{padding-left:1.5rem!important;padding-right:1.5rem!important}.py-5{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-6{padding:3rem!important}.pt-6{padding-top:3rem!important}.pr-6{padding-right:3rem!important}.pb-6{padding-bottom:3rem!important}.pl-6{padding-left:3rem!important}.px-6{padding-left:3rem!important;padding-right:3rem!important}.py-6{padding-top:3rem!important;padding-bottom:3rem!important}.p-auto{padding:auto!important}.pt-auto{padding-top:auto!important}.pr-auto{padding-right:auto!important}.pb-auto{padding-bottom:auto!important}.pl-auto{padding-left:auto!important}.px-auto{padding-left:auto!important;padding-right:auto!important}.py-auto{padding-top:auto!important;padding-bottom:auto!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1023px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1024px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1216px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1408px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1023px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1024px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1216px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1408px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1023px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1024px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1216px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1408px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1023px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1024px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1216px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1408px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1023px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1024px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1216px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1408px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.is-underlined{text-decoration:underline!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-medium{font-weight:500!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-family-primary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-secondary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-sans-serif{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-monospace{font-family:monospace!important}.is-family-code{font-family:monospace!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1023px){.is-block-touch{display:block!important}}@media screen and (min-width:1024px){.is-block-desktop{display:block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1216px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1408px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1023px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1024px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1216px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1408px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1023px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1024px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1216px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1408px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1023px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1024px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1216px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1408px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1023px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1024px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1216px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1408px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}.is-sr-only{border:none!important;clip:rect(0,0,0,0)!important;height:.01em!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:.01em!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1023px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1024px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1216px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1408px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1023px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1024px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1216px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1408px){.is-invisible-fullhd{visibility:hidden!important}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1023px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{color:#fff!important;opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{color:#0a0a0a!important;opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-light strong{color:inherit}.hero.is-light .title{color:rgba(0,0,0,.7)}.hero.is-light .subtitle{color:rgba(0,0,0,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(0,0,0,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.hero.is-light .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{color:#f5f5f5!important;opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#fff}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#fff}.hero.is-dark .subtitle{color:rgba(255,255,255,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(255,255,255,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#fff}.hero.is-dark .tabs a{color:#fff;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{color:#363636!important;opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#fff}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{color:#00d1b2!important;opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#485fc7;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-link .navbar-menu{background-color:#485fc7}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#3a51bb;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{color:#485fc7!important;opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#485fc7}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#2959b3 0,#485fc7 71%,#5658d2 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#2959b3 0,#485fc7 71%,#5658d2 100%)}}.hero.is-info{background-color:#3e8ed0;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-info .navbar-menu{background-color:#3e8ed0}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#3082c5;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{color:#3e8ed0!important;opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3e8ed0}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#208fbc 0,#3e8ed0 71%,#4d83db 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#208fbc 0,#3e8ed0 71%,#4d83db 100%)}}.hero.is-success{background-color:#48c78e;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-success .navbar-menu{background-color:#48c78e}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#3abb81;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{color:#48c78e!important;opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#48c78e}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#29b35e 0,#48c78e 71%,#56d2af 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#29b35e 0,#48c78e 71%,#56d2af 100%)}}.hero.is-warning{background-color:#ffe08a;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-warning .navbar-menu{background-color:#ffe08a}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd970;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{color:#ffe08a!important;opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffe08a}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffb657 0,#ffe08a 71%,#fff6a3 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffb657 0,#ffe08a 71%,#fff6a3 100%)}}.hero.is-danger{background-color:#f14668;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-danger .navbar-menu{background-color:#f14668}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ef2e55;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{color:#f14668!important;opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#f14668}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}}.hero.is-small .hero-body{padding:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding:9rem 4.5rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding:18rem 6rem}}.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}@media screen and (min-width:769px),print{.hero-body{padding:3rem 3rem}}.section{padding:3rem 1.5rem}@media screen and (min-width:1024px){.section{padding:3rem 3rem}.section.is-medium{padding:9rem 4.5rem}.section.is-large{padding:18rem 6rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem} \ No newline at end of file diff --git a/py-kms/templates/base.html b/py-kms/templates/base.html new file mode 100644 index 0000000..f64588d --- /dev/null +++ b/py-kms/templates/base.html @@ -0,0 +1,56 @@ + + + + + py-kms {% block title %}{% endblock %} + + + + +
+ {% block content %}{% endblock %} + + {% if path != '/' %} + + {% endif %} +
+ +
+
+

+ py-kms is online since {{ start_time }}. + This instance was accessed {{ get_serve_count() }} times. View this softwares license here. + {% if version_info %} +
This instance is running version "{{ version_info['hash'] }}" from branch "{{ version_info['branch'] }}" of py-kms. + {% endif %} +

+
+
+ + + + \ No newline at end of file diff --git a/py-kms/templates/clients.html b/py-kms/templates/clients.html new file mode 100644 index 0000000..fb3340b --- /dev/null +++ b/py-kms/templates/clients.html @@ -0,0 +1,103 @@ +{% extends 'base.html' %} + +{% block title %}clients{% endblock %} + +{% block style %} +th { + white-space: nowrap; +} +{% endblock %} + +{% block content %} +{% if error %} +
+
+ Whoops! Something went wrong... +
+
+ {{ error }} +
+
+{% else %} + + +
+ +{% if clients %} + + + + + + + + + + + + + + + {% for client in clients %} + + + + + + + + + + + {% endfor %} + +
Client IDMachine NameApplication IDSKU IDLicense StatusLast SeenKMS EPIDSeen Count
{{ client.clientMachineId }}
+ {% if client.machineName | length > 16 %} + {{ client.machineName | truncate(16, True, '...') }} + {% else %} + {{ client.machineName }} + {% endif %} + {{ client.applicationId }}{{ client.skuId }}{{ client.licenseStatus }}{{ client.lastRequestTime }} + {% if client.kmsEpid | length > 16 %} + {{ client.kmsEpid | truncate(16, True, '...') }} + {% else %} + {{ client.kmsEpid }} + {% endif %} + {{ client.requestCount }}
+{% else %} +
+
+

Whoops?

+
+
+ This page seems to be empty, because no clients are available. Try to use the server with a compartible client to add it to the database. +
+
+{% endif %} +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/py-kms/templates/license.html b/py-kms/templates/license.html new file mode 100644 index 0000000..34ef0d7 --- /dev/null +++ b/py-kms/templates/license.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} + +{% block title %}license{% endblock %} + +{% block content %} +
+
{{ license }}
+
+{% endblock %} \ No newline at end of file diff --git a/py-kms/templates/products.html b/py-kms/templates/products.html new file mode 100644 index 0000000..9665304 --- /dev/null +++ b/py-kms/templates/products.html @@ -0,0 +1,53 @@ +{% extends 'base.html' %} + +{% block title %}clients{% endblock %} + +{% block content %} + + +
+ + + + + + + + + + {% for name, gvlk in products | dictsort %} + {% if gvlk %} + + + + + {% endif %} + {% endfor %} + +
NameGVLK
{{ name }}
{{ gvlk }}
+{% endblock %} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 120000 index 0000000..bec651b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +docker/docker-py3-kms/requirements.txt \ No newline at end of file