diff --git a/homepage/static/dialog-polyfill.css b/homepage/static/dialog-polyfill.css
new file mode 100644
index 0000000..6b38bf0
--- /dev/null
+++ b/homepage/static/dialog-polyfill.css
@@ -0,0 +1,37 @@
+dialog {
+ position: absolute;
+ left: 0; right: 0;
+ width: -moz-fit-content;
+ width: -webkit-fit-content;
+ width: fit-content;
+ height: -moz-fit-content;
+ height: -webkit-fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+ display: block;
+}
+
+dialog:not([open]) {
+ display: none;
+}
+
+dialog + .backdrop {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+ background: rgba(0,0,0,0.1);
+}
+
+._dialog_overlay {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+}
+
+dialog.fixed {
+ position: fixed;
+ top: 50%;
+ transform: translate(0, -50%);
+}
\ No newline at end of file
diff --git a/homepage/static/dialog-polyfill.js b/homepage/static/dialog-polyfill.js
new file mode 100644
index 0000000..aee7d96
--- /dev/null
+++ b/homepage/static/dialog-polyfill.js
@@ -0,0 +1,866 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = global || self, global.dialogPolyfill = factory());
+}(this, function () { 'use strict';
+
+ // nb. This is for IE10 and lower _only_.
+ var supportCustomEvent = window.CustomEvent;
+ if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
+ supportCustomEvent = function CustomEvent(event, x) {
+ x = x || {};
+ var ev = document.createEvent('CustomEvent');
+ ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
+ return ev;
+ };
+ supportCustomEvent.prototype = window.Event.prototype;
+ }
+
+ /**
+ * Dispatches the passed event to both an "on
" handler as well as via the
+ * normal dispatch operation. Does not bubble.
+ *
+ * @param {!EventTarget} target
+ * @param {!Event} event
+ * @return {boolean}
+ */
+ function safeDispatchEvent(target, event) {
+ var check = 'on' + event.type.toLowerCase();
+ if (typeof target[check] === 'function') {
+ target[check](event);
+ }
+ return target.dispatchEvent(event);
+ }
+
+ /**
+ * @param {Element} el to check for stacking context
+ * @return {boolean} whether this el or its parents creates a stacking context
+ */
+ function createsStackingContext(el) {
+ while (el && el !== document.body) {
+ var s = window.getComputedStyle(el);
+ var invalid = function(k, ok) {
+ return !(s[k] === undefined || s[k] === ok);
+ };
+
+ if (s.opacity < 1 ||
+ invalid('zIndex', 'auto') ||
+ invalid('transform', 'none') ||
+ invalid('mixBlendMode', 'normal') ||
+ invalid('filter', 'none') ||
+ invalid('perspective', 'none') ||
+ s['isolation'] === 'isolate' ||
+ s.position === 'fixed' ||
+ s.webkitOverflowScrolling === 'touch') {
+ return true;
+ }
+ el = el.parentElement;
+ }
+ return false;
+ }
+
+ /**
+ * Finds the nearest