diff --git a/elections2021/constants.py b/elections2021/constants.py
index 1e2b68f93b1d36647e5e78920ec4916f873e82c2..73018227d3a4914b72e9ae64f1664404bad300c1 100644
--- a/elections2021/constants.py
+++ b/elections2021/constants.py
@@ -17,7 +17,7 @@ STYLE_CSS = {
     GREEN: "emphasized-acidgreen",
 }
 
-RICH_TEXT_FEATURES = [
+ARTICLE_RICH_TEXT_FEATURES = [
     "h2",
     "h3",
     "h4",
@@ -34,41 +34,71 @@ RICH_TEXT_FEATURES = [
     "document-link",
 ]
 
+CANDIDATE_RICH_TEXT_FEATURES = [
+    "h3",
+    "bold",
+    "italic",
+    "superscript",
+    "subscript",
+    "strikethrough",
+    "ul-elections2021",
+    "ol-elections2021",
+    "blockquote-elections2021",
+    "link",
+]
+
 ARTICLES_PER_PAGE = 9
 TOP_CANDIDATES_NUM = 12
 
+REGION_PHA = "PHA"
 REGION_JHC = "JHC"
 REGION_JHM = "JHM"
 REGION_KVK = "KVK"
+REGION_VYS = "VYS"
 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_PHA, "Hlavní město Praha"),
     (REGION_JHC, "JihoÄŤeskĂ˝ kraj"),
     (REGION_JHM, "JihomoravskĂ˝ kraj"),
     (REGION_KVK, "KarlovarskĂ˝ kraj"),
+    (REGION_VYS, "Kraj VysoÄŤina"),
     (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"),
 )
 
+REGION_NAME_VARIANT = {
+    REGION_PHA: "Hlavním městě Praha",
+    REGION_JHC: "Jihočeském kraji",
+    REGION_JHM: "Jihomoravském kraji",
+    REGION_KVK: "Karlovarském kraji",
+    REGION_VYS: "Kraji VysoÄŤina",
+    REGION_KHK: "Královéhradeckém kraji",
+    REGION_LBK: "Libereckém kraji",
+    REGION_MSK: "Moravskoslezském kraji",
+    REGION_OLK: "Olomouckém kraji",
+    REGION_PAK: "Pardubickém kraji",
+    REGION_PLK: "Plzeňském kraji",
+    REGION_STC: "Středočeském kraji",
+    REGION_ULK: "Ústeckém kraji",
+    REGION_ZLK: "Zlínském kraji",
+}
+
 PIRATES = "pirati"
 STAN = "stan"
 PARTY_CHOICES = ((PIRATES, "Piráti"), (STAN, "STAN"))
diff --git a/elections2021/migrations/0003_auto_20210505_0137.py b/elections2021/migrations/0003_auto_20210505_0137.py
new file mode 100644
index 0000000000000000000000000000000000000000..a282049976aaf65f86707037040064021cc597ac
--- /dev/null
+++ b/elections2021/migrations/0003_auto_20210505_0137.py
@@ -0,0 +1,40 @@
+# Generated by Django 3.1.7 on 2021-05-04 23:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        (
+            "elections2021",
+            "0002_elections2021candidatepage_elections2021candidateslistpage_elections2021candidatesmappage",
+        ),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="elections2021candidatepage",
+            name="region",
+            field=models.CharField(
+                choices=[
+                    ("PHA", "Hlavní město Praha"),
+                    ("JHC", "JihoÄŤeskĂ˝ kraj"),
+                    ("JHM", "JihomoravskĂ˝ kraj"),
+                    ("KVK", "KarlovarskĂ˝ kraj"),
+                    ("VYS", "Kraj VysoÄŤina"),
+                    ("KHK", "Královéhradecký kraj"),
+                    ("LBK", "LibereckĂ˝ kraj"),
+                    ("MSK", "MoravskoslezskĂ˝ kraj"),
+                    ("OLK", "OlomouckĂ˝ kraj"),
+                    ("PAK", "PardubickĂ˝ kraj"),
+                    ("PLK", "Plzeňský kraj"),
+                    ("STC", "Středočeský kraj"),
+                    ("ULK", "ĂšsteckĂ˝ kraj"),
+                    ("ZLK", "ZlĂ­nskĂ˝ kraj"),
+                ],
+                max_length=3,
+                verbose_name="kraj",
+            ),
+        ),
+    ]
diff --git a/elections2021/models.py b/elections2021/models.py
index 7a65cb412c3d918ec44933fc234bd472d38ddabd..d7af61c2d838ac802cee4cc4e594d592edae710d 100644
--- a/elections2021/models.py
+++ b/elections2021/models.py
@@ -25,19 +25,26 @@ from shared.utils import get_subpage_url
 from tuning import help
 
 from .constants import (
+    ARTICLE_RICH_TEXT_FEATURES,
     ARTICLES_PER_PAGE,
+    CANDIDATE_RICH_TEXT_FEATURES,
     PARTY_CHOICES,
     PARTY_NAME,
     PIRATES,
     REGION_CHOICES,
+    REGION_NAME_VARIANT,
     REGION_PHA,
-    RICH_TEXT_FEATURES,
     STYLE_CHOICES,
     STYLE_CSS,
     TOP_CANDIDATES_NUM,
     WHITE,
 )
 
