diff --git a/docs/python-tutorial/chapters/02-intro-to-variables.md b/docs/python-tutorial/chapters/02-intro-to-variables.md
index 9eb965d..8788148 100644
--- a/docs/python-tutorial/chapters/02-intro-to-variables.md
+++ b/docs/python-tutorial/chapters/02-intro-to-variables.md
@@ -86,12 +86,17 @@ Python. There are 4 simple rules which follow:
Here is a list of **invalid** python variables:
```python
-full name = "James Bond" # Contains a space
-age! = 90 # Contains a special character
-finally = "yes" # Conflicts with the Python keyword "finally"
-20money = 900 # Starts with a number
+full name = "James Bond"
+age! = 90
+finally = "yes"
+20money = 900
```
+1. `full name` contains a space in it
+1. `age!` contains a special character (`!`)
+1. `finally` conflicts with the Python keyword of the same name
+1. `20money` starts with a number
+
Below is a list of **valid** python variables:
```python
diff --git a/docs/python-tutorial/chapters/05-string-functions.md b/docs/python-tutorial/chapters/05-string-functions.md
new file mode 100644
index 0000000..b038dd1
--- /dev/null
+++ b/docs/python-tutorial/chapters/05-string-functions.md
@@ -0,0 +1,224 @@
+---
+description: String concatenation and interpolation in Python
+---
+
+# String Interpolation and Concatenation
+
+We learned what the string data type was from our [third chapter](03-data-types-pt01.md). A quick
+recap: Strings are used to represent any textual data.
+
+We also saw how adding two inputs resulted in a different output than we expected.
+
+```py
+num1 = input("Enter the first number: ")
+num2 = input("Enter the second number: ")
+result = num1 + num2
+
+print("The sum of", num1, "and", num2, "is", result)
+```
+
+After running the program:
+
+```text
+Enter the first number: 23
+Enter the second number: 12
+The sum of 23 and 12 is 2312
+```
+
+We briefly touched on the fact that this was caused by _string concatenation_. In this chapter, we
+will look at what concatenation is, and at a more powerful way of combining text and variables
+called _string interpolation_.
+
+## Why do we need this?
+
+Most programs need to build sentences out of a mix of fixed text and changing data. For example,
+greeting a user by name, or showing a player's score. So far, the only way we've done this is by
+passing multiple values to `print`:
+
+```python
+name = "Alan"
+age = 34
+
+print("My name is", name, "and I am", age, "years old.")
+```
+
+This works, but it only works with `print`. What if we want to, for example, store the full sentence
+in a variable instead? This is where concatenation and interpolation come in.
+
+## String Concatenation
+
+Concatenation simply means joining two or more strings together, one after the other. In Python, we
+can do this with the `+` operator.
+
+```python
+first_name = "Alan"
+last_name = "Turing"
+
+full_name = first_name + " " + last_name
+print(full_name)
+```
+
+```text
+Alan Turing
+```
+
+Notice that we added `" "` (a string containing a single space) in the middle. Concatenation joins
+strings _exactly_ as they are, so Python will not add any spaces for us. Without it, we would get
+`AlanTuring` as the output.
+
+### Concatenating with other data types
+
+Concatenation only works between strings. This means that if we try to join a string with a number,
+Python will raise an error:
+
+```python
+name = "Alan"
+age = 34
+
+print("My name is " + name + " and I am " + age + " years old.")
+```
+
+```text
+TypeError: can only concatenate str (not "int") to str
+```
+
+Python doesn't know whether `"I am " + 34` should mean "add" or "join", so instead of guessing, it
+stops with an error. To fix it, we need to convert the number to a string first. Remember type
+casting from the previous chapter? We can use the `str` function for this:
+
+```python
+name = "Alan"
+age = 34
+
+print("My name is " + name + " and I am " + str(age) + " years old.")
+```
+
+```text
+My name is Alan and I am 34 years old.
+```
+
+## String Interpolation
+
+Interpolation is the process of inserting values directly into a string. Instead of breaking the
+string apart and gluing the pieces back together, we write the whole sentence at once and mark the
+places where the values should go. Python gives us two ways of doing this.
+
+### f-strings
+
+An f-string (short for _formatted string literal_) is a string with an `f` placed right before the
+opening quote. Inside it, anything wrapped in curly braces `{}` is replaced with its value.
+
+```python
+name = "Alan"
+age = 34
+
+print(f"My name is {name} and I am {age} years old.")
+```
+
+```text
+My name is Alan and I am 34 years old.
+```
+
+That's the same output as before, but much easier to read and write! Also notice that we didn't need
+to use `str(age)`. This is because f-strings convert the values for us automatically.
+
+!!! warning
+
+ Don't forget the `f` before the quote! Without it, Python treats the string as normal text and
+ prints the curly braces as-is: `My name is {name} and I am {age} years old.`
+
+#### Expressions inside f-strings
+
+You can put any valid Python expression (not just variable names) inside the curly braces, and
+Python will work out the result:
+
+```python
+price = 20
+quantity = 3
+
+print(f"Total cost: {price * quantity}")
+```
+
+```text
+Total cost: 60
+```
+
+#### Formatting values
+
+You can also control how a value is displayed by adding a colon `:` followed by a format
+specification inside the braces. One of the most common uses of this is limiting the number of
+decimal places of a float:
+
+```python
+pi = 3.14159265
+
+print(f"Pi is roughly {pi:.2f}")
+```
+
+```text
+Pi is roughly 3.14
+```
+
+Here, `.2f` means "show this as a float with 2 digits after the decimal point".
+
+### The `str.format()` method
+
+Before f-strings were introduced in Python 3.6, the most common way to interpolate was the `format`
+method of strings. You will still see this in a lot of older code, so it is worth knowing.
+
+Instead of placing the variables inside the string, you leave empty curly braces `{}` as
+placeholders, and pass the values to `.format()` afterwards. The values are filled in order like so:
+
+```python
+name = "Alan"
+age = 34
+
+print("My name is {} and I am {} years old.".format(name, age))
+```
+
+```text
+My name is Alan and I am 34 years old.
+```
+
+You can also number the placeholders to control which value goes where, or give them names:
+
+```python
+# Using positions
+print("{1} is {0} years old.".format(age, name))
+
+# Using names
+print("{n} is {a} years old.".format(n=name, a=age))
+```
+
+!!! note
+
+ Anything after a `#` on a line is a comment. Comments are notes for humans reading the code, so
+ Python ignores them completely.
+
+It's output:
+
+```text
+Alan is 34 years old.
+Alan is 34 years old.
+```
+
+## Which one should I use?
+
+All three approaches produce the same kind of result: a new string. Here's a quick comparison using
+the same example.
+
+```python
+name = "Alan"
+age = 34
+
+print("My name is " + name + " and I am " + str(age) + " years old.") # concatenation
+print("My name is {} and I am {} years old.".format(name, age)) # str.format()
+print(f"My name is {name} and I am {age} years old.") # f-string
+```
+
+Here are some general rules for their usage.
+
+- Use **f-string** interpolation for most situations. They are the shortest, the most readable, and
+ the most common way to write modern Python.
+- Use **concatenation (`+`)** for simple cases where you want to join two strings only.
+- **`str.format()`** when you are reading older code.
diff --git a/docs/python-tutorial/chapters/06-operators-pt01.md b/docs/python-tutorial/chapters/06-operators-pt01.md
new file mode 100644
index 0000000..f18fbc0
--- /dev/null
+++ b/docs/python-tutorial/chapters/06-operators-pt01.md
@@ -0,0 +1,445 @@
+---
+description: Comparison and Arithmetic Operators in Python.
+---
+
+# Comparison and Arithmetic Operators
+
+A quick revision of the concepts we have learned so far:
+
+- Brief on Python history
+- Variables
+- Data types (strings, integers, floats, booleans)
+- Built in functions
+- String concatenation & interpolation
+
+In this chapter, we will be looking at what comparison and arithmetic operations are.
+
+## Comparison Operators
+
+Comparison Operators (also known as Relational Operators) are used to compare values. They work very
+similarly to how they do in mathematics.
+
+There are six comparison operators:
+
+| Operator Symbol | Name |
+| ---------------- | ----------------------------- |
+| `>` | Greater than |
+| `<` | Less than |
+| `==` | Equal to |
+| `!=` | Not equal to |
+| `>=` | Greater than or equal To |
+| `<=` | Less than or equal to |
+
+Comparison operators always return a boolean value (`True` or `False`) when used.
+
+
+
+{ width="640" }
+
+
+ Either side of an operator is what we call an operand. An operator is a symbol that performs an action on values, called operands.
+
+
+
+
+### Greater Than Operator (`>`)
+
+Checks if the left-hand side operand is larger than the right-hand side operand.
+
+```python
+print(1 > 2)
+print(20 > 1)
+print(3.2 > 9)
+```
+
+Running this will give us the following output: ```text False True False ```
+
+This reads almost like plain English! 1 is not greater than 2, hence it returns `False` and prints
+it out. 20 is larger than 1, which results in `True` being the output, and so on.
+
+### Less Than Operator (`<`)
+
+Checks if the right-hand side operand is larger than the left-hand side operand.
+
+```python
+print(3 < 9)
+print(200 < 100)
+print(23.12 < 9)
+```
+
+Output:
+
+```text
+True
+False
+False
+```
+
+### Equal To Operator (`==`)
+
+Checks if both left and right-hand side operands are exactly equal.
+
+```python
+print(123 == 321)
+print(909 == 909)
+print(812 == 22)
+```
+
+Output:
+
+```text
+False
+True
+False
+```
+
+!!! warning
+
+ Do not confuse the Equal To Operator (`==`) with the Assignment Operator (`=`). The Equal To
+ Operator is used to check if both the operands are equal, whereas the Assignment Operator is used to
+ assign data to a variable.
+
+### Not Equal To Operator (`!=`)
+
+Checks if both the operands are _not_ equal.
+
+```python
+print(10 != 10)
+print(3 != 4)
+print(3 != 8)
+```
+
+Output:
+
+```text
+False
+True
+True
+```
+
+### Greater Than Or Equal To Operator (`>=`)
+
+Checks if the left-hand side operand is either greater than or equal to the right-hand side operand.
+
+```python
+print(90 >= 18)
+print(23 >= 32)
+print(1 >= 0)
+```
+
+Output:
+
+```text
+True
+False
+True
+```
+
+Here, 90 is greater than 18 which gives us `True` as the output. 23 is not greater but it is equal
+to 23 which also gives us `True` as the output.
+
+### Less Than Or Equal To Operator (`<=`)
+
+Checks if the right-hand side operand is either greater than or equal to the left-hand side operand.
+
+```python
+print(4 <= 18)
+print(9 <= 3)
+print(10 <= 10)
+```
+
+Output:
+
+```text
+True
+False
+True
+```
+
+### Comparison with Strings
+
+Comparison operators aren't limited to numbers. They work with strings too. The most common use is
+checking whether two strings are equal:
+
+```python
+print("Alan" == "Alan")
+print("Alan" != "James")
+```
+
+Output:
+
+```text
+True
+True
+```
+
+!!! warning
+
+ String comparison is case sensitive, so `"alan"` and `"Alan"` are **not** equal:
+
+```python
+print("alan" == "Alan")
+```
+
+Output:
+
+```text
+False
+```
+
+Python compares strings character by character, so even a single difference, including an extra
+space, makes the strings unequal.
+
+??? note "Using `<` and `>` with strings"
+
+ The other operators work on strings as well. Python compares strings alphabetically, character by
+ character, so `"apple" < "banana"` is `True` because `"a"` comes before `"b"`.
+
+ Be careful with uppercase and lowercase, though. Every uppercase letter is considered "smaller" than
+ every lowercase letter, so `"Zebra" < "apple"` is `True`.
+
+## Arithmetic Operators
+
+Arithmetic operators let us perform mathematical calculations in Python. You've already seen one of
+them, the `+` operator, in our calculator example. Python supports seven of them:
+
+| Operator | Name | Example | Result |
+| -------- | -------------- | -------- | ------ |
+| `+` | Addition | `7 + 2` | `9` |
+| `-` | Subtraction | `7 - 2` | `5` |
+| `*` | Multiplication | `7 * 2` | `14` |
+| `/` | Division | `7 / 2` | `3.5` |
+| `//` | Floor Division | `7 // 2` | `3` |
+| `%` | Modulus | `7 % 2` | `1` |
+| `**` | Exponentiation | `7 ** 2` | `49` |
+
+Let's take a look at each of them.
+
+### Addition, Subtraction & Multiplication
+
+These three work exactly as they do in mathematics:
+
+```python
+apples = 10
+oranges = 4
+
+print(apples + oranges)
+print(apples - oranges)
+print(apples * oranges)
+```
+
+```text
+14
+6
+40
+```
+
+!!! note
+
+ Notice that we use `*` for multiplication, not `x`. The same goes for division, where we use `/`
+ instead of `÷`.
+
+### Division
+
+The `/` operator divides the left value by the right value:
+
+```python
+print(10 / 4)
+print(10 / 5)
+```
+
+```text
+2.5
+2.0
+```
+
+Notice that the second result is `2.0` and not `2`. In Python, the `/` operator **always** returns a
+float, even when the result is a whole number.
+
+!!! warning
+
+ Dividing by zero is not possible, in Python or in mathematics. If you try, Python will stop your
+ program with a `ZeroDivisionError`.
+
+### Floor Division
+
+Floor division (`//`) divides the left value by the right value, and then rounds the result **down**
+to the nearest whole number. In other words, it throws away the decimal part:
+
+```python
+print(7 // 2)
+print(10 // 3)
+```
+
+```text
+3
+3
+```
+
+This is useful when you only care about how many _complete_ times a number fits into another. For
+example, how many full hours are in 150 minutes:
+
+```python
+minutes = 150
+print(minutes // 60)
+```
+
+Output:
+
+```text
+2
+```
+
+??? note "Floor division and negative numbers"
+
+ Floor division always rounds **down**, toward negative infinity. With positive numbers this looks
+ like the decimal part is simply cut off, but with negative numbers it behaves differently:
+
+ ```python
+ print(7 // 2)
+ print(-7 // 2)
+ ```
+
+ Output:
+
+ ```text
+ 3
+ -4
+ ```
+
+ `-7 / 2` is `-3.5`. Rounding down means moving to the next smaller number, which is `-4`, not `-3`.
+
+### Modulus
+
+The modulus operator (`%`) gives us the **remainder** left over after division. Remember long
+division from school? `7 ÷ 2` is `3` with a remainder of `1`, so `7 % 2` is `1`:
+
+```python
+print(7 % 2)
+print(10 % 3)
+print(10 % 5)
+```
+
+```text
+1
+1
+0
+```
+
+If we continue our minutes example, `%` tells us how many minutes are _left over_ after taking out
+the full hours:
+
+```python
+minutes = 150
+print(minutes // 60) # full hours
+print(minutes % 60) # leftover minutes
+```
+
+```text
+2
+30
+```
+
+So 150 minutes is 2 hours and 30 minutes.
+
+### Exponentiation
+
+The `**` operator raises the left value to the power of the right value:
+
+```python
+print(2 ** 3)
+print(5 ** 2)
+```
+
+```text
+8
+25
+```
+
+`2 ** 3` means `2 * 2 * 2`, which is `8`.
+
+## Order of Operations
+
+When an expression has more than one operator, Python follows the same order of operations you
+learned in mathematics (often remembered as PEMDAS or BODMAS). Operators with the same priority are
+evaluated from left to right. Let's see it in action:
+
+```python
+print(2 + 3 * 4)
+print((2 + 3) * 4)
+```
+
+```text
+14
+20
+```
+
+In the first line, multiplication happens before addition, so `3 * 4` is calculated first, giving
+`2 + 12`. In the second line, the parentheses force `2 + 3` to be calculated first, giving `5 * 4`.
+
+!!! tip
+
+ When in doubt, use parentheses. Even if they aren't needed, they make your code easier to read and
+ make sure the calculation happens in the order you intended.
+
+## Mixing Integers and Floats
+
+When you do arithmetic between an integer and a float, the result will always be a float:
+
+```python
+print(5 + 2.0)
+print(3 * 1.5)
+```
+
+```text
+7.0
+4.5
+```
+
+## In-place Operations
+
+It's very common to update a variable using its own value, such as adding to a score:
+
+```python
+score = 10
+score = score + 5
+print(score)
+```
+
+```text
+15
+```
+
+Python provides a shorter way of writing this. Placing the operator before the `=` does the same
+thing:
+
+```python
+score = 10
+score += 5
+print(score)
+```
+
+```text
+15
+```
+
+`score += 5` is exactly the same as `score = score + 5`. This works with all of the arithmetic
+operators:
+
+```python
+number = 100
+
+number -= 20 # number = number - 20
+number *= 2 # number = number * 2
+number /= 4 # number = number / 4
+number %= 10 # number = number %= 10
+number += 1 # number = number + 1
+
+print(number)
+```
+
+Output:
+
+```text
+1.0
+```
diff --git a/docs/python-tutorial/chapters/07-conditional-statements.md b/docs/python-tutorial/chapters/07-conditional-statements.md
new file mode 100644
index 0000000..bba9c2d
--- /dev/null
+++ b/docs/python-tutorial/chapters/07-conditional-statements.md
@@ -0,0 +1,703 @@
+---
+description: Conditional Statements and Control Flow in Python.
+---
+
+# Conditional Statements
+
+When developing programs, we often need our code to execute certain instructions _conditionally_.
+Let's take a simple banking program as an example.
+
+Our task is to ask the user how much money they have so they can open a bank account. If they have
+less than `$25`, we print a message declining the request. If they have `$25` or more, we accept the
+money and deposit it into their new account.
+
+One way of doing this problem is like so:
+
+```python
+bank_amount = 0
+
+print("Welcome to Practical Python Bank!")
+print("Let's open you an account!")
+
+current_amount = int(input("How much money do you have (in dollars)? "))
+# Don't forget to convert user input to an integer
+# so we can compare it with another integer.
+
+if current_amount >= 25:
+ bank_amount += current_amount
+ current_amount = 0
+
+ print("You have successfully opened a bank account!")
+ print("All your money has been deposited.")
+ print(f"You have ${bank_amount} in your account.")
+
+else:
+ print("Sorry, you cannot open an account.")
+ print("You need at least $25 to open a bank account.")
+```
+
+Running the program and entering a number below 25 gives us:
+
+```
+Welcome to Practical Python Bank!
+Let's open you an account!
+How much money do you have (in dollars)? 10
+Sorry, you cannot open an account.
+You need at least $25 to open a bank account.
+```
+
+Now let's enter a value of 25 or more:
+
+```
+Welcome to Practical Python Bank!
+Let's open you an account!
+How much money do you have (in dollars)? 25
+You have successfully opened a bank account!
+All your money has been deposited.
+You have $25 in your account.
+```
+
+!!! warning
+
+ When entering input, type only the number itself, with no `$` symbol or other characters. The
+ `int()` function can only convert strings that look like whole numbers (such as `"25"`), so text or
+ symbols (such as `"$25"` or `"twenty"`) will make it fail with an error. The same applies to
+ `float()`, which accepts numbers like `"12.5"`.
+
+The code almost reads like plain English! If you don't understand what's going on immediately, it's
+alright. We'll take it apart and see what each piece is doing. First, the `if` statement.
+
+## `if` Condition Statement
+
+This is the starting block of any conditional statement. Its general syntax is:
+
+```
+if :
+
+```
+
+!!! note
+
+ In programming, syntax refers to the grammar of a language. Just as grammar lets us form coherent
+ sentences in human languages, syntax tells the computer how to read our code.
+
+The `if` condition statement only runs the `` within it when the `` is `True`. Here's
+an example:
+
+```python
+if True:
+ print("Hi")
+ print("Running from the True statement")
+ print("Good bye!")
+
+if False:
+ print("Hello")
+ print("Running from the False statement")
+ print("See you later!")
+```
+
+Upon running this code, we get the following output:
+
+```text
+Hi
+Running from the True statement
+Good bye!
+```
+
+The code within the `if False:` statement never ran.
+
+One important thing you may have noticed so far is the indentation (the four spaces in front of each
+`print` calls). In Python, indentation is what tells the computer that "this block of code belongs
+to this statement".
+
+
+
+{ width="640" }
+
+
+
+A quick way to remember when you need to apply indentation is to look out for colons (`:`). If a
+statement ends with a colon, it means that the next line must be indented one level further. Python
+recommends 1 indent to consist of 4 spaces.
+
+!!! warning
+
+ You can press the ++tab++ key to place indents in your code. Depending on the code editor you use,
+ it can either place spaces or a single tab character. If your editor places a tab character, make
+ sure to change it to spaces.
+
+From the previous chapter, we have learned about how comparison operators return a bool. The same
+concept is being used here. Let's take a look at an example and an animation that explains how it
+works.
+
+```python
+print("Program start")
+
+age = 15
+
+if age >= 18:
+ print("Is an adult")
+
+if age >= 13:
+ print("Is a teenager")
+
+print("Program end")
+```
+
+When you run your code, this is how Python will interpret it:
+
+
+
+{ width="640" }
+
+
+
+```text
+Program start
+Is a teenager
+Program end
+```
+
+!!! note
+
+ This program isn't perfect. There are some glaring issues; can you notice them before we fix it in
+ the next subtopics?
+
+## `else` Condition Statement
+
+The else statement runs when the condition of the if directly above it is False. Its general syntax
+is:
+
+```text
+if :
+
+else:
+
+```
+
+`` only executes when the `` of the `if` statement is `False`. Let's look at a few
+examples to get familiar with it.
+
+=== "Example 1"
+
+ ```python
+ if False:
+ print("if statement executed")
+ else:
+ print("else statement executed")
+ ```
+
+ Output:
+
+ ```text
+ else statement executed
+ ```
+
+=== "Example 2"
+
+ ```python
+ if False:
+ print("if statement 1 executed")
+ if False:
+ print("if statement 2 executed")
+ else:
+ print("else statement executed")
+ ```
+
+ Output:
+
+ ```text
+ else statement executed
+ ```
+=== "Example 3"
+
+ ```python
+ if True:
+ print("if statement 1 executed")
+ if False:
+ print("if statement 2 executed")
+ else:
+ print("else statement executed")
+ ```
+
+ Output:
+
+ ```text
+ if statement 1 executed
+ else statement executed
+ ```
+
+=== "Example 4"
+
+ ```python
+ if True:
+ print("if statement 1 executed")
+ if True:
+ print("if statement 2 executed")
+ else:
+ print("else statement executed")
+ ```
+
+ Output:
+
+ ```text
+ if statement 1 executed
+ if statement 2 executed
+ ```
+
+=== "Example 5"
+
+ ```python
+ if 5 > 1:
+ print("Five is greater than one")
+ else:
+ print("Five is not greater than one")
+ ```
+
+ Output:
+
+ ```text
+ Five is greater than one
+ ```
+
+---
+
+!!! note
+
+ An `else` always belongs to the `if` directly above it, and it can't stand on its own. Writing an
+ `else` without an `if` before it raises a `SyntaxError`.
+
+Now that we are familiar with its syntax, let's take a look at our example from the previous
+section.
+
+```python
+print("Program start")
+
+age = 15
+
+if age >= 18:
+ print("Is an adult")
+
+if age >= 13:
+ print("Is a teenager")
+
+print("Program end")
+```
+
+You may have noticed a few problems with it. If not, assume `age` is `7` instead. What would be the
+output then? Once you have figured it out, check your answer in the collapsible block below:
+
+??? note "Output"
+
+ ```text
+ Program start
+ Program end
+ ```
+
+### Fixing our age program (v1)
+
+As you can see, that is _not_ the output we'd like. We would still want the program to print
+_something_. This is where our `else` statement can help us.
+
+```python
+print("Program start")
+
+age = 7
+
+if age >= 18:
+ print("Is an adult")
+
+if age >= 13:
+ print("Is a teenager")
+
+else:
+ print("Is below 13")
+
+print("Program end")
+```
+
+Output:
+
+```text
+Program start
+Is below 13
+Program end
+```
+
+Our program still isn't perfect. Try finding the issue by tweaking the value of the `age` variable.
+
+## Nesting Conditional Statements
+
+An `if` or `else` block can contain any code, including other `if` and `else` statements. Placing a
+conditional statement inside another one is called **nesting**. The inner statement only gets
+checked if the outer one allows its block to run.
+
+Our previous example still had one problem in it. What if the `age` variable was, let's say, 25?
+
+```python
+print("Program start")
+
+age = 25
+
+if age >= 18:
+ print("Is an adult")
+
+if age >= 13:
+ print("Is a teenager")
+
+else:
+ print("Is below 13")
+
+print("Program end")
+```
+
+Its output would be:
+
+```text
+Program start
+Is an adult
+Is a teenager
+Program end
+```
+
+This is wrong again. We would like the program to only print out `Is an adult` for ages `18` and
+above. But that doesn't seem to be the case since the age satisfies both conditions.
+
+Let's look at how nested conditional statements helps solve this with different values.
+
+### Fixing our age program (v2)
+
+=== "For age = 25"
+
+ ```python
+ print("Program start")
+
+ age = 25
+
+ if age >= 18:
+ print("Is an adult")
+ else:
+ if age >= 13:
+ print("Is a teenager")
+ else:
+ print("Is below 13")
+
+ print("Program end")
+ ```
+
+
+
+ { width="640" }
+
+
+
+ Gives us the output:
+
+ ```
+ Program start
+ Is an adult
+ Program end
+ ```
+
+=== "For age = 15"
+
+ ```python
+ print("Program start")
+
+ age = 15
+
+ if age >= 18:
+ print("Is an adult")
+ else:
+ if age >= 13:
+ print("Is a teenager")
+ else:
+ print("Is below 13")
+
+ print("Program end")
+ ```
+
+
+
+ { width="640" }
+
+
+
+ Output:
+
+ ```
+ Program start
+ Is a teenager
+ Program end
+ ```
+
+---
+
+!!! note
+
+ There's no limit to how deeply you can nest statements, but each level adds more indentation and
+ makes the code harder to follow. If you find yourself nesting more than two or three levels deep,
+ there is usually a cleaner way to write it. We'll look at one in the next section.
+
+## `elif` Statement
+
+The `elif` statement is a combination of `else` and `if` with a cleaner syntax. As always, let's
+have a look at its syntax:
+
+```text
+if :
+
+elif :
+
+```
+
+`` only executes when the `` of the `if` is `False` **and** the `` of the
+`elif` is `True`.
+
+In a series of regular `if` statements, each statement is independent of the others. But `if`- and
+`elif`-statements are dependent on each other. This is because the pseudocode above is the same as:
+
+```text
+if :
+
+else:
+ if :
+
+```
+
+Remember the `else:` followed by an indented `if` from our nested example? `elif` is just a shorter
+way of writing it.
+
+### Fixing our age program (v3)
+
+Let's rewrite our age program using `elif`:
+
+```python
+print("Program start")
+
+age = 25
+
+if age >= 18:
+ print("Is an adult")
+elif age >= 13:
+ print("Is a teenager")
+else:
+ print("Is below 13")
+
+print("Program end")
+```
+
+This does exactly the same thing as our nested version, but with no extra indentation.
+
+Take `age = 25`. Python checks `age >= 18`, finds it `True`, and runs that block. It then skips the
+`elif` and `else` entirely, without even checking `age >= 13`. With `age = 15`, the first condition
+is `False`, so Python moves on to the `elif`, which is `True`, so it runs that block and skips the
+`else`. Only when every condition above is `False` does the `else` run.
+
+### Using multiple `elif` statements
+
+You can chain as many `elif` statements as you need between the `if` and the `else`:
+
+```text
+if :
+
+elif :
+
+elif :
+
+else:
+
+```
+
+Python checks the conditions from top to bottom, and runs the block of the **first** condition that
+is `True`. Every other block in the chain is skipped, even if its condition would also have been
+`True`. Let's take a grading program as an example:
+
+```python
+score = 85
+
+if score >= 90:
+ print("Grade: A")
+elif score >= 80:
+ print("Grade: B")
+elif score >= 70:
+ print("Grade: C")
+else:
+ print("Grade: F")
+```
+
+Output:
+
+```text
+Grade: B
+```
+
+A score of `85` is also greater than or equal to `70`, but Python never gets that far. It stops at
+the first `True` condition, `score >= 80`.
+
+!!! warning
+
+ Because Python stops at the first `True` condition, the **order** of your conditions matters. What
+ do you think happens if we check the smaller age first?
+
+ ```python
+ age = 25
+
+ if age >= 13:
+ print("Is a teenager")
+ elif age >= 18:
+ print("Is an adult")
+ ```
+
+ Output:
+
+ ```text
+ Is a teenager
+ ```
+
+ `25 >= 13` is `True`, so Python runs the first block and never reaches `age >= 18`. The adult block
+ can never run for any value. When using ranges like this, check the most specific (or highest)
+ condition first.
+
+### `if` vs `elif`
+
+Let's compare the two with the same value, `age = 25`:
+
+=== "Separate `if` statements"
+
+ ```python
+ age = 25
+
+ if age >= 18:
+ print("Is an adult")
+ if age >= 13:
+ print("Is a teenager")
+ ```
+
+ Output:
+
+ ```text
+ Is an adult
+ Is a teenager
+ ```
+
+=== "`if` and `elif`"
+
+ ```python
+ age = 25
+
+ if age >= 18:
+ print("Is an adult")
+ elif age >= 13:
+ print("Is a teenager")
+ ```
+
+ Output:
+
+ ```text
+ Is an adult
+ ```
+
+---
+
+With separate `if` statements, every condition is checked on its own, so more than one block can
+run. With `elif`, the chain stops after the first match. Use separate `if` statements when the
+checks are unrelated, and `elif` when you want exactly one outcome out of several.
+
+!!! note
+
+ A conditional chain follows these rules:
+
+ - It must start with exactly one `if`.
+ - It can have as many `elif` statements as you need, all placed after the `if`.
+ - It can end with at most one `else`, which is optional and always comes last.
+
+ An `elif` or `else` without an `if` before it raises a `SyntaxError`.
+
+## Truthy and Falsy Values
+
+In Python, every value can be treated as a bool. Values that aren't actually a bool are considered
+either truthy (they behave like `True`) or falsy (they behave like `False`). Let's take a
+look at what exactly makes a value one or the other.
+
+Any non-empty string, or in the case of integers and floats, any number other than `0`, is
+considered truthy.
+
+The opposite is true for falsy values. An empty string, or the number `0` (or `0.0`), is considered
+falsy.
+
+We can confirm this by using the `bool()` function. It takes in a value of any data type and
+converts it into a bool, following the rules above. This is type casting again, just like `int()`
+and `str()`.
+
+```python
+print(bool(100))
+print(bool(-330))
+print(bool(0))
+
+print() # Prints out an empty line
+
+print(bool(0.0))
+print(bool(0.1))
+
+print()
+
+print(bool("This is a non-empty string."))
+print(bool(""))
+print(bool("0")) # This is a string, and it's non-empty.
+```
+
+Its output is as follows:
+
+```text
+True
+True
+False
+
+False
+True
+
+True
+False
+True
+```
+
+!!! warning
+
+ The string `"0"` is **truthy**, even though the number `0` is falsy. Only a completely empty
+ string (`""`) is falsy. The same goes for `"False"`, which is just text, and a string containing
+ only a space (`" "`).
+
+### Using truthy and falsy values in conditions
+
+So far, we have placed bools and comparisons (like `age >= 18`) after an `if`. But an `if` statement
+actually accepts _any_ value and implicitly converts it into a bool. This lets us skip the comparison
+in some cases.
+
+A common use is checking whether the user typed anything at all:
+
+```python
+name = input("Enter your name: ")
+
+if name:
+ print(f"Hello, {name}!")
+else:
+ print("You didn't enter a name.")
+```
+
+If the user types `Alan`, `name` is `"Alan"`, which is a non-empty string and therefore, truthy:
+
+```text
+Enter your name: Alan
+Hello, Alan!
+```
+
+If the user just presses ++enter++, `name` is `""`, which is falsy, so the `else` block runs:
+
+```text
+Enter your name:
+You didn't enter a name.
+```
+
+This is the same as writing `if name != "":`, only shorter. Both are correct, so use whichever you
+find easier to read.
diff --git a/docs/python-tutorial/chapters/index.md b/docs/python-tutorial/chapters/index.md
index 7a6f00c..efb24c5 100644
--- a/docs/python-tutorial/chapters/index.md
+++ b/docs/python-tutorial/chapters/index.md
@@ -12,10 +12,10 @@ in-depth Python features.
- [Brief Python History](01-python-history.md)
- [Introduction to Variables](02-intro-to-variables.md)
- [Data Types pt 1 (`string`, `int`, `float` & `bool`)](03-data-types-pt01.md)
-- [Builtin Functions pt 1 (`print` & `input` and type casting)](04-built-in-functions-pt01.md)
-- String Interpolation & Concatenation (f-strings, str.format & +)
-- Operators pt 1 (Comparison & Arithmetic)
-- Conditional Statements (`if`, `else` & `elif`)
+- [Builtin Functions pt 1 (`print`, `input` & type casting)](04-built-in-functions-pt01.md)
+- [String Interpolation & Concatenation (f-strings, `str.format` & `+`)](05-string-functions.md)
+- [Operators pt 1 (Comparison & Arithmetic)](06-operators-pt01.md)
+- [Conditional Statements (`if`, `else` & `elif`)](07-conditional-statements.md)
- Operators & Conditional Statements pt 2 (Logical operators)
- Data Types pt 2 (`list` & `tuple`)
- Data Types pt 3 (dictionary)
diff --git a/docs/static/python-tutorial/02-memory.png b/docs/static/python-tutorial/02-memory.png
index bbd145b..e4f1031 100644
Binary files a/docs/static/python-tutorial/02-memory.png and b/docs/static/python-tutorial/02-memory.png differ
diff --git a/docs/static/python-tutorial/06-operators.png b/docs/static/python-tutorial/06-operators.png
new file mode 100644
index 0000000..9eb8932
Binary files /dev/null and b/docs/static/python-tutorial/06-operators.png differ
diff --git a/docs/static/python-tutorial/07-conditional-statements-01.png b/docs/static/python-tutorial/07-conditional-statements-01.png
new file mode 100644
index 0000000..78f2761
Binary files /dev/null and b/docs/static/python-tutorial/07-conditional-statements-01.png differ
diff --git a/docs/static/python-tutorial/07-conditional-statements-02.gif b/docs/static/python-tutorial/07-conditional-statements-02.gif
new file mode 100644
index 0000000..b9a6085
Binary files /dev/null and b/docs/static/python-tutorial/07-conditional-statements-02.gif differ
diff --git a/docs/static/python-tutorial/07-conditional-statements-03.gif b/docs/static/python-tutorial/07-conditional-statements-03.gif
new file mode 100644
index 0000000..ce71136
Binary files /dev/null and b/docs/static/python-tutorial/07-conditional-statements-03.gif differ
diff --git a/docs/static/python-tutorial/07-conditional-statements-04.gif b/docs/static/python-tutorial/07-conditional-statements-04.gif
new file mode 100644
index 0000000..1504032
Binary files /dev/null and b/docs/static/python-tutorial/07-conditional-statements-04.gif differ
diff --git a/zensical.toml b/zensical.toml
index 74bf513..2773037 100644
--- a/zensical.toml
+++ b/zensical.toml
@@ -25,6 +25,9 @@ nav = [
"python-tutorial/chapters/02-intro-to-variables.md",
"python-tutorial/chapters/03-data-types-pt01.md",
"python-tutorial/chapters/04-built-in-functions-pt01.md",
+ "python-tutorial/chapters/05-string-functions.md",
+ "python-tutorial/chapters/06-operators-pt01.md",
+ "python-tutorial/chapters/07-conditional-statements.md",
] },
{ "Resources" = [
"resources/index.md",