diff --git a/elections2021/constants.py b/elections2021/constants.py
index ec2ca0a4fc55455b43ac6f6b0363fea62c5f8a44..1e2b68f93b1d36647e5e78920ec4916f873e82c2 100644
--- a/elections2021/constants.py
+++ b/elections2021/constants.py
@@ -35,15 +35,53 @@ RICH_TEXT_FEATURES = [
 ]
 
 ARTICLES_PER_PAGE = 9
+TOP_CANDIDATES_NUM = 12
+
+REGION_JHC = "JHC"
+REGION_JHM = "JHM"
+REGION_KVK = "KVK"
+REGION_KHK = "KHK"
+REGION_LBK = "LBK"
+REGION_MSK = "MSK"
+REGION_OLK = "OLK"
+REGION_PAK = "PAK"
+REGION_PLK = "PLK"
+REGION_PHA = "PHA"
+REGION_STC = "STC"
+REGION_ULK = "ULK"
+REGION_VYS = "VYS"
+REGION_ZLK = "ZLK"
+
+REGION_CHOICES = (
+    (REGION_JHC, "Jihočeský kraj"),
+    (REGION_JHM, "Jihomoravský kraj"),
+    (REGION_KVK, "Karlovarský kraj"),
+    (REGION_KHK, "Královéhradecký kraj"),
+    (REGION_LBK, "Liberecký kraj"),
+    (REGION_MSK, "Moravskoslezský kraj"),
+    (REGION_OLK, "Olomoucký kraj"),
+    (REGION_PAK, "Pardubický kraj"),
+    (REGION_PLK, "Plzeňský kraj"),
+    (REGION_PHA, "Praha"),
+    (REGION_STC, "Středočeský kraj"),
+    (REGION_ULK, "Ústecký kraj"),
+    (REGION_VYS, "Vysočina"),
+    (REGION_ZLK, "Zlínský kraj"),
+)
+
+PIRATES = "pirati"
+STAN = "stan"
+PARTY_CHOICES = ((PIRATES, "Piráti"), (STAN, "STAN"))
+PARTY_NAME = {PIRATES: "Pirátská strana", STAN: "Starostové a nezávislí"}
 
 # cílovky
 
 CHILDLESS = "childless"
-PARENT = "parent"
+PARENTS = "parents"
 MATURE = "mature"
-GRANDPARENT = "grandparent"
-WORKING_SENIOR = "working_senior"
-STUDENT = "student"
+SENIORS = "seniors"
+WORKING_SENIORS = "working_seniors"
+STUDENTS = "students"
 
 SELF_EMPLOYED = "self_employed"
 SOCIALLY_WEAK = "socially_weak"
