diff --git a/uniweb/blocks.py b/uniweb/blocks.py
new file mode 100644
index 0000000000000000000000000000000000000000..1aaf9c48bca5524a4594ef6f237a453fb736c356
--- /dev/null
+++ b/uniweb/blocks.py
@@ -0,0 +1,42 @@
+from wagtail.blocks import (
+    CharBlock,
+    ListBlock,
+    PageChooserBlock,
+    StructBlock,
+    URLBlock,
+)
+
+
+class PersonUrlBlock(StructBlock):
+    title = CharBlock(label="Název", required=True)
+    url = URLBlock(label="URL", required=True)
+    custom_icon = CharBlock(
+        label="Vlastní ikonka ze styleguide",
+        required=False,
+        help_text="Pro vlastní ikonku zadejde název ikonky z https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons (bez tečky), např. 'ico--beer'",
+    )
+
+
+class PersonCustomPositionBlock(StructBlock):
+    position = CharBlock(label="Název pozice", required=False)
+    person = PageChooserBlock(
+        label="Osoba",
+        page_type=["uniweb.UniwebPersonPage"],
+    )
+
+    class Meta:
+        icon = "user"
+        label = "Osoba s volitelnou pozicí"
+
+
+class PeopleGroupListBlock(StructBlock):
+    group_title = CharBlock(label="Titulek", required=True)
+    person_list = ListBlock(
+        PersonCustomPositionBlock(),
+        label="List osob",
+    )
+
+    class Meta:
+        template = "uniweb/blocks/people_group_block.html"
+        icon = "list-ul"
+        label = "Skupina členů" 
diff --git a/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py b/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa8762745cfffa846f5ba15f7324f107056ee32d
--- /dev/null
+++ b/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py
@@ -0,0 +1,64 @@
+# Generated by Django 4.1.6 on 2023-03-09 23:46
+
+from django.db import migrations, models
+import django.db.models.deletion
+import shared.models
+import wagtail.blocks
+import wagtail.fields
+import wagtailmetadata.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wagtailcore', '0083_workflowcontenttype'),
+        ('wagtailimages', '0025_alter_image_file_alter_rendition_file'),
+        ('uniweb', '0037_alter_uniwebflexiblepage_content_and_more'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='UniwebPersonPage',
+            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')),
+                ('job', models.CharField(blank=True, help_text="Např. 'Informatik'", max_length=128, null=True, verbose_name='Povolání')),
+                ('job_function', models.CharField(blank=True, help_text="Např. 'Předseda'", max_length=128, null=True, verbose_name='Funkce')),
+                ('text', wagtail.fields.RichTextField(blank=True, verbose_name='text')),
+                ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')),
+                ('show_email', models.BooleanField(default=True, verbose_name='Zobrazovat email na stránce?')),
+                ('phone', models.CharField(blank=True, max_length=16, null=True, verbose_name='Telefon')),
+                ('city', models.CharField(blank=True, max_length=64, null=True, verbose_name='Město/obec')),
+                ('age', models.IntegerField(blank=True, null=True, verbose_name='Věk')),
+                ('is_pirate', models.BooleanField(default=True, verbose_name='Je členem Pirátské strany?')),
+                ('other_party', models.CharField(blank=True, help_text='Vyplňte pokud osoba není Pirát', max_length=64, null=True, verbose_name='Strana')),
+                ('facebook_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Facebook')),
+                ('instagram_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Instagram')),
+                ('twitter_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Twitter')),
+                ('youtube_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Youtube kanál')),
+                ('flickr_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Flickr')),
+                ('custom_web_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na vlastní web')),
+                ('other_urls', wagtail.fields.StreamField([('other_url', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Název', required=True)), ('url', wagtail.blocks.URLBlock(label='URL', required=True)), ('custom_icon', wagtail.blocks.CharBlock(help_text="Pro vlastní ikonku zadejde název ikonky z https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons (bez tečky), např. 'ico--beer'", label='Vlastní ikonka ze styleguide', required=False))]))], blank=True, use_json_field=True, verbose_name='Další odkaz')),
+                ('background_photo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='obrázek do záhlaví')),
+                ('other_party_logo', models.ForeignKey(blank=True, help_text='Vyplňte pokud osoba není Pirát', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='Logo strany')),
+                ('profile_photo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='profilová 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': 'Detail osoby',
+                'ordering': ('title',),
+            },
+            bases=(shared.models.SubpageMixin, wagtailmetadata.models.WagtailImageMetadataMixin, 'wagtailcore.page', models.Model),
+        ),
+        migrations.CreateModel(
+            name='UniwebPeoplePage',
+            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')),
+                ('content', wagtail.fields.StreamField([('text', wagtail.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'hr', 'link', 'document-link', 'image', 'superscript', 'subscript', 'strikethrough', 'blockquote', 'embed'], label='Textový editor')), ('people_group', wagtail.blocks.StructBlock([('group_title', wagtail.blocks.CharBlock(label='Titulek', required=True)), ('person_list', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('position', wagtail.blocks.CharBlock(label='Název pozice', required=False)), ('person', wagtail.blocks.PageChooserBlock(label='Osoba', page_type=['uniweb.UniwebPersonPage']))]), label='List osob'))]))], blank=True, use_json_field=True, verbose_name='Obsah stránky')),
+                ('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': 'Lidé',
+            },
+            bases=(shared.models.SubpageMixin, wagtailmetadata.models.WagtailImageMetadataMixin, 'wagtailcore.page', models.Model),
+        ),
+    ]
diff --git a/uniweb/models.py b/uniweb/models.py
index e6b613cf50ee0eddeaf417428af235fa41ef0cf4..947ffc2e9f66d65c9a4915c97373d1e337152b18 100644
--- a/uniweb/models.py
+++ b/uniweb/models.py
@@ -17,13 +17,14 @@ from wagtail.admin.panels import (
 from wagtail.contrib.forms.models import AbstractForm, AbstractFormField
 from wagtail.contrib.forms.panels import FormSubmissionsPanel
 from wagtail.contrib.table_block.blocks import TableBlock
-from wagtail.fields import StreamField
+from wagtail.fields import RichTextField, StreamField
 from wagtail.images.blocks import ImageChooserBlock
 from wagtail.models import Page
 from wagtailmetadata.models import MetadataPageMixin
 
 from calendar_utils.models import CalendarMixin
 from shared.blocks import ChartBlock
+from shared.const import RICH_TEXT_DEFAULT_FEATURES
 from shared.models import (
     ArticleMixin,
     ExtendedMetadataHomePageMixin,
@@ -33,6 +34,11 @@ from shared.models import (
 from shared.utils import make_promote_panels
 from tuning import admin_help
 
+from .blocks import (
+    PersonCustomPositionBlock,
+    PeopleGroupListBlock,
+    PersonUrlBlock,
+)
 from .constants import (
     ALIGN_CHOICES,
     ALIGN_CSS,
@@ -580,3 +586,204 @@ class UniwebFormPage(
         form = super().get_form_class()
         form.base_fields["captcha"] = CaptchaField(label="opište písmena z obrázku")
         return form
+
+
+# Don't waste time making a new mixin for this, we'll be doing Octopus imports
+# within a short while.
+class UniwebPersonPage(
+    ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
+):
+    ### FIELDS
+    job = models.CharField(
+        "Povolání",
+        max_length=128,
+        blank=True,
+        null=True,
+        help_text="Např. 'Informatik'",
+    )
+    job_function = models.CharField(
+        "Funkce", max_length=128, blank=True, null=True, help_text="Např. 'Předseda'"
+    )
+    background_photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name="+",
+        verbose_name="obrázek do záhlaví",
+    )
+    profile_photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name="+",
+        verbose_name="profilová fotka",
+    )
+    text = RichTextField("text", blank=True, features=RICH_TEXT_DEFAULT_FEATURES)
+
+    email = models.EmailField("Email", null=True, blank=True)
+    show_email = models.BooleanField("Zobrazovat email na stránce?", default=True)
+    phone = models.CharField("Telefon", max_length=16, blank=True, null=True)
+    city = models.CharField("Město/obec", max_length=64, blank=True, null=True)
+    age = models.IntegerField("Věk", blank=True, null=True)
+    is_pirate = models.BooleanField("Je členem Pirátské strany?", default=True)
+    other_party = models.CharField(
+        "Strana",
+        max_length=64,
+        blank=True,
+        null=True,
+        help_text="Vyplňte pokud osoba není Pirát",
+    )
+    other_party_logo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name="+",
+        verbose_name="Logo strany",
+        help_text="Vyplňte pokud osoba není Pirát",
+    )
+
+    facebook_url = models.URLField("Odkaz na Facebook", blank=True, null=True)
+    instagram_url = models.URLField("Odkaz na Instagram", blank=True, null=True)
+    twitter_url = models.URLField("Odkaz na Twitter", blank=True, null=True)
+    youtube_url = models.URLField("Odkaz na Youtube kanál", blank=True, null=True)
+    flickr_url = models.URLField("Odkaz na Flickr", blank=True, null=True)
+    custom_web_url = models.URLField("Odkaz na vlastní web", blank=True, null=True)
+    other_urls = StreamField(
+        [("other_url", PersonUrlBlock())],
+        verbose_name="Další odkaz",
+        blank=True,
+        use_json_field=True,
+    )
+
+    ### PANELS
+
+    content_panels = Page.content_panels + [
+        MultiFieldPanel(
+            [
+                FieldPanel("job"),
+                FieldPanel("job_function"),
+            ],
+            "Základní údaje",
+        ),
+        MultiFieldPanel(
+            [
+                FieldPanel("profile_photo"),
+                FieldPanel("background_photo"),
+            ],
+            "Fotky",
+        ),
+        FieldPanel("text"),
+        MultiFieldPanel(
+            [
+                FieldPanel("email"),
+                FieldPanel("show_email"),
+                FieldPanel("phone"),
+                FieldPanel("city"),
+                FieldPanel("age"),
+                FieldPanel("is_pirate"),
+                FieldPanel("other_party"),
+                FieldPanel("other_party_logo"),
+            ],
+            "Kontaktní informace",
+        ),
+        MultiFieldPanel(
+            [
+                FieldPanel("facebook_url"),
+                FieldPanel("instagram_url"),
+                FieldPanel("twitter_url"),
+                FieldPanel("youtube_url"),
+                FieldPanel("flickr_url"),
+                FieldPanel("custom_web_url"),
+                FieldPanel("other_urls"),
+            ],
+            "Sociální sítě",
+        ),
+    ]
+
+    settings_panels = []
+
+    ### RELATIONS
+
+    parent_page_types = ["uniweb.UniwebPeoplePage"]
+    subpage_types = []
+
+    ### OTHERS
+
+    class Meta:
+        verbose_name = "Detail osoby"
+        ordering = ("title",)
+
+    def get_background_photo(self):
+        """
+        Vrací background_photo pro pozadí na stránce, pokud není nastaveno,
+        vezme falbback z homepage
+        """
+        return (
+            self.background_photo
+            if self.background_photo
+            else self.root_page.fallback_image
+        )
+
+    def get_context(self, request):
+        context = super().get_context(request)
+        # Na strance detailu cloveka se vpravo zobrazuji 3 dalsi nahodne profily
+        context["random_people"] = list(
+            self.get_siblings(inclusive=False).live().specific()
+        )
+        random.shuffle(context["random_people"])
+        context["random_people"] = context["random_people"][:3]
+        return context
+
+    def get_meta_image(self):
+        return self.search_image or self.profile_photo
+
+    def get_meta_description(self):
+        if self.search_description:
+            return self.search_description
+
+        if self.text:
+            return trim_to_length(strip_all_html_tags(self.text))
+
+        return None
+
+
+class UniwebPeoplePage(
+    ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
+):
+    ### FIELDS
+
+    content = StreamField(
+        [
+            (
+                "text",
+                blocks.RichTextBlock(
+                    label="Textový editor", features=RICH_TEXT_DEFAULT_FEATURES
+                ),
+            ),
+            ("people_group", PeopleGroupListBlock()),
+        ],
+        verbose_name="Obsah stránky",
+        blank=True,
+        use_json_field=True,
+    )
+
+    ### PANELS
+
+    content_panels = Page.content_panels + [FieldPanel("content")]
+
+    promote_panels = make_promote_panels()
+
+    settings_panels = []
+
+    ### RELATIONS
+
+    parent_page_types = ["uniweb.UniwebHomePage"]
+    subpage_types = ["uniweb.UniwebPersonPage"]
+
+    ### OTHERS
+
+    class Meta:
+        verbose_name = "Lidé"