diff --git a/build/_concats.js b/build/_concats.js
new file mode 100644
index 0000000..fdeaa5a
--- /dev/null
+++ b/build/_concats.js
@@ -0,0 +1,31 @@
+
+module.exports = exports = [
+  {
+    output: '/gdb/printable.md',
+    sources: [
+      'public/gdb/index.md',
+      'public/gdb/what-is-gender.md',
+      'public/gdb/history.md',
+      'public/gdb/euphoria.md',
+      'public/gdb/physical-dysphoria.md',
+      'public/gdb/biochemical-dysphoria.md',
+      'public/gdb/social-dysphoria.md',
+      'public/gdb/societal-dysphoria.md',
+      'public/gdb/sexual-dysphoria.md',
+      'public/gdb/presentational-dysphoria.md',
+      'public/gdb/historical-dysphoria.md',
+      'public/gdb/managed-dysphoria.md',
+      'public/gdb/impostor-syndrome.md',
+      'public/gdb/diagnoses.md',
+      'public/gdb/treatment.md',
+      'public/gdb/causes.md',
+      'public/gdb/chromosomes.md',
+      'public/gdb/conclusion.md',
+    ],
+    meta: {
+      title: 'The Gender Dysphoria Bible',
+      description: 'A dive into the multitude of ways that gender dysphoria manifests and what it means to be transgender.',
+      classes: [ 'longform' ],
+    },
+  },
+];
diff --git a/build/index.js b/build/index.js
index 9b7226e..7c413e9 100644
--- a/build/index.js
+++ b/build/index.js
@@ -11,6 +11,7 @@ const { sortBy } = require('lodash');
 const getEngines = require('./engines');
 const primeTweets = require('./page-tweets');
 const pageWriter = require('./page-writer');
+const pageConcatinator = require('./page-concatinator');
 const evaluate = require('./evaluate');
 const { resolve } = require('./resolve');
 
@@ -18,6 +19,7 @@ const favicon = require('./favicon');
 const scss    = require('./scss');
 const svg     = require('./svg');
 const scripts = require('./scripts');
+const concats = require('./_concats');
 
 function writeIndex (destination, files, compressed) {
   files = files.map((p) => !p.draft && (p.toJson ? p.toJson() : p));
@@ -72,6 +74,11 @@ exports.everything = function (prod = false) {
     await evaluate(tasks.flat(), cache);
     const { revManifest } = await cache.save();
 
+    for (const cset of concats) {
+      const cpage = pageConcatinator(pages, cset.output, cset.sources, cset.meta);
+      pages.push(cpage);
+    }
+
     const engines = await getEngines(prod);
     const postIndex = await pageWriter(prod, engines, pages, posts);
     postIndex.rev = revManifest;
@@ -101,6 +108,11 @@ exports.pages = function () {
     // prime tweet data for all pages
     const pages = await primeTweets(PublicFiles.pages.filter((p) => !p.meta.ignore));
 
+    for (const cset of concats) {
+      const cpage = pageConcatinator(pages, cset.output, cset.sources, cset.meta);
+      pages.push(cpage);
+    }
+
     let posts = await primeTweets(PostFiles.pages.filter((p) => !p.meta.ignore));
     posts = sortBy(posts, 'date');
     posts.reverse();
diff --git a/build/page-concatinator.js b/build/page-concatinator.js
new file mode 100644
index 0000000..b444f7d
--- /dev/null
+++ b/build/page-concatinator.js
@@ -0,0 +1,60 @@
+
+const Page = require('./page');
+
+function isDate (input) { return input instanceof Date; }
+
+function min (...collection) {
+  if (isDate(collection[0])) return new Date(Math.min(...collection));
+  return Math.min(...collection);
+}
+
+function max (...collection) {
+  if (isDate(collection[0])) return new Date(Math.max(...collection));
+  return Math.max(...collection);
+}
+
+module.exports = exports = function (pages, target, paths, meta) {
+  const sources = {};
+  for (const page of pages) {
+    if (paths.includes(page.input)) sources[page.input] = page;
+  }
+
+  const result = new CombinedPage(target);
+  result.source = '';
+  result.meta = {};
+  result.dateCreated = new Date();
+  result.dateModified = new Date(1990, 12, 1);
+  result.tweets = {};
+  result.images = {};
+  result.meta = meta;
+  result.classes = Array.from(new Set(meta.classes || []));
+  result.flags = result.classes.reduce((res, item) => {
+    var camelCased = item.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
+    res[camelCased] = true;
+    return res;
+  }, {});
+
+  for (const path of paths) {
+    const p = sources[path];
+    result.source += p.source;
+
+    result.dateCreated = min(result.dateCreated, p.dateCreated);
+    result.dateModified = max(result.dateModified, p.dateModified);
+    Object.assign(result.images, p.images);
+    Object.assign(result.tweets, p.tweets);
+  }
+
+  result.meta.date = result.dateCreated;
+
+  return result;
+};
+
+
+
+class CombinedPage extends Page {
+
+  async load () {
+    // do nothing
+  }
+
+}
diff --git a/public/gdb.pdf b/public/gdb.pdf
new file mode 100644
index 0000000..9f56eb2
Binary files /dev/null and b/public/gdb.pdf differ