@@ -59,11 +97,11 @@ EDUCATION = "education"
 
 TARGET_CHOICES = (
     (CHILDLESS, "bezdětní"),
-    (PARENT, "rodiče"),
-    (MATURE, "zralý"),
-    (GRANDPARENT, "prarodiče"),
-    (WORKING_SENIOR, "pracující senioři"),
-    (STUDENT, "studenti"),
+    (PARENTS, "rodiče"),
+    (MATURE, "zralí"),
+    (SENIORS, "senioři"),
+    (WORKING_SENIORS, "pracující senioři"),
+    (STUDENTS, "studenti"),
     (SELF_EMPLOYED, "OSVČ"),
     (SOCIALLY_WEAK, "sociálně slabí"),
     (NATURE, "příroda"),
diff --git a/elections2021/migrations/0002_elections2021candidatepage_elections2021candidateslistpage_elections2021candidatesmappage.py b/elections2021/migrations/0002_elections2021candidatepage_elections2021candidateslistpage_elections2021candidatesmappage.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf92e9e070caf3b0ea6d11288a30b17b28116ac7
--- /dev/null
+++ b/elections2021/migrations/0002_elections2021candidatepage_elections2021candidateslistpage_elections2021candidatesmappage.py
@@ -0,0 +1,233 @@
+# Generated by Django 3.1.7 on 2021-05-04 22:31
+
+import django.db.models.deletion
+import wagtail.core.fields
+import wagtailmetadata.models
+from django.db import migrations, models
+
+import shared.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("wagtailcore", "0060_fix_workflow_unique_constraint"),
+        ("wagtailimages", "0023_add_choose_permissions"),
+        ("elections2021", "0001_initial"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="Elections2021CandidatesMapPage",
+            fields=[
+                (
+                    "page_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="wagtailcore.page",
+                    ),
+                ),
+                (
+                    "photo",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.PROTECT,
+                        to="wagtailimages.image",
+                        verbose_name="hlavní fotka",
+                    ),
+                ),
+                (
+                    "search_image",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.SET_NULL,
+                        related_name="+",
+                        to="wagtailimages.image",
+                        verbose_name="Search image",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Kandidáti mapa",
+            },
+            bases=(
+                shared.models.SubpageMixin,
+                wagtailmetadata.models.WagtailImageMetadataMixin,
+                "wagtailcore.page",
+                models.Model,
+            ),
+        ),
+        migrations.CreateModel(
+            name="Elections2021CandidatesListPage",
+            fields=[
+                (
+                    "page_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="wagtailcore.page",
+                    ),
+                ),
+                (
+                    "photo",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.PROTECT,
+                        to="wagtailimages.image",
+                        verbose_name="hlavní fotka",
+                    ),
+                ),
+                (
+                    "search_image",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.SET_NULL,
+                        related_name="+",
+                        to="wagtailimages.image",
+                        verbose_name="Search image",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Kandidáti",
+            },
+            bases=(
+                shared.models.SubpageMixin,
+                wagtailmetadata.models.WagtailImageMetadataMixin,
+                "wagtailcore.page",
+                models.Model,
+            ),
+        ),
+        migrations.CreateModel(
+            name="Elections2021CandidatePage",
+            fields=[
+                (
+                    "page_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="wagtailcore.page",
+                    ),
+                ),
+                (
+                    "party",
+                    models.CharField(
+                        choices=[("pirati", "Piráti"), ("stan", "STAN")],
+                        default="pirati",
+                        max_length=6,
+                        verbose_name="strana",
+                    ),
+                ),
+                (
+                    "region",
+                    models.CharField(
+                        choices=[
+                            ("JHC", "Jihočeský kraj"),
+                            ("JHM", "Jihomoravský kraj"),
+                            ("KVK", "Karlovarský kraj"),
+                            ("KHK", "Královéhradecký kraj"),
+                            ("LBK", "Liberecký kraj"),
+                            ("MSK", "Moravskoslezský kraj"),
+                            ("OLK", "Olomoucký kraj"),
+                            ("PAK", "Pardubický kraj"),
+                            ("PLK", "Plzeňský kraj"),
+                            ("PHA", "Praha"),
+                            ("STC", "Středočeský kraj"),
+                            ("ULK", "Ústecký kraj"),
+                            ("VYS", "Vysočina"),
+                            ("ZLK", "Zlínský kraj"),
+                        ],
+                        max_length=3,
+                        verbose_name="kraj",
+                    ),
+                ),
+                ("number", models.IntegerField(verbose_name="pořadí")),
+                ("age", models.IntegerField(verbose_name="věk")),
+                (
+                    "occupation",
+                    models.CharField(max_length=255, verbose_name="povolání"),
+                ),
+                ("city", models.CharField(max_length=100, verbose_name="bydliště")),
+                (
+                    "resume",
+                    wagtail.core.fields.RichTextField(verbose_name="medailonek"),
+                ),
+                (
+                    "email",
+                    models.EmailField(
+                        blank=True, max_length=254, null=True, verbose_name="email"
+                    ),
+                ),
+                (
+                    "phone",
+                    models.CharField(
+                        blank=True, max_length=20, null=True, verbose_name="telefon"
+                    ),
+                ),
+                (
+                    "facebook_url",
+                    models.URLField(blank=True, null=True, verbose_name="Facebook URL"),
+                ),
+                (
+                    "twitter_url",
+                    models.URLField(blank=True, null=True, verbose_name="Twitter URL"),
+                ),
+                (
+                    "instagram_url",
+                    models.URLField(
+                        blank=True, null=True, verbose_name="Instagram URL"
+                    ),
+                ),
+                (
+                    "website_url",
+                    models.URLField(
+                        blank=True, null=True, verbose_name="webová stránka"
+                    ),
+                ),
+                (
+                    "photo",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.PROTECT,
+                        to="wagtailimages.image",
+                        verbose_name="foto",
+                    ),
+                ),
+                (
+                    "search_image",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.SET_NULL,
+                        related_name="+",
+                        to="wagtailimages.image",
+                        verbose_name="Search image",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Detail kandidáta",
+            },
+            bases=(
+                shared.models.SubpageMixin,
+                wagtailmetadata.models.WagtailImageMetadataMixin,
+                "wagtailcore.page",
+                models.Model,
+            ),
+        ),
+    ]
diff --git a/elections2021/models.py b/elections2021/models.py
index 971f1f67536d81e72eb9b4e94d4632620cae3ba4..7a65cb412c3d918ec44933fc234bd472d38ddabd 100644
--- a/elections2021/models.py
+++ b/elections2021/models.py
@@ -1,9 +1,19 @@
+from collections import defaultdict
+from functools import cached_property
+
+from django import forms
 from django.core.paginator import Paginator
 from django.db import models
