diff --git a/rv_voting_calc/templates/rv_voting_calc/combined_steps.html b/rv_voting_calc/templates/rv_voting_calc/combined_steps.html
new file mode 100644
index 0000000000000000000000000000000000000000..394215e538e4840986e5b5e7302a7771bbcaacbe
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/combined_steps.html
@@ -0,0 +1,15 @@
+{% comment %}
+    It's fine to pass in |safe here, as the template variables have
+    already been sanitized once, as each step got rendered.
+{% endcomment %}
+
+{{ html_steps|safe }}
+
+<div id="md-steps" class="hidden">
+    {% for md_step in md_steps %}
+{{ md_step }}{% if not forloop.last %}
+
+---
+
+{% endif %}{% endfor %}
+</div>
diff --git a/rv_voting_calc/templates/rv_voting_calc/steps/found_popularity_winner.html b/rv_voting_calc/templates/rv_voting_calc/html_steps/found_popularity_winner.html
similarity index 100%
rename from rv_voting_calc/templates/rv_voting_calc/steps/found_popularity_winner.html
rename to rv_voting_calc/templates/rv_voting_calc/html_steps/found_popularity_winner.html
diff --git a/rv_voting_calc/templates/rv_voting_calc/steps/initial_sort.html b/rv_voting_calc/templates/rv_voting_calc/html_steps/initial_sort.html
similarity index 100%
rename from rv_voting_calc/templates/rv_voting_calc/steps/initial_sort.html
rename to rv_voting_calc/templates/rv_voting_calc/html_steps/initial_sort.html
diff --git a/rv_voting_calc/templates/rv_voting_calc/steps/no_winner.html b/rv_voting_calc/templates/rv_voting_calc/html_steps/no_winner.html
similarity index 100%
rename from rv_voting_calc/templates/rv_voting_calc/steps/no_winner.html
rename to rv_voting_calc/templates/rv_voting_calc/html_steps/no_winner.html
diff --git a/rv_voting_calc/templates/rv_voting_calc/steps/removed_option.html b/rv_voting_calc/templates/rv_voting_calc/html_steps/removed_option.html
similarity index 100%
rename from rv_voting_calc/templates/rv_voting_calc/steps/removed_option.html
rename to rv_voting_calc/templates/rv_voting_calc/html_steps/removed_option.html
diff --git a/rv_voting_calc/templates/rv_voting_calc/steps/with_support.html b/rv_voting_calc/templates/rv_voting_calc/html_steps/with_support.html
similarity index 73%
rename from rv_voting_calc/templates/rv_voting_calc/steps/with_support.html
rename to rv_voting_calc/templates/rv_voting_calc/html_steps/with_support.html
index f40b5dfee2e03d8f9fac30fe51fdbee34ee2fdab..6f3367a1c992d8cb47f9a8b1a1181d980756930b 100644
--- a/rv_voting_calc/templates/rv_voting_calc/steps/with_support.html
+++ b/rv_voting_calc/templates/rv_voting_calc/html_steps/with_support.html
@@ -3,9 +3,9 @@
         <h2 class="text-2xl font-semibold font-bebas">Po vyřazení možností bez nadpoloviční podpory</h2>
     </div>
 
-    {% if options|length != 0 %}
+    {% if options_with_support_count != 0 %}
         {% include "rv_voting_calc/includes/option_list.html" with options=options options_by_member=options_by_member %}
     {% else %}
-        <span class="text-gray-800 mt-1">Žádné možnosti nemají nadpoloviční podporu.</span>
+        <p class="text-gray-800 mt-3">Žádné možnosti nemají nadpoloviční podporu.</p>
     {% endif %}
 </li>
diff --git a/rv_voting_calc/templates/rv_voting_calc/includes/option_list.md b/rv_voting_calc/templates/rv_voting_calc/includes/option_list.md
new file mode 100644
index 0000000000000000000000000000000000000000..d605fed05c80226347bbae7a1512f567d7af5f88
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/includes/option_list.md
@@ -0,0 +1,6 @@
+{% load index %}
+
+{% for option_key, option in options.items %}{% if not option.marked_for_deletion or show_marked_for_deletion %}{% if option.has_support or show_has_support %}
+-  **Možnost {{ option_key }}** - {{ option.ticket_count }} lístků, {{ option.vote_count }} hlasů.{% if show_has_support and not option.has_support %} *(Nemá nadpoloviční podporu)*{% endif %}{% if option.marked_for_deletion or not option.has_support %} *(K odstranění)*{% endif %}{% if option.ticket_votes|length != 0 %} Volby: {% for vote in option.ticket_votes %}{% if not vote.hidden %}
+    - **{{ rv_members|index:vote.member|index:"displayName" }}** - přijatelné možnosti:{% for option in options_by_member|index:vote.member %}
+        - {{ option }}{% endfor %}{% endif %}{% endfor %}{% endif %}{% endif %}{% endif %}{% endfor %}
diff --git a/rv_voting_calc/templates/rv_voting_calc/index.html b/rv_voting_calc/templates/rv_voting_calc/index.html
index 12bb2a7964b9793ea7c63bf0bd22e12966c43d09..b08953579cda5387d16add0d4fba2f0f8de6f34a 100644
--- a/rv_voting_calc/templates/rv_voting_calc/index.html
+++ b/rv_voting_calc/templates/rv_voting_calc/index.html
@@ -33,7 +33,7 @@
                 <button
                     class="btn disabled:cursor-progress"
                     id="count-votes"
