+ {% include '_types/blog/_card/authors.html' %}
+
+
+ {% endif %}
+
diff --git a/templates/_types/blog/_card/author.html b/templates/_types/blog/_card/author.html
new file mode 100644
index 0000000..7ddddf7
--- /dev/null
+++ b/templates/_types/blog/_card/author.html
@@ -0,0 +1,21 @@
+{% macro author(author) %}
+ {% if author %}
+ {% if author.profile_image %}
+
+ {% else %}
+
+ {# optional fallback circle with first letter
+
+ {{ author.name[:1] }}
+
#}
+ {% endif %}
+
+
+ {{ author.name }}
+
+ {% endif %}
+{% endmacro %}
\ No newline at end of file
diff --git a/templates/_types/blog/_card/authors.html b/templates/_types/blog/_card/authors.html
new file mode 100644
index 0000000..5b8911d
--- /dev/null
+++ b/templates/_types/blog/_card/authors.html
@@ -0,0 +1,32 @@
+{# --- AUTHORS LIST STARTS HERE --- #}
+ {% if post.authors and post.authors|length %}
+ {% for a in post.authors %}
+ {% for author in authors if author.slug==a.slug %}
+
+ {% endfor %}
+ {% endfor %}
+ {% endif %}
+
+ {# --- AUTHOR LIST ENDS HERE --- #}
\ No newline at end of file
diff --git a/templates/_types/blog/_card/tag.html b/templates/_types/blog/_card/tag.html
new file mode 100644
index 0000000..137cb0c
--- /dev/null
+++ b/templates/_types/blog/_card/tag.html
@@ -0,0 +1,19 @@
+{% macro tag(tag) %}
+ {% if tag %}
+ {% if tag.feature_image %}
+
+ {% else %}
+
+ {{ tag.name[:1] }}
+
+ {% endif %}
+
+
+ {{ tag.name }}
+
+ {% endif %}
+{% endmacro %}
\ No newline at end of file
diff --git a/templates/_types/blog/_card/tag_group.html b/templates/_types/blog/_card/tag_group.html
new file mode 100644
index 0000000..21c9974
--- /dev/null
+++ b/templates/_types/blog/_card/tag_group.html
@@ -0,0 +1,22 @@
+{% macro tag_group(group) %}
+ {% if group %}
+ {% if group.feature_image %}
+
+ {% else %}
+
+ {{ group.name[:1] }}
+
+ {% endif %}
+
+
+ {{ group.name }}
+
+ {% endif %}
+{% endmacro %}
diff --git a/templates/_types/blog/_card/tags.html b/templates/_types/blog/_card/tags.html
new file mode 100644
index 0000000..2ea7ad1
--- /dev/null
+++ b/templates/_types/blog/_card/tags.html
@@ -0,0 +1,17 @@
+{% import '_types/blog/_card/tag.html' as dotag %}
+{# --- TAG LIST STARTS HERE --- #}
+ {% if post.tags and post.tags|length %}
+ {% for t in post.tags %}
+ {% for tag in tags if tag.slug==t.slug %}
+
+ {% include '_types/blog/desktop/menu/tag_groups.html' %}
+ {% include '_types/blog/desktop/menu/authors.html' %}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/_types/blog/desktop/menu/authors.html b/templates/_types/blog/desktop/menu/authors.html
new file mode 100644
index 0000000..de939e0
--- /dev/null
+++ b/templates/_types/blog/desktop/menu/authors.html
@@ -0,0 +1,62 @@
+ {% import '_types/blog/_card/author.html' as doauthor %}
+
+ {# Author filter bar #}
+
+
diff --git a/templates/_types/blog/desktop/menu/tag_groups.html b/templates/_types/blog/desktop/menu/tag_groups.html
new file mode 100644
index 0000000..e23a879
--- /dev/null
+++ b/templates/_types/blog/desktop/menu/tag_groups.html
@@ -0,0 +1,70 @@
+ {# Tag group filter bar #}
+
diff --git a/templates/_types/blog/desktop/menu/tags.html b/templates/_types/blog/desktop/menu/tags.html
new file mode 100644
index 0000000..c20b5bc
--- /dev/null
+++ b/templates/_types/blog/desktop/menu/tags.html
@@ -0,0 +1,59 @@
+ {% import '_types/blog/_card/tag.html' as dotag %}
+
+ {# Tag filter bar #}
+
+
diff --git a/templates/_types/blog/header/_header.html b/templates/_types/blog/header/_header.html
new file mode 100644
index 0000000..67325b9
--- /dev/null
+++ b/templates/_types/blog/header/_header.html
@@ -0,0 +1,7 @@
+
+{% import 'macros/links.html' as links %}
+{% macro header_row(oob=False) %}
+ {% call links.menu_row(id='blog-row', oob=oob) %}
+
+ {% endcall %}
+{% endmacro %}
\ No newline at end of file
diff --git a/templates/_types/blog/index.html b/templates/_types/blog/index.html
new file mode 100644
index 0000000..5978020
--- /dev/null
+++ b/templates/_types/blog/index.html
@@ -0,0 +1,37 @@
+{% extends '_types/root/_index.html' %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+
+{% block root_header_child %}
+ {% from '_types/root/_n/macros.html' import index_row with context %}
+ {% call index_row('root-blog-header', '_types/blog/header/_header.html') %}
+ {% block root_blog_header %}
+ {% endblock %}
+ {% endcall %}
+{% endblock %}
+
+
+{% block aside %}
+ {% include "_types/blog/desktop/menu.html" %}
+{% endblock %}
+
+{% block filter %}
+ {% include "_types/blog/mobile/_filter/summary.html" %}
+{% endblock %}
+
+{% block content %}
+ {% include '_types/blog/_main_panel.html' %}
+{% endblock %}
diff --git a/templates/_types/blog/mobile/_filter/_hamburger.html b/templates/_types/blog/mobile/_filter/_hamburger.html
new file mode 100644
index 0000000..10e0b9c
--- /dev/null
+++ b/templates/_types/blog/mobile/_filter/_hamburger.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/_types/blog/mobile/_filter/summary.html b/templates/_types/blog/mobile/_filter/summary.html
new file mode 100644
index 0000000..4ed013b
--- /dev/null
+++ b/templates/_types/blog/mobile/_filter/summary.html
@@ -0,0 +1,14 @@
+{% import 'macros/layout.html' as layout %}
+
+{% call layout.details('/filter', 'md:hidden') %}
+ {% call layout.filter_summary("filter-summary-mobile", current_local_href, search, search_count, hx_select) %}
+ {% include '_types/blog/mobile/_filter/summary/tag_groups.html' %}
+ {% include '_types/blog/mobile/_filter/summary/authors.html' %}
+ {% endcall %}
+ {% include '_types/blog/_action_buttons.html' %}
+
+ {% include '_types/blog/desktop/menu/tag_groups.html' %}
+ {% include '_types/blog/desktop/menu/authors.html' %}
+
+{% endcall %}
+
\ No newline at end of file
diff --git a/templates/_types/blog/mobile/_filter/summary/authors.html b/templates/_types/blog/mobile/_filter/summary/authors.html
new file mode 100644
index 0000000..32796d9
--- /dev/null
+++ b/templates/_types/blog/mobile/_filter/summary/authors.html
@@ -0,0 +1,31 @@
+{% if selected_authors and selected_authors|length %}
+
+ {% for st in selected_authors %}
+ {% for author in authors %}
+ {% if st == author.slug %}
+
+ {% if author.profile_image %}
+
+ {% else %}
+ {# optional fallback circle with first letter #}
+
+{% endif %}
\ No newline at end of file
diff --git a/templates/_types/blog/mobile/_filter/summary/tag_groups.html b/templates/_types/blog/mobile/_filter/summary/tag_groups.html
new file mode 100644
index 0000000..7bf142e
--- /dev/null
+++ b/templates/_types/blog/mobile/_filter/summary/tag_groups.html
@@ -0,0 +1,33 @@
+{% if selected_groups and selected_groups|length %}
+
+ {% for sg in selected_groups %}
+ {% for group in tag_groups %}
+ {% if sg == group.slug %}
+
+ {% include '_types/post/_entry_items.html' with context %}
+
+
+
+
diff --git a/templates/_types/post/_entry_items.html b/templates/_types/post/_entry_items.html
new file mode 100644
index 0000000..d221e85
--- /dev/null
+++ b/templates/_types/post/_entry_items.html
@@ -0,0 +1,38 @@
+{# Get entries from either direct variable or associated_entries dict #}
+{% set entry_list = entries if entries is defined else (associated_entries.entries if associated_entries is defined else []) %}
+{% set current_page = page if page is defined else (associated_entries.page if associated_entries is defined else 1) %}
+{% set has_more_entries = has_more if has_more is defined else (associated_entries.has_more if associated_entries is defined else False) %}
+
+{% for entry in entry_list %}
+ {% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %}
+
+ {% if post.feature_image %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
diff --git a/templates/_types/post/_oob_elements.html b/templates/_types/post/_oob_elements.html
new file mode 100644
index 0000000..d8bda2c
--- /dev/null
+++ b/templates/_types/post/_oob_elements.html
@@ -0,0 +1,36 @@
+{% extends 'oob_elements.html' %}
+
+
+{# OOB elements for HTMX navigation - all elements that need updating #}
+{# Import shared OOB macros #}
+{% from '_types/root/header/_oob.html' import root_header_start, root_header_end with context %}
+{% from '_types/root/_oob_menu.html' import mobile_menu with context %}
+
+
+
+{% block oobs %}
+ {% from '_types/root/header/_header.html' import header_row with context %}
+ {{ header_row(oob=True) }}
+{% endblock %}
+
+{% from '_types/root/_n/macros.html' import header with context %}
+{% call header(id='root-header-child', oob=True) %}
+ {% call header() %}
+ {% from '_types/post/header/_header.html' import header_row with context %}
+ {{header_row()}}
+
+
+
+ {% endcall %}
+{% endcall %}
+
+
+{# Mobile menu #}
+
+{% block mobile_menu %}
+ {% include '_types/post/_nav.html' %}
+{% endblock %}
+
+{% block content %}
+ {% include '_types/post/_main_panel.html' %}
+{% endblock %}
diff --git a/templates/_types/post/admin/_associated_entries.html b/templates/_types/post/admin/_associated_entries.html
new file mode 100644
index 0000000..d9fe853
--- /dev/null
+++ b/templates/_types/post/admin/_associated_entries.html
@@ -0,0 +1,50 @@
+
+
Associated Entries
+ {% if associated_entry_ids %}
+
+ {% for calendar in all_calendars %}
+ {% for entry in calendar.entries %}
+ {% if entry.id in associated_entry_ids and entry.deleted_at is none %}
+
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+
+ {% else %}
+
No entries associated yet. Browse calendars below to add entries.
+ {% endif %}
+
diff --git a/templates/_types/post/admin/_calendar_view.html b/templates/_types/post/admin/_calendar_view.html
new file mode 100644
index 0000000..80ae33f
--- /dev/null
+++ b/templates/_types/post/admin/_calendar_view.html
@@ -0,0 +1,88 @@
+
+ {% for week in weeks %}
+ {% for day in week %}
+
+
{{ day.date.day }}
+
+ {# Entries for this day #}
+
+ {% for e in month_entries %}
+ {% if e.start_at.date() == day.date %}
+ {% if e.id in associated_entry_ids %}
+ {# Associated entry - show with delete button #}
+
diff --git a/templates/_types/post/admin/_main_panel.html b/templates/_types/post/admin/_main_panel.html
new file mode 100644
index 0000000..58d5238
--- /dev/null
+++ b/templates/_types/post/admin/_main_panel.html
@@ -0,0 +1,7 @@
+{# Main panel fragment for HTMX navigation - post admin #}
+
+
+
diff --git a/templates/_types/post/admin/_nav_entries.html b/templates/_types/post/admin/_nav_entries.html
new file mode 100644
index 0000000..47290d4
--- /dev/null
+++ b/templates/_types/post/admin/_nav_entries.html
@@ -0,0 +1,50 @@
+
+ {# Left scroll arrow - desktop only #}
+
+
+ {# Widget-driven nav items container #}
+
+
+ {% for wdata in container_nav_widgets %}
+ {% with ctx=wdata.ctx %}
+ {% include wdata.widget.template with context %}
+ {% endwith %}
+ {% endfor %}
+
+
+
+
+
+ {# Right scroll arrow - desktop only #}
+
diff --git a/templates/_types/post/admin/_nav_entries_oob.html b/templates/_types/post/admin/_nav_entries_oob.html
new file mode 100644
index 0000000..eecc3d5
--- /dev/null
+++ b/templates/_types/post/admin/_nav_entries_oob.html
@@ -0,0 +1,80 @@
+{# OOB swap for nav entries and calendars when toggling associations or editing calendars #}
+{% import 'macros/links.html' as links %}
+
+{# Associated Entries and Calendars - vertical on mobile, horizontal with arrows on desktop #}
+{% if (associated_entries and associated_entries.entries) or calendars %}
+
+ {% set ident = [] %}
+ {% for k in ['id','ghost_id','uuid','slug','name','title'] if k in it.__mapper__.c %}
+ {% set v = (it|attr(k))|default('', true) %}
+ {% do ident.append(k ~ '=' ~ v) %}
+ {% endfor %}
+
{{ (ident|join(' β’ ')) or it|string }}
+
+ {% if depth < max_depth %}
+
+ {{ render_model(it, depth+1, max_depth) }}
+
+ {% else %}
+
β¦max depth reachedβ¦
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+ {% endif %}
+
+ {% else %}
+ {% set child = value %}
+ {% set ident = [] %}
+ {% for k in ['id','ghost_id','uuid','slug','name','title'] if k in child.__mapper__.c %}
+ {% set v = (child|attr(k))|default('', true) %}
+ {% do ident.append(k ~ '=' ~ v) %}
+ {% endfor %}
+
{{ (ident|join(' β’ ')) or child|string }}
+
+ {% if depth < max_depth %}
+
+ {{ render_model(child, depth+1, max_depth) }}
+
+ {% else %}
+
β¦max depth reachedβ¦
+ {% endif %}
+ {% endif %}
+
+
+ {% endfor %}
+
+
+ {% endif %}
+{%- endmacro %}
+
+
+
+ Model: Post β’ Table: {{ original_post.__tablename__ }}
+
+ {{ render_model(original_post, 0, 2) }}
+
+
diff --git a/templates/_types/post_data/_nav.html b/templates/_types/post_data/_nav.html
new file mode 100644
index 0000000..f5c504d
--- /dev/null
+++ b/templates/_types/post_data/_nav.html
@@ -0,0 +1,2 @@
+{% from 'macros/admin_nav.html' import placeholder_nav %}
+{{ placeholder_nav() }}
diff --git a/templates/_types/post_data/_oob_elements.html b/templates/_types/post_data/_oob_elements.html
new file mode 100644
index 0000000..32fd0c7
--- /dev/null
+++ b/templates/_types/post_data/_oob_elements.html
@@ -0,0 +1,28 @@
+{% extends 'oob_elements.html' %}
+
+{# OOB elements for HTMX navigation - all elements that need updating #}
+
+{# Import shared OOB macros #}
+{% from '_types/root/_oob_menu.html' import mobile_menu with context %}
+
+
+{% block oobs %}
+
+ {% from '_types/root/_n/macros.html' import oob_header with context %}
+ {{oob_header('post-admin-header-child', 'post_data-header-child', '_types/post_data/header/_header.html')}}
+
+ {% from '_types/post/admin/header/_header.html' import header_row with context %}
+ {{ header_row(oob=True) }}
+{% endblock %}
+
+
+{% block mobile_menu %}
+ {% include '_types/post_data/_nav.html' %}
+{% endblock %}
+
+
+{% block content %}
+ {% include "_types/post_data/_main_panel.html" %}
+{% endblock %}
+
+
diff --git a/templates/_types/post_data/header/_header.html b/templates/_types/post_data/header/_header.html
new file mode 100644
index 0000000..27eaf6f
--- /dev/null
+++ b/templates/_types/post_data/header/_header.html
@@ -0,0 +1,15 @@
+{% import 'macros/links.html' as links %}
+{% macro header_row(oob=False) %}
+ {% call links.menu_row(id='post_data-row', oob=oob) %}
+
+
+