diff --git a/theme/basic.css b/theme/basic.css
index 8845713..2ee7b34 100644
--- a/theme/basic.css
+++ b/theme/basic.css
@@ -78,3 +78,12 @@ img {
.cat5, .cat13, .cat21 {background-color: #e31a1c; color: white;}
.cat6, .cat14, .cat22 {background-color: #fdbf6f; color: black;}
.cat7, .cat15, .cat23 {background-color: #ff7f00; color: black;}
+
+
+#devel {
+ background-color: #c88;
+ color: black;
+}
+.kvpair {
+ border: solid black 2px;
+}
\ No newline at end of file
diff --git a/theme/devel.js b/theme/devel.js
new file mode 100644
index 0000000..6abe921
--- /dev/null
+++ b/theme/devel.js
@@ -0,0 +1,47 @@
+// Devel server addons
+
+// devel_addin drops a bunch of development extensions into element e.
+// It will only modify stuff inside e.
+function devel_addin(obj, e) {
+ let h = document.createElement("h2");
+ e.appendChild(h);
+ h.textContent = "Development Options";
+
+ let g = document.createElement("p");
+ e.appendChild(g);
+ g.innerText = "This section will not appear for participants."
+
+ let keys = Object.keys(obj);
+ keys.sort();
+ for (let key of keys) {
+ switch (key) {
+ case "body":
+ continue;
+ }
+
+ let d = document.createElement("div");
+ e.appendChild(d);
+ d.classList.add("kvpair");
+
+ let ktxt = document.createElement("span");
+ d.appendChild(ktxt);
+ ktxt.textContent = key;
+
+ let val = obj[key];
+ if (Array.isArray(val)) {
+ let vi = document.createElement("select");
+ d.appendChild(vi);
+ vi.multiple = true;
+ for (let a of val) {
+ let opt = document.createElement("option");
+ vi.appendChild(opt);
+ opt.innerText = a;
+ }
+ } else {
+ let vi = document.createElement("input");
+ d.appendChild(vi);
+ vi.value = val;
+ vi.disabled = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/theme/puzzle.html b/theme/puzzle.html
index 1e82577..1dcf892 100644
--- a/theme/puzzle.html
+++ b/theme/puzzle.html
@@ -5,6 +5,7 @@
+