diff --git a/public/styles.css b/public/styles.css
index 9032288..a3bbe4b 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -5,17 +5,18 @@ body {
   padding: 1em;
 }
 
+.article-content {
+  max-width: 40em;
+  margin: auto;
+}
+
+/* {{{ General element tweaks*/
 blockquote {
   padding-left: 1.25rem;
   border-left: 3px solid;
   margin-left: 0;
 }
 
-ul,
-ol {
-  padding-left: 1rem;
-}
-
 h1,
 h2,
 h3,
@@ -30,33 +31,45 @@ h6 {
   }
 }
 
-.article-content {
-  max-width: 40em;
-  margin: auto;
+math[display="block"] {
+  margin: 1.5em 0;
 }
-
-/* {{{ Asides*/
-.aside {
-  margin: 1.5rem 0;
-}
-
-/* {{{ Header*/
+/* }}}*/
+/* {{{ Asides */
+/* TODO: remove aside-header altogether */
 .aside-header {
   display: inline-flex;
   align-items: center;
   box-sizing: border-box;
 }
 
-.aside-header > .aside-title {
-  text-decoration: underline;
-  font-weight: bold;
+.aside-header > img {
+  height: 1.75em;
+  margin-right: 0.75em;
 }
 
-.aside-header > * {
-  padding: 0;
-  margin: 0;
+.aside {
+  background: #f4daf7;
+  border-radius: 3px;
+
+  box-sizing: border-box;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  margin: 1.5rem 0;
+
+  /* Prevents margin-collapsing of the children. */
+  border: 1px solid transparent;
 }
-/* }}}*/
+
+.aside-long > :not(summary) {
+  margin-left: 1rem;
+}
+
+.aside-long:has-child(.aside-header) > :not(summary):nth-child(2) {
+  margin-top: 0;
+  padding-top: 0;
+}
+
 /* {{{ Override marker*/
 .aside-summary::marker {
   display: none;
@@ -74,34 +87,8 @@ h6 {
 }
 
 /* }}}*/
-
-img.aside-icon {
-  height: 1.75rem;
-  margin-right: 0.75rem;
-}
-
-.aside {
-  background: #f4daf7;
-  border-radius: 3px;
-
-  box-sizing: border-box;
-  padding: 1rem;
-}
-
-.aside-short {
-  padding: 1rem;
-}
-
-.aside-long {
-  /* padding: 0.65rem; */
-
-  .aside-content {
-    padding-left: 1rem;
-  }
-}
-
 /* }}}*/
-/* {{{ Tier lists*/
+/* {{{ Tier lists */
 .tier-list {
   background: #444;
 
diff --git a/src/html.rs b/src/html.rs
index 0beb240..5df5581 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -1,6 +1,7 @@
 use std::collections::HashMap;
 
 use anyhow::anyhow;
+use anyhow::bail;
 use chrono::DateTime;
 use chrono::TimeZone;
 use jotdown::Alignment;
@@ -77,13 +78,14 @@ impl<'s> Writer<'s> {
 			return Ok(());
 		}
 		// }}}
-		// {{{ Handle blocks which trigger the `Ignore` state.
+		// {{{ Handle important state changes
 		match e {
 			Event::Start(Container::LinkDefinition { .. }, ..) => {
 				self.states.push(State::Ignore);
 				return Ok(());
 			}
 			Event::End(Container::LinkDefinition { .. }) => {
+				// Sanity check
 				assert!(matches!(self.states.last(), Some(State::Ignore)));
 				self.states.pop();
 			}
@@ -97,12 +99,12 @@ impl<'s> Writer<'s> {
 
 				return Ok(());
 			}
-			Event::End(Container::RawBlock { format } | Container::RawInline { format }) => {
-				if format == &"html" {
-					assert!(matches!(self.states.last(), Some(State::Raw)));
-				} else {
-					assert!(matches!(self.states.last(), Some(State::Ignore)));
-				};
+			Event::End(Container::RawBlock { .. } | Container::RawInline { .. }) => {
+				// Sanity check
+				assert!(matches!(
+					self.states.last(),
+					Some(State::Raw | State::Ignore)
+				));
 
 				self.states.pop();
 			}
@@ -126,79 +128,23 @@ impl<'s> Writer<'s> {
 					Container::RawBlock { .. } => {}
 					Container::RawInline { .. } => unreachable!(),
 					Container::Footnote { .. } => unreachable!(),
-					// {{{ List
-					Container::List { kind, tight } => {
-						self.list_tightness.push(*tight);
-						match kind {
-							ListKind::Unordered(..) | ListKind::Task(..) => out.write_str("<ul")?,
-							ListKind::Ordered {
-								numbering, start, ..
-							} => {
-								out.write_str("<ol")?;
-								if *start > 1 {
-									write!(out, r#" start="{}""#, start)?;
-								}
-
-								if let Some(ty) = match numbering {
-									Decimal => None,
-									AlphaLower => Some('a'),
-									AlphaUpper => Some('A'),
-									RomanLower => Some('i'),
-									RomanUpper => Some('I'),
-								} {
-									write!(out, r#" type="{}""#, ty)?;
-								}
-							}
-						}
-					}
-					// }}}
-					// {{{ Link
-					Container::Link(dst, ty) => {
-						if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
-							out.write_str("<a")?;
-						} else {
-							out.write_str(r#"<a href=""#)?;
-							if matches!(ty, LinkType::Email) {
-								out.write_str("mailto:")?;
-							}
-							write_attr_contents(dst, &mut out)?;
-							out.write_char('"')?;
-						}
-					}
-					// }}}
-					// {{{ Paragraph
-					Container::Paragraph => {
-						if self.list_tightness.last() == Some(&true) {
-							return Ok(());
-						}
-
-						out.write_str("<p")?;
-					}
-					// }}}
-					Container::Blockquote => out.write_str("<blockquote")?,
-					Container::ListItem { .. } => out.write_str("<li")?,
-					Container::TaskListItem { .. } => out.write_str("<li")?,
-					Container::DescriptionList => out.write_str("<dl")?,
-					Container::DescriptionDetails => out.write_str("<dd")?,
-					Container::Table => out.write_str("<table")?,
-					Container::TableRow { .. } => out.write_str("<tr")?,
+					// {{{ Section
 					Container::Section { id } => match self.metadata {
 						Some(meta)
 							if &meta.title.id == id && matches!(meta.route, PageRoute::Post(_)) =>
 						{
 							let renderer = template!("templates/post.html", &mut out)?;
+							// Sanity check
 							assert_eq!(renderer.current(), Some("attrs"));
 							self.states.push(State::Article(renderer));
 						}
 						_ => out.write_str("<section")?,
 					},
+					// }}}
+					// {{{ Aside
 					Container::Div {
 						class: class @ ("aside" | "long-aside" | "char-aside"),
 					} => {
-						if *class == "aside" {
-							self.list_tightness.push(true);
-						}
-
 						let mut renderer = if *class == "aside" {
 							template!("templates/aside.html", &mut out)?
 						} else if *class == "char-aside" {
@@ -233,6 +179,63 @@ impl<'s> Writer<'s> {
 
 						self.states.push(State::Aside(renderer));
 					}
+					// }}}
+					// {{{ List
+					Container::List { kind, tight } => {
+						self.list_tightness.push(*tight);
+						match kind {
+							ListKind::Unordered(..) => out.write_str("<ul")?,
+							ListKind::Ordered {
+								numbering, start, ..
+							} => {
+								out.write_str("<ol")?;
+								if *start > 1 {
+									write!(out, r#" start="{}""#, start)?;
+								}
+
+								if let Some(ty) = match numbering {
+									Decimal => None,
+									AlphaLower => Some('a'),
+									AlphaUpper => Some('A'),
+									RomanLower => Some('i'),
+									RomanUpper => Some('I'),
+								} {
+									write!(out, r#" type="{}""#, ty)?;
+								}
+							}
+							ListKind::Task(_) => bail!("Task lists are not supported"),
+						}
+					}
+					// }}}
+					// {{{ Link
+					Container::Link(dst, ty) => {
+						if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
+							out.write_str("<a")?;
+						} else {
+							out.write_str(r#"<a href=""#)?;
+							if matches!(ty, LinkType::Email) {
+								out.write_str("mailto:")?;
+							}
+							write_attr_contents(dst, &mut out)?;
+							out.write_char('"')?;
+						}
+					}
+					// }}}
+					// {{{ Paragraph
+					Container::Paragraph => {
+						if self.list_tightness.last() == Some(&true) {
+							return Ok(());
+						}
+
+						out.write_str("<p")?;
+					}
+					// }}}
+					Container::Blockquote => out.write_str("<blockquote")?,
+					Container::ListItem { .. } => out.write_str("<li")?,
+					Container::DescriptionList => out.write_str("<dl")?,
+					Container::DescriptionDetails => out.write_str("<dd")?,
+					Container::Table => out.write_str("<table")?,
+					Container::TableRow { .. } => out.write_str("<tr")?,
 					Container::Div { .. } => out.write_str("<div")?,
 					Container::Heading { level, .. } => write!(out, "<h{}", level)?,
 					Container::TableCell { head: false, .. } => out.write_str("<td")?,
@@ -251,8 +254,10 @@ impl<'s> Writer<'s> {
 					Container::Emphasis => out.write_str("<em")?,
 					Container::Mark => out.write_str("<mark")?,
 					Container::LinkDefinition { .. } => return Ok(()),
+					e => bail!("DJot element {e:?} is not supported"),
 				}
 
+				// {{{ Decide whether this element supports attributes
 				let mut write_attr_contentsibs = true;
 				if matches!(
 					c,
@@ -262,6 +267,7 @@ impl<'s> Writer<'s> {
 				) {
 					write_attr_contentsibs = false;
 				}
+				// }}}
 
 				if write_attr_contentsibs {
 					// {{{ Write attributes
@@ -293,26 +299,12 @@ impl<'s> Writer<'s> {
 							write_attr("aria-labeledby", id, &mut out)?;
 						}
 						Container::Div { class } if !class.is_empty() && !class_written => {
-							out.write_str(r#" class=""#)?;
-							write_class(c, false, &mut out)?;
-							out.write_char('"')?;
-						}
-						Container::Math { .. }
-						| Container::List {
-							kind: ListKind::Task(..),
-							..
-						}
-						| Container::TaskListItem { .. }
-							if !class_written =>
-						{
-							out.write_str(r#" class=""#)?;
-							write_class(c, false, &mut out)?;
-							out.write_char('"')?;
+							write_attr("class", class, &mut out)?;
 						}
 						_ => {}
 					}
 					// }}}
-
+					// {{{ Write special attributes
 					match c {
 						// {{{ Write css for aligning table cell text
 						Container::TableCell { alignment, .. }
@@ -352,8 +344,10 @@ impl<'s> Writer<'s> {
 							_ => out.write_char('>')?,
 						},
 					}
+					// }}}
 				}
 
+				// {{{ Post-start effects
 				match &c {
 					Container::Heading { id, .. } => {
 						out.write_str(r##"<a href="#"##)?;
@@ -365,17 +359,21 @@ impl<'s> Writer<'s> {
 					}
 					_ => {}
 				}
+				// }}}
 			}
 			// }}}
 			// {{{ Container end
 			Event::End(c) => {
+				// {{{ Pre-end effects
 				match &c {
 					Container::Image(..) => {
+						// Sanity check
 						assert!(matches!(self.states.last(), Some(State::TextOnly)));
 						self.states.pop();
 					}
 					_ => {}
 				}
+				// }}}
 
 				if matches!(self.states.last(), Some(State::TextOnly)) {
 					return Ok(());
@@ -389,10 +387,10 @@ impl<'s> Writer<'s> {
 					Container::List { kind, .. } => {
 						self.list_tightness.pop();
 						match kind {
-							ListKind::Unordered(..) | ListKind::Task(..) => {
-								out.write_str("</ul>")?
-							}
+							ListKind::Unordered(..) => out.write_str("</ul>")?,
 							ListKind::Ordered { .. } => out.write_str("</ol>")?,
+							// We error out when the task list begins
+							ListKind::Task(..) => unreachable!(),
 						}
 					}
 					// }}}
@@ -402,9 +400,7 @@ impl<'s> Writer<'s> {
 							return Ok(());
 						}
 
-						if !self.footnotes.in_epilogue() {
-							out.write_str("</p>")?;
-						}
+						out.write_str("</p>")?;
 					}
 					// }}}
 					// {{{ Image
@@ -419,18 +415,13 @@ impl<'s> Writer<'s> {
 					// }}}
 					// {{{ Math
 					Container::Math { .. } => {
+						// Sanity check
 						assert!(matches!(self.states.last(), Some(State::Math(_))));
 						self.states.pop();
 						out.write_str(r#"</span>"#)?;
 					}
 					// }}}
-					Container::Blockquote => out.write_str("</blockquote>")?,
-					Container::ListItem { .. } => out.write_str("</li>")?,
-					Container::TaskListItem { .. } => out.write_str("</li>")?,
-					Container::DescriptionList => out.write_str("</dl>")?,
-					Container::DescriptionDetails => out.write_str("</dd>")?,
-					Container::Table => out.write_str("</table>")?,
-					Container::TableRow { .. } => out.write_str("</tr>")?,
+					// {{{ Section
 					Container::Section { id, .. } => match self.metadata {
 						Some(meta)
 							if &meta.title.id == id
@@ -444,25 +435,25 @@ impl<'s> Writer<'s> {
 						}
 						_ => out.write_str("</section>")?,
 					},
+					// }}}
+					// {{{ Aside
 					Container::Div {
-						class: class @ ("aside" | "long-aside" | "char-aside"),
+						class: "aside" | "long-aside" | "char-aside",
 					} => {
-						if *class == "aside" {
-							self.list_tightness.pop();
-						}
-
 						let state = self.states.pop().unwrap();
 						let State::Aside(renderer) = state else {
 							panic!("Finished `aside` element without being in the `Aside` state.")
 						};
 
+						// Sanity check
 						assert_eq!(renderer.current(), Some("content"));
 						renderer.finish(&mut out)?;
 					}
-					Container::Div { .. } => out.write_str("</div>")?,
+					// }}}
 					Container::Heading { level, .. } => {
 						write!(out, "</h{}>", level)?;
 
+						// {{{ Article title
 						if let Some(State::Article(renderer)) = self.states.last_mut() {
 							if renderer.current() == Some("title") {
 								// SAFETY: we can never enter into the `article` state without having
@@ -516,7 +507,15 @@ impl<'s> Writer<'s> {
 								}
 							}
 						}
+						// }}}
 					}
+					Container::Blockquote => out.write_str("</blockquote>")?,
+					Container::ListItem { .. } => out.write_str("</li>")?,
+					Container::DescriptionList => out.write_str("</dl>")?,
+					Container::DescriptionDetails => out.write_str("</dd>")?,
+					Container::Table => out.write_str("</table>")?,
+					Container::TableRow { .. } => out.write_str("</tr>")?,
+					Container::Div { .. } => out.write_str("</div>")?,
 					Container::TableCell { head: false, .. } => out.write_str("</td>")?,
 					Container::TableCell { head: true, .. } => out.write_str("</th>")?,
 					Container::Caption => out.write_str("</caption>")?,
@@ -533,6 +532,7 @@ impl<'s> Writer<'s> {
 					Container::Emphasis => out.write_str("</em>")?,
 					Container::Mark => out.write_str("</mark>")?,
 					Container::LinkDefinition { .. } => unreachable!(),
+					e => bail!("DJot element {e:?} is not supported"),
 				}
 			}
 			// }}}
@@ -540,6 +540,7 @@ impl<'s> Writer<'s> {
 			Event::Str(s) => match self.states.last() {
 				Some(State::TextOnly) => write_attr_contents(s, &mut out)?,
 				Some(State::Raw) => out.write_str(s)?,
+				// {{{ Math
 				Some(State::Math(display)) => {
 					let config = pulldown_latex::RenderConfig {
 						display_mode: {
@@ -562,7 +563,8 @@ impl<'s> Writer<'s> {
 					pulldown_latex::push_mathml(&mut mathml, parser, config).unwrap();
 					out.write_str(&mathml)?;
 				}
-				_ => write_text(s, &mut out)?,
+				// }}}
+				_ => write_escape(s, false, &mut out)?,
 			},
 			// }}}
 			// {{{ Footnote reference
@@ -571,7 +573,7 @@ impl<'s> Writer<'s> {
 				if !matches!(self.states.last(), Some(State::TextOnly)) {
 					write!(
 						out,
-						r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##,
+						r##"<sup><a id="fnref{}" href="#fn{}" role="doc-noteref">{}</a></sup>"##,
 						number, number, number
 					)?;
 				}
@@ -611,41 +613,24 @@ impl<'s> Writer<'s> {
 	// {{{ Render epilogue
 	fn render_epilogue(&mut self, mut out: impl std::fmt::Write) -> anyhow::Result<()> {
 		if self.footnotes.reference_encountered() {
-			out.write_str("<section role=\"doc-endnotes\">")?;
-			out.write_str("<hr>")?;
-			out.write_str("<ol>")?;
+			// TODO: rewrite this using a template
+			out.write_str("<section role=\"doc-endnotes\"><hr><ol>")?;
 
 			while let Some((number, events)) = self.footnotes.next() {
 				write!(out, "<li id=\"fn{}\">", number)?;
 
-				let mut unclosed_para = false;
 				for e in events.iter().flatten() {
-					if matches!(&e, Event::Blankline | Event::Escape) {
-						continue;
-					}
-					if unclosed_para {
-						// not a footnote, so no need to add href before para close
-						out.write_str("</p>")?;
-					}
 					self.render_event(e, &mut out)?;
-					unclosed_para = matches!(e, Event::End(Container::Paragraph { .. }))
-						&& !matches!(self.list_tightness.last(), Some(true));
-				}
-				if !unclosed_para {
-					// create a new paragraph
-					out.write_str("<p>")?;
 				}
+
 				write!(
 					out,
-					"<a href=\"#fnref{}\" role=\"doc-backlink\">\u{21A9}\u{FE0E}</a></p>",
+					"<a href=\"#fnref{}\" role=\"doc-backlink\">Return to content \u{21A9}\u{FE0E}</a></li>",
 					number,
 				)?;
-
-				out.write_str("</li>")?;
 			}
 
-			out.write_str("</ol>")?;
-			out.write_str("</section>")?;
+			out.write_str("</ol></section>")?;
 		}
 
 		Ok(())
@@ -654,40 +639,6 @@ impl<'s> Writer<'s> {
 }
 
 // {{{ Writing helpers
-fn write_class<W>(c: &Container, mut first_written: bool, out: &mut W) -> std::fmt::Result
-where
-	W: std::fmt::Write,
-{
-	if let Some(cls) = match c {
-		Container::List {
-			kind: ListKind::Task(..),
-			..
-		} => Some("task-list"),
-		Container::TaskListItem { checked: false } => Some("unchecked"),
-		Container::TaskListItem { checked: true } => Some("checked"),
-		Container::Math { display: false } => Some("math inline"),
-		Container::Math { display: true } => Some("math display"),
-		_ => None,
-	} {
-		first_written = true;
-		out.write_str(cls)?;
-	}
-	if let Container::Div { class } = c {
-		if !class.is_empty() {
-			if first_written {
-				out.write_char(' ')?;
-			}
-			out.write_str(class)?;
-		}
-	}
-	Ok(())
-}
-
-#[inline]
-fn write_text(s: &str, out: impl std::fmt::Write) -> std::fmt::Result {
-	write_escape(s, false, out)
-}
-
 #[inline]
 fn write_attr_contents(s: &str, out: impl std::fmt::Write) -> std::fmt::Result {
 	write_escape(s, true, out)
@@ -765,11 +716,6 @@ impl<'s> Footnotes<'s> {
 		!self.references.is_empty()
 	}
 
-	/// Returns `true` if within the epilogue, i.e. if any footnotes have been pulled.
-	fn in_epilogue(&self) -> bool {
-		self.number > 0
-	}
-
 	/// Add a footnote reference.
 	fn reference(&mut self, label: &'s str) -> usize {
 		self.references
diff --git a/src/templates/aside.html b/src/templates/aside.html
index bab44c7..e1ef5da 100644
--- a/src/templates/aside.html
+++ b/src/templates/aside.html
@@ -1 +1 @@
-<div class="aside aside-short aside-content">{{content}}</div>
+<aside class="aside">{{content}}</aside>
diff --git a/src/templates/character-aside.html b/src/templates/character-aside.html
index a9d088d..65bf2b7 100644
--- a/src/templates/character-aside.html
+++ b/src/templates/character-aside.html
@@ -1,11 +1,11 @@
-<div class="aside aside-short">
+<aside class="aside">
   <div class="aside-header">
     <img
-      class="aside-icon"
       alt="{{character}}"
       src="/assets/icons/characters/{{character}}.webp"
     />
-    <span class="aside-title">{{title}}</span>
+    <h3>{{title}}</h3>
   </div>
-  <div class="aside-content">{{content}}</div>
-</div>
+
+  {{content}}
+</aside>
diff --git a/src/templates/long-aside.html b/src/templates/long-aside.html
index 20ab658..cc411da 100644
--- a/src/templates/long-aside.html
+++ b/src/templates/long-aside.html
@@ -1,11 +1,12 @@
-<details class="aside aside-long">
-  <summary class="aside-summary aside-header">
-    <img
-      class="aside-icon"
-      alt="{{character}}"
-      src="/assets/icons/characters/{{character}}.webp"
-    />
-    <span class="aside-title">{{title}}</span>
-  </summary>
-  <div class="aside-content">{{content}}</div>
-</details>
+<aside>
+  <details class="aside aside-long">
+    <summary class="aside-summary aside-header">
+      <img
+        alt="{{character}}"
+        src="/assets/icons/characters/{{character}}.webp"
+      />
+      <h3>{{title}}</h3>
+    </summary>
+    {{content}}
+  </details>
+</aside>
diff --git a/src/templates/post.html b/src/templates/post.html
index 7c2dec1..54cf6c0 100644
--- a/src/templates/post.html
+++ b/src/templates/post.html
@@ -5,7 +5,7 @@
     <ul>
       <li>
         {{posted_on}} by <a href="about:blank">prescientmoon</a> on their
-        <a href="moonythm.dev">website</a>
+        <a href="https://moonythm.dev">website</a>
       </li>
       <li>
         Last updated on {{updated_on}}. <a href="about:blank">Source</a> |