diff --git a/mobile/templates/mobile/_calendar_row.html b/mobile/templates/mobile/_calendar_row.html
new file mode 100644
index 0000000..606cc70
--- /dev/null
+++ b/mobile/templates/mobile/_calendar_row.html
@@ -0,0 +1,26 @@
+{% load i18n %}
+{% comment %}
+ One M3 agenda row -- included from calendar.html once per bucket. Expects
+ ``row`` ({event, pill_class, pill_label}) in scope. A 4px club-red left
+ border marks games; other kinds get a 3px colour bar (info for training,
+ warn for everything else) next to the date, per the design doc's M3 row
+ description.
+{% endcomment %}
+
+
+
{{ row.event.start|date:"D" }}
+
+
+ {% if row.event.kind != "game" %}
+
+ {% endif %}
+
+
{{ row.event.title }}
+
+ {{ row.event.start|date:"H:i" }}
+ {% for team in row.event.teams.all %}· {{ team.name }}{% endfor %}
+ {% if row.event.location %}· {{ row.event.location.name }}{% endif %}
+
+
+ {{ row.pill_label }}
+
diff --git a/mobile/templates/mobile/calendar.html b/mobile/templates/mobile/calendar.html
new file mode 100644
index 0000000..c9e375c
--- /dev/null
+++ b/mobile/templates/mobile/calendar.html
@@ -0,0 +1,57 @@
+{% extends "mobile/base.html" %}
+{% load i18n %}
+
+{% comment %}
+ M3 -- design_handoff_rosterchief_platform/README.md's M3 section: a
+ chronological agenda, grouped under "This week"/"Next week", scoped to
+ scope_person's own invites by default or, with the "All members" toggle
+ below, every club event. See CalendarView's own docstring for the
+ browsing-window judgment call (current + next calendar week, no further
+ paging).
+{% endcomment %}
+
+{% block content %}
+
+
+
{% trans "Once you're linked to a member record, their schedule will show up here." %}
+
+ {% elif not this_week and not next_week %}
+
+
{% trans "Nothing scheduled in the next two weeks." %}
+
+ {% else %}
+ {% if this_week %}
+