+NO_SEARCH_IMAGE_USE_PHOTO = (
+    "Pokud není zadán <strong>Search image</strong>, použije se <strong>hlavní "
+    "fotka</strong> (tab obsah)."
+)
+
 
 class LinkBlock(blocks.StructBlock):
     title = blocks.CharBlock(label="titulek")
@@ -63,7 +70,17 @@ class Elections2021HomePage(Page, MetadataPageMixin):
 
     ### PANELS
 
-    # TODO promote_panels
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                ImageChooserPanel("search_image"),
+                HelpPanel(help.build(help.IMPORTANT_TITLE)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
 
     settings_panels = [
         FieldPanel("matomo_id"),
@@ -111,7 +128,7 @@ class Elections2021ArticleTag(TaggedItemBase):
 class Elections2021ArticlePage(ArticleMixin, SubpageMixin, MetadataPageMixin, Page):
     ### FIELDS
 
-    text = RichTextField("článek", blank=True, features=RICH_TEXT_FEATURES)
+    text = RichTextField("článek", blank=True, features=ARTICLE_RICH_TEXT_FEATURES)
     tags = ClusterTaggableManager(through=Elections2021ArticleTag, blank=True)
     card_style = models.CharField(
         "styl karty článku", choices=STYLE_CHOICES, default=WHITE, max_length=10
@@ -119,13 +136,23 @@ class Elections2021ArticlePage(ArticleMixin, SubpageMixin, MetadataPageMixin, Pa
 
     ### PANELS
 
-    # TODO promote_panels
-
     content_panels = ArticleMixin.content_panels + [
         FieldPanel("tags"),
         FieldPanel("card_style"),
     ]
 
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("slug"),
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_DESCRIPTION_USE_PEREX)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
+
     ### RELATIONS
 
     parent_page_types = ["elections2021.Elections2021ArticlesPage"]
@@ -136,6 +163,16 @@ class Elections2021ArticlePage(ArticleMixin, SubpageMixin, MetadataPageMixin, Pa
     class Meta:
         verbose_name = "Aktualita"
 
+    def get_meta_image(self):
+        return self.image
+
+    def get_meta_description(self):
+        if self.search_description:
+            return self.search_description
+        if len(self.perex) > 150:
+            return str(self.perex)[:150] + "..."
+        return self.perex
+
     def card_css_class(self):
         return STYLE_CSS[self.card_style]
 
@@ -163,10 +200,21 @@ class Elections2021ArticlesPage(SubpageMixin, MetadataPageMixin, Page):
 
     ### PANELS
 
-    # TODO promote_panels
-
     content_panels = Page.content_panels + [ImageChooserPanel("photo")]
 
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("slug"),
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                ImageChooserPanel("search_image"),
+                HelpPanel(help.build(help.NO_SEO_TITLE, NO_SEARCH_IMAGE_USE_PHOTO)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
+
     settings_panels = []
 
     ### RELATIONS
@@ -179,6 +227,9 @@ class Elections2021ArticlesPage(SubpageMixin, MetadataPageMixin, Page):
     class Meta:
         verbose_name = "Aktuality"
 
+    def get_meta_image(self):
+        return self.search_image or self.photo
+
     def get_context(self, request):
         context = super().get_context(request)
         context["articles"] = Paginator(
@@ -201,10 +252,21 @@ class Elections2021CandidatesListPage(SubpageMixin, MetadataPageMixin, Page):
 
     ### PANELS
 
-    # TODO promote_panels
-
     content_panels = Page.content_panels + [ImageChooserPanel("photo")]
 
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("slug"),
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                ImageChooserPanel("search_image"),
+                HelpPanel(help.build(help.NO_SEO_TITLE, NO_SEARCH_IMAGE_USE_PHOTO)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
+
     settings_panels = []
 
     ### RELATIONS
@@ -217,6 +279,9 @@ class Elections2021CandidatesListPage(SubpageMixin, MetadataPageMixin, Page):
     class Meta:
         verbose_name = "Kandidáti"
 
+    def get_meta_image(self):
+        return self.search_image or self.photo
+
     def get_context(self, request):
         context = super().get_context(request)
 
@@ -263,10 +328,21 @@ class Elections2021CandidatesMapPage(SubpageMixin, MetadataPageMixin, Page):
 
     ### PANELS
 
-    # TODO promote_panels
-
     content_panels = Page.content_panels + [ImageChooserPanel("photo")]
 
+    promote_panels = [
+        MultiFieldPanel(
+            [
+                FieldPanel("slug"),
+                FieldPanel("seo_title"),
+                FieldPanel("search_description"),
+                ImageChooserPanel("search_image"),
+                HelpPanel(help.build(help.NO_SEO_TITLE, NO_SEARCH_IMAGE_USE_PHOTO)),
+            ],
+            gettext_lazy("Common page configuration"),
+        ),
+    ]
+
     settings_panels = []
 
     ### RELATIONS
@@ -279,6 +355,9 @@ class Elections2021CandidatesMapPage(SubpageMixin, MetadataPageMixin, Page):
     class Meta:
         verbose_name = "Kandidáti mapa"
 
+    def get_meta_image(self):
+        return self.search_image or self.photo
+
 
 class Elections2021CandidatePage(SubpageMixin, MetadataPageMixin, Page):
     ### FIELDS
@@ -291,8 +370,7 @@ class Elections2021CandidatePage(SubpageMixin, MetadataPageMixin, Page):
     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")
+    resume = RichTextField("medailonek", features=CANDIDATE_RICH_TEXT_FEATURES)
     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)
@@ -345,9 +423,6 @@ class Elections2021CandidatePage(SubpageMixin, MetadataPageMixin, Page):
         MultiFieldPanel(
             [
                 FieldPanel("slug"),
-                FieldPanel("seo_title"),
-                FieldPanel("search_description"),
-                HelpPanel(help.build(help.NO_SEO_TITLE)),
             ],
             gettext_lazy("Common page configuration"),
         ),
@@ -368,6 +443,10 @@ class Elections2021CandidatePage(SubpageMixin, MetadataPageMixin, Page):
     def get_meta_image(self):
         return self.photo
 
+    def get_meta_description(self):
+        name = REGION_NAME_VARIANT[self.region]
+        return f"Kandidát č. {self.number} za koalici Piráti a Starostové v {name}."
+
     @property
     def party_name(self):
         return PARTY_NAME[self.party]