├── .github └── workflows │ └── aksCreate.yml ├── .gitignore ├── LICENSE ├── README.md ├── aca ├── container-apps-cli.md ├── container-apps-ui.md └── images │ ├── 1.png │ ├── 10.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── aks ├── aks-terraform │ ├── aks-cilium │ │ ├── main.tf │ │ └── variables.tf │ ├── aks-standalone │ │ ├── main.tf │ │ └── variables.tf │ └── create.md ├── connect-to-aks.md └── why-aks.md ├── automated-deployments └── cicd │ └── github_actions │ ├── pipeline.md │ ├── secrets.md │ └── terraformaks.yml ├── azure-setup ├── cli-setup.md └── create-account.md ├── container ├── Dockerfile ├── create-a-manifest.md ├── create-acr.md ├── create-docker-image.md ├── push-image.md ├── pyweb │ ├── .github │ │ └── workflows │ │ │ ├── main.yml │ │ │ └── main1.yml │ ├── README.md │ ├── pyweb.py │ ├── requirements.txt │ ├── runtime.txt │ ├── static │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── fontawesome-all.min.css │ │ │ │ ├── main.css │ │ │ │ └── noscript.css │ │ │ ├── js │ │ │ │ ├── breakpoints.min.js │ │ │ │ ├── browser.min.js │ │ │ │ ├── jquery.min.js │ │ │ │ └── main.js │ │ │ ├── sass │ │ │ │ ├── base │ │ │ │ │ ├── _page.scss │ │ │ │ │ ├── _reset.scss │ │ │ │ │ └── _typography.scss │ │ │ │ ├── components │ │ │ │ │ ├── _actions.scss │ │ │ │ │ ├── _button.scss │ │ │ │ │ ├── _contact-icons.scss │ │ │ │ │ ├── _form.scss │ │ │ │ │ ├── _gallery.scss │ │ │ │ │ ├── _grid-icons.scss │ │ │ │ │ ├── _icon.scss │ │ │ │ │ ├── _icons.scss │ │ │ │ │ ├── _image.scss │ │ │ │ │ ├── _list.scss │ │ │ │ │ ├── _panel-banner.scss │ │ │ │ │ ├── _panel-spotlight.scss │ │ │ │ │ ├── _panel.scss │ │ │ │ │ └── _table.scss │ │ │ │ ├── layout │ │ │ │ │ ├── _page-wrapper.scss │ │ │ │ │ └── _wrapper.scss │ │ │ │ ├── libs │ │ │ │ │ ├── _breakpoints.scss │ │ │ │ │ ├── _functions.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _vars.scss │ │ │ │ │ └── _vendor.scss │ │ │ │ ├── main.scss │ │ │ │ └── noscript.scss │ │ │ └── webfonts │ │ │ │ ├── fa-brands-400.eot │ │ │ │ ├── fa-brands-400.svg │ │ │ │ ├── fa-brands-400.ttf │ │ │ │ ├── fa-brands-400.woff │ │ │ │ ├── fa-brands-400.woff2 │ │ │ │ ├── fa-regular-400.eot │ │ │ │ ├── fa-regular-400.svg │ │ │ │ ├── fa-regular-400.ttf │ │ │ │ ├── fa-regular-400.woff │ │ │ │ ├── fa-regular-400.woff2 │ │ │ │ ├── fa-solid-900.eot │ │ │ │ ├── fa-solid-900.svg │ │ │ │ ├── fa-solid-900.ttf │ │ │ │ ├── fa-solid-900.woff │ │ │ │ └── fa-solid-900.woff2 │ │ ├── images │ │ │ ├── bg.jpg │ │ │ ├── gallery │ │ │ │ ├── fulls │ │ │ │ │ ├── 01.jpg │ │ │ │ │ ├── 02.jpg │ │ │ │ │ ├── 03.jpg │ │ │ │ │ ├── 04.jpg │ │ │ │ │ ├── 05.jpg │ │ │ │ │ ├── 06.jpg │ │ │ │ │ ├── 07.jpg │ │ │ │ │ ├── 08.jpg │ │ │ │ │ └── 09.jpg │ │ │ │ └── thumbs │ │ │ │ │ ├── 01.jpg │ │ │ │ │ ├── 02.jpg │ │ │ │ │ ├── 03.jpg │ │ │ │ │ ├── 04.jpg │ │ │ │ │ ├── 05.jpg │ │ │ │ │ ├── 06.jpg │ │ │ │ │ ├── 07.jpg │ │ │ │ │ ├── 08.jpg │ │ │ │ │ └── 09.jpg │ │ │ ├── overlay.png │ │ │ ├── pic01.jpg │ │ │ ├── pic02.jpg │ │ │ └── pic03.jpg │ │ ├── moreassets │ │ │ ├── css │ │ │ │ ├── fontawesome-all.min.css │ │ │ │ ├── main.css │ │ │ │ └── noscript.css │ │ │ ├── js │ │ │ │ ├── breakpoints.min.js │ │ │ │ ├── browser.min.js │ │ │ │ ├── jquery.min.js │ │ │ │ ├── jquery.scrollex.min.js │ │ │ │ ├── jquery.scrolly.min.js │ │ │ │ ├── main.js │ │ │ │ └── util.js │ │ │ ├── sass │ │ │ │ ├── base │ │ │ │ │ ├── _page.scss │ │ │ │ │ ├── _reset.scss │ │ │ │ │ └── _typography.scss │ │ │ │ ├── components │ │ │ │ │ ├── _actions.scss │ │ │ │ │ ├── _box.scss │ │ │ │ │ ├── _button.scss │ │ │ │ │ ├── _form.scss │ │ │ │ │ ├── _icon.scss │ │ │ │ │ ├── _icons.scss │ │ │ │ │ ├── _image.scss │ │ │ │ │ ├── _list.scss │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ ├── _row.scss │ │ │ │ │ ├── _section.scss │ │ │ │ │ └── _table.scss │ │ │ │ ├── layout │ │ │ │ │ ├── _footer.scss │ │ │ │ │ ├── _header.scss │ │ │ │ │ ├── _intro.scss │ │ │ │ │ ├── _main.scss │ │ │ │ │ ├── _nav.scss │ │ │ │ │ ├── _navPanel.scss │ │ │ │ │ └── _wrapper.scss │ │ │ │ ├── libs │ │ │ │ │ ├── _breakpoints.scss │ │ │ │ │ ├── _fixed-grid.scss │ │ │ │ │ ├── _functions.scss │ │ │ │ │ ├── _html-grid.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _vars.scss │ │ │ │ │ └── _vendor.scss │ │ │ │ ├── main.scss │ │ │ │ └── noscript.scss │ │ │ └── webfonts │ │ │ │ ├── fa-brands-400.eot │ │ │ │ ├── fa-brands-400.svg │ │ │ │ ├── fa-brands-400.ttf │ │ │ │ ├── fa-brands-400.woff │ │ │ │ ├── fa-brands-400.woff2 │ │ │ │ ├── fa-regular-400.eot │ │ │ │ ├── fa-regular-400.svg │ │ │ │ ├── fa-regular-400.ttf │ │ │ │ ├── fa-regular-400.woff │ │ │ │ ├── fa-regular-400.woff2 │ │ │ │ ├── fa-solid-900.eot │ │ │ │ ├── fa-solid-900.svg │ │ │ │ ├── fa-solid-900.ttf │ │ │ │ ├── fa-solid-900.woff │ │ │ │ └── fa-solid-900.woff2 │ │ └── moreimages │ │ │ ├── bg.jpg │ │ │ ├── overlay.png │ │ │ ├── pic01.jpg │ │ │ ├── pic02.jpg │ │ │ ├── pic03.jpg │ │ │ ├── pic04.jpg │ │ │ ├── pic05.jpg │ │ │ ├── pic06.jpg │ │ │ ├── pic07.jpg │ │ │ ├── pic08.jpg │ │ │ └── pic09.jpg │ └── templates │ │ ├── home.html │ │ ├── index.html │ │ └── index1.html └── scanning.md ├── images ├── account.png ├── imageScan1.png ├── imageScan2.png ├── imageScan3.png ├── login.png ├── picklogin.png ├── pipeline1.png ├── pipeline10.png ├── pipeline2.png ├── pipeline3.png ├── pipeline4.png ├── pipeline5.png ├── pipeline6.png ├── pipeline7.png ├── pipeline8.png ├── pipeline9.png ├── secret1.png ├── secret2.png ├── secret3.png ├── secret4.png ├── secret5.png └── sku.png └── prerequisites.md /.github/workflows/aksCreate.yml: -------------------------------------------------------------------------------- 1 | name: 'AKS Deployment' 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | terraform: 11 | name: 'Terraform' 12 | env: 13 | ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} 14 | ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} 15 | ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION }} 16 | ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} 17 | runs-on: ubuntu-latest 18 | environment: production 19 | 20 | defaults: 21 | run: 22 | shell: bash 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v3 27 | 28 | - name: Setup Terraform 29 | uses: hashicorp/setup-terraform@v1 30 | 31 | - name: Terraform Init 32 | working-directory: aks/aks-terraform/aks-standalone 33 | run: terraform init 34 | 35 | - name: Terraform Format 36 | working-directory: aks/aks-terraform/aks-standalone 37 | run: terraform fmt 38 | 39 | - name: Terraform Plan 40 | working-directory: aks/aks-terraform/aks-standalone 41 | run: terraform plan 42 | 43 | - name: Terraform Apply 44 | working-directory: aks/aks-terraform/aks-standalone 45 | run: terraform apply -auto-approve -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Ignore transient lock info files created by terraform apply 27 | .terraform.tfstate.lock.info 28 | 29 | # Include override files you do wish to add to version control using negated pattern 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Michael Levan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /aca/container-apps-cli.md: -------------------------------------------------------------------------------- 1 | 1. First, add the `containerapp` extension. 2 | 3 | ``` 4 | az extension add --name containerapp --upgrade 5 | ``` 6 | 7 | 2. Next, register the providers for `OperationalInsights` and `Microsoft.App`. 8 | ``` 9 | az provider register --namespace Microsoft.App 10 | 11 | az provider register --namespace Microsoft.OperationalInsights 12 | 13 | ``` 14 | 15 | 3. Deploy the app. 16 | 17 | **One Thing To Note** 18 | 19 | When deploying to Azure Container Apps with an image from ACR, you have to give ACA specific permissions to 20 | ACR even if you've run the `acr login` command from the same terminal that you're using to deploy to ACA. 21 | 22 | You'll see in the `az containerapp create` command three flags: 23 | `--registry-server` 24 | `--registry-username` 25 | `--registry-password` 26 | 27 | You can pull this information from your ACR under **Settings > Access keys** 28 | 29 | As an example, let's say I set the following when pushing the container image to ACR: 30 | - A registry name of **devopsthehardway** with a container image called **py** 31 | - If I want the app to mimic what I called the container image, the name would be **py** 32 | - The port number when I built the container image, which is **5000** 33 | 34 | My config would look like the following: 35 | ``` 36 | az containerapp env create \ 37 | --name mypywebappenvironment \ 38 | --resource-group devrelasaservice \ 39 | --location eastus 40 | ``` 41 | 42 | ``` 43 | az containerapp create \ 44 | --name py \ 45 | --image devopsthehardway.azurecr.io/py:v1 \ 46 | --ingress external \ 47 | --environment mypywebappenvironment \ 48 | --resource-group devrelasaservice \ 49 | --target-port 5000 \ 50 | --registry-server devopsthehardway.azurecr.io \ 51 | --registry-username devopsthehardway \ 52 | --registry-password *password-obtained-from-acr-access-keys* 53 | ``` -------------------------------------------------------------------------------- /aca/container-apps-ui.md: -------------------------------------------------------------------------------- 1 | 1. First, look up "container apps" in the Azure porta. 2 | 3 | ![](images/1.png) 4 | 5 | 2. Click the blue **+ Create > Container App** button 6 | 7 | ![](images/2.png) 8 | 9 | 3. Fill in the information for your resource group, subscription, region, and container app name. 10 | 11 | There's a toggle to choose between choosing code or choosing a container image. Choose the Container Image option. 12 | 13 | ![](images/3.png) 14 | 15 | 4. You may see this message in the **Container Tab**. If you do, take a look at step 5. If you don't, skip step 5. 16 | 17 | ![](images/4.png) 18 | 19 | 5. Within the ACR portal under youe registry, go to **Settings > Access Keys** and click the **Admin user** button. 20 | 21 | ![](images/5.png) 22 | 23 | 6. You'll see a toggle to choose a container image from ACR. Choose your registry along with the container image and tag. 24 | 25 | ![](images/6.png) 26 | 27 | 7. Under **Container resource allocation**, you can leave these default. It's all going to depend on the app you're deploying 28 | and the Python web app is pretty small, so these defaults are fine. 29 | 30 | ![](images/7.png) 31 | 32 | 8. Under the **Ingress** tab is where you can specify whether or not you want an endpoint/URL for your app. Because it's 33 | a web app, it makes sense to toggle this feature. 34 | 35 | ![](images/8.png) 36 | 37 | 9. There will be a few options including whether or not you want anyone to be able to reach the app and the target 38 | port which is `5000`. You can toggle the **Accepting traffic from anywhere** as this is a public app. 39 | 40 | ![](images/10.png) 41 | 42 | 10. Once complete, click the blue ** Review + create** button. 43 | 44 | ![](images/9.png) -------------------------------------------------------------------------------- /aca/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/1.png -------------------------------------------------------------------------------- /aca/images/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/10.png -------------------------------------------------------------------------------- /aca/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/2.png -------------------------------------------------------------------------------- /aca/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/3.png -------------------------------------------------------------------------------- /aca/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/4.png -------------------------------------------------------------------------------- /aca/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/5.png -------------------------------------------------------------------------------- /aca/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/6.png -------------------------------------------------------------------------------- /aca/images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/7.png -------------------------------------------------------------------------------- /aca/images/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/8.png -------------------------------------------------------------------------------- /aca/images/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/aca/images/9.png -------------------------------------------------------------------------------- /aks/aks-terraform/aks-cilium/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | } 6 | } 7 | } 8 | 9 | provider "azurerm" { 10 | features {} 11 | subscription_id = var.sub 12 | } 13 | 14 | resource "azurerm_kubernetes_cluster" "k8squickstart" { 15 | name = var.name 16 | location = var.location 17 | resource_group_name = var.resource_group_name 18 | dns_prefix = "${var.name}-dns01" 19 | 20 | network_profile { 21 | network_plugin = "azure" 22 | network_plugin_mode = "overlay" 23 | network_policy = "cilium" 24 | network_data_plane = "cilium" 25 | } 26 | 27 | default_node_pool { 28 | name = "default" 29 | node_count = var.node_count 30 | vm_size = "Standard_A2_v2" 31 | } 32 | 33 | identity { 34 | type = "SystemAssigned" 35 | } 36 | 37 | tags = { 38 | Environment = "Production" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /aks/aks-terraform/aks-cilium/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | default = "" 4 | } 5 | 6 | variable "resource_group_name" { 7 | type = string 8 | default = "" 9 | } 10 | 11 | variable "location" { 12 | type = string 13 | default = "" 14 | } 15 | 16 | variable "node_count" { 17 | type = string 18 | default = 1 19 | } 20 | 21 | variable "sub" { 22 | type = string 23 | default = "" 24 | } -------------------------------------------------------------------------------- /aks/aks-terraform/aks-standalone/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | } 6 | } 7 | } 8 | 9 | provider "azurerm" { 10 | features {} 11 | subscription_id = var.sub 12 | } 13 | 14 | resource "azurerm_kubernetes_cluster" "k8squickstart" { 15 | name = var.name 16 | location = var.location 17 | resource_group_name = var.resource_group_name 18 | dns_prefix = "${var.name}-dns01" 19 | 20 | kubernetes_version = var.k8s_version 21 | 22 | 23 | network_profile { 24 | network_plugin = "azure" 25 | network_policy = "azure" 26 | } 27 | 28 | default_node_pool { 29 | name = "default" 30 | node_count = var.node_count 31 | vm_size = "Standard_A2_v2" 32 | 33 | } 34 | 35 | identity { 36 | type = "SystemAssigned" 37 | } 38 | 39 | tags = { 40 | Environment = "Production" 41 | } 42 | } -------------------------------------------------------------------------------- /aks/aks-terraform/aks-standalone/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | default = "aksenvironment01" 4 | } 5 | 6 | variable "resource_group_name" { 7 | type = string 8 | default = "devrelasaservice" 9 | } 10 | 11 | variable "location" { 12 | type = string 13 | default = "eastus" 14 | } 15 | 16 | variable "node_count" { 17 | type = string 18 | default = 3 19 | } 20 | 21 | variable "k8s_version" { 22 | type = string 23 | default = "1.31.2" 24 | } 25 | 26 | variable "sub" { 27 | type = string 28 | default = "686b24a1-5707-4415-be01-f2b6e879e680" 29 | } -------------------------------------------------------------------------------- /aks/aks-terraform/create.md: -------------------------------------------------------------------------------- 1 | In this directory, you will find the `main.tf` and `variable.tf` for both AKS with the Azure CNI and AKS with Cilium. 2 | 3 | If you're wondering which one you should choose, it's going to be based off of use case. 4 | 5 | With Cilium, you have the ability to remove: 6 | - Remove `kube-proxy` 7 | - Replace `iptables` with `eBPF` 8 | 9 | There are pros and cons that take up books and books, so it wouldn't be fair to give a "pick this one or that one" statement. However, if utilizing eBPF sounds interesting to you from a scalability (over `iptables`) and security perspective, choose the Cilium option. 10 | 11 | Please note that to use Terraform with Azure, you'll need to be programmatically logged in. You already did that in the labs in section 1. 12 | 13 | If you want to run the Terraform configuration via CICD, you'll need to set up authentication and authorization to Azure via the CICD platform you're using. Luckily, there will be labs coming up for that in the next section. 14 | 15 | For whichever option you choose (standalone AKS or AKS with cilium), please remember to fill in the variables in the `variable.tf` file. -------------------------------------------------------------------------------- /aks/connect-to-aks.md: -------------------------------------------------------------------------------- 1 | Connecting to an AKS cluster allows you to automatically update your local `kubeconfig` with the correct authentication and permissions per your logged in account on the Azure portal to properly connect to your Kubernetes cluster. 2 | 3 | 1. To update your local `kubeconfig` with the AKS cluster, it's a simply command: 4 | 5 | ``` 6 | az aks get-credentials -n CLUSTER_NAME -g RG_NAME 7 | ``` 8 | 9 | For example, if I had a cluster named **devopsaks** and a Resource Group named **dev**, the command would be: 10 | 11 | ``` 12 | az aks get-credentials -n devopsaks -g dev 13 | ``` 14 | 15 | 2. You should now be able to confirm you're connected to the AKS cluster: 16 | 17 | ``` 18 | kubectl get nodes 19 | ``` -------------------------------------------------------------------------------- /aks/why-aks.md: -------------------------------------------------------------------------------- 1 | In the two previous labs, you learned how to deploy an Azure Container App both from a UI perspective and a CLI perspective. From an Application Modernization perspective, ACA makes sense for a few reasons: 2 | 3 | 1. To test and confirm the container actually works. 4 | 2. If you want to containerize an application without worrying about managing Kubernetes. 5 | 6 | The second point makes the most sense. You want to use containers without having the overhead of Kubernetes. 7 | 8 | I'm not going to steer you in any particular direction. Instead, I'll lay out some facts to keep in mind. 9 | 10 | Azure Container Apps are great. There are several containerization services like AWS ECS and GCP Cloud Run. The only thing to keep in mind with these is if you reach a point where you want to get a bit more "vendor agnostic", thinking about higher levels of managing scalability and performance, and want to use third-party tools/addons like GitOps, resource optimization tools, and different deployment methods, you'll be stuck with migrating from one of these containerization services to Kubernetes. 11 | 12 | Whereas if you start out with Kubernetes, you'll already have the k8s manifests and primitives defined. 13 | 14 | On the other side of it, how, for example, AKS works vs how EKS or GKE works differs a bit. It's all Kubernetes underneath the hood, but each cloud provider sprinkles a little "goodness" on top to make it integrate better with specific services. For example, using Azure Active Directory (Entra) for RBAC is very easy in AKS. You could also use it in EKS or GKE, but there's far more configuration required. 15 | 16 | tldr; if you're planning on running this long-term and have a larger application stack, think about the pros and cons prior to deploying. -------------------------------------------------------------------------------- /automated-deployments/cicd/github_actions/pipeline.md: -------------------------------------------------------------------------------- 1 | 1. Log into GitHub Actions and go to a repo where you want to run this pipeline. For example, if I'm running this pipeline in the **DevOps-The-Hard-Way-Azure** repo, it would look like the below: 2 | 3 | ![](../../../images/pipeline1.jpg) 4 | ![](../../../images/pipeline2.jpg) 5 | 6 | 2. Click on the blue **set up a workflow yourself** button 7 | 8 | ![](../../../images/pipeline3.jpg) 9 | 10 | 3. Add in the following pipeline: 11 | ``` 12 | name: 'AKS Deployment' 13 | 14 | on: 15 | workflow_dispatch: 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | terraform: 22 | name: 'Terraform' 23 | env: 24 | ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} 25 | ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} 26 | ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION }} 27 | ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} 28 | runs-on: ubuntu-latest 29 | environment: production 30 | 31 | defaults: 32 | run: 33 | shell: bash 34 | 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v3 38 | 39 | - name: Setup Terraform 40 | uses: hashicorp/setup-terraform@v1 41 | 42 | - name: Terraform Init 43 | working-directory: aks/aks-terraform/aks-standalone 44 | run: terraform init 45 | 46 | - name: Terraform Format 47 | working-directory: aks/aks-terraform/aks-standalone 48 | run: terraform fmt 49 | 50 | - name: Terraform Plan 51 | working-directory: aks/aks-terraform/aks-standalone 52 | run: terraform plan 53 | 54 | - name: Terraform Apply 55 | working-directory: aks/aks-terraform/aks-standalone 56 | run: terraform apply -auto-approve 57 | ``` 58 | 59 | It should look like the below: 60 | ![](../../../images/pipeline4.png) 61 | 62 | 4. Let's break down the pipeline: 63 | 64 | First, you can see under `env` that the secrets are being called upon. 65 | 66 | Next, you can see that the Terraform commands like `init`, `apply`, and `plan`. Notice that it specifies a `working-directory`. This is where the Terraform code via **DevOps-The-Hard-Way-Azure** is. If you put the Terraform configs in another directory, you'll want to change the path. 67 | 68 | The rest of the pipeline is standard CICD configurations like the name, permissions, and how it's running. 69 | 70 | 5. Once ready, click the green **Commit changes...** button. 71 | ![](../../../images/pipeline5.png) 72 | 73 | 6. You'll now see the saved pipeline. Click the **Actions** button. 74 | ![](../../../images/pipeline6.png) 75 | 76 | 7. Under **Actions**, you'll see the workflow you created. You can click the **Run workflow** button to kick off the pipeline. 77 | ![](../../../images/pipeline7.png) 78 | 79 | 8. You'll now see the pipeline queued up. 80 | 81 | ![](../../../images/pipeline8.png) 82 | 83 | 9. If you click on the pipeline, you'll see it running. 84 | 85 | ![](../../../images/pipeline9.png) 86 | 87 | 10. Once complete, you'll see a successful deployment. 88 | 89 | ![](../../../images/pipeline10.png) -------------------------------------------------------------------------------- /automated-deployments/cicd/github_actions/secrets.md: -------------------------------------------------------------------------------- 1 | In the previous sections labs, you learned how to deploy an AKS cluster via the terminal. 2 | 3 | Let's now focus on how to automate this process with CICD. 4 | 5 | There are two primary steps you'll need: 6 | 1. Authentication and authorization to the CICD platform. 7 | 2. Create the CICD pipeline. 8 | 9 | For the purposes of these labs, you'll learn how to configure a pipeline in GitHub Actions. Although there are some amazing CICD tools, GitHub Actions seemed to make the most sense as the majority of people have a GitHub Account. 10 | 11 | ## Secret Creation 12 | 13 | For GitHub Actions to authenticate (log in) and authorize (what permissions are available) to Azure for the creation of the AKS cluster, you'll need: 14 | - A client ID (addID) 15 | - A tenant ID 16 | - A password 17 | 18 | 1. To create one for Azure, you can use the `create-for-rbac` subcommand under the `ad sp` command. 19 | 20 | ``` 21 | az ad sp create-for-rbac -n MyApp \ 22 | --role Contributor \ 23 | --scopes /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1 24 | ``` 25 | 26 | For example, if my resource group name was **devrelasaservice** and I wanted the name to be **git**, it would look like the following: 27 | ``` 28 | az ad sp create-for-rbac -n git \ 29 | --role Contributor \ 30 | --scopes /subscriptions/SUB_ID_HERE/resourceGroups/devrelasaservice 31 | ``` 32 | 33 | 2. You'll see an output similiar to the below: 34 | ``` 35 | { 36 | "appId": "GUID-HERE", 37 | "displayName": "git", 38 | "password": "PASSWORD-HERE", 39 | "tenant": "TENANT-ID-HERE" 40 | } 41 | ``` 42 | 43 | 3. Log into GitHub and go to a repo where you want to run this pipeline. 44 | 45 | For example, if I'm running this pipeline in the **DevOps-The-Hard-Way-Azure** repo, I would click on the repo and then go to *Settings** 46 | 47 | ![](../../../images/secret1.png) 48 | 49 | 4. Next, under **Security**, go to **Secrets and variables > Actions** 50 | 51 | ![](../../../images/secret2.png) 52 | 53 | 5. Click the green **New repository secret** button 54 | 55 | ![](../../../images/secret3.png) 56 | 57 | 6. You'll need to add four secrets. Each secret will have one of the values that were output via the `ad sp create-for-rbac` command. 58 | 59 | The reason why you need to name them a certain way is because the secret variables will be called upon via the CICD pipeline. 60 | 61 | Create 4 with the following names: 62 | - AZURE_CLIENT_ID 63 | - AZURE_CLIENT_SECRET 64 | - AZURE_SUBSCRIPTION 65 | - AZURE_TENANT_ID 66 | 67 | `AZURE_CLIENT_ID` == the appId 68 | `AZURE_CLIENT_SECRET` == the password 69 | `AZURE_SUBSCRIPTION` == your Azure subscription ID 70 | `AZURE_TENANT_ID` == the tenand ID 71 | 72 | For example, here is one secret with the azure client ID/app id 73 | 74 | ![](../../../images/secret4.png) 75 | 76 | This is what all for should look like: 77 | 78 | ![](../../../images/secret5.png) 79 | 80 | 7. You're now prepared to go on to the next lab to create the pipeline. -------------------------------------------------------------------------------- /automated-deployments/cicd/github_actions/terraformaks.yml: -------------------------------------------------------------------------------- 1 | name: 'AKS Deployment' 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | terraform: 11 | name: 'Terraform' 12 | env: 13 | ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} 14 | ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} 15 | ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION }} 16 | ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} 17 | runs-on: ubuntu-latest 18 | environment: production 19 | 20 | defaults: 21 | run: 22 | shell: bash 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v3 27 | 28 | - name: Setup Terraform 29 | uses: hashicorp/setup-terraform@v1 30 | 31 | - name: Terraform Init 32 | working-directory: aks/aks-terraform/aks-standalone 33 | run: terraform init 34 | 35 | - name: Terraform Format 36 | working-directory: aks/aks-terraform/aks-standalone 37 | run: terraform fmt 38 | 39 | - name: Terraform Plan 40 | working-directory: aks/aks-terraform/aks-standalone 41 | run: terraform plan 42 | 43 | - name: Terraform Apply 44 | working-directory: aks/aks-terraform/aks-standalone 45 | run: terraform apply -auto-approve -------------------------------------------------------------------------------- /azure-setup/cli-setup.md: -------------------------------------------------------------------------------- 1 | To interact with Azure locally from a programmatic perspective, you have two usual choices: 2 | 3 | 1. Log into the AZ CLI locally. 4 | 2. Use an App Registration to specify a tenant ID, client ID, secret, and app ID to log in. 5 | 6 | From a production perspective, you're going to use the second option and you'll see how to do this in the *CICD** section. 7 | 8 | For the purposes of this specific lab, let's go with number 1. 9 | 10 | 1. First, ensure that you went through the prerequisite section which is number 1 in the lab section.2 11 | 2. Next, run the following command to ensure that the CLI works. 12 | ``` 13 | az 14 | ``` 15 | 16 | You should see an output like the below. 17 | ``` 18 | /\ 19 | / \ _____ _ _ ___ _ 20 | / /\ \ |_ / | | | \'__/ _\ 21 | / ____ \ / /| |_| | | | __/ 22 | /_/ \_\/___|\__,_|_| \___| 23 | 24 | 25 | Welcome to the cool new Azure CLI! 26 | 27 | Use `az --version` to display the current version. 28 | Here are the base commands: 29 | 30 | account : Manage Azure subscription information. 31 | acr : Manage private registries with Azure Container Registries. 32 | ad : Manage Microsoft Entra ID (formerly known as Azure Active Directory, Azure 33 | AD, AAD) entities needed for Azure role-based access control (Azure RBAC) 34 | through Microsoft Graph API. 35 | advisor : Manage Azure Advisor. 36 | afd : Manage Azure Front Door Standard/Premium. 37 | aks : Manage Azure Kubernetes Services. 38 | ams : Manage Azure Media Services resources. 39 | apim : Manage Azure API Management services. 40 | appconfig : Manage App Configurations. 41 | ``` 42 | 43 | 3. Now that you know the Azure CLI works, run the following command to log into the CLI. 44 | 45 | ``` 46 | az login 47 | ``` 48 | 49 | You'll see a prompt that asks you to sign in via the email address that you used to create the Azure account. 50 | ![](../images/picklogin.png) 51 | 52 | You'll then see another screen that shows a successful login. 53 | ![](../images/login.png) 54 | 55 | 4. Test to confirm that your account shows up. 56 | ``` 57 | az account show 58 | ``` 59 | 60 | You should see a JSON output with your account info. -------------------------------------------------------------------------------- /azure-setup/create-account.md: -------------------------------------------------------------------------------- 1 | Luckily, creating an account is pretty easy. 2 | 3 | First, go [here](https://go.microsoft.com/fwlink/?linkid=2227353&clcid=0x409&l=en-us) to try Azure for free 4 | 5 | Next, you'll have to type in some info like name, email address, etc... 6 | 7 | After that, you should have an Azure subscription up and running. 8 | 9 | With the free Azure account, you also get $200.00 in credits for up to 30 days. 10 | 11 | After the 30 days, you can put your credit card in and pay-as-you-go. 12 | 13 | ![](../images/account.png) -------------------------------------------------------------------------------- /container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:latest 2 | 3 | RUN mkdir /build 4 | WORKDIR /build 5 | 6 | COPY pyweb /build 7 | 8 | # COPY pyweb/requirements.txt /build 9 | 10 | RUN pip install -r requirements.txt 11 | 12 | EXPOSE 5000 13 | 14 | CMD [ "python", "pyweb.py"] -------------------------------------------------------------------------------- /container/create-a-manifest.md: -------------------------------------------------------------------------------- 1 | Throughout the labs, you've done everything from create the container image to deploy it to various container services and even learn various ways to create an AKS cluster. Now that you have all of that underneath your belt, it's time to see what deploying the containerized application you built looks like. 2 | 3 | Luckily, you have all of the prereqs already done which are: 4 | 1. Create a container registry 5 | 2. Containerize the app 6 | 7 | Now it's just a matter of calling that application with a Kubernetes Manifest. 8 | 9 | When creating a Kubernetes Manifest, you have a few options: 10 | 11 | 1. Deployment 12 | 2. StatefulSet 13 | 3. DaemonSet 14 | 15 | You can also deploy individual Pods, but because they don't have a high level Controller behind them which helps with self-healing (ensuring that if a Pod goes down, a new one comes up), it's best to pick one of the three high level Controllers. 16 | 17 | For the purposes of this lab, you'll use a Deployment. 18 | 19 | If you've never created/deployed a Kubernetes Manifest before, you can find a full break down from this amazing blog post: https://8grams.medium.com/kubernetes-101-yaml-manifest-c60da98922e3 20 | 21 | 1. Copy the following YAML and ensure that you cahnge the `image` path as that'll need to be your ACR. 22 | 23 | ``` 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: pyweb 28 | spec: 29 | selector: 30 | matchLabels: 31 | app: pyweb 32 | replicas: 2 33 | template: 34 | metadata: 35 | labels: 36 | app: pyweb 37 | spec: 38 | containers: 39 | - name: pyweb 40 | image: devopsthehardway.azurecr.io/py:v1 41 | ports: 42 | - containerPort: 5001 43 | --- 44 | apiVersion: v1 45 | kind: Service 46 | metadata: 47 | name: pyweb 48 | spec: 49 | selector: 50 | app: pyweb 51 | ports: 52 | - protocol: TCP 53 | port: 80 54 | targetPort: 5001 55 | ``` 56 | 57 | 2. Save it to a file called `deployment.yaml`. 58 | 59 | 3. Run the following command: 60 | 61 | ``` 62 | kubectl apply -f deployment.yaml 63 | ``` 64 | 65 | 4. Run the following command to ensure that the Pods are running: 66 | 67 | ``` 68 | kubectl get pods 69 | ``` 70 | 71 | A quick note on `apply` vs `create`: 72 | 73 | When you use `apply`, you can modify the Kubernetes Manifest (for example, change the replica count) and re-run `kubectl apply`. The running Deployment will get updated almost like a patch. 74 | 75 | If you use `create`, you can not re-run the Manifest. You'll have to run `kubectl delete -f deployment.yaml` and then re-deploy. -------------------------------------------------------------------------------- /container/create-acr.md: -------------------------------------------------------------------------------- 1 | 1. Create an Azure Container Registry (ACR) specifying: 2 | - The resource group that you want the ACR to exist in. 3 | - The name of the ACR 4 | 5 | For the purposes of the SKU, you can leave it as `Basic`. 6 | 7 | If you're wondering, here are the differences in the SKU per the Microsoft docs. 8 | 9 | ![](../images/sku.png) 10 | 11 | ``` 12 | az acr create --resource-group myResourceGroup --name mycontainerregistry --sku Basic 13 | ``` 14 | 15 | If you'd like to do the same with Terraform, here's the resource block: 16 | 17 | ``` 18 | resource "azurerm_container_registry" "devopsthehardway" { 19 | name = "devopsthehardwayregistry" 20 | resource_group_name = var.rgName 21 | location = var.location 22 | sku = "Standard" 23 | } 24 | ``` -------------------------------------------------------------------------------- /container/create-docker-image.md: -------------------------------------------------------------------------------- 1 | # Creating the Docker image for the Uber app 2 | 3 | In this lab you will create a Docker image to containerize the UI app 4 | 5 | ## Create The Docker Image 6 | 7 | 1. `cd` into the *container* directory where you will see a *Dockerfile* and *app* directory. The app directory is what stores the Python application and the Dockerfile will be used to build the app. 8 | 9 | 2. Open the Dockerfile 10 | 11 | 3. Within the Dockerfile, you'll see a few key components 12 | - The Docker image that's being used is Python. It's using the latest version 13 | - There's a new directory being created called `/build`, which is where the Python app will reside 14 | - The *app* directory will be copied into the `/build` directory, along with the `requirements.txt` file to install all of the Python requirements for the app 15 | - The app will run as soon as the container gets created and comes up 16 | 17 | 4. To create the Docker image, you'll run the following command: 18 | `docker build -t pyweb .` 19 | 20 | The `-t` is for the tag (the name) of the Docker image and the `.` is telling the Docker CLI that the Dockerfile is in the current directory 21 | 22 | 5. After the Docker image is created, run the following command to confirm the Docker image is on your machine. 23 | `docker image ls` 24 | 25 | ## Run The Docker Image Locally 26 | 27 | Now that the Docker image is created, you can run the container locally just to confirm it'll work and not crash. 28 | 29 | 1. To run the Docker container, run the following command: 30 | `docker run -tid -p 5001:5001 pyweb` 31 | 32 | - `t` stands for a TTY console 33 | - `i` stands for interactive 34 | - `d` stands for detach so your terminal isn't directly connected to the Docker container 35 | - `p` is to specifiy the applications port and what port you want to access the app on 36 | 37 | 2. To confirm the Docker container is running, run the following command: 38 | `docker container ls` 39 | 40 | You should now see the container running. 41 | -------------------------------------------------------------------------------- /container/push-image.md: -------------------------------------------------------------------------------- 1 | # Push Image To ACR 2 | 3 | The ACR repo will be where you store the Docker image that you created in the previous lab. 4 | 5 | ## Log Into The ACR Repository 6 | 1. Log in to ACR with Azure CLI 7 | 8 | Ensure to change the name of the registry to the name of your registry. 9 | 10 | ``` 11 | az acr login --name mycontainerregistry 12 | ``` 13 | 14 | ## Tag The Docker image 15 | 1. Tag the Docker image 16 | 17 | This will consist of: 18 | - The name of the Docker image 19 | - The name of the container registry and the path 20 | 21 | For example, if my container registry name is `devopsthehardway` and my container image is `py`, the container 22 | registry path to tag is going to be `devopsthehardway.azurecr.io/py:v1` 23 | `docker tag py devopsthehardway.azurecr.io/py:v1` 24 | 25 | ## Push The Docker Image To ACR 26 | 1. Push the Docker image to ACR 27 | `docker push devopsthehardway.azurecr.io/py:v1` 28 | -------------------------------------------------------------------------------- /container/pyweb/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Pyweb 2 | 3 | # on: push can also be used if you want to run the GitHub Actions pipeline when any branch is pushed to 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: python-lint 18 | uses: CyberZHG/github-action-python-lint@0.0.2 19 | 20 | - uses: azure/login@v1 21 | with: 22 | creds: ${{ secrets.AZURE_CREDENTIALS }} 23 | 24 | - run: | 25 | az appservice plan create -g Dev10 -n pywebplan 26 | az webapp create -g Dev10 -p pywebplan -n pyweb 27 | 28 | - name: Azure WebApp 29 | uses: Azure/webapps-deploy@v2 30 | with: 31 | app-name: pyweb 32 | -------------------------------------------------------------------------------- /container/pyweb/.github/workflows/main1.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v2 24 | 25 | - name: python-lint 26 | uses: CyberZHG/github-action-python-lint@0.0.2 27 | 28 | - uses: azure/login@v1 29 | with: 30 | creds: ${{ secrets.AZURE_CREDENTIALS }} 31 | 32 | - run: | 33 | az appservice plan create -g Dev10 -n pywebplan 34 | az webapp create -g Dev10 -p pywebplan -n pyweb 35 | 36 | - name: Azure WebApp 37 | uses: Azure/webapps-deploy@v2 38 | with: 39 | app-name: pyweb 40 | -------------------------------------------------------------------------------- /container/pyweb/README.md: -------------------------------------------------------------------------------- 1 | # pyweb 2 | Flask API example app 3 | 4 | ![Pyweb](https://github.com/AdminTurnedDevOps/pyweb/workflows/Pyweb/badge.svg) 5 | -------------------------------------------------------------------------------- /container/pyweb/pyweb.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, jsonify 2 | from datetime import date 3 | 4 | 5 | pyweb = Flask(__name__) 6 | 7 | 8 | @pyweb.route("/") 9 | def home(): 10 | return render_template("home.html") 11 | 12 | @pyweb.route("/aboutme") 13 | def about(): 14 | return render_template("index.html") 15 | 16 | @pyweb.route("/time") 17 | def time(): 18 | day = date.today().day.__str__() 19 | month = date.today().month.__str__() 20 | year = date.today().year.__str__() 21 | return month + "/" + day + "/" + year 22 | 23 | if __name__ == "__main__": 24 | pyweb.run(debug=True,host='0.0.0.0',port='5001') 25 | -------------------------------------------------------------------------------- /container/pyweb/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | -------------------------------------------------------------------------------- /container/pyweb/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.6 2 | -------------------------------------------------------------------------------- /container/pyweb/static/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Ethereal by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Page */ 8 | 9 | body { 10 | overflow-x: scroll; 11 | } 12 | 13 | /* Wrapper */ 14 | 15 | body.is-preload #wrapper { 16 | -moz-transform: none; 17 | -webkit-transform: none; 18 | -ms-transform: none; 19 | transform: none; 20 | opacity: 1; 21 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/js/breakpoints.min.js: -------------------------------------------------------------------------------- 1 | /* breakpoints.js v1.0 | @ajlkn | MIT licensed */ 2 | var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e'); 62 | background-position: center; 63 | background-repeat: no-repeat; 64 | background-size: 4rem; 65 | opacity: 0; 66 | } 67 | 68 | &:after { 69 | @include vendor('pointer-events', 'none'); 70 | content: ''; 71 | display: block; 72 | position: fixed; 73 | top: 0; 74 | left: 0; 75 | width: 100%; 76 | height: 100%; 77 | z-index: -1; 78 | background-attachment: fixed; 79 | background-color: _palette(bg-alt); 80 | background-image: url('../../images/overlay.png'), url('../../images/bg.jpg'); 81 | background-repeat: repeat, repeat-x; 82 | background-size: 128px 128px, cover; 83 | } 84 | 85 | // Stops initial animations until page loads. 86 | &.is-preload { 87 | *, *:before, *:after { 88 | @include vendor('animation', 'none !important'); 89 | @include vendor('transition', 'none !important'); 90 | } 91 | 92 | &:before { 93 | @include vendor('transition', 'opacity 1s ease'); 94 | @include vendor('transition-delay', '0.75s'); 95 | opacity: 0.25; 96 | } 97 | } 98 | 99 | } 100 | 101 | @include breakpoint('<=small') { 102 | html { 103 | height: auto; 104 | } 105 | 106 | body { 107 | height: auto; 108 | overflow-x: hidden; 109 | overflow-y: auto; 110 | } 111 | } 112 | 113 | @include breakpoint('<=xsmall') { 114 | html, body { 115 | min-width: 320px; 116 | } 117 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/base/_reset.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | // Reset. 8 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 9 | 10 | html, body, div, span, applet, object, 11 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 12 | pre, a, abbr, acronym, address, big, cite, 13 | code, del, dfn, em, img, ins, kbd, q, s, samp, 14 | small, strike, strong, sub, sup, tt, var, b, 15 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 16 | form, label, legend, table, caption, tbody, 17 | tfoot, thead, tr, th, td, article, aside, 18 | canvas, details, embed, figure, figcaption, 19 | footer, header, hgroup, menu, nav, output, ruby, 20 | section, summary, time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | 34 | body { 35 | line-height: 1; 36 | } 37 | 38 | ol, ul { 39 | list-style:none; 40 | } 41 | 42 | blockquote, q { 43 | quotes: none; 44 | 45 | &:before, 46 | &:after { 47 | content: ''; 48 | content: none; 49 | } 50 | } 51 | 52 | table { 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | body { 58 | -webkit-text-size-adjust: none; 59 | } 60 | 61 | mark { 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | 66 | input::-moz-focus-inner { 67 | border: 0; 68 | padding: 0; 69 | } 70 | 71 | input, select, textarea { 72 | -moz-appearance: none; 73 | -webkit-appearance: none; 74 | -ms-appearance: none; 75 | appearance: none; 76 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Typography */ 8 | 9 | html { 10 | font-size: 18pt; 11 | font-size: 1vmax; 12 | 13 | @include breakpoint('<=xlarge') { 14 | font-size: 12pt; 15 | font-size: 1.1vmax; 16 | } 17 | 18 | @include breakpoint('<=large') { 19 | font-size: 11pt; 20 | font-size: 1.5vmax; 21 | } 22 | } 23 | 24 | body, input, select, textarea { 25 | color: _palette(fg); 26 | font-family: _font(family); 27 | font-size: 1rem; 28 | font-weight: _font(weight); 29 | line-height: 1.65; 30 | } 31 | 32 | a { 33 | @include vendor('transition', ( 34 | 'color #{_duration(transition)} ease-in-out', 35 | 'border-bottom-color #{_duration(transition)} ease-in-out' 36 | )); 37 | color: inherit; 38 | border-bottom: dotted 1px; 39 | text-decoration: none; 40 | 41 | &:hover { 42 | border-bottom-color: transparent; 43 | color: _palette(accent3); 44 | } 45 | } 46 | 47 | strong, b { 48 | color: _palette(fg-bold); 49 | font-weight: _font(weight-bold); 50 | } 51 | 52 | em, i { 53 | font-style: italic; 54 | } 55 | 56 | p { 57 | margin: 0 0 _size(element-margin) 0; 58 | 59 | body.is-ie & { 60 | width: 100%; 61 | } 62 | } 63 | 64 | h1, h2, h3, h4, h5, h6 { 65 | color: _palette(fg-bold); 66 | font-family: _font(family-heading); 67 | font-weight: _font(weight-heading); 68 | line-height: 1.3; 69 | margin: 0 0 (_size(element-margin) * 0.5) 0; 70 | letter-spacing: -0.05em; 71 | 72 | a { 73 | color: inherit; 74 | text-decoration: none; 75 | } 76 | } 77 | 78 | h1, h2, h3 { 79 | &.major { 80 | position: relative; 81 | 82 | &:after { 83 | content: ''; 84 | position: absolute; 85 | left: 0; 86 | width: 3.5rem; 87 | height: 0.1rem; 88 | background-color: _palette(border); 89 | } 90 | } 91 | } 92 | 93 | h1 { 94 | font-size: 3rem; 95 | line-height: 1.2; 96 | 97 | &.major { 98 | margin: 0 0 (_size(element-margin) * 1.75) 0; 99 | 100 | &:after { 101 | bottom: -1.325rem; 102 | } 103 | } 104 | } 105 | 106 | h2 { 107 | font-size: 1.75rem; 108 | line-height: 1.2; 109 | 110 | &.major { 111 | margin: 0 0 (_size(element-margin) * 1.325) 0; 112 | 113 | &:after { 114 | bottom: -1.2rem; 115 | } 116 | } 117 | } 118 | 119 | h3 { 120 | font-size: 1.325rem; 121 | 122 | &.major { 123 | margin: 0 0 (_size(element-margin) * 1.25) 0; 124 | 125 | &:after { 126 | bottom: -0.75rem; 127 | } 128 | } 129 | } 130 | 131 | h4 { 132 | font-size: 1rem; 133 | } 134 | 135 | h5 { 136 | font-size: 0.9rem; 137 | } 138 | 139 | h6 { 140 | font-size: 0.7rem; 141 | } 142 | 143 | sub { 144 | font-size: 0.8rem; 145 | position: relative; 146 | top: 0.5rem; 147 | } 148 | 149 | sup { 150 | font-size: 0.8rem; 151 | position: relative; 152 | top: -0.5rem; 153 | } 154 | 155 | blockquote { 156 | border-left: solid 0.25rem _palette(border); 157 | font-style: italic; 158 | margin: 0 0 _size(element-margin) 0; 159 | padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); 160 | } 161 | 162 | code { 163 | background: _palette(border-bg); 164 | border-radius: _size(border-radius); 165 | font-family: _font(family-fixed); 166 | font-size: 0.8rem; 167 | margin: 0 0.25rem; 168 | padding: 0.25rem 0.65rem; 169 | } 170 | 171 | pre { 172 | -webkit-overflow-scrolling: touch; 173 | font-family: _font(family-fixed); 174 | font-size: 0.8rem; 175 | margin: 0 0 _size(element-margin) 0; 176 | white-space: pre-wrap; 177 | 178 | code { 179 | display: block; 180 | line-height: 1.625; 181 | padding: 1rem 1.5rem; 182 | overflow-x: auto; 183 | margin: 0; 184 | } 185 | } 186 | 187 | hr { 188 | border: 0; 189 | border-bottom: solid 2px _palette(border); 190 | margin: (_size(element-margin) * 1.25) 0; 191 | } 192 | 193 | .align-left { 194 | text-align: left; 195 | } 196 | 197 | .align-center { 198 | text-align: center; 199 | } 200 | 201 | .align-right { 202 | text-align: right; 203 | } 204 | 205 | @include breakpoint('<=small') { 206 | html { 207 | font-size: 12pt; 208 | } 209 | 210 | h1 { 211 | font-size: 2.25rem; 212 | line-height: 1.2; 213 | 214 | &.major { 215 | margin: 0 0 (_size(element-margin) * 1.75) 0; 216 | 217 | &:after { 218 | bottom: -1.325rem; 219 | } 220 | } 221 | } 222 | 223 | h2 { 224 | font-size: 1.5rem; 225 | line-height: 1.2; 226 | 227 | &.major { 228 | margin: 0 0 (_size(element-margin) * 1.325) 0; 229 | 230 | &:after { 231 | bottom: -1.2rem; 232 | } 233 | } 234 | } 235 | 236 | h3 { 237 | font-size: 1rem; 238 | 239 | &.major { 240 | margin: 0 0 (_size(element-margin) * 1.25) 0; 241 | 242 | &:after { 243 | bottom: -0.75rem; 244 | } 245 | } 246 | } 247 | 248 | h4 { 249 | font-size: 1rem; 250 | } 251 | 252 | h5 { 253 | font-size: 0.9rem; 254 | } 255 | 256 | h6 { 257 | font-size: 0.7rem; 258 | } 259 | 260 | h1, h2, h3, h4, h5, h6 { 261 | br { 262 | display: none; 263 | } 264 | } 265 | } 266 | 267 | @include breakpoint('<=xxsmall') { 268 | html { 269 | font-size: 11pt; 270 | } 271 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_actions.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Actions */ 8 | 9 | ul.actions { 10 | @include vendor('display', 'flex'); 11 | cursor: default; 12 | list-style: none; 13 | margin-left: (_size(element-margin) * -0.5); 14 | padding-left: 0; 15 | 16 | li { 17 | padding: 0 0 0 (_size(element-margin) * 0.5); 18 | vertical-align: middle; 19 | } 20 | 21 | &.special { 22 | @include vendor('justify-content', 'center'); 23 | width: 100%; 24 | margin-left: 0; 25 | 26 | li { 27 | &:first-child { 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | 33 | &.stacked { 34 | @include vendor('flex-direction', 'column'); 35 | margin-left: 0; 36 | 37 | li { 38 | padding: (_size(element-margin) * 0.65) 0 0 0; 39 | 40 | &:first-child { 41 | padding-top: 0; 42 | } 43 | } 44 | } 45 | 46 | &.fit { 47 | width: calc(100% + #{_size(element-margin) * 0.5}); 48 | 49 | li { 50 | @include vendor('flex-grow', '1'); 51 | @include vendor('flex-shrink', '1'); 52 | width: 100%; 53 | 54 | > * { 55 | width: 100%; 56 | } 57 | } 58 | 59 | &.stacked { 60 | width: 100%; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Button */ 8 | 9 | input[type="submit"], 10 | input[type="reset"], 11 | input[type="button"], 12 | button, 13 | .button { 14 | @include vendor('appearance', 'none'); 15 | @include vendor('transition', ( 16 | 'background-color #{_duration(transition)} ease-in-out', 17 | 'box-shadow #{_duration(transition)} ease-in-out', 18 | 'color #{_duration(transition)} ease-in-out' 19 | )); 20 | background-color: transparent; 21 | border: 0; 22 | border-radius: _size(border-radius); 23 | box-shadow: inset 0 0 0 2px _palette(border); 24 | color: _palette(fg-bold) !important; 25 | cursor: pointer; 26 | display: inline-block; 27 | font-family: _font(family-heading); 28 | font-size: 0.6rem; 29 | font-weight: _font(weight-heading); 30 | height: _size(element-height) * 1.1; 31 | letter-spacing: 0.15rem; 32 | line-height: _size(element-height) * 1.1; 33 | padding: 0 1.5rem 0 (1.5rem + 0.15rem); 34 | text-align: center; 35 | text-decoration: none; 36 | text-transform: uppercase; 37 | white-space: nowrap; 38 | 39 | &:hover { 40 | box-shadow: inset 0 0 0 2px _palette(accent3); 41 | color: _palette(accent3) !important; 42 | } 43 | 44 | &:active { 45 | background-color: transparentize(_palette(accent3), 0.875); 46 | } 47 | 48 | &.icon { 49 | &:before { 50 | display: inline-block; 51 | position: relative; 52 | top: -0.075rem; 53 | vertical-align: middle; 54 | font-size: 0.8rem; 55 | margin: 0 0.375rem 0 -0.325rem; 56 | } 57 | 58 | &.circle { 59 | position: relative; 60 | width: _size(element-height) * 1.25; 61 | height: _size(element-height) * 1.25; 62 | line-height: _size(element-height) * 1.25; 63 | text-indent: _size(element-height) * 1.25; 64 | border-radius: 100%; 65 | overflow: hidden; 66 | padding: 0; 67 | letter-spacing: 0; 68 | 69 | &:before { 70 | display: block; 71 | position: absolute; 72 | top: 0; 73 | left: 0; 74 | width: inherit; 75 | height: inherit; 76 | font-size: 1.25rem; 77 | line-height: inherit; 78 | margin: 0; 79 | text-indent: 0; 80 | } 81 | 82 | &.fa-angle-left { 83 | &:before { 84 | position: relative; 85 | left: -0.1rem; 86 | font-size: 1.75rem; 87 | } 88 | } 89 | 90 | &.fa-angle-right { 91 | &:before { 92 | position: relative; 93 | left: 0.1rem; 94 | font-size: 1.75rem; 95 | } 96 | } 97 | } 98 | } 99 | 100 | &.fit { 101 | width: 100%; 102 | } 103 | 104 | &.small { 105 | font-size: 0.4rem; 106 | height: _size(element-height) * 0.75; 107 | line-height: _size(element-height) * 0.75; 108 | padding: 0 1.25rem 0 (1.25rem + 0.15rem); 109 | } 110 | 111 | &.large { 112 | font-size: 0.8rem; 113 | height: _size(element-height) * 1.325; 114 | line-height: _size(element-height) * 1.325; 115 | padding: 0 2rem 0 (2rem + 0.15rem); 116 | } 117 | 118 | &.primary { 119 | background-color: rgba(_palette(fg-bold), 1); 120 | box-shadow: none; 121 | color: _palette(bg) !important; 122 | 123 | &.color1 { 124 | color: _palette(accent1) !important; 125 | } 126 | 127 | &.color2 { 128 | color: _palette(accent2) !important; 129 | } 130 | 131 | &.color3 { 132 | color: _palette(accent3) !important; 133 | } 134 | 135 | &.color4 { 136 | color: _palette(accent4) !important; 137 | } 138 | 139 | &:hover { 140 | background-color: _palette(accent3); 141 | } 142 | 143 | &:active { 144 | background-color: desaturate(darken(_palette(accent3), 6), 3); 145 | } 146 | } 147 | 148 | &.disabled, 149 | &:disabled { 150 | @include vendor('pointer-events', 'none'); 151 | cursor: default; 152 | opacity: 0.5; 153 | } 154 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_contact-icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Contact Icons */ 8 | 9 | ul.contact-icons { 10 | list-style: none; 11 | padding-left: 0; 12 | 13 | > li { 14 | margin: 1.25rem 0 0 0; 15 | padding-left: 0; 16 | 17 | &:before { 18 | display: inline-block; 19 | width: 2.25rem; 20 | height: 2.25rem; 21 | line-height: 2.25rem; 22 | border-radius: 2.25rem; 23 | background-color: rgba(_palette(fg-bold), 1); 24 | color: _palette(bg); 25 | cursor: default; 26 | font-size: 1.125rem; 27 | margin-right: 1rem; 28 | text-align: center; 29 | vertical-align: middle; 30 | 31 | body.is-ie & { 32 | line-height: 2.125; 33 | } 34 | } 35 | 36 | a { 37 | border-bottom: 0; 38 | } 39 | } 40 | 41 | &.color1 { 42 | > li { 43 | &:before { 44 | color: _palette(accent1); 45 | } 46 | } 47 | } 48 | 49 | &.color2 { 50 | > li { 51 | &:before { 52 | color: _palette(accent2); 53 | } 54 | } 55 | } 56 | 57 | &.color3 { 58 | > li { 59 | &:before { 60 | color: _palette(accent3); 61 | } 62 | } 63 | } 64 | 65 | &.color4 { 66 | > li { 67 | &:before { 68 | color: _palette(accent4); 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_gallery.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Gallery */ 8 | 9 | $pad: _size(pad); 10 | $pad-small-tb: _size(pad-small-tb); 11 | $pad-small-lr: _size(pad-small-lr); 12 | 13 | @include keyframes('gallery-modal-spinner') { 14 | 0% { 15 | @include vendor('transform', 'rotate(0deg)'); 16 | } 17 | 18 | 100% { 19 | @include vendor('transform', 'rotate(360deg)'); 20 | } 21 | } 22 | 23 | .gallery { 24 | @include vendor('align-items', 'stretch'); 25 | @include vendor('display', 'flex'); 26 | height: 100%; 27 | 28 | > * { 29 | width: 20rem; 30 | height: 100%; 31 | } 32 | 33 | .image { 34 | display: block; 35 | position: relative; 36 | border-bottom: 0; 37 | overflow: hidden; 38 | 39 | img { 40 | @include vendor('transition', 'transform #{_duration(transition)} ease-in-out'); 41 | } 42 | 43 | &:after { 44 | @include vendor('transition', 'opacity #{_duration(transition)} ease-in-out'); 45 | } 46 | 47 | &:hover { 48 | img { 49 | @include vendor('transform', 'scale(1.025)'); 50 | } 51 | 52 | &:after { 53 | opacity: 0; 54 | } 55 | } 56 | } 57 | 58 | .group { 59 | @include vendor('display', 'flex'); 60 | @include vendor('flex-wrap', 'wrap'); 61 | 62 | > * { 63 | height: 50%; 64 | } 65 | } 66 | 67 | .modal { 68 | @include vendor('display', 'flex'); 69 | @include vendor('align-items', 'center'); 70 | @include vendor('justify-content', 'center'); 71 | @include vendor('pointer-events', 'none'); 72 | @include vendor('user-select', 'none'); 73 | @include vendor('transition', ( 74 | 'opacity #{_duration(gallery-lightbox-fadein)} ease', 75 | 'visibility #{_duration(gallery-lightbox-fadein)}', 76 | 'z-index #{_duration(gallery-lightbox-fadein)}' 77 | )); 78 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 79 | position: fixed; 80 | top: 0; 81 | left: 0; 82 | width: 100%; 83 | height: 100%; 84 | background-color: transparentize(_palette(bg), 1 - _misc(gallery-lightbox-opacity)); 85 | opacity: 0; 86 | outline: 0; 87 | visibility: none; 88 | z-index: 0; 89 | 90 | &:before { 91 | @include vendor('animation', 'gallery-modal-spinner 1s infinite linear'); 92 | @include vendor('transition', 'opacity #{_duration(gallery-lightbox-fadein) * 0.5} ease'); 93 | @include vendor('transition-delay', '#{_duration(gallery-lightbox-fadein)}'); 94 | content: ''; 95 | display: block; 96 | position: absolute; 97 | top: 50%; 98 | left: 50%; 99 | width: 4rem; 100 | height: 4rem; 101 | margin: -2rem 0 0 -2rem; 102 | background-image: svg-url(''); 103 | background-position: center; 104 | background-repeat: no-repeat; 105 | background-size: 4rem; 106 | opacity: 0; 107 | } 108 | 109 | &:after { 110 | content: ''; 111 | display: block; 112 | position: absolute; 113 | top: 0.5rem; 114 | right: 0.5rem; 115 | width: 4rem; 116 | height: 4rem; 117 | background-image: svg-url(''); 118 | background-position: center; 119 | background-repeat: no-repeat; 120 | background-size: 3rem; 121 | cursor: pointer; 122 | } 123 | 124 | .inner { 125 | @include vendor('transform', 'translateY(0.75rem)'); 126 | @include vendor('transition', ( 127 | 'opacity #{_duration(gallery-lightbox-fadein) * 0.5} ease', 128 | 'transform #{_duration(gallery-lightbox-fadein) * 0.5} ease' 129 | )); 130 | opacity: 0; 131 | 132 | img { 133 | display: block; 134 | max-width: 90vw; 135 | max-height: 85vh; 136 | box-shadow: 0 1rem 3rem 0 rgba(0, 0, 0, 0.35); 137 | } 138 | } 139 | 140 | &.visible { 141 | @include vendor('pointer-events', 'auto'); 142 | opacity: 1; 143 | visibility: visible; 144 | z-index: _misc(z-index-base) + 1000; 145 | 146 | &:before { 147 | opacity: 1; 148 | } 149 | } 150 | 151 | &.loaded { 152 | .inner { 153 | @include vendor('transform', 'translateY(0)'); 154 | @include vendor('transition', ( 155 | 'opacity #{_duration(gallery-lightbox-fadein)} ease', 156 | 'transform #{_duration(gallery-lightbox-fadein)} ease' 157 | )); 158 | opacity: 1; 159 | } 160 | 161 | &:before { 162 | @include vendor('transition-delay', '0s'); 163 | opacity: 0; 164 | } 165 | } 166 | } 167 | } 168 | 169 | @include breakpoint('<=medium') { 170 | .gallery { 171 | .modal { 172 | .inner { 173 | img { 174 | max-width: 100vw; 175 | } 176 | } 177 | } 178 | } 179 | } 180 | 181 | @include breakpoint('<=small') { 182 | .gallery { 183 | @include vendor('flex-direction', 'column'); 184 | height: auto; 185 | 186 | > * { 187 | height: auto; 188 | width: 100%; 189 | } 190 | 191 | .image { 192 | width: 100%; 193 | height: 40rem; 194 | } 195 | 196 | .group { 197 | @include spans(33.33333%); 198 | 199 | .image { 200 | height: 20rem; 201 | } 202 | } 203 | } 204 | } 205 | 206 | @include breakpoint('<=xsmall') { 207 | .gallery { 208 | .image { 209 | height: 30rem; 210 | } 211 | 212 | .group { 213 | .image { 214 | height: 12.5rem; 215 | } 216 | } 217 | } 218 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_grid-icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Grid Icons */ 8 | 9 | ul.grid-icons { 10 | @include vendor('display', 'flex'); 11 | @include vendor('flex-wrap', 'wrap'); 12 | @include vendor('justify-content', 'center'); 13 | list-style: none; 14 | margin: 0 0 _size(element-margin) 0; 15 | padding-left: 0; 16 | 17 | .icon { 18 | display: block; 19 | position: relative; 20 | width: 100%; 21 | text-align: center; 22 | 23 | &:before { 24 | display: block; 25 | width: 6rem; 26 | height: 6rem; 27 | line-height: 6rem; 28 | border-radius: 6rem; 29 | box-shadow: inset 0 0 0 2px _palette(border); 30 | font-size: 2.5rem; 31 | margin: 0 auto; 32 | text-align: center; 33 | 34 | body.is-ie & { 35 | line-height: 2.375; 36 | } 37 | } 38 | } 39 | 40 | > li { 41 | @include vendor('flex-grow', '0'); 42 | @include vendor('flex-shrink', '0'); 43 | position: relative; 44 | margin: 1.5rem 0 0 1.5rem; 45 | padding-left: 0; 46 | } 47 | 48 | &.connected { 49 | > li { 50 | &:before { 51 | content: ''; 52 | display: block; 53 | position: absolute; 54 | width: 1.5rem; 55 | height: 2px; 56 | top: 50%; 57 | left: -1.5rem; 58 | background-color: _palette(border); 59 | } 60 | 61 | &:after { 62 | content: ''; 63 | display: block; 64 | position: absolute; 65 | width: 2px; 66 | height: 1.5rem; 67 | top: -1.5rem; 68 | left: 50%; 69 | background-color: _palette(border); 70 | } 71 | } 72 | } 73 | 74 | &.two { 75 | width: 14rem; 76 | 77 | > li { 78 | &:nth-child(-n + 2) { 79 | margin-top: 0; 80 | 81 | &:after { 82 | display: none; 83 | } 84 | } 85 | 86 | &:nth-child(2n - 1) { 87 | margin-left: 0; 88 | 89 | &:before { 90 | display: none; 91 | } 92 | } 93 | } 94 | } 95 | 96 | &.three { 97 | width: 21.5rem; 98 | 99 | > li { 100 | &:nth-child(-n + 3) { 101 | margin-top: 0; 102 | 103 | &:after { 104 | display: none; 105 | } 106 | } 107 | 108 | &:nth-child(3n - 2) { 109 | margin-left: 0; 110 | 111 | &:before { 112 | display: none; 113 | } 114 | } 115 | } 116 | } 117 | 118 | &.four { 119 | width: 29rem; 120 | 121 | > li { 122 | &:nth-child(-n + 4) { 123 | margin-top: 0; 124 | 125 | &:after { 126 | display: none; 127 | } 128 | } 129 | 130 | &:nth-child(4n - 3) { 131 | margin-left: 0; 132 | 133 | &:before { 134 | display: none; 135 | } 136 | } 137 | } 138 | } 139 | 140 | @include breakpoint('<=small') { 141 | @include vendor('justify-content', 'flex-start'); 142 | width: 100% !important; 143 | margin: -1rem 0 _size(element-margin) -1rem; 144 | 145 | .icon { 146 | &:before { 147 | width: 4.5rem; 148 | height: 4.5rem; 149 | line-height: 4.5rem; 150 | font-size: 1.75rem; 151 | } 152 | } 153 | 154 | > li { 155 | margin: 1rem 0 0 1rem !important; 156 | 157 | &:before { 158 | display: none !important; 159 | } 160 | 161 | &:after { 162 | display: none !important; 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icon */ 8 | 9 | .icon { 10 | @include icon; 11 | position: relative; 12 | border-bottom: none; 13 | 14 | > .label { 15 | display: none; 16 | } 17 | 18 | &:before { 19 | line-height: inherit; 20 | } 21 | 22 | &.solid { 23 | &:before { 24 | font-weight: 900; 25 | } 26 | } 27 | 28 | &.brands { 29 | &:before { 30 | font-family: 'Font Awesome 5 Brands'; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icons */ 8 | 9 | ul.icons { 10 | cursor: default; 11 | list-style: none; 12 | padding-left: 0; 13 | 14 | li { 15 | display: inline-block; 16 | padding: 0 1rem 0 0; 17 | 18 | &:last-child { 19 | padding-right: 0; 20 | } 21 | 22 | .icon { 23 | &:before { 24 | font-size: 1.25em; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_image.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Image */ 8 | 9 | .image { 10 | display: inline-block; 11 | position: relative; 12 | border: 0; 13 | 14 | &.filtered { 15 | &:after { 16 | @include gradient-background; 17 | @include vendor('pointer-events', 'none'); 18 | content: ''; 19 | position: absolute; 20 | top: 0; 21 | left: 0; 22 | width: 100%; 23 | height: 100%; 24 | opacity: 1; 25 | z-index: 1; 26 | } 27 | 28 | &.tinted { 29 | &:after { 30 | @include gradient-background(true); 31 | } 32 | } 33 | } 34 | 35 | &[data-position] { 36 | img { 37 | @include vendor('object-fit', 'cover'); 38 | display: block; 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | width: 100%; 43 | height: 100%; 44 | } 45 | } 46 | 47 | &[data-position="top left"] { 48 | img { 49 | @include vendor('object-position', 'top left'); 50 | } 51 | } 52 | 53 | &[data-position="top"] { 54 | img { 55 | @include vendor('object-position', 'top'); 56 | } 57 | } 58 | 59 | &[data-position="top right"] { 60 | img { 61 | @include vendor('object-position', 'top right'); 62 | } 63 | } 64 | 65 | &[data-position="right"] { 66 | img { 67 | @include vendor('object-position', 'right'); 68 | } 69 | } 70 | 71 | &[data-position="bottom right"] { 72 | img { 73 | @include vendor('object-position', 'bottom right'); 74 | } 75 | } 76 | 77 | &[data-position="bottom"] { 78 | img { 79 | @include vendor('object-position', 'bottom'); 80 | } 81 | } 82 | 83 | &[data-position="bottom left"] { 84 | img { 85 | @include vendor('object-position', 'bottom left'); 86 | } 87 | } 88 | 89 | &[data-position="left"] { 90 | img { 91 | @include vendor('object-position', 'left'); 92 | } 93 | } 94 | 95 | &[data-position="center"] { 96 | img { 97 | @include vendor('object-position', 'center'); 98 | } 99 | } 100 | 101 | &[data-position="25% 25%"] { 102 | img { 103 | @include vendor('object-position', '25% 25%'); 104 | } 105 | } 106 | 107 | &[data-position="75% 25%"] { 108 | img { 109 | @include vendor('object-position', '75% 25%'); 110 | } 111 | } 112 | 113 | &[data-position="75% 75%"] { 114 | img { 115 | @include vendor('object-position', '75% 75%'); 116 | } 117 | } 118 | 119 | &[data-position="25% 75%"] { 120 | img { 121 | @include vendor('object-position', '25% 75%'); 122 | } 123 | } 124 | 125 | img { 126 | display: block; 127 | } 128 | 129 | &.left, 130 | &.right { 131 | max-width: 40%; 132 | 133 | img { 134 | width: 100%; 135 | } 136 | } 137 | 138 | &.left { 139 | float: left; 140 | padding: 0 1.5rem 1rem 0; 141 | top: 0.25rem; 142 | } 143 | 144 | &.right { 145 | float: right; 146 | padding: 0 0 1rem 1.5rem; 147 | top: 0.25rem; 148 | } 149 | 150 | &.fit { 151 | display: block; 152 | margin: 0 0 _size(element-margin) 0; 153 | width: 100%; 154 | 155 | img { 156 | width: 100%; 157 | } 158 | } 159 | 160 | &.main { 161 | display: block; 162 | margin: 0 0 (_size(element-margin) * 1.5) 0; 163 | width: 100%; 164 | 165 | img { 166 | width: 100%; 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* List */ 8 | 9 | ol { 10 | list-style: decimal; 11 | margin: 0 0 _size(element-margin) 0; 12 | padding-left: 1.25rem; 13 | 14 | li { 15 | padding-left: 0.25rem; 16 | } 17 | } 18 | 19 | ul { 20 | list-style: disc; 21 | margin: 0 0 _size(element-margin) 0; 22 | padding-left: 1rem; 23 | 24 | li { 25 | padding-left: 0.5rem; 26 | } 27 | 28 | &.alt { 29 | list-style: none; 30 | padding-left: 0; 31 | 32 | li { 33 | border-top: solid 1px _palette(border); 34 | padding: 0.5rem 0; 35 | 36 | &:first-child { 37 | border-top: 0; 38 | padding-top: 0; 39 | } 40 | } 41 | } 42 | } 43 | 44 | dl { 45 | margin: 0 0 _size(element-margin) 0; 46 | 47 | dt { 48 | display: block; 49 | font-weight: _font(weight-bold); 50 | margin: 0 0 (_size(element-margin) * 0.5) 0; 51 | } 52 | 53 | dd { 54 | margin-left: _size(element-margin); 55 | } 56 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_panel-banner.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Panel (Banner) */ 8 | 9 | $pad: _size(pad); 10 | $pad-small-tb: _size(pad-small-tb); 11 | $pad-small-lr: _size(pad-small-lr); 12 | 13 | .panel.banner { 14 | @include vendor('align-items', 'stretch'); 15 | 16 | .content { 17 | @include padding($pad, $pad); 18 | @include vendor('display', 'flex'); 19 | @include vendor('flex-direction', 'column'); 20 | @include vendor('justify-content', 'center'); 21 | @include vendor('flex-grow', '0'); 22 | @include vendor('flex-shrink', '0'); 23 | 24 | > .actions:last-child { 25 | margin-bottom: 0; 26 | } 27 | } 28 | 29 | .image { 30 | @include vendor('flex-grow', '0'); 31 | @include vendor('flex-shrink', '0'); 32 | position: relative; 33 | 34 | img { 35 | @include vendor('object-fit', 'cover'); 36 | display: block; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | width: 100%; 41 | height: 100%; 42 | } 43 | } 44 | 45 | &.left { 46 | @include vendor('flex-direction', 'row'); 47 | } 48 | 49 | &.right { 50 | @include vendor('flex-direction', 'row-reverse'); 51 | } 52 | } 53 | 54 | @include breakpoint('<=small') { 55 | .panel.banner { 56 | .content { 57 | @include padding($pad-small-tb, $pad-small-lr); 58 | @include vendor('flex-basis', '60%'); 59 | 60 | > .actions:last-child { 61 | margin-bottom: _size(element-margin); 62 | } 63 | } 64 | 65 | .image { 66 | @include vendor('flex-basis', '40%'); 67 | } 68 | } 69 | 70 | @include orientation(portrait) { 71 | .panel.banner { 72 | .content { 73 | @include vendor('flex-basis', 'auto'); 74 | } 75 | 76 | .image { 77 | @include vendor('flex-basis', 'auto'); 78 | height: 18rem; 79 | } 80 | 81 | &.left { 82 | @include vendor('flex-direction', 'column'); 83 | } 84 | 85 | &.right { 86 | @include vendor('flex-direction', 'column-reverse'); 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_panel-spotlight.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Panel (Spotlight) */ 8 | 9 | $pad: _size(pad); 10 | $pad-small-tb: _size(pad-small-tb); 11 | $pad-small-lr: _size(pad-small-lr); 12 | 13 | .panel.spotlight { 14 | @include vendor('align-items', 'stretch'); 15 | position: relative; 16 | 17 | > * { 18 | z-index: 1; 19 | } 20 | 21 | .content { 22 | @include vendor('display', 'flex'); 23 | @include vendor('flex-direction', 'column'); 24 | @include vendor('justify-content', 'center'); 25 | @include padding($pad, $pad); 26 | } 27 | 28 | .image { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | width: 100%; 33 | height: 100%; 34 | z-index: 0; 35 | 36 | img { 37 | @include vendor('object-fit', 'cover'); 38 | display: block; 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | width: 100%; 43 | height: 100%; 44 | } 45 | } 46 | 47 | &.left { 48 | @include vendor('justify-content', 'flex-start'); 49 | 50 | .content { 51 | background-image: linear-gradient(-90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.125) 30%, rgba(0,0,0,0.175) 50%); 52 | } 53 | } 54 | 55 | &.right { 56 | @include vendor('justify-content', 'flex-end'); 57 | 58 | .content { 59 | background-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.125) 30%, rgba(0,0,0,0.175) 50%); 60 | } 61 | } 62 | } 63 | 64 | @include breakpoint('<=small') { 65 | .panel.spotlight { 66 | .content { 67 | @include padding($pad-small-tb, $pad-small-lr); 68 | @include vendor('flex-direction', 'column !important'); 69 | background-image: linear-gradient(0deg, rgba(0,0,0,0.25) 70%, rgba(0,0,0,0.175)) !important; 70 | min-height: 25rem; 71 | } 72 | } 73 | } 74 | 75 | @include breakpoint('<=xsmall') { 76 | .panel.spotlight { 77 | .content { 78 | min-height: 30rem; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/components/_table.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Table */ 8 | 9 | .table-wrapper { 10 | -webkit-overflow-scrolling: touch; 11 | overflow-x: auto; 12 | } 13 | 14 | table { 15 | margin: 0 0 _size(element-margin) 0; 16 | width: 100%; 17 | 18 | tbody { 19 | tr { 20 | border: solid 1px _palette(border); 21 | border-left: 0; 22 | border-right: 0; 23 | 24 | &:nth-child(2n + 1) { 25 | background-color: _palette(border-bg); 26 | } 27 | } 28 | } 29 | 30 | td { 31 | padding: 0.75rem 0.75rem; 32 | } 33 | 34 | th { 35 | color: _palette(fg-bold); 36 | font-size: 0.9rem; 37 | font-weight: _font(weight-bold); 38 | padding: 0 0.75rem 0.75rem 0.75rem; 39 | text-align: left; 40 | } 41 | 42 | thead { 43 | border-bottom: solid 2px _palette(border); 44 | } 45 | 46 | tfoot { 47 | border-top: solid 2px _palette(border); 48 | } 49 | 50 | &.alt { 51 | border-collapse: separate; 52 | 53 | tbody { 54 | tr { 55 | td { 56 | border: solid 1px _palette(border); 57 | border-left-width: 0; 58 | border-top-width: 0; 59 | 60 | &:first-child { 61 | border-left-width: 1px; 62 | } 63 | } 64 | 65 | &:first-child { 66 | td { 67 | border-top-width: 1px; 68 | } 69 | } 70 | } 71 | } 72 | 73 | thead { 74 | border-bottom: 0; 75 | } 76 | 77 | tfoot { 78 | border-top: 0; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/layout/_page-wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Page Wrapper */ 8 | 9 | #page-wrapper { 10 | @include vendor('display', 'flex'); 11 | @include vendor('align-items', 'center'); 12 | @include vendor('justify-content', 'flex-start'); 13 | @include vendor('flex-grow', '1'); 14 | @include vendor('flex-shrink', '1'); 15 | height: 100%; 16 | padding: 5rem; 17 | 18 | @include orientation(portrait) { 19 | padding-left: 2rem; 20 | padding-right: 2rem; 21 | } 22 | 23 | @include breakpoint('short') { 24 | padding: 6vh; 25 | } 26 | 27 | @include breakpoint('xshort') { 28 | padding: 0; 29 | } 30 | } 31 | 32 | @include breakpoint('<=small') { 33 | #page-wrapper { 34 | height: auto; 35 | padding: 1rem; 36 | } 37 | } 38 | 39 | @include breakpoint('<=xsmall') { 40 | #page-wrapper { 41 | padding: 0; 42 | } 43 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/layout/_wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Ethereal by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Wrapper */ 8 | 9 | #wrapper { 10 | @include vendor('display', 'flex'); 11 | @include vendor('flex-direction', 'row'); 12 | @include vendor('transition', ( 13 | 'opacity 1s ease-out', 14 | 'transform 0.75s ease-out' 15 | )); 16 | @include vendor('transition-delay', '0.25s'); 17 | cursor: default; 18 | position: relative; 19 | height: 32rem; 20 | box-shadow: 0 2rem 4rem 0.25rem transparentize(_palette(bg), 0.425); 21 | 22 | > .scrollZone { 23 | position: fixed; 24 | width: 6rem; 25 | height: inherit; 26 | cursor: -moz-grab; 27 | cursor: -webkit-grab; 28 | cursor: -ms-grab; 29 | cursor: grab; 30 | z-index: _misc(z-index-base) + 100; 31 | 32 | &.left { 33 | left: 0; 34 | } 35 | 36 | &.right { 37 | right: 0; 38 | } 39 | } 40 | 41 | > .copyright { 42 | position: absolute; 43 | bottom: -3rem; 44 | right: 0; 45 | font-size: 0.8rem; 46 | color: transparentize(_palette(bg), 0.625); 47 | margin-bottom: 0; 48 | 49 | a { 50 | &:hover { 51 | color: inherit; 52 | } 53 | } 54 | } 55 | 56 | &.is-dragging { 57 | @include vendor('user-select', 'none'); 58 | cursor: -moz-grab; 59 | cursor: -webkit-grab; 60 | cursor: -ms-grab; 61 | cursor: grab; 62 | 63 | * { 64 | @include vendor('user-select', 'none'); 65 | } 66 | 67 | *:not(a, .image) { 68 | cursor: -moz-grab; 69 | cursor: -webkit-grab; 70 | cursor: -ms-grab; 71 | cursor: grab; 72 | } 73 | } 74 | 75 | &.is-dragged { 76 | * { 77 | @include vendor('pointer-events', 'none'); 78 | } 79 | } 80 | 81 | body.is-preload & { 82 | @include vendor('transform', 'translateX(2rem)'); 83 | opacity: 0; 84 | } 85 | } 86 | 87 | @include breakpoint('<=small') { 88 | #wrapper { 89 | @include vendor('flex-direction', 'column'); 90 | height: auto; 91 | margin: 0 0 5rem 0; 92 | box-shadow: 0 0.25rem 1.5rem 0.25rem transparentize(_palette(bg), 0.5); 93 | 94 | > .scrollZone { 95 | display: none; 96 | } 97 | 98 | > .copyright { 99 | display: block; 100 | width: 100%; 101 | text-align: center; 102 | } 103 | 104 | body.is-preload & { 105 | @include vendor('transform', 'translateY(1rem)'); 106 | } 107 | } 108 | } 109 | 110 | @include breakpoint('<=xsmall') { 111 | #wrapper { 112 | box-shadow: none; 113 | 114 | body.is-preload & { 115 | @include vendor('transform', 'none'); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $category Optional category to use. 4 | /// @param {string} $where Optional pseudoelement to target (before or after). 5 | @mixin icon($content: false, $category: regular, $where: before) { 6 | 7 | text-decoration: none; 8 | 9 | &:#{$where} { 10 | 11 | @if $content { 12 | content: $content; 13 | } 14 | 15 | -moz-osx-font-smoothing: grayscale; 16 | -webkit-font-smoothing: antialiased; 17 | display: inline-block; 18 | font-style: normal; 19 | font-variant: normal; 20 | text-rendering: auto; 21 | line-height: 1; 22 | text-transform: none !important; 23 | 24 | @if ($category == brands) { 25 | font-family: 'Font Awesome 5 Brands'; 26 | } 27 | @elseif ($category == solid) { 28 | font-family: 'Font Awesome 5 Free'; 29 | font-weight: 900; 30 | } 31 | @else { 32 | font-family: 'Font Awesome 5 Free'; 33 | font-weight: 400; 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | /// Applies padding to an element, taking the current element-margin value into account. 41 | /// @param {mixed} $tb Top/bottom padding. 42 | /// @param {mixed} $lr Left/right padding. 43 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 44 | /// @param {bool} $important If true, adds !important. 45 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 46 | 47 | @if $important { 48 | $important: '!important'; 49 | } 50 | 51 | $x: 0.1em; 52 | 53 | @if unit(_size(element-margin)) == 'rem' { 54 | $x: 0.1rem; 55 | } 56 | 57 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 58 | 59 | } 60 | 61 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 62 | /// @param {string} $svg SVG data URL. 63 | /// @return {string} Encoded SVG data URL. 64 | @function svg-url($svg) { 65 | 66 | $svg: str-replace($svg, '"', '\''); 67 | $svg: str-replace($svg, '%', '%25'); 68 | $svg: str-replace($svg, '<', '%3C'); 69 | $svg: str-replace($svg, '>', '%3E'); 70 | $svg: str-replace($svg, '&', '%26'); 71 | $svg: str-replace($svg, '#', '%23'); 72 | $svg: str-replace($svg, '{', '%7B'); 73 | $svg: str-replace($svg, '}', '%7D'); 74 | $svg: str-replace($svg, ';', '%3B'); 75 | 76 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 77 | 78 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000, 4 | gallery-lightbox-opacity: 0.875 5 | ); 6 | 7 | // Duration. 8 | $duration: ( 9 | transition: 0.2s, 10 | gallery-lightbox-fadein: 0.5s 11 | ); 12 | 13 | // Size. 14 | $size: ( 15 | border-radius: 0.25rem, 16 | element-height: 2.5rem, 17 | element-margin: 1.5rem, 18 | pad: 3.5rem, 19 | pad-small-tb: 3.5rem * 0.825, 20 | pad-small-lr: 3.5rem * 0.5, 21 | span-fixed: 10rem, 22 | span-variable: 10% 23 | ); 24 | 25 | // Font. 26 | $font: ( 27 | family: ('Source Sans Pro', Helvetica, sans-serif), 28 | family-heading: (Arial, Helvetica, sans-serif), 29 | family-fixed: ('Courier New', monospace), 30 | weight: 300, 31 | weight-bold: 400, 32 | weight-heading: 700 33 | ); 34 | 35 | // Palette. 36 | $palette: ( 37 | bg: #2e2b37, 38 | bg-alt: #e1e6e1, 39 | fg: rgba(255,255,255,0.75), 40 | fg-bold: rgba(255,255,255,0.875), 41 | fg-light: rgba(255,255,255,0.5), 42 | border: rgba(255,255,255,0.25), 43 | border-bg: rgba(255,255,255,0.075), 44 | border-bg-alt: rgba(255,255,255,0.125), 45 | accent1: #726193, 46 | accent2: #e37b7c, 47 | accent3: #ffe4b4, 48 | accent4: #353865 49 | ); -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'fontawesome-all.min.css'; 7 | @import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,300i,400i"); 8 | 9 | /* 10 | Ethereal by HTML5 UP 11 | html5up.net | @ajlkn 12 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 13 | */ 14 | 15 | // Breakpoints. 16 | 17 | @include breakpoints(( 18 | xlarge: ( 1281px, 1680px ), 19 | large: ( 981px, 1280px ), 20 | medium: ( 737px, 980px ), 21 | small: ( 481px, 736px ), 22 | xsmall: ( 361px, 480px ), 23 | xxsmall: ( null, 360px ), 24 | short: '(min-aspect-ratio: 16/7)', 25 | xshort: '(min-aspect-ratio: 16/6)' 26 | )); 27 | 28 | // Mixins. 29 | 30 | @mixin gradient-background($tint: false, $opacity: 0.25, $stop1: 25%, $stop2: 50%, $angle: 45deg) { 31 | $background-image: ( 32 | url('../../images/overlay.png'), 33 | linear-gradient($angle, transparentize(_palette(accent1), (1 - $opacity)) $stop1, transparentize(_palette(accent2), (1 - $opacity)) $stop2, transparentize(_palette(accent3), (1 - $opacity))) 34 | ); 35 | 36 | $background-size: ( 37 | 128px 128px, 38 | auto 39 | ); 40 | 41 | @if ($tint) { 42 | $x: linear-gradient(0deg, rgba(0,0,0,0.125), rgba(0,0,0,0.125)); 43 | $y: auto; 44 | $background-image: append($background-image, $x); 45 | $background-size: append($background-size, $y); 46 | } 47 | 48 | background-image: $background-image; 49 | background-size: $background-size; 50 | } 51 | 52 | @mixin gradient-background-small($tint: false, $opacity: 0.25, $stop1: 25%, $stop2: 50%) { 53 | @include gradient-background($tint, $opacity, $stop1, $stop2, 135deg); 54 | } 55 | 56 | @mixin spans($x) { 57 | @for $i from 0 through 10 { 58 | 59 | @if ($i > 0) { 60 | .span-#{$i} { 61 | width: ($x * $i); 62 | } 63 | } 64 | 65 | @if ($i < 10) { 66 | .span-#{$i}-25 { 67 | width: ($x * $i) + ($x * 0.25); 68 | } 69 | 70 | .span-#{$i}-5 { 71 | width: ($x * $i) + ($x * 0.5); 72 | } 73 | 74 | .span-#{$i}-75 { 75 | width: ($x * $i) + ($x * 0.75); 76 | } 77 | } 78 | } 79 | } 80 | 81 | @mixin spans-small($x) { 82 | @for $i from 0 through 10 { 83 | 84 | @if ($i > 0) { 85 | .span-#{$i} { 86 | width: 100%; 87 | } 88 | } 89 | 90 | @if ($i < 10) { 91 | .span-#{$i}-25 { 92 | width: 100%; 93 | } 94 | 95 | .span-#{$i}-5 { 96 | width: 100%; 97 | } 98 | 99 | .span-#{$i}-75 { 100 | width: 100%; 101 | } 102 | } 103 | } 104 | } 105 | 106 | // Base. 107 | 108 | @import 'base/reset'; 109 | @import 'base/page'; 110 | @import 'base/typography'; 111 | 112 | // Component. 113 | 114 | @import 'components/form'; 115 | @import 'components/icon'; 116 | @import 'components/image'; 117 | @import 'components/list'; 118 | @import 'components/actions'; 119 | @import 'components/icons'; 120 | @import 'components/grid-icons'; 121 | @import 'components/contact-icons'; 122 | @import 'components/table'; 123 | @import 'components/button'; 124 | @import 'components/gallery'; 125 | @import 'components/panel'; 126 | @import 'components/panel-banner'; 127 | @import 'components/panel-spotlight'; 128 | 129 | // Layout. 130 | 131 | @import 'layout/page-wrapper'; 132 | @import 'layout/wrapper'; -------------------------------------------------------------------------------- /container/pyweb/static/assets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | 7 | /* 8 | Ethereal by HTML5 UP 9 | html5up.net | @ajlkn 10 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 11 | */ 12 | 13 | /* Page */ 14 | 15 | body { 16 | overflow-x: scroll; 17 | } 18 | 19 | /* Wrapper */ 20 | 21 | #wrapper { 22 | body.is-preload & { 23 | @include vendor('transform', 'none'); 24 | opacity: 1; 25 | } 26 | } -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /container/pyweb/static/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/bg.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/01.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/02.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/03.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/04.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/05.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/06.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/07.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/08.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/fulls/09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/fulls/09.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/01.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/02.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/03.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/04.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/05.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/06.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/07.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/08.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/gallery/thumbs/09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/gallery/thumbs/09.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/overlay.png -------------------------------------------------------------------------------- /container/pyweb/static/images/pic01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/pic01.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/pic02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/pic02.jpg -------------------------------------------------------------------------------- /container/pyweb/static/images/pic03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/images/pic03.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/css/noscript.css: -------------------------------------------------------------------------------- 1 | @import url(font-awesome.min.css); 2 | 3 | /* 4 | Massively by HTML5 UP 5 | html5up.net | @ajlkn 6 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 7 | */ 8 | 9 | /* Wrapper */ 10 | 11 | #wrapper { 12 | background-color: #212931; 13 | background-image: url("../../images/overlay.png"), linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("../../images/bg.jpg"); 14 | background-size: auto, auto, 100% auto; 15 | background-position: center, center, top center; 16 | background-repeat: repeat, no-repeat, no-repeat; 17 | background-attachment: fixed, fixed, fixed; 18 | } 19 | 20 | #wrapper.fade-in:before { 21 | display: none; 22 | } 23 | 24 | /* Intro */ 25 | 26 | body.is-preload #intro { 27 | opacity: 1; 28 | } 29 | 30 | body.is-preload #intro:not(.hidden) + #header + #nav { 31 | -moz-transform: none; 32 | -webkit-transform: none; 33 | -ms-transform: none; 34 | transform: none; 35 | opacity: 1; 36 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/js/breakpoints.min.js: -------------------------------------------------------------------------------- 1 | /* breakpoints.js v1.0 | @ajlkn | MIT licensed */ 2 | var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e1){for(var r=0;r=i&&o>=t};break;case"bottom":h=function(t,e,n,i,o){return n>=i&&o>=n};break;case"middle":h=function(t,e,n,i,o){return e>=i&&o>=e};break;case"top-only":h=function(t,e,n,i,o){return i>=t&&n>=i};break;case"bottom-only":h=function(t,e,n,i,o){return n>=o&&o>=t};break;default:case"default":h=function(t,e,n,i,o){return n>=i&&o>=t}}return c=function(t){var i,o,l,s,r,a,u=this.state,h=!1,c=this.$element.offset();i=n.height(),o=t+i/2,l=t+i,s=this.$element.outerHeight(),r=c.top+e(this.options.top,s,i),a=c.top+s-e(this.options.bottom,s,i),h=this.test(t,o,l,r,a),h!=u&&(this.state=h,h?this.options.enter&&this.options.enter.apply(this.element):this.options.leave&&this.options.leave.apply(this.element)),this.options.scroll&&this.options.scroll.apply(this.element,[(o-r)/(a-r)])},p={id:a,options:u,test:h,handler:c,state:null,element:this,$element:s,timeoutId:null},o[a]=p,s.data("_scrollexId",p.id),p.options.initialize&&p.options.initialize.apply(this),s},jQuery.fn.unscrollex=function(){var e=t(this);if(0==this.length)return e;if(this.length>1){for(var n=0;n1){for(o=0;o=320px. 20 | @include breakpoint('<=xsmall') { 21 | html, body { 22 | min-width: 320px; 23 | } 24 | } 25 | 26 | // Set box model to border-box. 27 | // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice 28 | html { 29 | box-sizing: border-box; 30 | } 31 | 32 | *, *:before, *:after { 33 | box-sizing: inherit; 34 | } 35 | 36 | body { 37 | 38 | background-color: _palette(invert, bg); 39 | 40 | // Stops initial animations until page loads. 41 | &.is-preload { 42 | *, *:before, *:after { 43 | @include vendor('animation', 'none !important'); 44 | @include vendor('transition', 'none !important'); 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/base/_reset.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | // Reset. 8 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 9 | 10 | html, body, div, span, applet, object, 11 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 12 | pre, a, abbr, acronym, address, big, cite, 13 | code, del, dfn, em, img, ins, kbd, q, s, samp, 14 | small, strike, strong, sub, sup, tt, var, b, 15 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 16 | form, label, legend, table, caption, tbody, 17 | tfoot, thead, tr, th, td, article, aside, 18 | canvas, details, embed, figure, figcaption, 19 | footer, header, hgroup, menu, nav, output, ruby, 20 | section, summary, time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | 34 | body { 35 | line-height: 1; 36 | } 37 | 38 | ol, ul { 39 | list-style:none; 40 | } 41 | 42 | blockquote, q { 43 | quotes: none; 44 | 45 | &:before, 46 | &:after { 47 | content: ''; 48 | content: none; 49 | } 50 | } 51 | 52 | table { 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | body { 58 | -webkit-text-size-adjust: none; 59 | } 60 | 61 | mark { 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | 66 | input::-moz-focus-inner { 67 | border: 0; 68 | padding: 0; 69 | } 70 | 71 | input, select, textarea { 72 | -moz-appearance: none; 73 | -webkit-appearance: none; 74 | -ms-appearance: none; 75 | appearance: none; 76 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Type */ 8 | 9 | html { 10 | font-size: 16pt; 11 | 12 | @include breakpoint('<=xlarge') { 13 | font-size: 12pt; 14 | } 15 | 16 | @include breakpoint('<=large') { 17 | font-size: 11pt; 18 | } 19 | 20 | @include breakpoint('<=xxsmall') { 21 | font-size: 10pt; 22 | } 23 | } 24 | 25 | body { 26 | color: _palette(fg); 27 | } 28 | 29 | body, input, select, textarea { 30 | font-family: _font(family); 31 | font-weight: _font(weight); 32 | font-size: 1rem; 33 | line-height: 2.375; 34 | } 35 | 36 | a { 37 | @include vendor('transition', ( 38 | 'color #{_duration(transition)} ease-in-out', 39 | 'background-color #{_duration(transition)} ease-in-out', 40 | 'border-color #{_duration(transition)} ease-in-out', 41 | 'box-shadow #{_duration(transition)} ease-in-out' 42 | )); 43 | border-bottom: dotted 1px; 44 | text-decoration: none; 45 | 46 | &:hover { 47 | border-bottom-color: transparent; 48 | } 49 | } 50 | 51 | strong, b { 52 | font-weight: _font(weight-bold); 53 | } 54 | 55 | em, i { 56 | font-style: italic; 57 | } 58 | 59 | p { 60 | text-align: justify; 61 | margin: 0 0 _size(element-margin) 0; 62 | } 63 | 64 | h1, h2, h3, h4, h5, h6 { 65 | font-family: _font(family-heading); 66 | font-weight: _font(weight-heading); 67 | line-height: 1.5; 68 | letter-spacing: 0.075em; 69 | text-transform: uppercase; 70 | margin: 0 0 (_size(element-margin) * 0.5) 0; 71 | 72 | a { 73 | border-bottom: 0; 74 | color: inherit; 75 | text-decoration: none; 76 | } 77 | } 78 | 79 | h1 { 80 | font-size: 4rem; 81 | line-height: 1.1; 82 | margin: 0 0 _size(element-margin) 0; 83 | } 84 | 85 | h2 { 86 | font-size: 1.75rem; 87 | line-height: 1.3; 88 | margin: 0 0 (_size(element-margin) * 0.75) 0; 89 | } 90 | 91 | h3 { 92 | font-size: 1.25rem; 93 | margin: 0 0 (_size(element-margin) * 0.75) 0; 94 | } 95 | 96 | h4 { 97 | font-size: 1rem; 98 | } 99 | 100 | h5 { 101 | font-size: 0.9rem; 102 | } 103 | 104 | h6 { 105 | font-size: 0.8rem; 106 | } 107 | 108 | sub { 109 | font-size: 0.8rem; 110 | position: relative; 111 | top: 0.5rem; 112 | } 113 | 114 | sup { 115 | font-size: 0.8rem; 116 | position: relative; 117 | top: -0.5rem; 118 | } 119 | 120 | blockquote { 121 | border-left: solid 4px; 122 | font-style: italic; 123 | margin: 0 0 _size(element-margin) 0; 124 | padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); 125 | } 126 | 127 | code { 128 | border-radius: _size(border-radius); 129 | border: solid 2px; 130 | font-family: _font(family-fixed); 131 | font-size: 0.9rem; 132 | margin: 0 0.25rem; 133 | padding: 0.25rem 0.65rem; 134 | } 135 | 136 | pre { 137 | -webkit-overflow-scrolling: touch; 138 | font-family: _font(family-fixed); 139 | font-size: 0.9rem; 140 | margin: 0 0 _size(element-margin) 0; 141 | 142 | code { 143 | display: block; 144 | line-height: 1.75; 145 | padding: 1rem 1.5rem; 146 | overflow-x: auto; 147 | } 148 | } 149 | 150 | hr { 151 | border: 0; 152 | border-bottom: solid 2px; 153 | margin: (_size(element-margin) * 1.5) 0; 154 | 155 | &.major { 156 | margin: (_size(element-margin) * 2.5) 0; 157 | } 158 | } 159 | 160 | .align-left { 161 | text-align: left; 162 | } 163 | 164 | .align-center { 165 | text-align: center; 166 | } 167 | 168 | .align-right { 169 | text-align: right; 170 | } 171 | 172 | @mixin color-typography($p: null) { 173 | @if $p != null { 174 | color: _palette($p, fg); 175 | } 176 | 177 | input, select, textarea { 178 | color: _palette($p, fg-bold); 179 | } 180 | 181 | a { 182 | color: _palette($p, fg-bold); 183 | border-bottom-color: transparentize(_palette($p, fg), 0.5); 184 | 185 | &:hover { 186 | border-bottom-color: transparent; 187 | color: _palette($p, accent) !important; 188 | } 189 | } 190 | 191 | strong, b { 192 | color: _palette($p, fg-bold); 193 | } 194 | 195 | h1, h2, h3, h4, h5, h6 { 196 | color: _palette($p, fg-bold); 197 | } 198 | 199 | blockquote { 200 | border-left-color: _palette($p, border); 201 | } 202 | 203 | code { 204 | background: _palette($p, border-bg); 205 | border-color: _palette($p, border); 206 | } 207 | 208 | hr { 209 | border-bottom-color: _palette($p, border); 210 | } 211 | } 212 | 213 | @include color-typography; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_actions.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Actions */ 8 | 9 | ul.actions { 10 | @include vendor('display', 'flex'); 11 | cursor: default; 12 | list-style: none; 13 | margin-left: (_size(element-margin) * -0.5); 14 | padding-left: 0; 15 | 16 | li { 17 | padding: 0 0 0 (_size(element-margin) * 0.5); 18 | vertical-align: middle; 19 | } 20 | 21 | &.special { 22 | @include vendor('justify-content', 'center'); 23 | width: 100%; 24 | margin-left: 0; 25 | 26 | li { 27 | &:first-child { 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | 33 | &.stacked { 34 | @include vendor('flex-direction', 'column'); 35 | margin-left: 0; 36 | 37 | li { 38 | padding: (_size(element-margin) * 0.65) 0 0 0; 39 | 40 | &:first-child { 41 | padding-top: 0; 42 | } 43 | } 44 | } 45 | 46 | &.fit { 47 | width: calc(100% + #{_size(element-margin) * 0.5}); 48 | 49 | li { 50 | @include vendor('flex-grow', '1'); 51 | @include vendor('flex-shrink', '1'); 52 | width: 100%; 53 | 54 | > * { 55 | width: 100%; 56 | } 57 | } 58 | 59 | &.stacked { 60 | width: 100%; 61 | } 62 | } 63 | 64 | @include breakpoint('<=xsmall') { 65 | &:not(.fixed) { 66 | @include vendor('flex-direction', 'column'); 67 | margin-left: 0; 68 | width: 100% !important; 69 | 70 | li { 71 | @include vendor('flex-grow', '1'); 72 | @include vendor('flex-shrink', '1'); 73 | padding: (_size(element-margin) * 0.5) 0 0 0; 74 | text-align: center; 75 | width: 100%; 76 | 77 | > * { 78 | width: 100%; 79 | } 80 | 81 | &:first-child { 82 | padding-top: 0; 83 | } 84 | 85 | input[type="submit"], 86 | input[type="reset"], 87 | input[type="button"], 88 | button, 89 | .button { 90 | width: 100%; 91 | 92 | &.icon { 93 | &:before { 94 | margin-left: -0.5rem; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_box.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Box */ 8 | 9 | .box { 10 | border-radius: _size(border-radius); 11 | border: solid 2px; 12 | margin-bottom: _size(element-margin); 13 | padding: 1.5rem; 14 | 15 | > :last-child, 16 | > :last-child > :last-child, 17 | > :last-child > :last-child > :last-child { 18 | margin-bottom: 0; 19 | } 20 | 21 | &.alt { 22 | border: 0; 23 | border-radius: 0; 24 | padding: 0; 25 | } 26 | } 27 | 28 | @mixin color-box($p: null) { 29 | .box { 30 | border-color: _palette($p, border); 31 | } 32 | } 33 | 34 | @include color-box; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Button */ 8 | 9 | input[type="submit"], 10 | input[type="reset"], 11 | input[type="button"], 12 | button, 13 | .button { 14 | @include vendor('appearance', 'none'); 15 | @include vendor('transition', ( 16 | 'background-color #{_duration(transition)} ease-in-out', 17 | 'box-shadow #{_duration(transition)} ease-in-out', 18 | 'color #{_duration(transition)} ease-in-out' 19 | )); 20 | border: 0; 21 | border-radius: 0; 22 | cursor: pointer; 23 | display: inline-block; 24 | font-family: _font(family-heading); 25 | font-size: 0.8rem; 26 | font-weight: _font(weight-heading); 27 | letter-spacing: 0.075em; 28 | height: 3rem; 29 | line-height: 3rem; 30 | padding: 0 2rem; 31 | text-align: center; 32 | text-decoration: none; 33 | text-transform: uppercase; 34 | white-space: nowrap; 35 | 36 | &.icon { 37 | &:before { 38 | margin-right: 0.5rem; 39 | } 40 | 41 | &.solo { 42 | position: relative; 43 | width: 4rem; 44 | height: 4rem; 45 | line-height: 4rem; 46 | border-radius: 4rem; 47 | text-indent: 4rem; 48 | overflow: hidden; 49 | padding: 0; 50 | white-space: nowrap; 51 | 52 | &:before { 53 | position: absolute; 54 | display: block; 55 | top: 0; 56 | left: 0; 57 | width: inherit; 58 | height: inherit; 59 | line-height: inherit; 60 | font-size: 1.25rem; 61 | margin-right: 0; 62 | text-align: center; 63 | text-indent: 0; 64 | } 65 | } 66 | } 67 | 68 | &.fit { 69 | width: 100%; 70 | } 71 | 72 | &.small { 73 | font-size: 0.7rem; 74 | height: 2.5rem; 75 | line-height: 2.5rem; 76 | padding: 0 1.5rem; 77 | } 78 | 79 | &.large { 80 | font-size: 0.9rem; 81 | height: 3.5rem; 82 | line-height: 3.5rem; 83 | padding: 0 2.75rem; 84 | } 85 | 86 | @include breakpoint('<=medium') { 87 | font-size: 0.9rem; 88 | height: 3.25rem; 89 | line-height: 3.25rem; 90 | 91 | &.large { 92 | font-size: 1rem; 93 | height: 3.75rem; 94 | line-height: 3.75rem; 95 | } 96 | } 97 | 98 | &.disabled, 99 | &:disabled { 100 | @include vendor('pointer-events', 'none'); 101 | opacity: 0.25; 102 | } 103 | } 104 | 105 | @mixin color-button($p: null) { 106 | $highlight: _palette($p, highlight); 107 | 108 | input[type="submit"], 109 | input[type="reset"], 110 | input[type="button"], 111 | button, 112 | .button { 113 | background-color: transparent; 114 | box-shadow: inset 0 0 0 2px _palette($p, fg-bold); 115 | color: _palette($p, fg-bold) !important; 116 | 117 | &:hover { 118 | box-shadow: inset 0 0 0 2px _palette($p, accent); 119 | color: _palette($p, accent) !important; 120 | } 121 | 122 | &.primary { 123 | background-color: _palette($p, fg-bold); 124 | box-shadow: none; 125 | color: _palette($p, bg) !important; 126 | 127 | &:hover { 128 | background-color: _palette($p, accent); 129 | } 130 | } 131 | } 132 | } 133 | 134 | @include color-button; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icon */ 8 | 9 | .icon { 10 | @include icon; 11 | border-bottom: none; 12 | position: relative; 13 | 14 | > .label { 15 | display: none; 16 | } 17 | 18 | &:before { 19 | line-height: inherit; 20 | } 21 | 22 | &.solid { 23 | &:before { 24 | font-weight: 900; 25 | } 26 | } 27 | 28 | &.brands { 29 | &:before { 30 | font-family: 'Font Awesome 5 Brands'; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icons */ 8 | 9 | ul.icons { 10 | cursor: default; 11 | list-style: none; 12 | padding-left: 0; 13 | 14 | li { 15 | display: inline-block; 16 | padding: 0 0.5rem 0 0; 17 | vertical-align: middle; 18 | 19 | &:last-child { 20 | padding-right: 0; 21 | } 22 | 23 | .icon { 24 | &:before { 25 | width: 2.25rem; 26 | height: 2.25rem; 27 | line-height: 2.25rem; 28 | display: inline-block; 29 | text-align: center; 30 | border-radius: 100%; 31 | font-size: 1.25rem; 32 | } 33 | } 34 | } 35 | 36 | &.alt { 37 | li { 38 | .icon { 39 | &:before { 40 | @include vendor('transition', ( 41 | 'color #{_duration(transition)} ease-in-out', 42 | 'background-color #{_duration(transition)} ease-in-out', 43 | 'border-color #{_duration(transition)} ease-in-out', 44 | 'box-shadow #{_duration(transition)} ease-in-out' 45 | )); 46 | 47 | font-size: 1rem; 48 | } 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_image.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Image */ 8 | 9 | .image { 10 | border: 0; 11 | border-radius: _size(border-radius); 12 | display: inline-block; 13 | position: relative; 14 | 15 | img { 16 | border-radius: _size(border-radius); 17 | display: block; 18 | } 19 | 20 | &.left, 21 | &.right { 22 | max-width: 40%; 23 | 24 | img { 25 | width: 100%; 26 | } 27 | } 28 | 29 | &.left { 30 | float: left; 31 | margin: 0 2rem 2rem 0; 32 | top: 0.75rem; 33 | } 34 | 35 | &.right { 36 | float: right; 37 | margin: 0 0 2rem 2rem; 38 | top: 0.75rem; 39 | } 40 | 41 | &.fit { 42 | display: block; 43 | margin: (_size(element-margin) * 1.25) 0; 44 | width: 100%; 45 | 46 | &:first-child { 47 | margin-top: 0; 48 | } 49 | 50 | img { 51 | width: 100%; 52 | } 53 | } 54 | 55 | &.main { 56 | display: block; 57 | margin: (_size(element-margin) * 2) 0; 58 | width: 100%; 59 | 60 | &:first-child { 61 | margin-top: 0; 62 | } 63 | 64 | img { 65 | width: 100%; 66 | } 67 | } 68 | 69 | @include breakpoint('<=small') { 70 | &.fit { 71 | margin: _size(element-margin) 0; 72 | } 73 | 74 | &.main { 75 | margin: _size(element-margin) 0; 76 | } 77 | } 78 | } 79 | 80 | a.image { 81 | overflow: hidden; 82 | 83 | img { 84 | @include vendor('transition', 'transform #{_duration(transition)} ease-out'); 85 | } 86 | 87 | &:hover { 88 | img { 89 | @include vendor('transform', 'scale(1.05)'); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* List */ 8 | 9 | ol { 10 | list-style: decimal; 11 | margin: 0 0 _size(element-margin) 0; 12 | padding-left: 1.25rem; 13 | 14 | li { 15 | padding-left: 0.25rem; 16 | } 17 | } 18 | 19 | ul { 20 | list-style: disc; 21 | margin: 0 0 _size(element-margin) 0; 22 | padding-left: 1rem; 23 | 24 | li { 25 | padding-left: 0.5rem; 26 | } 27 | 28 | &.divided { 29 | list-style: none; 30 | padding-left: 0; 31 | 32 | li { 33 | border-top: solid 1px; 34 | padding: 0.5rem 0; 35 | 36 | &:first-child { 37 | border-top: 0; 38 | padding-top: 0; 39 | } 40 | } 41 | } 42 | } 43 | 44 | dl { 45 | margin: 0 0 _size(element-margin) 0; 46 | 47 | dt { 48 | display: block; 49 | font-weight: _font(weight-bold); 50 | margin: 0 0 (_size(element-margin) * 0.5) 0; 51 | } 52 | 53 | dd { 54 | margin-left: _size(element-margin); 55 | } 56 | } 57 | 58 | @mixin color-list($p: null) { 59 | ul { 60 | &.divided { 61 | li { 62 | border-top-color: _palette($p, border); 63 | } 64 | } 65 | 66 | &.icons { 67 | li { 68 | a.icon { 69 | &:hover { 70 | &:before { 71 | color: _palette($p, accent); 72 | } 73 | } 74 | } 75 | } 76 | 77 | &.alt { 78 | li { 79 | .icon { 80 | &:before { 81 | box-shadow: inset 0 0 0 2px _palette($p, border); 82 | } 83 | } 84 | 85 | a.icon { 86 | &:hover { 87 | &:before { 88 | box-shadow: inset 0 0 0 2px _palette($p, accent); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | @include color-list; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_pagination.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Pagination */ 8 | 9 | .pagination { 10 | @include vendor('display', 'inline-flex'); 11 | @include vendor('user-select', 'none'); 12 | cursor: default; 13 | list-style: none; 14 | margin: 0 0 _size(element-margin) 2px; 15 | padding: 0; 16 | 17 | a, span { 18 | @include vendor('transition', ( 19 | 'background-color #{_duration(transition)} ease-in-out', 20 | 'border-color #{_duration(transition)} ease-in-out', 21 | 'box-shadow #{_duration(transition)} ease-in-out', 22 | 'color #{_duration(transition)} ease-in-out' 23 | )); 24 | border: solid 2px; 25 | display: inline-block; 26 | font-family: _font(family-heading); 27 | font-size: 0.8rem; 28 | font-weight: _font(weight-heading); 29 | height: _size(element-height); 30 | letter-spacing: 0.075em; 31 | letter-spacing: _font(letter-spacing-heading); 32 | line-height: calc(#{_size(element-height)} - 4px); 33 | margin-left: -2px; 34 | min-width: _size(element-height); 35 | position: relative; 36 | text-align: center; 37 | text-decoration: none; 38 | text-transform: uppercase; 39 | } 40 | 41 | .next, .previous { 42 | @include icon(false, solid); 43 | padding: 0 1.75rem; 44 | 45 | &:before { 46 | display: inline-block; 47 | color: inherit !important; 48 | line-height: inherit; 49 | } 50 | } 51 | 52 | .previous { 53 | &:before { 54 | content: '\f104'; 55 | margin-right: (0.75em / 0.8); 56 | } 57 | } 58 | 59 | .next { 60 | &:before { 61 | content: '\f105'; 62 | float: right; 63 | margin-left: (0.75em / 0.8); 64 | } 65 | } 66 | 67 | @include breakpoint('<=medium') { 68 | a, span { 69 | font-size: 0.9rem; 70 | } 71 | } 72 | 73 | @include breakpoint('<=xsmall') { 74 | .page, .extra { 75 | display: none; 76 | } 77 | } 78 | } 79 | 80 | @mixin color-pagination($p: null) { 81 | .pagination { 82 | a, span { 83 | border-color: _palette($p, border); 84 | } 85 | 86 | a { 87 | color: _palette($p, fg-bold) !important; 88 | 89 | &:hover { 90 | color: _palette($p, accent) !important; 91 | border-color: _palette($p, accent); 92 | z-index: 1; 93 | 94 | & + a, 95 | & + span { 96 | border-left-color: _palette($p, accent); 97 | } 98 | } 99 | 100 | &.active { 101 | background-color: _palette($p, border); 102 | } 103 | } 104 | 105 | span { 106 | color: _palette($p, border); 107 | } 108 | } 109 | } 110 | 111 | @include color-pagination; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_row.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Row */ 8 | 9 | .row { 10 | @include html-grid(1.5rem); 11 | 12 | @include breakpoint('<=xlarge') { 13 | @include html-grid(1.5rem, 'xlarge'); 14 | } 15 | 16 | @include breakpoint('<=large') { 17 | @include html-grid(1.5rem, 'large'); 18 | } 19 | 20 | @include breakpoint('<=medium') { 21 | @include html-grid(1.5rem, 'medium'); 22 | } 23 | 24 | @include breakpoint('<=small') { 25 | @include html-grid(1.5rem, 'small'); 26 | } 27 | 28 | @include breakpoint('<=xsmall') { 29 | @include html-grid(1.5rem, 'xsmall'); 30 | } 31 | 32 | @include breakpoint('<=xxsmall') { 33 | @include html-grid(1.5rem, 'xxsmall'); 34 | } 35 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_section.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Section/Article */ 8 | 9 | section, article { 10 | &.special { 11 | text-align: center; 12 | } 13 | } 14 | 15 | header { 16 | cursor: default; 17 | 18 | > .date { 19 | display: block; 20 | font-size: 0.8rem; 21 | height: 1; 22 | margin: 0 0 (_size(element-margin) * 0.5) 0; 23 | position: relative; 24 | } 25 | 26 | > p { 27 | font-style: italic; 28 | } 29 | 30 | > h1 + p { 31 | font-size: 1.1rem; 32 | margin-top: -0.5rem; 33 | line-height: 2; 34 | } 35 | 36 | > h2 + p { 37 | font-size: 1rem; 38 | margin-top: -0.75rem; 39 | } 40 | 41 | > h3 + p { 42 | font-size: 0.9rem; 43 | margin-top: -0.75rem; 44 | } 45 | 46 | > h4 + p { 47 | font-size: 0.8rem; 48 | margin-top: -0.75rem; 49 | } 50 | 51 | &.major { 52 | margin: 0 0 (_size(element-margin) * 2) 0; 53 | text-align: center; 54 | 55 | > :last-child { 56 | margin-bottom: 0; 57 | } 58 | 59 | > p { 60 | margin-top: 0; 61 | text-align: center; 62 | } 63 | 64 | > .date { 65 | font-size: 1rem; 66 | margin: 0 0 (_size(element-margin) * 2) 0; 67 | 68 | &:before, &:after { 69 | content: ''; 70 | display: block; 71 | position: absolute; 72 | top: 50%; 73 | width: calc(50% - 6rem); 74 | border-top: solid 2px; 75 | } 76 | 77 | &:before { 78 | left: 0; 79 | } 80 | 81 | &:after { 82 | right: 0; 83 | } 84 | } 85 | } 86 | 87 | @include breakpoint('<=medium') { 88 | br { 89 | display: none; 90 | } 91 | } 92 | 93 | @include breakpoint('<=small') { 94 | &.major { 95 | margin: 0 0 _size(element-margin) 0; 96 | } 97 | } 98 | } 99 | 100 | @mixin color-section($p: null) { 101 | header { 102 | &.major { 103 | .date { 104 | &:before, &:after { 105 | border-top-color: _palette($p, border); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | @include color-section; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/components/_table.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Table */ 8 | 9 | .table-wrapper { 10 | -webkit-overflow-scrolling: touch; 11 | overflow-x: auto; 12 | } 13 | 14 | table { 15 | margin: 0 0 _size(element-margin) 0; 16 | width: 100%; 17 | 18 | tbody { 19 | tr { 20 | border: solid 1px; 21 | border-left: 0; 22 | border-right: 0; 23 | } 24 | } 25 | 26 | td { 27 | padding: 0.75rem 0.75rem; 28 | } 29 | 30 | th { 31 | font-family: _font(family-heading); 32 | font-size: 0.8rem; 33 | font-weight: _font(weight-heading); 34 | letter-spacing: 0.075em; 35 | line-height: 1.5; 36 | padding: 0 0.75rem 0.75rem 0.75rem; 37 | text-align: left; 38 | text-transform: uppercase; 39 | 40 | @include breakpoint('<=medium') { 41 | font-size: 0.9rem; 42 | } 43 | } 44 | 45 | thead { 46 | border-bottom: solid 2px; 47 | } 48 | 49 | tfoot { 50 | border-top: solid 2px; 51 | } 52 | 53 | &.alt { 54 | border-collapse: separate; 55 | 56 | tbody { 57 | tr { 58 | td { 59 | border: solid 1px; 60 | border-left-width: 0; 61 | border-top-width: 0; 62 | 63 | &:first-child { 64 | border-left-width: 1px; 65 | } 66 | } 67 | 68 | &:first-child { 69 | td { 70 | border-top-width: 1px; 71 | } 72 | } 73 | } 74 | } 75 | 76 | thead { 77 | border-bottom: 0; 78 | } 79 | 80 | tfoot { 81 | border-top: 0; 82 | } 83 | } 84 | } 85 | 86 | @mixin color-table($p: null) { 87 | table { 88 | tbody { 89 | tr { 90 | border-color: _palette($p, border); 91 | 92 | &:nth-child(2n + 1) { 93 | background-color: _palette($p, border-bg); 94 | } 95 | } 96 | } 97 | 98 | th { 99 | color: _palette($p, fg-bold); 100 | } 101 | 102 | thead { 103 | border-bottom-color: _palette($p, border); 104 | } 105 | 106 | tfoot { 107 | border-top-color: _palette($p, border); 108 | } 109 | 110 | &.alt { 111 | tbody { 112 | tr { 113 | td { 114 | border-color: _palette($p, border); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | @include color-table; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Footer */ 8 | 9 | #footer { 10 | @include color(alt); 11 | @include vendor('display', 'flex'); 12 | background-color: _palette(alt, bg); 13 | color: _palette(fg-light); 14 | cursor: default; 15 | position: relative; 16 | margin: 0 auto; 17 | width: calc(100% - #{_size(padding) * 2}); 18 | max-width: _size(wrapper); 19 | z-index: 2; 20 | 21 | > section { 22 | @include vendor('flex-basis', '50%'); 23 | @include vendor('flex-grow', '1'); 24 | @include vendor('flex-shrink', '1'); 25 | @include padding((_size(padding) * 2), (_size(padding) * 2)); 26 | border-left: solid 2px _palette(alt, border); 27 | 28 | &:first-child { 29 | border-left: 0; 30 | } 31 | 32 | &.split { 33 | @include vendor('display', 'flex'); 34 | @include vendor('flex-direction', 'column'); 35 | padding: 0; 36 | 37 | > section { 38 | @include padding((_size(padding) * 2) - 1, (_size(padding) * 2)); 39 | border-top: solid 2px _palette(alt, border); 40 | 41 | &:first-child { 42 | @include padding((_size(padding) * 2) - 1, (_size(padding) * 2), (_size(padding), 0, 0, 0)); 43 | border-top: 0; 44 | } 45 | 46 | &:last-child { 47 | @include padding((_size(padding) * 2) - 1, (_size(padding) * 2), (0, 0, _size(padding), 0)); 48 | } 49 | } 50 | 51 | &.contact { 52 | > section { 53 | @include vendor('display', 'flex'); 54 | @include vendor('align-items', 'center'); 55 | padding: (_size(padding) * 1.575) (_size(padding) * 2); 56 | 57 | > * { 58 | margin-bottom: 0; 59 | } 60 | 61 | > :first-child { 62 | @include vendor('flex-shrink', '0'); 63 | @include vendor('flex-grow', '0'); 64 | width: 6rem; 65 | } 66 | 67 | > :last-child { 68 | @include vendor('flex-shrink', '1'); 69 | @include vendor('flex-grow', '1'); 70 | } 71 | 72 | &:first-child { 73 | padding: (_size(padding) * 2) (_size(padding) * 2) ((_size(padding) * 2) - 1) (_size(padding) * 2); 74 | } 75 | 76 | &:last-child { 77 | padding: ((_size(padding) * 2) - 1) (_size(padding) * 2) (_size(padding) * 2) (_size(padding) * 2); 78 | } 79 | 80 | &.alt { 81 | @include vendor('align-items', 'flex-start'); 82 | 83 | > :last-child { 84 | margin-top: -0.325rem; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | form label, 93 | h3, 94 | p { 95 | font-size: 0.8rem; 96 | } 97 | 98 | @include breakpoint('<=medium') { 99 | display: block; 100 | 101 | > section { 102 | border-top: solid 2px _palette(alt, border); 103 | 104 | &:first-child { 105 | border-top: 0; 106 | } 107 | 108 | &.split { 109 | > section { 110 | @include padding((_size(padding) * 2), (_size(padding) * 2)); 111 | 112 | &:first-child { 113 | @include padding((_size(padding) * 2), (_size(padding) * 2)); 114 | } 115 | 116 | &:last-child { 117 | @include padding((_size(padding) * 2), (_size(padding) * 2)); 118 | } 119 | } 120 | 121 | &.contact { 122 | > section { 123 | padding: (_size(padding) * 2); 124 | 125 | &:first-child { 126 | padding: (_size(padding) * 2); 127 | } 128 | 129 | &:last-child { 130 | padding: (_size(padding) * 2); 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | form label, 138 | h3, 139 | p { 140 | font-size: 0.9rem; 141 | } 142 | } 143 | 144 | @include breakpoint('<=small') { 145 | > section { 146 | @include padding((_size(padding) * 1), (_size(padding) * 1)); 147 | 148 | &.split { 149 | > section { 150 | @include padding((_size(padding) * 1), (_size(padding) * 1)); 151 | 152 | &:first-child { 153 | @include padding((_size(padding) * 1), (_size(padding) * 1)); 154 | } 155 | 156 | &:last-child { 157 | @include padding((_size(padding) * 1), (_size(padding) * 1)); 158 | } 159 | } 160 | 161 | &.contact { 162 | > section { 163 | padding: (_size(padding) * 1); 164 | 165 | &:first-child { 166 | padding: (_size(padding) * 1); 167 | } 168 | 169 | &:last-child { 170 | padding: (_size(padding) * 1); 171 | } 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | @include breakpoint('<=xsmall') { 179 | width: 100%; 180 | } 181 | } 182 | 183 | #copyright { 184 | @include color-typography(invert); 185 | position: relative; 186 | color: transparentize(_palette(invert, fg), 0.75); 187 | cursor: default; 188 | font-family: _font(family-heading); 189 | font-size: 0.8rem; 190 | font-weight: _font(weight-heading); 191 | letter-spacing: 0.075em; 192 | line-height: 1.5; 193 | text-align: center; 194 | text-transform: uppercase; 195 | margin: (_size(padding) * 2) auto (_size(padding) * 4) auto; 196 | width: calc(100% - #{_size(padding) * 2}); 197 | max-width: _size(wrapper); 198 | z-index: 2; 199 | 200 | a { 201 | color: inherit; 202 | border-bottom-color: inherit; 203 | } 204 | 205 | ul { 206 | list-style: none; 207 | margin: 0; 208 | padding-left: 0; 209 | 210 | li { 211 | border-left: solid 2px; 212 | display: inline-block; 213 | line-height: 1; 214 | margin-left: 1rem; 215 | padding-left: 1rem; 216 | 217 | &:first-child { 218 | border-left: 0; 219 | margin-left: 0; 220 | padding-left: 0; 221 | } 222 | } 223 | } 224 | 225 | @include breakpoint('<=large') { 226 | margin: (_size(padding) * 2) auto; 227 | } 228 | 229 | @include breakpoint('<=xsmall') { 230 | ul { 231 | li { 232 | border-left: 0; 233 | margin: 1rem 0 0 0; 234 | padding-left: 0; 235 | display: block; 236 | 237 | &:first-child { 238 | margin-top: 0; 239 | } 240 | } 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_header.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Header */ 8 | 9 | #header { 10 | @include color-typography(invert); 11 | @include vendor('align-items', 'center'); 12 | @include vendor('display', 'flex'); 13 | @include vendor('flex-direction', 'column'); 14 | @include vendor('justify-content', 'flex-end'); 15 | @include vendor('pointer-events', 'none'); 16 | @include vendor('user-select', 'none'); 17 | height: 20rem; 18 | padding-bottom: (_size(padding) * 4); 19 | position: relative; 20 | text-align: center; 21 | z-index: 2; 22 | 23 | .logo { 24 | @include vendor('transition', ( 25 | 'border-color #{_duration(transition)} ease-in-out', 26 | 'color #{_duration(transition)} ease-in-out', 27 | 'opacity 0.5s ease', 28 | 'transform 0.5s ease', 29 | 'visibility 0.5s' 30 | )); 31 | @include vendor('pointer-events', 'auto'); 32 | border-style: solid; 33 | border-color: _palette(invert, border); 34 | border-width: 5px !important; 35 | font-family: _font(family-heading); 36 | font-size: 2.25rem; 37 | font-weight: _font(weight-heading); 38 | letter-spacing: 0.075em; 39 | line-height: 1; 40 | padding: 1rem 1.75rem; 41 | text-transform: uppercase; 42 | visibility: visible; 43 | 44 | &:hover { 45 | border-color: _palette(invert, accent) !important; 46 | color: _palette(invert, accent) !important; 47 | } 48 | } 49 | 50 | @include breakpoint('<=medium') { 51 | height: 14rem; 52 | padding-bottom: (_size(padding) * 2); 53 | } 54 | 55 | @include breakpoint('<=small') { 56 | padding-bottom: (_size(padding) * 1.5); 57 | 58 | .logo { 59 | font-size: 1.75rem; 60 | border-width: 3px !important; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_intro.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Intro */ 8 | 9 | #intro { 10 | @include color-typography(invert); 11 | @include color-button(invert); 12 | @include padding(_size(padding) * 4, _size(padding) * 2); 13 | @include vendor('align-items', 'center'); 14 | @include vendor('display', 'flex'); 15 | @include vendor('flex-direction', 'column'); 16 | @include vendor('justify-content', 'flex-end'); 17 | @include vendor('transition', ( 18 | 'opacity 1s ease', 19 | 'transform 1s ease' 20 | )); 21 | position: relative; 22 | cursor: default; 23 | text-align: center; 24 | z-index: 1; 25 | min-height: 100vh; 26 | 27 | h1 { 28 | font-size: 5rem; 29 | line-height: 1; 30 | } 31 | 32 | p { 33 | font-size: 1.25rem; 34 | font-style: italic; 35 | margin-top: -0.25rem; 36 | text-align: center; 37 | } 38 | 39 | & + #header { 40 | margin-top: -20rem; 41 | 42 | .logo { 43 | @include vendor('transform', 'translateY(2rem)'); 44 | opacity: 0; 45 | visibility: hidden; 46 | } 47 | } 48 | 49 | &.hidden { 50 | @include vendor('pointer-events', 'none'); 51 | @include vendor('transform', 'translateY(2rem)'); 52 | @include vendor('transition', ( 53 | 'opacity 0.5s ease', 54 | 'transform 0.5s ease', 55 | 'visibility 0.5s' 56 | )); 57 | opacity: 0; 58 | visibility: hidden; 59 | 60 | & + #header { 61 | .logo { 62 | @include vendor('transform', 'translateY(0)'); 63 | opacity: 1; 64 | visibility: visible; 65 | } 66 | } 67 | } 68 | 69 | body.is-preload & { 70 | @include vendor('transform', 'translateY(2rem)'); 71 | opacity: 0; 72 | 73 | &:not(.hidden) { 74 | & + #header + #nav { 75 | @include vendor('transform', 'translateY(4rem)'); 76 | opacity: 0; 77 | } 78 | } 79 | } 80 | 81 | @include breakpoint('<=medium') { 82 | @include padding(_size(padding) * 2, _size(padding) * 2); 83 | min-height: 90vh; 84 | 85 | p { 86 | br { 87 | display: none; 88 | } 89 | } 90 | 91 | & + #header { 92 | margin-top: -14rem; 93 | } 94 | } 95 | 96 | @include breakpoint('<=small') { 97 | @include padding(_size(padding) * 1.5, _size(padding) * 1); 98 | min-height: 80vh; 99 | 100 | h1 { 101 | font-size: 3.25rem; 102 | line-height: 1.1; 103 | margin-bottom: _size(element-margin) * 0.5; 104 | } 105 | 106 | p { 107 | font-size: 1rem; 108 | margin-top: 0rem; 109 | } 110 | 111 | .actions { 112 | display: none; 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Main */ 8 | 9 | #main { 10 | background-color: _palette(bg); 11 | position: relative; 12 | margin: 0 auto; 13 | width: calc(100% - #{_size(padding) * 2}); 14 | max-width: _size(wrapper); 15 | z-index: 2; 16 | 17 | > * { 18 | @include padding((_size(padding) * 2), (_size(padding) * 2)); 19 | border-top: solid 2px _palette(border); 20 | margin: 0; 21 | 22 | &:first-child { 23 | border-top: 0; 24 | } 25 | } 26 | 27 | > footer { 28 | text-align: center; 29 | } 30 | 31 | > .post { 32 | @include padding((_size(padding) * 4), (_size(padding) * 4)); 33 | 34 | header { 35 | &.major { 36 | > .date { 37 | margin-top: -2rem; 38 | } 39 | 40 | > h1, h2 { 41 | font-size: 4rem; 42 | line-height: 1.1; 43 | margin: 0 0 _size(element-margin) 0; 44 | } 45 | } 46 | } 47 | 48 | &.featured { 49 | text-align: center; 50 | } 51 | 52 | @include breakpoint('<=large') { 53 | @include padding((_size(padding) * 3), (_size(padding) * 2)); 54 | } 55 | 56 | @include breakpoint('<=small') { 57 | @include padding((_size(padding) * 2), (_size(padding) * 1)); 58 | 59 | header { 60 | &.major { 61 | > .date { 62 | margin-top: -1rem; 63 | margin-bottom: _size(element-margin); 64 | } 65 | 66 | > h1, h2 { 67 | font-size: 2.5rem; 68 | line-height: 1.2; 69 | margin: 0 0 (_size(element-margin) * 0.75) 0; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | > .posts { 77 | @include fixed-grid(( 78 | columns: 2, 79 | gutters: (_size(padding) * 4), 80 | horizontal-align: center, 81 | vertical-align: flex-start, 82 | flush: false 83 | )); 84 | width: 100%; 85 | padding: 0; 86 | 87 | > article { 88 | border-color: _palette(border); 89 | border-left-width: 2px; 90 | border-style: solid; 91 | border-top-width: 2px; 92 | text-align: center; 93 | 94 | > :last-child { 95 | margin-bottom: 0; 96 | } 97 | 98 | &:nth-child(2n - 1) { 99 | border-left-width: 0; 100 | } 101 | 102 | &:nth-child(-n + 2) { 103 | border-top-width: 0; 104 | } 105 | } 106 | 107 | @include breakpoint('<=medium') { 108 | @include fixed-grid-resize(( 109 | columns: 2, 110 | gutters: (_size(padding) * 2.5), 111 | flush: false 112 | )); 113 | } 114 | 115 | @include breakpoint('<=small') { 116 | @include fixed-grid-resize(( 117 | columns: 1, 118 | gutters: (_size(padding) * 2), 119 | prev-columns: 2, 120 | flush: false 121 | )); 122 | 123 | > article { 124 | &:nth-child(2n - 1) { 125 | border-left-width: 2px; 126 | } 127 | 128 | &:nth-child(-n + 2) { 129 | border-top-width: 2px; 130 | } 131 | 132 | &:nth-child(n) { 133 | border-left-width: 0; 134 | } 135 | 136 | &:nth-child(-n + 1) { 137 | border-top-width: 0; 138 | } 139 | 140 | .image { 141 | max-width: 25rem; 142 | margin-left: auto; 143 | margin-right: auto; 144 | } 145 | } 146 | } 147 | } 148 | 149 | @include breakpoint('<=small') { 150 | > * { 151 | @include padding((_size(padding) * 1), (_size(padding) * 1)); 152 | } 153 | } 154 | 155 | @include breakpoint('<=xsmall') { 156 | width: 100%; 157 | } 158 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_nav.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Nav */ 8 | 9 | #nav { 10 | @include color-list(invert); 11 | @include color-typography(invert); 12 | @include vendor('display', 'flex'); 13 | @include vendor('transition', ( 14 | 'transform 1s ease', 15 | 'opacity 1s ease' 16 | )); 17 | background: rgba(255,255,255,0.175); 18 | height: 4rem; 19 | line-height: 4rem; 20 | margin: -4rem auto 0 auto; 21 | overflow: hidden; 22 | padding: 0 2rem 0 0; 23 | position: relative; 24 | width: calc(100% - #{_size(padding) * 2}); 25 | max-width: _size(wrapper); 26 | z-index: 2; 27 | 28 | ul { 29 | &.links { 30 | @include vendor('display', 'flex'); 31 | @include vendor('flex-grow', '1'); 32 | @include vendor('flex-shrink', '1'); 33 | font-family: _font(family-heading); 34 | font-weight: _font(weight-heading); 35 | letter-spacing: 0.075em; 36 | list-style: none; 37 | margin-bottom: 0; 38 | padding-left: 0; 39 | text-transform: uppercase; 40 | 41 | li { 42 | display: block; 43 | padding-left: 0; 44 | 45 | a { 46 | @include vendor('transition', ( 47 | 'background-color #{_duration(transition)} ease-in-out', 48 | 'color #{_duration(transition)} ease-in-out' 49 | )); 50 | display: block; 51 | font-size: 0.8rem; 52 | outline: none; 53 | padding: 0 2rem; 54 | 55 | &:hover { 56 | color: inherit !important; 57 | background-color: transparentize(_palette(invert, fg), 0.9); 58 | } 59 | } 60 | 61 | &.active { 62 | background-color: _palette(invert, fg); 63 | 64 | a { 65 | color: _palette(invert, bg); 66 | 67 | &:hover { 68 | color: _palette(invert, accent) !important; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | &.icons { 76 | @include vendor('flex-grow', '0'); 77 | @include vendor('flex-shrink', '0'); 78 | margin-bottom: 0; 79 | } 80 | } 81 | 82 | @include breakpoint('<=medium') { 83 | display: none; 84 | } 85 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_navPanel.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Nav Panel */ 8 | 9 | #navPanelToggle { 10 | @include icon(false, solid); 11 | @include vendor('transition', ( 12 | 'color #{_duration(transition)} ease-in-out', 13 | 'background-color #{_duration(transition)} ease-in-out', 14 | 'box-shadow #{_duration(transition)} ease-in-out' 15 | )); 16 | display: none; 17 | position: fixed; 18 | top: 0.75rem; 19 | right: 0.75rem; 20 | border: 0; 21 | color: _palette(invert, fg-bold); 22 | font-family: _font(family-heading); 23 | font-size: 0.9rem; 24 | font-weight: _font(weight-heading); 25 | letter-spacing: 0.075em; 26 | padding: 0.375rem 1.25rem; 27 | text-transform: uppercase; 28 | z-index: _misc(z-index-base) + 1; 29 | 30 | &:before { 31 | content: '\f0c9'; 32 | margin-right: 0.5rem; 33 | } 34 | 35 | &.alt { 36 | background-color: transparentize(_palette(bg), 0.125); 37 | box-shadow: 0 0.125rem 0.75rem 0 transparentize(_palette(invert, bg), 0.75); 38 | color: _palette(fg-bold); 39 | 40 | &:hover { 41 | background-color: _palette(bg); 42 | } 43 | } 44 | 45 | @include breakpoint('<=medium') { 46 | display: block; 47 | } 48 | 49 | @include breakpoint('<=small') { 50 | font-size: 0.8rem; 51 | padding: 0.25rem 1rem; 52 | } 53 | } 54 | 55 | #navPanel { 56 | @include vendor('transform', 'translateX(20rem)'); 57 | @include vendor('transition', ('transform #{_duration(menu)} ease', 'box-shadow #{_duration(menu)} ease', 'visibility #{_duration(menu)}')); 58 | display: none; 59 | -webkit-overflow-scrolling: touch; 60 | background: _palette(bg); 61 | box-shadow: none; 62 | color: _palette(fg-bold); 63 | height: 100%; 64 | max-width: 80%; 65 | overflow-y: auto; 66 | padding: 3rem 2rem; 67 | position: fixed; 68 | right: 0; 69 | top: 0; 70 | visibility: hidden; 71 | width: 20rem; 72 | z-index: _misc(z-index-base) + 2; 73 | 74 | .links { 75 | list-style: none; 76 | padding-left: 0; 77 | 78 | li { 79 | border-top: solid 2px _palette(border); 80 | 81 | a { 82 | border-bottom: 0; 83 | display: block; 84 | font-family: _font(family-heading); 85 | font-size: 0.9rem; 86 | font-size: 0.9rem; 87 | font-weight: _font(weight-heading); 88 | letter-spacing: 0.075em; 89 | padding: 0.75rem 0; 90 | text-transform: uppercase; 91 | } 92 | 93 | &:first-child { 94 | border-top: 0; 95 | } 96 | } 97 | } 98 | 99 | .close { 100 | @include icon(false, solid); 101 | @include vendor('transition', 'color #{_duration(transition)} ease-in-out'); 102 | -webkit-tap-highlight-color: rgba(0,0,0,0); 103 | border: 0; 104 | color: _palette(fg-light); 105 | cursor: pointer; 106 | display: block; 107 | height: 3.25rem; 108 | line-height: 3.25rem; 109 | padding-right: 1.25rem; 110 | position: absolute; 111 | right: 0; 112 | text-align: right; 113 | top: 0; 114 | vertical-align: middle; 115 | width: 7rem; 116 | 117 | &:before { 118 | content: '\f00d'; 119 | font-size: 1.25rem; 120 | } 121 | 122 | &:hover { 123 | color: _palette(fg-bold); 124 | } 125 | 126 | @include breakpoint('<=small') { 127 | height: 4rem; 128 | line-height: 4rem; 129 | } 130 | } 131 | 132 | @include breakpoint('<=medium') { 133 | display: block; 134 | } 135 | 136 | @include breakpoint('<=small') { 137 | padding: 2.5rem 1.75rem; 138 | } 139 | } 140 | 141 | @include breakpoint('<=medium') { 142 | body.is-navPanel-visible { 143 | #wrapper { 144 | opacity: 0.5; 145 | } 146 | 147 | #navPanel { 148 | @include vendor('transform', 'translateX(0)'); 149 | box-shadow: 0 0 1.5rem 0 rgba(0,0,0,0.2); 150 | visibility: visible; 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/layout/_wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Massively by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Wrapper */ 8 | 9 | #wrapper { 10 | @include vendor('transition', 'opacity #{_duration(menu)} ease'); 11 | position: relative; 12 | z-index: 1; 13 | overflow: hidden; 14 | 15 | > .bg { 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 100%; 21 | background-color: _palette(wrapper-bg); 22 | background-image: url('../../images/overlay.png'), linear-gradient(0deg, rgba(0,0,0,0.1), rgba(0,0,0,0.1)), url('../../images/bg.jpg'); 23 | background-size: auto, auto, 100% auto; 24 | background-position: center, center, top center; 25 | background-repeat: repeat, no-repeat, no-repeat; 26 | background-attachment: scroll, scroll, scroll; 27 | z-index: -1; 28 | 29 | &.fixed { 30 | position: fixed; 31 | width: 100vw; 32 | height: 100vh; 33 | } 34 | } 35 | 36 | &.fade-in { 37 | &:before { 38 | @include vendor('pointer-events', 'none'); 39 | @include vendor('transition', 'opacity 1s ease-in-out'); 40 | @include vendor('transition-delay', '0.75s'); 41 | background: _palette(invert, bg); 42 | content: ''; 43 | display: block; 44 | height: 100%; 45 | left: 0; 46 | opacity: 0; 47 | position: fixed; 48 | top: 0; 49 | width: 100%; 50 | } 51 | 52 | body.is-preload & { 53 | &:before { 54 | opacity: 1; 55 | } 56 | } 57 | } 58 | 59 | @include orientation(portrait) { 60 | > .bg { 61 | background-size: auto, auto, auto 175%; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/libs/_html-grid.scss: -------------------------------------------------------------------------------- 1 | // html-grid.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Mixins. 4 | 5 | /// Initializes the current element as an HTML grid. 6 | /// @param {mixed} $gutters Gutters (either a single number to set both column/row gutters, or a list to set them individually). 7 | /// @param {mixed} $suffix Column class suffix (optional; either a single suffix or a list). 8 | @mixin html-grid($gutters: 1.5em, $suffix: '') { 9 | 10 | // Initialize. 11 | $cols: 12; 12 | $multipliers: 0, 0.25, 0.5, 1, 1.50, 2.00; 13 | $unit: 100% / $cols; 14 | 15 | // Suffixes. 16 | $suffixes: null; 17 | 18 | @if (type-of($suffix) == 'list') { 19 | $suffixes: $suffix; 20 | } 21 | @else { 22 | $suffixes: ($suffix); 23 | } 24 | 25 | // Gutters. 26 | $guttersCols: null; 27 | $guttersRows: null; 28 | 29 | @if (type-of($gutters) == 'list') { 30 | 31 | $guttersCols: nth($gutters, 1); 32 | $guttersRows: nth($gutters, 2); 33 | 34 | } 35 | @else { 36 | 37 | $guttersCols: $gutters; 38 | $guttersRows: 0; 39 | 40 | } 41 | 42 | // Row. 43 | display: flex; 44 | flex-wrap: wrap; 45 | box-sizing: border-box; 46 | align-items: stretch; 47 | 48 | // Columns. 49 | > * { 50 | box-sizing: border-box; 51 | } 52 | 53 | // Gutters. 54 | &.gtr-uniform { 55 | > * { 56 | > :last-child { 57 | margin-bottom: 0; 58 | } 59 | } 60 | } 61 | 62 | // Alignment. 63 | &.aln-left { 64 | justify-content: flex-start; 65 | } 66 | 67 | &.aln-center { 68 | justify-content: center; 69 | } 70 | 71 | &.aln-right { 72 | justify-content: flex-end; 73 | } 74 | 75 | &.aln-top { 76 | align-items: flex-start; 77 | } 78 | 79 | &.aln-middle { 80 | align-items: center; 81 | } 82 | 83 | &.aln-bottom { 84 | align-items: flex-end; 85 | } 86 | 87 | // Step through suffixes. 88 | @each $suffix in $suffixes { 89 | 90 | // Suffix. 91 | @if ($suffix != '') { 92 | $suffix: '-' + $suffix; 93 | } 94 | @else { 95 | $suffix: ''; 96 | } 97 | 98 | // Row. 99 | 100 | // Important. 101 | > .imp#{$suffix} { 102 | order: -1; 103 | } 104 | 105 | // Columns, offsets. 106 | @for $i from 1 through $cols { 107 | > .col-#{$i}#{$suffix} { 108 | width: $unit * $i; 109 | } 110 | 111 | > .off-#{$i}#{$suffix} { 112 | margin-left: $unit * $i; 113 | } 114 | } 115 | 116 | // Step through multipliers. 117 | @each $multiplier in $multipliers { 118 | 119 | // Gutters. 120 | $class: null; 121 | 122 | @if ($multiplier != 1) { 123 | $class: '.gtr-' + ($multiplier * 100); 124 | } 125 | 126 | &#{$class} { 127 | margin-top: ($guttersRows * $multiplier * -1); 128 | margin-left: ($guttersCols * $multiplier * -1); 129 | 130 | > * { 131 | padding: ($guttersRows * $multiplier) 0 0 ($guttersCols * $multiplier); 132 | } 133 | 134 | // Uniform. 135 | &.gtr-uniform { 136 | margin-top: $guttersCols * $multiplier * -1; 137 | 138 | > * { 139 | padding-top: $guttersCols * $multiplier; 140 | } 141 | } 142 | 143 | } 144 | 145 | } 146 | 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $category Optional category to use. 4 | /// @param {string} $where Optional pseudoelement to target (before or after). 5 | @mixin icon($content: false, $category: regular, $where: before) { 6 | 7 | text-decoration: none; 8 | 9 | &:#{$where} { 10 | 11 | @if $content { 12 | content: $content; 13 | } 14 | 15 | -moz-osx-font-smoothing: grayscale; 16 | -webkit-font-smoothing: antialiased; 17 | display: inline-block; 18 | font-style: normal; 19 | font-variant: normal; 20 | text-rendering: auto; 21 | line-height: 1; 22 | text-transform: none !important; 23 | 24 | @if ($category == brands) { 25 | font-family: 'Font Awesome 5 Brands'; 26 | } 27 | @elseif ($category == solid) { 28 | font-family: 'Font Awesome 5 Free'; 29 | font-weight: 900; 30 | } 31 | @else { 32 | font-family: 'Font Awesome 5 Free'; 33 | font-weight: 400; 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | /// Applies padding to an element, taking the current element-margin value into account. 41 | /// @param {mixed} $tb Top/bottom padding. 42 | /// @param {mixed} $lr Left/right padding. 43 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 44 | /// @param {bool} $important If true, adds !important. 45 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 46 | 47 | @if $important { 48 | $important: '!important'; 49 | } 50 | 51 | $x: 0.1em; 52 | 53 | @if unit(_size(element-margin)) == 'rem' { 54 | $x: 0.1rem; 55 | } 56 | 57 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 58 | 59 | } 60 | 61 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 62 | /// @param {string} $svg SVG data URL. 63 | /// @return {string} Encoded SVG data URL. 64 | @function svg-url($svg) { 65 | 66 | $svg: str-replace($svg, '"', '\''); 67 | $svg: str-replace($svg, '%', '%25'); 68 | $svg: str-replace($svg, '<', '%3C'); 69 | $svg: str-replace($svg, '>', '%3E'); 70 | $svg: str-replace($svg, '&', '%26'); 71 | $svg: str-replace($svg, '#', '%23'); 72 | $svg: str-replace($svg, '{', '%7B'); 73 | $svg: str-replace($svg, '}', '%7D'); 74 | $svg: str-replace($svg, ';', '%3B'); 75 | 76 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 77 | 78 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000 4 | ); 5 | 6 | // Duration. 7 | $duration: ( 8 | menu: 0.5s, 9 | transition: 0.2s 10 | ); 11 | 12 | // Size. 13 | $size: ( 14 | element-height: 3rem, 15 | element-margin: 2rem, 16 | padding: 2rem, 17 | wrapper: 72rem 18 | ); 19 | 20 | // Font. 21 | $font: ( 22 | family: ('Merriweather', Georgia, serif), 23 | family-heading: ('Source Sans Pro', Helvetica, sans-serif), 24 | family-fixed: ('Courier New', monospace), 25 | weight: 300, 26 | weight-bold: 600, 27 | weight-heading: 900 28 | ); 29 | 30 | // Palette. 31 | $palette: ( 32 | wrapper-bg: #212931, 33 | 34 | bg: #ffffff, 35 | fg: #212931, 36 | fg-bold: #212931, 37 | fg-light: mix(#212931, #ffffff, 50%), 38 | border: mix(#dcdcdc, #ffffff, 50%), 39 | border-bg: rgba(#dcdcdc, 0.25), 40 | accent: #18bfef, 41 | 42 | alt: ( 43 | bg: #f5f5f5, 44 | fg: #717981, 45 | fg-bold: #717981, 46 | fg-light: mix(#717981, #f5f5f5, 50%), 47 | border: mix(#dcdcdc, #f5f5f5, 75%), 48 | border-bg: rgba(#dcdcdc, 0.5), 49 | accent: #18bfef, 50 | ), 51 | 52 | invert: ( 53 | bg: #1e252d, 54 | bg-alt: #1e252d, 55 | fg: #ffffff, 56 | fg-bold: #ffffff, 57 | fg-light: rgba(#ffffff, 0.5), 58 | border: #ffffff, 59 | border-bg: rgba(#ffffff,0.075), 60 | accent: #18bfef, 61 | ), 62 | ); -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | @import 'libs/fixed-grid'; 8 | @import 'fontawesome-all.min.css'; 9 | @import url('https://fonts.googleapis.com/css?family=Merriweather:300,700,300italic,700italic|Source+Sans+Pro:900'); 10 | 11 | /* 12 | Massively by HTML5 UP 13 | html5up.net | @ajlkn 14 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 15 | */ 16 | 17 | // Breakpoints. 18 | 19 | @include breakpoints(( 20 | default: (1681px, null ), 21 | xlarge: (1281px, 1680px ), 22 | large: (981px, 1280px ), 23 | medium: (737px, 980px ), 24 | small: (481px, 736px ), 25 | xsmall: (361px, 480px ), 26 | xxsmall: (null, 360px ) 27 | )); 28 | 29 | // Mixins. 30 | 31 | @mixin color($p) { 32 | @include color-typography($p); 33 | @include color-box($p); 34 | @include color-button($p); 35 | @include color-form($p); 36 | @include color-list($p); 37 | @include color-section($p); 38 | @include color-table($p); 39 | @include color-pagination($p); 40 | } 41 | 42 | // Base. 43 | 44 | @import 'base/reset'; 45 | @import 'base/page'; 46 | @import 'base/typography'; 47 | 48 | // Component. 49 | 50 | @import 'components/row'; 51 | @import 'components/box'; 52 | @import 'components/button'; 53 | @import 'components/form'; 54 | @import 'components/icon'; 55 | @import 'components/image'; 56 | @import 'components/actions'; 57 | @import 'components/icons'; 58 | @import 'components/list'; 59 | @import 'components/section'; 60 | @import 'components/table'; 61 | @import 'components/pagination'; 62 | 63 | // Layout. 64 | 65 | @import 'layout/wrapper'; 66 | @import 'layout/intro'; 67 | @import 'layout/header'; 68 | @import 'layout/nav'; 69 | @import 'layout/main'; 70 | @import 'layout/footer'; 71 | @import 'layout/navPanel'; -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | @import 'libs/fixed-grid'; 8 | @import 'font-awesome.min.css'; 9 | 10 | /* 11 | Massively by HTML5 UP 12 | html5up.net | @ajlkn 13 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 14 | */ 15 | 16 | /* Wrapper */ 17 | 18 | #wrapper { 19 | background-color: _palette(wrapper-bg); 20 | background-image: url('../../images/overlay.png'), linear-gradient(0deg, rgba(0,0,0,0.1), rgba(0,0,0,0.1)), url('../../images/bg.jpg'); 21 | background-size: auto, auto, 100% auto; 22 | background-position: center, center, top center; 23 | background-repeat: repeat, no-repeat, no-repeat; 24 | background-attachment: fixed, fixed, fixed; 25 | 26 | &.fade-in { 27 | &:before { 28 | display: none; 29 | } 30 | } 31 | } 32 | 33 | /* Intro */ 34 | 35 | #intro { 36 | body.is-preload & { 37 | opacity: 1; 38 | 39 | &:not(.hidden) { 40 | & + #header + #nav { 41 | @include vendor('transform', 'none'); 42 | opacity: 1; 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /container/pyweb/static/moreassets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreassets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/bg.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/overlay.png -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic01.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic02.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic03.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic04.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic05.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic06.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic07.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic08.jpg -------------------------------------------------------------------------------- /container/pyweb/static/moreimages/pic09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/container/pyweb/static/moreimages/pic09.jpg -------------------------------------------------------------------------------- /container/pyweb/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Michael Levan 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 38 | 39 | 40 |
41 |
42 |

