diff --git a/opentech/apply/categories/admin.py b/opentech/apply/categories/admin.py
index 805d8aeae3a290387b25ebf04f13cba0121c7b9d..2eb24d179ee07a77d238c5cb068d6458ab2b1162 100644
--- a/opentech/apply/categories/admin.py
+++ b/opentech/apply/categories/admin.py
@@ -1,9 +1,42 @@
+from django.conf.urls import url
 from wagtail.contrib.modeladmin.options import ModelAdmin
 
-from .models import Category
+from .admin_helpers import MetaCategoryButtonHelper
+from .admin_views import AddChildMetaCategoryViewClass
+from .models import Category, MetaCategory
 
 
 class CategoryAdmin(ModelAdmin):
     menu_label = 'Category Questions'
     menu_icon = 'list-ul'
     model = Category
+
+
+class MetaCategoryAdmin(ModelAdmin):
+    model = MetaCategory
+
+    menu_icon = 'tag'
+
+    list_per_page = 50
+    list_display = ('get_as_listing_header', 'get_parent')
+    search_fields = ('name',)
+
+    inspect_view_enabled = True
+    inspect_view_fields = ('name', 'get_parent', 'id')
+
+    button_helper_class = MetaCategoryButtonHelper
+
+    def add_child_view(self, request, instance_pk):
+        kwargs = {'model_admin': self, 'parent_pk': instance_pk}
+        view_class = AddChildMetaCategoryViewClass
+        return view_class.as_view(**kwargs)(request)
+
+    def get_admin_urls_for_registration(self):
+        """Add the new url for add child page to the registered URLs."""
+        urls = super().get_admin_urls_for_registration()
+        add_child_url = url(
+            self.url_helper.get_action_url_pattern('add_child'),
+            self.add_child_view,
+            name=self.url_helper.get_action_url_name('add_child')
+        )
+        return urls + (add_child_url, )
diff --git a/opentech/apply/categories/admin_helpers.py b/opentech/apply/categories/admin_helpers.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f786fced1de4030624668a1e96c39297d414ba2
--- /dev/null
+++ b/opentech/apply/categories/admin_helpers.py
@@ -0,0 +1,47 @@
+from django.contrib.admin.utils import quote
+
+from wagtail.contrib.modeladmin.helpers import ButtonHelper
+
+
+class MetaCategoryButtonHelper(ButtonHelper):
+    def delete_button(self, pk, *args, **kwargs):
+        """Ensure that the delete button is not shown for root category."""
+        instance = self.model.objects.get(pk=pk)
+        if instance.is_root():
+            return
+        return super().delete_button(pk, *args, **kwargs)
+
+    def prepare_classnames(self, start=None, add=None, exclude=None):
+        """Parse classname sets into final css classess list."""
+        classnames = start or []
+        classnames.extend(add or [])
+        return self.finalise_classname(classnames, exclude or [])
+
+    def add_child_button(self, pk, child_verbose_name, **kwargs):
+        """Build a add child button, to easily add a child under category."""
+        classnames = self.prepare_classnames(
+            start=self.edit_button_classnames + ['icon', 'icon-plus'],
+            add=kwargs.get('classnames_add'),
+            exclude=kwargs.get('classnames_exclude')
+        )
+        return {
+            'classname': classnames,
+            'label': 'Add %s %s' % (
+                child_verbose_name, self.verbose_name),
+            'title': 'Add %s %s under this one' % (
+                child_verbose_name, self.verbose_name),
+            'url': self.url_helper.get_action_url('add_child', quote(pk)),
+        }
+
+    def get_buttons_for_obj(self, obj, exclude=None, *args, **kwargs):
+        """Override the getting of buttons, prepending create child button."""
+        buttons = super().get_buttons_for_obj(obj, *args, **kwargs)
+
+        add_child_button = self.add_child_button(
+            pk=getattr(obj, self.opts.pk.attname),
+            child_verbose_name=getattr(obj, 'node_child_verbose_name'),
+            **kwargs
+        )
+        buttons.append(add_child_button)
+
+        return buttons
diff --git a/opentech/apply/categories/admin_views.py b/opentech/apply/categories/admin_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..890bccbe451363c024e42286f885067a66515838
--- /dev/null
+++ b/opentech/apply/categories/admin_views.py
@@ -0,0 +1,31 @@
+from django.contrib.admin.utils import unquote
+from django.shortcuts import get_object_or_404
+
+from wagtail.contrib.modeladmin.views import CreateView
+
+
+class AddChildMetaCategoryViewClass(CreateView):
+    """View class that can take an additional URL param for parent id."""
+
+    parent_pk = None
+    parent_instance = None
+
+    def __init__(self, model_admin, parent_pk):
+        self.parent_pk = unquote(parent_pk)
+        object_qs = model_admin.model._default_manager.get_queryset()
+        object_qs = object_qs.filter(pk=self.parent_pk)
+        self.parent_instance = get_object_or_404(object_qs)
+        super().__init__(model_admin)
+
+    def get_page_title(self):
+        """Generate a title that explains you are adding a child."""
+        title = super().get_page_title()
+        return title + ' %s %s for %s' % (
+            self.model.node_child_verbose_name,
+            self.opts.verbose_name,
+            self.parent_instance
+        )
+
+    def get_initial(self):
+        """Set the selected parent field to the parent_pk."""
+        return {'parent': self.parent_pk}
diff --git a/opentech/apply/categories/migrations/0002_metacategory.py b/opentech/apply/categories/migrations/0002_metacategory.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f47ec33488f216aeafa47b0d35b9f11dcbe92c3
--- /dev/null
+++ b/opentech/apply/categories/migrations/0002_metacategory.py
@@ -0,0 +1,30 @@
+# Generated by Django 2.0.9 on 2019-02-22 15:06
+
+from django.db import migrations, models
+import wagtail.search.index
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('categories', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='MetaCategory',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('path', models.CharField(max_length=255, unique=True)),
+                ('depth', models.PositiveIntegerField()),
+                ('numchild', models.PositiveIntegerField(default=0)),
+                ('name', models.CharField(help_text='Keep the name short, ideally one word.', max_length=50, unique=True)),
+                ('node_order_index', models.IntegerField(blank=True, default=0, editable=False)),
+            ],
+            options={
+                'verbose_name': 'Meta Category',
+                'verbose_name_plural': 'Meta Categories',
+            },
+            bases=(wagtail.search.index.Indexed, models.Model),
+        ),
+    ]
diff --git a/opentech/apply/categories/models.py b/opentech/apply/categories/models.py
index 3200f4bf556a39dec0235ad72db8b50223f9f137..81ce64591b3e827bfe49197c62e2d7cb18f07824 100644
--- a/opentech/apply/categories/models.py
+++ b/opentech/apply/categories/models.py
@@ -1,11 +1,19 @@
+from django import forms
+from django.core.exceptions import PermissionDenied
 from django.db import models