-                    disabled
+                    {% if not options_by_member %}disabled{% endif %}
                 >
                     <div class="btn__body">
                         Vypočítat
@@ -66,12 +66,18 @@
                 >
                     
                 </ul>
-                <div class="mt-3">
+                <div class="mt-4 flex gap-4 justify-end">
                     <a
-                        class="hidden underline"
+                        class="hidden btn"
                         id="permalink"
                         href=""
-                    >Permalink</a>
+                    ><div class="btn__body">Permalink</div></a>
+                    <a
+                        class="hidden btn"
+                        id="download-log"
+                        download="kroky.md"
+                        href=""
+                    ><div class="btn__body">Stáhnout log</div></a>
                 </div>
             </div>
         </div>
diff --git a/rv_voting_calc/templates/rv_voting_calc/md_steps/found_popularity_winner.md b/rv_voting_calc/templates/rv_voting_calc/md_steps/found_popularity_winner.md
new file mode 100644
index 0000000000000000000000000000000000000000..c01793dd7d57f33b8f4d238823146cdefaba9edb
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/md_steps/found_popularity_winner.md
@@ -0,0 +1 @@
+**Možnost {{ option_key }} vyhrává s {{ option.vote_count }} hlasy.**
diff --git a/rv_voting_calc/templates/rv_voting_calc/md_steps/initial_sort.md b/rv_voting_calc/templates/rv_voting_calc/md_steps/initial_sort.md
new file mode 100644
index 0000000000000000000000000000000000000000..f4403f8f5a5c4ba829604b9bdf8b388a26b27858
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/md_steps/initial_sort.md
@@ -0,0 +1,5 @@
+# Počáteční rozdělení
+
+**{{ total_ticket_count }}** lístků, hranice pro nadpoloviční podporu **{{ has_support_treshold }}**.
+
+{% include "rv_voting_calc/includes/option_list.md" with options=options options_by_member=options_by_member show_proposers=True show_has_support=True %}
diff --git a/rv_voting_calc/templates/rv_voting_calc/md_steps/no_winner.md b/rv_voting_calc/templates/rv_voting_calc/md_steps/no_winner.md
new file mode 100644
index 0000000000000000000000000000000000000000..01483b3565635af8f55dcc7fa1c9c4b4c4f4235d
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/md_steps/no_winner.md
@@ -0,0 +1 @@
+**Ani jedna možnost nevyhrává.**
diff --git a/rv_voting_calc/templates/rv_voting_calc/md_steps/removed_option.md b/rv_voting_calc/templates/rv_voting_calc/md_steps/removed_option.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b22831c74424fafa07e7a60065047f1945e5edb
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/md_steps/removed_option.md
@@ -0,0 +1,7 @@
+{% load index %}
+
+# {{ iteration|add:1 }}. vyřazovací kolo
+
+**Možnost {{ option_key }} bude {% if randomly %}losováním{% else %}kvůli nepopularitě{% endif %} odstraněna.**
+
+{% include "rv_voting_calc/includes/option_list.md" with options=options options_by_member=options_by_member show_proposers=True show_marked_for_deletion=True %}
diff --git a/rv_voting_calc/templates/rv_voting_calc/md_steps/with_support.md b/rv_voting_calc/templates/rv_voting_calc/md_steps/with_support.md
new file mode 100644
index 0000000000000000000000000000000000000000..b55458fd1b96fd9aa6633dbcdda15694ffd5c4df
--- /dev/null
+++ b/rv_voting_calc/templates/rv_voting_calc/md_steps/with_support.md
@@ -0,0 +1,7 @@
+# Po vyřazení možností bez nadpoloviční podpory
+
+{% if options_with_support_count != 0 %}
+    {% include "rv_voting_calc/includes/option_list.md" with options=options options_by_member=options_by_member %}
+{% else %}
+    *Žádné možnosti nemají nadpoloviční podporu.*
+{% endif %}
diff --git a/rv_voting_calc/views.py b/rv_voting_calc/views.py
index 4810e7c1d72ece439321ec3427877d6cc105b97f..38091e416a283bde2817216daedfaf52318a0b84 100644
--- a/rv_voting_calc/views.py
+++ b/rv_voting_calc/views.py
@@ -149,7 +149,8 @@ def convert_rv_members_to_dict(source: list) -> dict:
 def do_step_b_through_d(
     request,
     iteration: int,
-    steps: list,
+    html_steps: list,
+    md_steps: list,
     options: dict,
     options_without_support: list,
     options_by_member: dict,
@@ -203,14 +204,31 @@ def do_step_b_through_d(
     # If we are on the first iteration, show the list of options with those that
     # don't have support removed.
     if iteration == 0:
-        steps.append(
+        # Get the number of options that have support, for displaying.
+        options_with_support_count = 0
+
+        for option in options.values():
+            if option["has_support"]:
+                options_with_support_count += 1
+
+        context = {
+            "options": options,
+            "options_by_member": options_by_member,
+            "options_with_support_count": options_with_support_count,
+            "rv_members": rv_members
+        }
+
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/with_support.html",
-                {
-                    "options": options,
-                    "options_by_member": options_by_member,
-                    "rv_members": rv_members
-                },
+                "rv_voting_calc/html_steps/with_support.html",
+                context,
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/with_support.md",
+                context,
                 request=request,
             )
         )
@@ -221,9 +239,16 @@ def do_step_b_through_d(
 
     # If no options remain, show that there are no winners and end everything.
     if len(options) == 0:
-        steps.append(
+        html_steps.append(
+            render_to_string(
+                "rv_voting_calc/html_steps/no_winner.html",
+                {},
+                request=request,
+            )
+        )
+        md_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/no_winner.html",
+                "rv_voting_calc/md_steps/no_winner.md",
                 {},
                 request=request,
             )
@@ -248,47 +273,69 @@ def do_step_b_through_d(
 
     # Find winners.
     winner_vote_count = options[next(iter(options))]["vote_count"]
+    ticket_count_same = True
     winner_count = 0
     winning_option_keys = []
 
     for option_key, option in options.items():
         if option["vote_count"] == winner_vote_count:
+            if option["ticket_count"] != options[next(iter(options))]["ticket_count"]:
+                ticket_count_same = False
+
             winning_option_keys.append(option_key)
             winner_count += 1
 
     # If there is exactly 1 winner, show the winner and end everything.
     if winner_count == 1:
-        steps.append(
+        context = {
+            "option_key": winning_option_keys[0],
+            "option": options[winning_option_keys[0]],
+        }
+
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/found_popularity_winner.html",
-                {
-                    "option_key": winning_option_keys[0],
-                    "option": options[winning_option_keys[0]],
-                },
+                "rv_voting_calc/html_steps/found_popularity_winner.html",
+                context,
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/found_popularity_winner.md",
+                context,
                 request=request,
             )
         )
 
         return options, True
-    # If all options are winners, randomly eliminate one and continue
-    # to the next iteration.
-    elif winner_count == len(options):
+    # If all options are winners and have the same ticket vote count,
+    # randomly eliminate one and continue to the next iteration.
+    elif winner_count == len(options) and ticket_count_same:
         eliminated_winner = random.choice(winning_option_keys)
         
         options[eliminated_winner]["marked_for_deletion"] = True
 
-        steps.append(
+        context = {
+            "iteration": iteration,
+            "option_key": eliminated_winner,
+            "option": options[eliminated_winner],
+            "options": options,
+            "options_by_member": options_by_member,
+            "rv_members": rv_members,
+            "randomly": True
+        }
+
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/removed_option.html",
-                {
-                    "iteration": iteration,
-                    "option_key": eliminated_winner,
-                    "option": options[eliminated_winner],
-                    "options": options,
-                    "options_by_member": options_by_member,
-                    "rv_members": rv_members,
-                    "randomly": True
-                },
+                "rv_voting_calc/html_steps/removed_option.html",
+                context,
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/removed_option.md",
+                context,
                 request=request,
             )
         )
@@ -326,17 +373,26 @@ def do_step_b_through_d(
 
         options[loser_key]["marked_for_deletion"] = True
 
-        steps.append(
+        context = {
+            "iteration": iteration,
+            "option_key": loser_key,
+            "option": options[loser_key],
+            "options": options,
+            "options_by_member": options_by_member,
+            "rv_members": rv_members,
+        }
+
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/removed_option.html",
-                {
-                    "iteration": iteration,
-                    "option_key": loser_key,
-                    "option": options[loser_key],
-                    "options": options,
-                    "options_by_member": options_by_member,
-                    "rv_members": rv_members,
-                },
+                "rv_voting_calc/html_steps/removed_option.html",
+                context,
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/removed_option.md",
+                context,
                 request=request,
             )
         )
@@ -349,21 +405,30 @@ def do_step_b_through_d(
             key=lambda key: options[key]["ticket_count"]
         )
 
-        options[losing_option_keys[0]]["marked_for_deletion"] = True
-
         # Delete the vote with the least ticket votes. There maybe more, but
         # those should be taken care of in the next iteration.
-        steps.append(
+        options[losing_option_keys[0]]["marked_for_deletion"] = True
+
+        context = {
+            "iteration": iteration,
+            "option_key": losing_option_keys[0],
+            "option": options[losing_option_keys[0]],
+            "options": options,
+            "options_by_member": options_by_member,
+            "rv_members": rv_members,
+        }
+
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/removed_option.html",
-                {
-                    "iteration": iteration,
-                    "option_key": losing_option_keys[0],
-                    "option": options[losing_option_keys[0]],
-                    "options": options,
-                    "options_by_member": options_by_member,
-                    "rv_members": rv_members,
-                },
+                "rv_voting_calc/html_steps/removed_option.html",
+                context,
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/removed_option.md",
+                context,
                 request=request,
             )
         )