What I Do

43 |

My goal is to translate technical complexity into practical value. I'm a seasoned engineer, consultant, trainer, and content creator in the Kubernetes and Platform Engineering space who spends my time working with startups and enterprises around the globe. I'm also a Microsoft MVP, 4x published author, podcast host, international public speaker, CNCF Ambassador, and was part of the Kubernetes v1.28 and v1.31 Release Team.

44 |
45 |
46 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |

Why?

54 |

I love tech. I spend most of my weekends behind the computer not because I "have to", but because I want to. I really enjoy learning, growing myself, and getting a good understanding of everything that is happening in cloud-native, DevOps, Platform Engineering, and security..

55 |
56 |
57 |
    58 |
  • Lorem
  • 59 |
  • Ipsum
  • 60 |
  • Dolor
  • 61 |
  • Sit
  • 62 |
  • Amet
  • 63 |
  • Nullam
  • 64 |
65 |
66 |
67 | 68 | 69 |
70 |
71 |

Where To Find Me

72 |

You can find me:

73 | https://www.linkedin.com/in/michaellevan/ 74 | https://x.com/TheNJDevOpsGuy 75 | https://bsky.app/profile/mikelevan.bsky.social 76 | https://www.youtube.com/@CloudDevEngineering 77 |
78 |
79 | 80 |
81 |
82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /container/pyweb/templates/index1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% if title %} 4 | {{ title }} - CloudDev 5 | {% else %} 6 | Welcome to CloudDev! 7 | {% endif %} 8 | 9 | 10 |

