diff --git a/_includes/eval.liquid b/_includes/eval.liquid
new file mode 100644
index 0000000000000000000000000000000000000000..762a0903bb4dcb964c2cada541078b16ea8c509c
--- /dev/null
+++ b/_includes/eval.liquid
@@ -0,0 +1,38 @@
+{% capture eval_workspace %}
+  {% comment %}
+    Evaluate Liquid tags stored in plaintext.
+
+    Supports:
+      `link` tag for site.pages. Does not raise errors on invalid links.
+
+    Parameters:
+      `content` (string): String containing Liquid tags.
+  {% endcomment %}
+
+  {% capture tags %}{{ "{%|" }}%}{% endcapture %}
+  {% assign tags = tags | split: "|" %}
+
+  {% assign join = "|" %}
+  {% capture parts %}{% include split.liquid content=include.content queries=tags join=join %}{% endcapture %}
+  {% assign parts = parts | split: join %}
+
+  {% assign result = "" %}
+  {% for part in parts %}
+    {% assign subparts = part | split: " " %}
+    {% assign rest = subparts | slice: 1, subparts.size | join: " " %}
+    {% case subparts[0] %}
+      {% when "link" %}
+        {% comment %}
+          Jekyll 4.0 supports Liquefied link tags: https://github.com/jekyll/jekyll/pull/6269
+        {% endcomment %}
+        {% for page in site.pages %}
+          {% if page.path == rest %}
+            {% assign link = page.url | relative_url %}
+            {% assign result = result | append: link %}
+          {% endif %}
+        {% endfor %}
+      {% else %}
+        {% assign result = result | append: part %}
+    {% endcase %}
+  {% endfor %}
+{% endcapture %}{% assign eval_workspace = '' %}{{ result }}
diff --git a/_includes/split.liquid b/_includes/split.liquid
new file mode 100644
index 0000000000000000000000000000000000000000..7fe1af12026fb19a135af4a1c4a55fa5b297a58a
--- /dev/null
+++ b/_includes/split.liquid
@@ -0,0 +1,34 @@
+{% capture split_workspace %}
+  {% comment %}
+    Split the string on all of the delimiters and join the result.
+
+    Parameters:
+      `content` (string): String to split.
+      `delimiters` (array of strings): Boundary strings defining what to split.
+      `join` (string): Join the resulting parts on this string.
+  {% endcomment %}
+
+  {% assign queries = include.queries | default: empty %}
+  {% assign join = include.join | default: "|" %}
+  {% if queries.size == 0 %}
+    {% assign result = include.content %}
+  {% else %}
+    {% assign result = include.content | split: queries[0] | join: join %}
+    {% assign rest = queries | slice: 1, queries.size %}
+    {% for query in rest %}
+      {% assign temp = result | split: join %}
+      {% assign result = "" %}
+      {% for part in temp %}
+        {% assign split = part | split: query %}
+        {% for subpart in split %}
+          {% comment %}
+            Suppress repeated delimiters (empty strings).
+          {% endcomment %}
+          {% if subpart != "" %}
+            {% assign result = result | append: join | append: subpart %}
+          {% endif %}
+        {% endfor %}
+      {% endfor %}
+    {% endfor %}
+  {% endif %}
+{% endcapture %}{% assign split_workspace = '' %}{{ result }}
diff --git a/_layouts/module.html b/_layouts/module.html
index dd612ed1d6af648fe1fcd740d7993c5f59166b28..ecb39698fc80a00aa7bc5335ea34a425c19b85ac 100644
--- a/_layouts/module.html
+++ b/_layouts/module.html
@@ -5,7 +5,11 @@
     {% for day in page.days %}
     <dt class="module-day">{{ day.date | date: '%b %e' }}</dt>
     {% for event in day.events %}
-    <dd class="module-event">{{ event | first | markdownify }} {{ event | last | markdownify }}</dd>
+    {% assign main = event | first %}
+    {% assign info = event | last %}
+    {% capture main %}{% include eval.liquid content=main %}{% endcapture %}
+    {% capture info %}{% include eval.liquid content=info %}{% endcapture %}
+    <dd class="module-event">{{ main | markdownify }} {{ info | markdownify }}</dd>
     {% endfor %}
     {% endfor %}
   </dl>