+from django.template.loader import render_to_string
+
 from modelcluster.fields import ParentalKey
 from modelcluster.models import ClusterableModel
 from wagtail.admin.edit_handlers import (
     FieldPanel,
     InlinePanel,
 )
+from wagtail.admin.forms import WagtailAdminModelForm
 from wagtail.core.models import Orderable
+from wagtail.search import index
+
+from treebeard.mp_tree import MP_Node
 
 
 class Option(Orderable):
@@ -33,3 +41,107 @@ class Category(ClusterableModel):
 
     class Meta:
         verbose_name_plural = 'Categories'
+
+
+class MetaCategory(index.Indexed, MP_Node):
+    """ Hierarchal "Meta" category """
+    name = models.CharField(
+        max_length=50, unique=True, help_text='Keep the name short, ideally one word.'
+    )
+
+    # node tree specific fields and attributes
+    node_order_index = models.IntegerField(blank=True, default=0, editable=False)
+    node_child_verbose_name = 'child'
+
+    # important: node_order_by should NOT be changed after first Node created
+    node_order_by = ['node_order_index', 'name']
+
+    panels = [
+        FieldPanel('parent'),
+        FieldPanel('name'),
+    ]
+
+    def get_as_listing_header(self):
+        depth = self.get_depth()
+        rendered = render_to_string(
+            'categories/admin/includes/meta_category_list_header.html',
+            {
+                'depth': depth,
+                'depth_minus_1': depth - 1,
+                'is_root': self.is_root(),
+                'name': self.name,
+            }
+        )
+        return rendered
+    get_as_listing_header.short_description = 'Name'
+    get_as_listing_header.admin_order_field = 'name'
+
+    def get_parent(self, *args, **kwargs):
+        return super().get_parent(*args, **kwargs)
+    get_parent.short_description = 'Parent'
+
+    search_fields = [
+        index.SearchField('name', partial_match=True),
+    ]
+
+    def delete(self):
+        if self.is_root():
+            raise PermissionDenied('Cannot delete root Category.')
+        else:
+            super().delete()
+
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        verbose_name = 'Meta Category'
+        verbose_name_plural = 'Meta Categories'
+
+
+class MetaCategoryChoiceField(forms.ModelChoiceField):
+    def label_from_instance(self, obj):
+        depth_line = '-' * (obj.get_depth() - 1)
+        return "{} {}".format(depth_line, super().label_from_instance(obj))
+
+
+class MetaCategoryForm(WagtailAdminModelForm):
+    parent = MetaCategoryChoiceField(
+        required=True,
+        queryset=MetaCategory.objects.all(),
+        empty_label=None,
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        instance = kwargs['instance']
+
+        if instance.is_root() or MetaCategory.objects.count() == 0:
+            self.fields['parent'].disabled = True
+            self.fields['parent'].required = False
+            self.fields['parent'].empty_label = 'N/A - Root Category'
+            self.fields['parent'].widget = forms.HiddenInput()
+
+            self.fields['name'].label += ' (Root - First category can be named root)'
+        elif instance.id:
+            self.fields['parent'].initial = instance.get_parent()
+
+    def save(self, commit=True, *args, **kwargs):
+        instance = super().save(commit=False, *args, **kwargs)
+        parent = self.cleaned_data['parent']
+
+        if not commit:
+            return instance
+
+        if instance.id is None:
+            if MetaCategory.objects.all().count() == 0:
+                MetaCategory.add_root(instance=instance)
+            else:
+                instance = parent.add_child(instance=instance)
+        else:
+            instance.save()
+            if instance.get_parent() != parent:
+                instance.move(parent, pos='sorted-child')
+        return instance
+
+
+MetaCategory.base_form_class = MetaCategoryForm
diff --git a/opentech/apply/categories/templates/categories/admin/includes/meta_category_list_header.html b/opentech/apply/categories/templates/categories/admin/includes/meta_category_list_header.html
new file mode 100644
index 0000000000000000000000000000000000000000..b6a816694bcffbec1fc4fb524ce0931a9e111fcb
--- /dev/null
+++ b/opentech/apply/categories/templates/categories/admin/includes/meta_category_list_header.html
@@ -0,0 +1,9 @@
+{% if is_root %}
+  <span><strong>{{ name }}</strong></span>
+{% else %}
+  <span>
+    <span class="inline-block" style="margin-left:{{ depth }}em; font-size:{% if depth is 1 %}90{% elif depth is 2 %}80{% else %}100{% endif %}%;"></span>
+    <i class="icon icon-fa-level-up icon-fa-rotate-90" style="display: inline-block;"></i>
+    {{ name }}
+  </span>
+{% endif %}
diff --git a/opentech/apply/funds/admin.py b/opentech/apply/funds/admin.py
index 689f73027216344b55a4b82eed731de5b7b55dfb..4dc9d3679087fbc58a8f9148b32a2b6da20b7e42 100644
--- a/opentech/apply/funds/admin.py
+++ b/opentech/apply/funds/admin.py
@@ -10,7 +10,7 @@ from .admin_helpers import (
     RoundFundChooserView,
 )
 from .models import ApplicationForm, FundType, LabType, RequestForPartners, Round, SealedRound
-from opentech.apply.categories.admin import CategoryAdmin
+from opentech.apply.categories.admin import CategoryAdmin, MetaCategoryAdmin
 
 
 class BaseRoundAdmin(ModelAdmin):
@@ -106,4 +106,5 @@ class ApplyAdminGroup(ModelAdminGroup):
         ReviewFormAdmin,
         CategoryAdmin,
         ScreeningStatusAdmin,
+        MetaCategoryAdmin,
     )