Hello DevOps/SRE/Platform/Cloud Friends!

11 |

{{name}}

12 | 13 | -------------------------------------------------------------------------------- /container/scanning.md: -------------------------------------------------------------------------------- 1 | There are a few steps that you should think about when securing an application stack that's going to be containerized. The majority of those steps are way before the containerization process even occurs. 2 | 3 | There are tests like: 4 | 1. Security linting 5 | 2. SAST 6 | 3. DAST 7 | 8 | And various tools like Sonarqube and Checkov to help. 9 | 10 | When the security scanning of the code itself is complete and the application is containerized, you can then begin to think about how to scan the container image itself. There are several tools available for this, but one that feels "out of the box" considering the majority of engineers have Docker Desktop running is `docker scout`. 11 | 12 | Docker Scout is built into the Docker Engine and scans container images for security vulnerabilities. 13 | 14 | You can use Docker Scout with: 15 | 1. The Docker Desktop GUI 16 | 2. The CLI 17 | 18 | 1. To use it with the GUI, you'll see a **Scout** button in the GUI. 19 | 20 | 2. You can then choose which image you'd like to scan. 21 | 22 | ![](../images/imageScan1.png) 23 | 24 | 3. Scout works with images that aren't on your local machine as well, so you can, for example, scan the container image that's in your Azure Container Registry. 25 | 26 | ![](../images/imageScan2.png) 27 | 28 | 4. Once scanned, you'll see what vulnerabilities exist and you can run a fix for them as well right from Docker Desktop. 29 | 30 | ![](../images/imageScan3.png) 31 | 32 | 5. Before running the automated fixes, let's do the same in the CLI. That way, we know how to automate the process later if we would like to. The command you'll most likely use is `quickview. 33 | 34 | For example, I can run the `quickview` command against the container image in my ACR. 35 | 36 | ``` 37 | docker scout quickview devopsthehardway.azurecr.io/py:v1 38 | ``` 39 | 40 | Once complete, you'll see an output similiar to the below. 41 | 42 | ``` 43 | i New version 1.16.1 available (installed version is 1.15.1) at https://github.com/docker/scout-cli 44 | ✓ SBOM of image already cached, 582 packages indexed 45 | ✓ Provenance obtained from attestation 46 | 47 | i Base image was auto-detected. To get more accurate results, build images with max-mode provenance attestations. 48 | Review docs.docker.com ↗ for more information. 49 | 50 | Target │ devopsthehardway.azurecr.io/py:v1 │ 1C 3H 11M 97L 4? 51 | digest │ 6c7d79320823 │ 52 | Base image │ python:3 │ 1C 3H 11M 97L 4? 53 | Refreshed base image │ python:3 │ 0C 0H 4M 119L 4? 54 | │ │ -1 -3 -7 +22 55 | Updated base image │ python:alpine │ 0C 0H 0M 0L 56 | │ │ -1 -3 -11 -97 -4 57 | 58 | What's next: 59 | View vulnerabilities → docker scout cves devopsthehardway.azurecr.io/py:v1 60 | View base image update recommendations → docker scout recommendations devopsthehardway.azurecr.io/py:v1 61 | Include policy results in your quickview by supplying an organization → docker scout quickview devopsthehardway.azurecr.io/py:v1 --org 62 | ``` 63 | 64 | You now have a good understanding of where the vulnerabilities exist within your container image. -------------------------------------------------------------------------------- /images/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/account.png -------------------------------------------------------------------------------- /images/imageScan1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/imageScan1.png -------------------------------------------------------------------------------- /images/imageScan2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/imageScan2.png -------------------------------------------------------------------------------- /images/imageScan3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/imageScan3.png -------------------------------------------------------------------------------- /images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/login.png -------------------------------------------------------------------------------- /images/picklogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/picklogin.png -------------------------------------------------------------------------------- /images/pipeline1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline1.png -------------------------------------------------------------------------------- /images/pipeline10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline10.png -------------------------------------------------------------------------------- /images/pipeline2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline2.png -------------------------------------------------------------------------------- /images/pipeline3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline3.png -------------------------------------------------------------------------------- /images/pipeline4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline4.png -------------------------------------------------------------------------------- /images/pipeline5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline5.png -------------------------------------------------------------------------------- /images/pipeline6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline6.png -------------------------------------------------------------------------------- /images/pipeline7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline7.png -------------------------------------------------------------------------------- /images/pipeline8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline8.png -------------------------------------------------------------------------------- /images/pipeline9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/pipeline9.png -------------------------------------------------------------------------------- /images/secret1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/secret1.png -------------------------------------------------------------------------------- /images/secret2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/secret2.png -------------------------------------------------------------------------------- /images/secret3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/secret3.png -------------------------------------------------------------------------------- /images/secret4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/secret4.png -------------------------------------------------------------------------------- /images/secret5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/secret5.png -------------------------------------------------------------------------------- /images/sku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdminTurnedDevOps/DevOps-The-Hard-Way-Azure/9f239c42043f4ba040c0ce7ea7bf1f5111803f09/images/sku.png -------------------------------------------------------------------------------- /prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | ## Prior Knowledge In Tech 4 | 5 | DevOps isn't an entry level role by any means if it's being done correctly. There's a lot of knowledge you need prior, including: 6 | - Some sort of cloud engineering/cloud knowledge experience. Although not all environments are running in the cloud, most of these roles will want it. 7 | - Scripting/automation/programming experience. You don't have to go out and build the next Instagram, but you should understand the basics of programming. 8 | - Network, storage, and compute knowledge. 9 | - Held a prior systems administration, infrastructure engineer, or cloud engineer role. 10 | 11 | ## Azure 12 | 13 | ### Use the Azure CLI 14 | 15 | The Azure CLI is a way for you to interact with all Azure services at a programmatic level using the terminal. 16 | 17 | To set this up, follow the directions [here](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) 18 | 19 | ## Installations 20 | You will need to download some software and services for this tutorial. 21 | 22 | ### Code Editor 23 | 24 | Because code will be written for *DevOps The Hard Way*, you will need a code editor. For the purposes of this tutorial, you can use [Visual Studio Code](https://code.visualstudio.com/download), which is a free code editor. 25 | 26 | ### Terraform 27 | 28 | [Terraform Download](https://www.terraform.io/downloads.html) 29 | 30 | ### Docker 31 | To build the Docker image, you can use Docker Desktop for Windows or MacOS. 32 | 33 | [Docker Desktop](https://www.docker.com/products/docker-desktop) 34 | 35 | ### Source Control 36 | To store the code that you'll be writing, you can create your very own GitHub account to showcase your project. 37 | 38 | [GitHub](https://www.github.com) --------------------------------------------------------------------------------