@@ -377,9 +442,16 @@ def do_step_b_through_d(
 
     # If there are no winners, show that and end everything.
     if len(options) == 0:
-        steps.append(
+        html_steps.append(
             render_to_string(
-                "rv_voting_calc/steps/no_winner.html",
+                "rv_voting_calc/html_steps/no_winner.html",
+                {},
+                request=request,
+            )
+        )
+        md_steps.append(
+            render_to_string(
+                "rv_voting_calc/md_steps/no_winner.md",
                 {},
                 request=request,
             )
@@ -411,7 +483,8 @@ def get_calculated_votes(request):
     ## BEGIN Step A
     # Sorting
 
-    steps = []
+    html_steps = []
+    md_steps = []
 
     options = {}
     total_ticket_count = len(options_by_member)  # 1 member = 1 ticket
@@ -485,16 +558,25 @@ def get_calculated_votes(request):
         )
     }
 
-    steps.append(
+    context = {
+        "options": options,
+        "options_by_member": options_by_member,
+        "rv_members": rv_members,
+        "total_ticket_count": total_ticket_count,
+        "has_support_treshold": has_support_treshold
+    }
+
+    html_steps.append(
+        render_to_string(
+            "rv_voting_calc/html_steps/initial_sort.html",
+            context,
+            request=request,
+        )
+    )
+    md_steps.append(
         render_to_string(
-            "rv_voting_calc/steps/initial_sort.html",
-            {
-                "options": options,
-                "options_by_member": options_by_member,
-                "rv_members": rv_members,
-                "total_ticket_count": total_ticket_count,
-                "has_support_treshold": has_support_treshold
-            },
+            "rv_voting_calc/md_steps/initial_sort.md",
+            context,
             request=request,
         )
     )
@@ -524,7 +606,8 @@ def get_calculated_votes(request):
         options, ended = do_step_b_through_d(
             request,
             iteration,
-            steps,
+            html_steps,
+            md_steps,
             options,
             options_without_support,
             options_by_member,
@@ -532,7 +615,14 @@ def get_calculated_votes(request):
         )
 
         if ended:
-            response = HttpResponse("\n".join(steps))
+            response = render(
+                request,
+                "rv_voting_calc/combined_steps.html",
+                {
+                    "html_steps": "\n".join(html_steps),
+                    "md_steps": md_steps
+                }
+            )
             # Set the random seed cookie to the one used in the calculation.
             response.set_cookie("seed", seed)
             # Do the same for RV's GID.
diff --git a/static_src/rv_voting_calc.js b/static_src/rv_voting_calc.js
index 136238f903b1ae67e2a9c685be75edc190ded1e6..69ba73f8742c06a6b0ddcffc70378f3d95022ef2 100644
--- a/static_src/rv_voting_calc.js
+++ b/static_src/rv_voting_calc.js
@@ -108,7 +108,7 @@ $(window).ready(
 
                 $("#result").html(await response.text());
 
-                $("#permalink").show();
+                $("#permalink,#download-log").show();
                 $("#permalink").attr(
                     "href",
                     (
@@ -119,6 +119,13 @@ $(window).ready(
                         + `&seed=${Cookies.get("seed")}`
                     )
                 );
+                $("#download-log").attr(
+                    "href",
+                    (
+                        "data:text/plain;charset=utf-8,"
+                        + encodeURIComponent($("#md-steps").html())
+                    )
+                );
 
                 $(event.currentTarget).removeClass("btn--loading").prop("disabled", false);
             }