+from django.utils.translation import gettext_lazy
 from modelcluster.contrib.taggit import ClusterTaggableManager
 from modelcluster.fields import ParentalKey
 from taggit.models import TaggedItemBase
-from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
+from wagtail.admin.edit_handlers import (
+    FieldPanel,
+    HelpPanel,
+    MultiFieldPanel,
+    StreamFieldPanel,
+)
 from wagtail.core import blocks
 from wagtail.core.fields import RichTextField, StreamField
 from wagtail.core.models import Page
@@ -12,12 +22,19 @@ from wagtailmetadata.models import MetadataPageMixin
 
 from shared.models import ArticleMixin, SubpageMixin
 from shared.utils import get_subpage_url
+from tuning import help
 
 from .constants import (
     ARTICLES_PER_PAGE,
+    PARTY_CHOICES,
+    PARTY_NAME,
+    PIRATES,
+    REGION_CHOICES,
+    REGION_PHA,
     RICH_TEXT_FEATURES,
     STYLE_CHOICES,
     STYLE_CSS,
+    TOP_CANDIDATES_NUM,
     WHITE,
 )
 
@@ -46,6 +63,8 @@ class Elections2021HomePage(Page, MetadataPageMixin):
 
     ### PANELS
 
+    # TODO promote_panels
+
     settings_panels = [
         FieldPanel("matomo_id"),
         StreamFieldPanel("header_links"),
@@ -55,7 +74,11 @@ class Elections2021HomePage(Page, MetadataPageMixin):
     ### RELATIONS
 
     parent_page_types = []
-    subpage_types = ["elections2021.Elections2021ArticlesPage"]
+    subpage_types = [
+        "elections2021.Elections2021ArticlesPage",
+        "elections2021.Elections2021CandidatesListPage",
+        "elections2021.Elections2021CandidatesMapPage",
+    ]
 
     ### OTHERS
 
@@ -66,11 +89,18 @@ class Elections2021HomePage(Page, MetadataPageMixin):
     def root_page(self):
         return self
 
-    @property
+    @cached_property
     def articles_page_url(self):
-        # TODO instance cache
         return get_subpage_url(self, Elections2021ArticlesPage)
 
+    @cached_property
+    def candidates_list_page_url(self):
+        return get_subpage_url(self, Elections2021CandidatesListPage)
+
+    @cached_property
+    def candidates_map_page_url(self):
+        return get_subpage_url(self, Elections2021CandidatesMapPage)
+
 
 class Elections2021ArticleTag(TaggedItemBase):
     content_object = ParentalKey(
@@ -89,6 +119,8 @@ class Elections2021ArticlePage(ArticleMixin, SubpageMixin, MetadataPageMixin, Pa
 
     ### PANELS
 
+    # TODO promote_panels
+
     content_panels = ArticleMixin.content_panels + [
         FieldPanel("tags"),
         FieldPanel("card_style"),
@@ -131,6 +163,8 @@ class Elections2021ArticlesPage(SubpageMixin, MetadataPageMixin, Page):
 
     ### PANELS
 
+    # TODO promote_panels
+
     content_panels = Page.content_panels + [ImageChooserPanel("photo")]
 
     settings_panels = []
@@ -152,3 +186,192 @@ class Elections2021ArticlesPage(SubpageMixin, MetadataPageMixin, Page):
             ARTICLES_PER_PAGE,
         ).get_page(request.GET.get("page"))
         return context
+
+
+class Elections2021CandidatesListPage(SubpageMixin, MetadataPageMixin, Page):
+    ### FIELDS
+
+    photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        verbose_name="hlavní fotka",
+    )
+
+    ### PANELS
+
+    # TODO promote_panels
+
+    content_panels = Page.content_panels + [ImageChooserPanel("photo")]
+
+    settings_panels = []
+
+    ### RELATIONS
+
+    parent_page_types = ["elections2021.Elections2021HomePage"]
+    subpage_types = ["elections2021.Elections2021CandidatePage"]
+
+    ### OTHERS
+
+    class Meta:
+        verbose_name = "Kandidáti"
+
+    def get_context(self, request):
+        context = super().get_context(request)
+
+        region_candidates_top = defaultdict(list)
+        region_candidates_bottom = defaultdict(list)
+
+        candidates = (
+            self.get_children()
+            .live()
+            .specific()
+            .order_by("elections2021candidatepage__number")
+        )
+
+        for can in candidates:
+            if len(region_candidates_top[can.region]) < TOP_CANDIDATES_NUM:
+                region_candidates_top[can.region].append(can)
+            else:
+                region_candidates_bottom[can.region].append(can)
+
+        context["region_candidates_top"] = dict(region_candidates_top)
+        context["region_candidates_bottom"] = dict(region_candidates_bottom)
+        context["region_choices"] = REGION_CHOICES
+        context["region_codes"] = [code for code, _ in REGION_CHOICES]
+
+        active_region = request.GET.get("kraj", REGION_PHA)
+        if active_region in context["region_codes"]:
+            context["active_region"] = active_region
+        else:
+            context["active_region"] = REGION_PHA
+
+        return context
+
+
+class Elections2021CandidatesMapPage(SubpageMixin, MetadataPageMixin, Page):
+    ### FIELDS
+
+    photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        verbose_name="hlavní fotka",
+    )
+
+    ### PANELS
+
+    # TODO promote_panels
+
+    content_panels = Page.content_panels + [ImageChooserPanel("photo")]
+
+    settings_panels = []
+
+    ### RELATIONS
+
+    parent_page_types = ["elections2021.Elections2021HomePage"]
+    subpage_types = []
+
+    ### OTHERS
+
+    class Meta:
+        verbose_name = "Kandidáti mapa"
+
+
+class Elections2021CandidatePage(SubpageMixin, MetadataPageMixin, Page):
+    ### FIELDS
+
+    party = models.CharField(
+        "strana", choices=PARTY_CHOICES, default=PIRATES, max_length=6
+    )
+    region = models.CharField("kraj", choices=REGION_CHOICES, max_length=3)
+    number = models.IntegerField("pořadí")
+    age = models.IntegerField("věk")
+    occupation = models.CharField("povolání", max_length=255)
+    city = models.CharField("bydliště", max_length=100)
+    # TODO RICH_TEXT_FEATURES
+    resume = RichTextField("medailonek")
+    email = models.EmailField("email", null=True, blank=True)
+    phone = models.CharField("telefon", null=True, blank=True, max_length=20)
+    facebook_url = models.URLField("Facebook URL", blank=True, null=True)
+    twitter_url = models.URLField("Twitter URL", blank=True, null=True)
+    instagram_url = models.URLField("Instagram URL", blank=True, null=True)
+    website_url = models.URLField("webová stránka", blank=True, null=True)
+    photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        verbose_name="foto",
+    )
+
+    ### PANELS
+
+    content_panels = Page.content_panels + [
+        MultiFieldPanel(
+            [
+                FieldPanel("party", widget=forms.RadioSelect),
+                FieldPanel("region"),
+                FieldPanel("number"),
+            ],
+            "zařazení",
+        ),
+        MultiFieldPanel(
+            [
+                FieldPanel("age"),
+                FieldPanel("occupation"),
+                FieldPanel("city"),
+            ],
+            "osobní údaje",
+        ),
+        ImageChooserPanel("photo"),
+        FieldPanel("resume"),
+        MultiFieldPanel(
+            [
+                FieldPanel("email"),
+                FieldPanel("phone"),
+                FieldPanel("facebook_url"),
+                FieldPanel("twitter_url"),
+                FieldPanel("instagram_url"),
+                FieldPanel("website_url"),
+            ],
+            "kontakty",
+        ),
+    ]
+
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("slug"),
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                HelpPanel(help.build(help.NO_SEO_TITLE)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
+
+    settings_panels = []
+
+    ### RELATIONS
+
+    parent_page_types = ["elections2021.Elections2021CandidatesListPage"]
+    subpage_types = []
+
+    ### OTHERS
+
+    class Meta:
+        verbose_name = "Detail kandidáta"
+
+    def get_meta_image(self):
+        return self.photo
+
+    @property
+    def party_name(self):
+        return PARTY_NAME[self.party]
+
+    @property
+    def is_pirate(self):
+        return self.party == PIRATES
diff --git a/elections2021/templates/elections2021/_candidate_card.html b/elections2021/templates/elections2021/_candidate_card.html
new file mode 100644
index 0000000000000000000000000000000000000000..d197ba2c3218e0a0b028507de23b083ead8bd59a
--- /dev/null
+++ b/elections2021/templates/elections2021/_candidate_card.html
@@ -0,0 +1,53 @@
+{% load static wagtailcore_tags wagtailimages_tags %}
+<div class="candidate-card candidate-card--pirati-stan container-padding--zero sm:container-padding--auto ">
+  <div class="candidate-card__wrapper candidate-card-list__item-wrapper {% if not candidate.is_pirate %}candidate-card-list__item-wrapper--stan-candidate{% endif %}">
+    <div class="card candidate-card__body elevation-0 hover:elevation-10 transition duration-200">
+      <div class="candidate-card__avatar sm:pl-0">
+        <div class="candidate-card__position">{{ candidate.number }}</div>
+        <a href="{% pageurl candidate %}" data-href="{% pageurl candidate %}">
+          <div class="avatar avatar--sm sm:avatar--lg">
+            {% image candidate.photo fill-112x112 %}
+          </div>
+        </a>
+      </div>
+      <div class="candidate-card__bio">
+        <h1 class="head-heavy-xs"><a href="{% pageurl candidate %}" data-href="{% pageurl candidate %}">{{ candidate.title }}</a></h1>
+        <a href="mailto:{{ candidate.email }}" class="candidate-card__bio-item block mb-4">{{ candidate.email|default:"" }}</a>
+        <h2 class="candidate-card__bio-item">{{ candidate.occupation }}</h2>
+        <h2 class="head-allcaps-4xs mt-4">{{ candidate.city }}</h2>
+      </div>
+      <div class="candidate-card__affiliation">
+        <div>{{ candidate.age }} let</div>
+        <div>
+          <div class="mr-2">
+            {% if candidate.is_pirate %}
+              <img src="{% static "elections2021/images/logo-pirati-21px.svg" %}">
+            {% else %}
+              <img src="{% static "elections2021/images/logo-stan-21px.svg" %}">
+            {% endif %}
+          </div>
+          <span class="font-bold font-condensed">{{ candidate.party_name }}</span>
+        </div>
+      </div>
+      <div class="card__body candidate-card__social">
+        <div class="social-icon-group space-x-2 ">
+          {% if candidate.facebook_url %}
+            <a href="{{ candidate.facebook_url }}" class="social-icon"><i class="ico--facebook"></i></a>
+          {% endif %}
+          {% if candidate.twitter_url %}
+            <a href="{{ candidate.twitter_url }}" class="social-icon"><i class="ico--twitter"></i></a>
+          {% endif %}
+          {% if candidate.instagram_url %}
+            <a href="{{ candidate.instagram_url }}" class="social-icon"><i class="ico--instagram"></i></a>
+          {% endif %}
+          {% if candidate.website_url %}
+            <a href="{{ candidate.website_url }}" class="social-icon"><i class="ico--home"></i></a>
+          {% endif %}
+          {% if candidate.email %}
+            <a href="mailto:{{ candidate.email }}" class="social-icon"><i class="ico--envelope"></i></a>
+          {% endif %}
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/elections2021/templates/elections2021/base.html b/elections2021/templates/elections2021/base.html
index 8760cceed5208d868a1388109d62d246bf0d063f..05d8f058e3a0508283c9312376529315c1e57b13 100644
--- a/elections2021/templates/elections2021/base.html
+++ b/elections2021/templates/elections2021/base.html
@@ -62,8 +62,8 @@
                 <li class="navbar-menu__item">
                   <ui-navbar-subitem label="Kandidáti" href="#">
                     <ul class="navbar-menu__submenu">
-                      <li><a href="#" data-href="#" class="navbar-menu__link">Výpis kandidátů</a></li>
-                      <li><a href="#" data-href="#" class="navbar-menu__link">Výpis kandidátů podle mapy</a></li>
+                      <li><a href="{{ page.root_page.candidates_list_page_url }}" data-href="{{ page.root_page.candidates_list_page_url }}" class="navbar-menu__link">Výpis kandidátů</a></li>
+                      <li><a href="{{ page.root_page.candidates_map_page_url }}" data-href="{{ page.root_page.candidates_map_page_url }}" class="navbar-menu__link">Výpis kandidátů podle mapy</a></li>
                     </ul>
                     </ui-navbar-subtitem>
                 </li>
@@ -114,7 +114,7 @@
                     <a href="{{ page.root_page.articles_page_url }}">Aktality</a>
                   </li>
                   <li>
-                    <a href="#">Kandidáti</a>
+                    <a href="{{ page.root_page.candidates_list_page_url }}">Kandidáti</a>
                   </li>
                   <li>
                     <a href="#">Programové body</a>
diff --git a/elections2021/templates/elections2021/elections2021_candidate_page.html b/elections2021/templates/elections2021/elections2021_candidate_page.html
new file mode 100644
index 0000000000000000000000000000000000000000..b037d42ce140e58d04a16a4e4cb98285904db6b8
--- /dev/null
+++ b/elections2021/templates/elections2021/elections2021_candidate_page.html
@@ -0,0 +1,90 @@
+{% extends "elections2021/base.html" %}
+{% load static wagtailcore_tags wagtailimages_tags %}
+
+{% block content %}
+<article class="hero hero--image pt-24 pb-20 lg:pt-28 pb-10 candidate-detail__hero ">
+  <div class="container container--default text-center lg:text-left">
+    <a href="{{ page.root_page.candidates_list_page_url }}?kraj={{ page.region }}" class="btn text-sm btn--hoveractive btn--to-black">
+      <div class="btn__body leading-5 bg-acidgreen text-black hover:text-white px-10 py-3">zpět na výpis</div>
+    </a>
+    <h1 class="head-alt-md md:head-alt-xl mt-9 text-black">{{ page.title }}</h1>
+  </div>
+</article>
+
+<div class="container container--default py-8 lg:pb-24">
+  <main>
+    <div class="flex flex-col lg:flex-row lg:space-x-8 xl:space-x-16">
+      <section class="lg:w-3/5 xl:w-2/3">
+        <div class="content-block w-full">
+          {{ page.resume|richtext }}
+        </div>
+      </section>
+      <section class="lg:w-2/5 xl:w-1/3 pt-8 lg:pt-0 order-first lg:order-last candidate-detail__sidebar">
+        <div class="container-padding--zero lg:card lg:elevation-10 lg:container-padding--auto">
+          <div class="card__body p-4 lg:p-8">
+            <div class="text-center mb-8">
+              <div class="avatar relative avatar--2xl lg:avatar--3xl avatar--bordered candidate-detail__avatar">
+                <div class="avatar-candidate-position">
+                  <span class="font-alt text-2xl">{{ page.number }}</span>
+                </div>
+                {% image page.photo fill-224x224 %}
+              </div>
+            </div>
+            <div class="flex items-center">
+              <div class="mr-2">
+                {% if page.is_pirate %}
+                  <img src="{% static "elections2021/images/logo-pirati-21px.svg" %}">
+                {% else %}
+                  <img src="{% static "elections2021/images/logo-stan-21px.svg" %}">
+                {% endif %}
+              </div>
+              <span class="font-bold font-condensed">{{ page.party_name }}</span>
+            </div>
+            <hr>
+            <div>
+              <p class="font-bold font-condensed text-base">{{ page.get_region_display }}</p>
+              <p class="text-sm mt-1">{{ page.city }}</p>
+            </div>
+            <hr>
+            <div class="space-y-4">
+              <div>
+                <p class="text-sm">
+                  <i class="ico--info text-xs"></i> {{ page.age }} let
+                </p>
+              </div>
+              <div>
+                <p class="text-sm">
+                  <i class="ico--info text-xs"></i> {{ page.occupation }}
+                </p>
+              </div>
+            </div>
+            <div>
+              <hr>
+              <div class="space-y-4">
+                <div>
+                  <div class="social-icon-group space-x-2 text-lg">
+                    {% if page.facebook_url %}
+                      <a href="{{ page.facebook_url }}" class="social-icon"><i class="ico--facebook"></i></a>
+                    {% endif %}
+                    {% if page.twitter_url %}
+                      <a href="{{ page.twitter_url }}" class="social-icon"><i class="ico--twitter"></i></a>
+                    {% endif %}
+                    {% if page.instagram_url %}
+                      <a href="{{ page.instagram_url }}" class="social-icon"><i class="ico--instagram"></i></a>
+                    {% endif %}
+                    {% if page.website_url %}
+                      <a href="{{ page.website_url }}" class="social-icon"><i class="ico--home"></i></a>
+                    {% endif %}
+                    {% if page.email %}
+                      <a href="mailto:{{ page.email }}" class="social-icon"><i class="ico--envelope"></i></a>
+                    {% endif %}
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+      </div></section>
+    </div>
+  </main>
+</div>
+{% endblock %}
diff --git a/elections2021/templates/elections2021/elections2021_candidates_list_page.html b/elections2021/templates/elections2021/elections2021_candidates_list_page.html
new file mode 100644
index 0000000000000000000000000000000000000000..6a5e9024db53d7f3e2c7b6be6ce84b48c0340549
--- /dev/null
+++ b/elections2021/templates/elections2021/elections2021_candidates_list_page.html
@@ -0,0 +1,135 @@
+{% extends "elections2021/base.html" %}
+{% load static wagtailcore_tags wagtailimages_tags %}
+
+{% block content %}
+<article class="relative bg-lemon md:bg-split-color px-4 md:pl-8 md:pr-0 2xl:px-8 hero py-0 w-full ">
+  <div class="2xl:container w-auto bg-lemon md:pl-20 pr-0 grid lg:grid-rows-2 lg:grid-cols-7 items-center 2xl:mx-auto">
+    <div class="lg:row-span-1 lg:col-span-4 order-1 pt-14 md:pr-20">
+      <h1 class="head-alt-md sm:head-alt-lg  max-w-xs">{{ page.title }}</h1>
+    </div>
+    <div class="lg:row-span-1 lg:col-span-4 order-3 pb-14 md:pr-20">
+      <div class="hidden md:block pt-8">
+        <div class="switch">
+          <a class="switch__item switch__item--active">seznam</a>
+          <a href="{{ page.root_page.candidates_map_page_url }}" class="switch__item">mapa</a>
+        </div>
+      </div>
+    </div>
+    <div class="hidden lg:block lg:row-span-2 lg:col-span-3 order-2 h-full 2xl:absolute 2xl:right-0 2xl:w-1/3">
+      {% image page.photo fill-618x256 as img %}
+      <img class="object-cover w-full h-full" src="{{ img.url }}">
+    </div>
+  </div>
+</article>
+
+<div class="container container--default py-8 lg:py-24">
+  <section class="text-center relative">
+    <h1 class="head-alt-md pt-1 mb-8">Výpis kandidátů</h1>
+
+    <div class="select md:absolute left-0 top-0 text-white w-full md:w-auto inline-flex md:float-left mb-8 md:mb-0">
+      <select id="region_select" class="select__control form-field__control bg-black" data-chosen="">
+        {% for code, name in region_choices %}
+          <option value="{{ code }}" {% if code == active_region %}selected=""{% endif %}>{{ name }}</option>
+        {% endfor %}
+      </select>
+    </div>
+
+    <main class="md:mt-20">
+
+      {% for code, candidates in region_candidates_top.items %}
+        <div id="candidates_top_{{ code }}" {% if code != active_region %}class="hidden"{% endif %}>
+          <div class="candidate-card-list grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
+            {% for candidate in candidates %}
+              {% include "elections2021/_candidate_card.html" %}
+            {% endfor %}
+          </div>
+        </div>
+      {% endfor %}
+
+      {% for code, candidates in region_candidates_bottom.items %}
+        <div id="candidates_bottom_{{ code }}" {% if code != active_region %}class="hidden"{% endif %}>
+          <div class="candidate-table container-padding--zero lg:container-padding--auto pt-8">
+            {% for candidate in candidates %}
+              <a href="{% pageurl candidate %}" data-href="{% pageurl candidate %}" class="candidate-table-row">
+                <div class="candidate-table-row__position head-allcaps-heavy-2xs text-right">{{ candidate.number }}</div>
+                <div class="avatar candidate-table-row__avatar">
+                  {% image candidate.photo fill-40x40 %}
+                </div>
+                <div class="candidate-table-row__name head-heavy-2xs font-bold">{{ candidate.title }}</div>
+                <div class="candidate-table-row__bio font-condensed">{{ candidate.age }} let, {{ candidate.occupation }}, {{ candidate.city }}</div>
+                <div class="candidate-table-row__affiliation">
+                  <div class="w-6 mr-2">
+                    {% if candidate.is_pirate %}
+                      <img src="{% static "elections2021/images/logo-pirati-21px.svg" %}">
+                    {% else %}
+                      <img src="{% static "elections2021/images/logo-stan-21px.svg" %}">
+                    {% endif %}
+                  </div>
+                  <span class="font-bold font-condensed">{{ candidate.party_name }}</span>
+                </div>
+              </a>
+            {% endfor %}
+          </div>
+        </div>
+      {% endfor %}
+
+      <script>
+        var codes = ['{{ region_codes|join:"','" }}'];
+        var regionSelect = document.getElementById('region_select');
+        regionSelect.addEventListener('change', function(e) {
+          var activeCode = this.value;
+          for (var i = 0; i < codes.length; i++) {
+            var code = codes[i];
+            var classValue = (code == activeCode) ? '' : 'hidden';
+            var top = document.getElementById('candidates_top_' + code);
+            var bottom = document.getElementById('candidates_bottom_' + code);
+            if (top !== null) {
+              top.setAttribute('class', classValue);
+            }
+            if (bottom !== null) {
+              bottom.setAttribute('class', classValue);
+            }
+          }
+          window.history.replaceState('', '', '?kraj=' + activeCode);
+        });
+      </script>
+
+      {% comment %}
+      <div class="pt-11 text-center">
+        <button class="btn btn--icon showfulltable text-xl btn--hoveractive btn--to-black">
+          <div class="btn__body-wrap">
+            <div class="btn__body py-4 px-11 leading-5 bg-acidgreen text-black hover:text-white">Call to action</div>
+            <div class="btn__icon ">
+              <i class="ico--chevron-right"></i>
+            </div>
+          </div>
+        </button>
+      </div>
+
+      <script>
+        //collapse toggle
+        //assumes only one present on page
+        var anchorShowFull = document.getElementsByClassName('showfulltable');
+
+        if (anchorShowFull !== null) {
+
+          for (var i = 0; i < anchorShowFull.length; i++) {
+            anchorShowFull[i].addEventListener('click', function(e) {
+              e.preventDefault();
+              var candidatestable = document.getElementsByClassName("candidate-table");
+              //if fadeout
+              //candidatestable[0].classList.remove("candidate-table--fadeout");
+              var collapsepart = document.getElementsByClassName("candidates-list-collapsing-part");
+              collapsepart[0].style.maxHeight = collapsepart[0].scrollHeight + "px";
+              this.remove();
+            });
+          }
+        }
+      </script>
+      {% endcomment %}
+
+    </main>
+  </section>
+</div>
+
+{% endblock %}