terrain-map
ARCHIVED - repo for blog post http://www.vogt.world/writing/procedural-terrain-generation/
git clone https://git.vogt.world/terrain-map.git
Log | Files | README.md
← Commit log
commit
message
DS, DS-smooth, and combined maps all working.
author
Ben Vogt <[email protected]>
date
2016-05-14 01:24:12
stats
3 file(s) changed, 939 insertions(+), 0 deletions(-)
files
index.html
main.js
style.css
  1diff --git a/index.html b/index.html
  2index e69de29..847ef4b 100644
  3--- a/index.html
  4+++ b/index.html
  5@@ -0,0 +1,59 @@
  6+<html lang="en">
  7+  <head>
  8+    <title>Terrain Maps</title>
  9+    <link rel="stylesheet" type="text/css" href="style.css">
 10+    <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
 11+    <script type="text/javascript" src="main.js"></script>
 12+  </head>
 13+  <body>
 14+    <div class="container">
 15+      <div class="row">
 16+        <h1>Terrain Maps</h1>
 17+        <h4>Generating terrain maps using Diamon-Square, smoothing, and erosion algorithms.</h4>
 18+        <p>I've been messing around with different terrain smoothing algorithms to run on Diamond-Square-generated maps. I'm putting them here to so I remember them and write them out a little more clearly.</p>
 19+      </div>
 20+    </div>
 21+    <div class="container">
 22+      <hr>
 23+      <div class="row">
 24+        <h2>
 25+          Standard Diamond-Square algorithm
 26+        </h2>
 27+        <p>
 28+          Diamon-Square with smoothing of 10, and 20.
 29+          <button class="run button-primary" id="standard">RUN</button>
 30+        </p>
 31+      </div>
 32+      <div class="set-of" id="container-1"></div>
 33+      <hr>
 34+      <div class="row">
 35+        <h2>
 36+          Combining Diamond-Square maps
 37+        </h2>
 38+        <p>
 39+          Combining a DS, DS smoothed (10), and DS smoothed (20) maps. This is done by generating <code>standard</code>, then <code>standard-two</code>, a completely different map. Then using each of these to generate a smoothed version of themselves. Then
 40+          we can combine standard with <code>standard-10</code>, using <code>standard-two-20</code> as the map controlling which parts of the map are used for the result. For example the higher the value of a given point on <code>standard-two-20</code>, the
 41+          stronger the value taken from <code>standard</code> and assigned to the resulting map. The lower the value of a given point on <code>standard-two-20</code>, the stronger the value taken from <code>standard-10</code> and assigned to the resulting
 42+          map.
 43+        </p>
 44+        <p>
 45+          This allows us to generate a map that has terrain that is smooth in some parts, and rugged in other parts.
 46+          <br>The final <code>reverse</code> map, is with the <code>standard</code> and <code>standard-10</code> maps swapped, so the smooth and rugged values are reversed.
 47+          <button class="run button-primary" id="combined">RUN</button>
 48+        </p>
 49+      </div>
 50+      <div class="set-of" id="container-2"></div>
 51+    </div>
 52+    <div class="hidden">
 53+      <canvas id="tmp" width="256" height="256"></canvas>
 54+    </div>
 55+    <div class="container">
 56+      <hr>
 57+      <div class="row">
 58+        <p>
 59+          That's all!
 60+        </p>
 61+      </div>.
 62+    </div>
 63+  </body>
 64+</html>
 65diff --git a/main.js b/main.js
 66index e69de29..32e5550 100644
 67--- a/main.js
 68+++ b/main.js
 69@@ -0,0 +1,232 @@
 70+$(document).ready(function(){
 71+  function LandMap(options) {
 72+    var level = 8;
 73+    this.containerId = options.containerId;
 74+    this.title = options.title;
 75+    this.size = Math.pow(2, level) + 1;
 76+    this.max = this.size - 1;
 77+    this.maps = {};
 78+  }
 79+
 80+  LandMap.prototype.get = function(which, x, y) {
 81+    if (x < 0 || x > this.max || y < 0 || y > this.max) {
 82+      return -1;
 83+    } else {
 84+      return this.maps[which][x + this.size * y];
 85+    }
 86+  };
 87+
 88+  LandMap.prototype.set = function(which, x, y, value) {
 89+    this.maps[which][(x + this.size * y)] = value;
 90+  };
 91+
 92+  LandMap.prototype.generate = function(deviationAmount, feature) {
 93+    var self = this;
 94+
 95+    if (!(feature in self.maps)) {
 96+      this.maps[feature] = new Array(this.size * this.size);
 97+    }
 98+
 99+    this.set(feature, 0, 0, Math.random() * self.max);
100+    this.set(feature, this.max, 0, Math.random() * self.max);
101+    this.set(feature, this.max, this.max, Math.random() * self.max);
102+    this.set(feature, 0, this.max, Math.random() * self.max);
103+
104+    subdivide(this.max);
105+
106+    function subdivide(size) {
107+      var x, y, half = size / 2;
108+      var scale = deviationAmount * size;
109+      if (half < 1) return;
110+
111+      for (y = half; y < self.max; y += size) {
112+        for (x = half; x < self.max; x += size) {
113+          square(feature, x, y, half, Math.random() * scale * 2 - scale);
114+        }
115+      }
116+      for (y = 0; y <= self.max; y += half) {
117+        for (x = (y + half) % size; x <= self.max; x += size) {
118+          diamond(feature, x, y, half, Math.random() * scale * 2 - scale);
119+        }
120+      }
121+      subdivide(size / 2);
122+    }
123+
124+    function average(values) {
125+      var valid = values.filter(function(val) {
126+        return val !== -1;
127+      });
128+      var total = valid.reduce(function(sum, val) {
129+        return sum + val;
130+      }, 0);
131+      return total / valid.length;
132+    }
133+
134+    function square(which, x, y, size, offset) {
135+      var ave = average([
136+        self.get(which, x - size, y - size), // upper left
137+        self.get(which, x + size, y - size), // upper right
138+        self.get(which, x + size, y + size), // lower right
139+        self.get(which, x - size, y + size) // lower left
140+      ]);
141+      self.set(which, x, y, ave + offset);
142+    }
143+
144+    function diamond(which, x, y, size, offset) {
145+      var ave = average([
146+        self.get(which, x, y - size), // top
147+        self.get(which, x + size, y), // right
148+        self.get(which, x, y + size), // bottom
149+        self.get(which, x - size, y) // left
150+      ]);
151+      self.set(which, x, y, ave + offset);
152+    }
153+  };
154+
155+  LandMap.prototype.smooth = function(amount, featureFrom, featureTo) {
156+    if (!(featureTo in this.maps)) {
157+      this.maps[featureTo] = new Array(this.size * this.size);
158+    }
159+
160+    var self = this;
161+    var MARGIN = amount;
162+    for (var y = 0; y < this.size; y++) {
163+      for (var x = 0; x < this.size; x++) {
164+        var nextValue = self.get(featureFrom, x, y);
165+        var nextValueCount = 0;
166+        for (var xRange = Math.max(x - Math.round(MARGIN), 0); xRange < Math.min(x + Math.round(MARGIN), this.size); xRange++) {
167+          for (var yRange = Math.max(y - Math.round(MARGIN), 0); yRange < Math.min(y + Math.round(MARGIN), this.size); yRange++) {
168+            if (distanceBetween(x, y, xRange, yRange) <= MARGIN) {
169+              var value = self.get(featureFrom, xRange, yRange);
170+              nextValue = nextValue + value;
171+              nextValueCount++;
172+            }
173+          }
174+        }
175+        var finalVal = ((nextValue / nextValueCount));
176+        self.set(featureTo, x, y, finalVal);
177+      }
178+    }
179+
180+    function distanceBetween(ax, ay, bx, by) {
181+      return Math.abs(Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)));
182+    }
183+  };
184+
185+  LandMap.prototype.combine = function(one, two, three, result) {
186+
187+    function percent(value, max, min) {
188+      return value / Math.abs((max - min));
189+    }
190+
191+    if (!(result in this.maps)) {
192+      this.maps[result] = new Array(this.size * this.size);
193+    }
194+
195+    var max = Number.MIN_SAFE_INTEGER;
196+    var min = Number.MAX_SAFE_INTEGER;
197+    for (var y = 0; y < this.size; y++) {
198+      for (var x = 0; x < this.size; x++) {
199+        var val = this.get(two, x, y);
200+        if (val !== undefined) {
201+          max = Math.max(max, val);
202+          min = Math.min(min, val);
203+        }
204+      }
205+    }
206+
207+    for (var y = 0; y < this.size; y++) {
208+      for (var x = 0; x < this.size; x++) {
209+        var featureOne = this.get(one, x, y);
210+        var featureTwo = this.get(two, x, y);
211+        var featureThree = this.get(three, x, y);
212+        if (featureOne !== undefined && featureTwo !== undefined && featureThree !== undefined) {
213+          var featureThreePercent = percent(featureThree, min, max);
214+          var val = (1 - featureThreePercent) * featureOne + featureThreePercent * featureTwo;
215+          this.set(result, x, y, val);
216+        }
217+      }
218+    }
219+  };
220+
221+  LandMap.prototype.draw = function() {
222+    var html = '<div class="row">';
223+    var featureCount = 0;
224+    for (feature in this.maps) {
225+      html += '<div class="four columns"><strong>' + feature + '</strong><div class="box"><span id="' + this.containerId + feature + '"></span><span id="' + feature + '-description"></span></div></div>';
226+      featureCount++;
227+      if (featureCount == 3) {
228+        html += '</div><div class="row">'
229+        featureCount = 0;
230+      }
231+    }
232+    document.getElementById(this.containerId).innerHTML = html;
233+    for (feature in this.maps) {
234+      var display = document.getElementById("tmp");
235+      var ctx = display.getContext('2d');
236+      var width = display.width = 256;
237+      var height = display.height = 256;
238+      var self = this;
239+
240+      var cellWidth = 1;
241+
242+      function drawCell(x, y, inensity) {
243+        ctx.fillStyle = inensity;
244+        ctx.fillRect(x * cellWidth, y * cellWidth, cellWidth, cellWidth);
245+      }
246+
247+      // finding max, min
248+      var max = Number.MIN_SAFE_INTEGER;
249+      var min = Number.MAX_SAFE_INTEGER;
250+      for (var y = 0; y < this.size; y++) {
251+        for (var x = 0; x < this.size; x++) {
252+          var val = this.get(feature, x, y);
253+          if (val !== undefined) {
254+            max = Math.max(max, val);
255+            min = Math.min(min, val);
256+          }
257+        }
258+      }
259+      // Drawing each cell
260+      for (var y = 0; y < this.size; y++) {
261+        for (var x = 0; x < this.size; x++) {
262+          var val = this.get(feature, x, y);
263+          if (val !== undefined) {
264+            drawCell(x, y, brightness(val, max, min));
265+          }
266+        }
267+      }
268+
269+      document.getElementById(this.containerId + feature).innerHTML = '<img src="' + display.toDataURL("image/png") + '" class="u-max-full-width map"/>';
270+    }
271+
272+    function brightness(value, max, min) {
273+      var delta = Math.abs((max - min));
274+      var b = Math.floor((value / delta) * 255);
275+      return 'rgba(' + b + ',' + b + ',' + b + ',1)';
276+    }
277+  };
278+
279+  $("#standard").click(function() {
280+    var standardMap = new LandMap({
281+      containerId: "container-1"
282+    });
283+    standardMap.generate(0.75, "standard");
284+    standardMap.smooth(10, "standard", "smoothed-10");
285+    standardMap.smooth(20, "standard", "smoothed-20");
286+    standardMap.draw();
287+  });
288+
289+  $("#combined").click(function() {
290+    var terrain = new LandMap({
291+      containerId: "container-2"
292+    });
293+    terrain.generate(0.75, "standard");
294+    terrain.generate(0.75, "standard-two", "DS with 0.75");
295+    terrain.smooth(10, "standard", "standard-10");
296+    terrain.smooth(20, "standard-two", "standard-two-20");
297+    terrain.combine("standard", "standard-10", "standard-two-20", "combined");
298+    terrain.combine("standard-10", "standard", "standard-two-20", "reversed");
299+    terrain.draw();
300+  });
301+})
302diff --git a/style.css b/style.css
303index e69de29..8d6f247 100644
304--- a/style.css
305+++ b/style.css
306@@ -0,0 +1,648 @@
307+body {
308+  background: #FFFFFF;
309+}
310+
311+img.map {
312+  border: 1px solid #E0E0E0;
313+}
314+
315+div.set-of {
316+  min-height: 256px;
317+}
318+
319+div.set-of:empty {
320+  background: #F5F5F5;
321+}
322+
323+.run {
324+  float: right;
325+}
326+
327+.hidden {
328+  display: none;
329+}
330+
331+
332+/*
333+* Skeleton V2.0.4
334+* Copyright 2014, Dave Gamache
335+* www.getskeleton.com
336+* Free to use under the MIT license.
337+* http://www.opensource.org/licenses/mit-license.php
338+* 12/29/2014
339+*/
340+
341+
342+/* Table of contents
343+––––––––––––––––––––––––––––––––––––––––––––––––––
344+- Grid
345+- Base Styles
346+- Typography
347+- Links
348+- Buttons
349+- Forms
350+- Lists
351+- Code
352+- Tables
353+- Spacing
354+- Utilities
355+- Clearing
356+- Media Queries
357+*/
358+
359+
360+/* Grid
361+–––––––––––––––––––––––––––––––––––––––––––––––––– */
362+
363+.container {
364+  position: relative;
365+  width: 100%;
366+  max-width: 960px;
367+  margin: 0 auto;
368+  padding: 0 20px;
369+  box-sizing: border-box;
370+}
371+
372+.column,
373+.columns {
374+  width: 100%;
375+  float: left;
376+  box-sizing: border-box;
377+}
378+
379+
380+/* For devices larger than 400px */
381+
382+@media (min-width: 400px) {
383+  .container {
384+    width: 85%;
385+    padding: 0;
386+  }
387+}
388+
389+
390+/* For devices larger than 550px */
391+
392+@media (min-width: 550px) {
393+  .container {
394+    width: 80%;
395+  }
396+  .column,
397+  .columns {
398+    margin-left: 4%;
399+  }
400+  .column:first-child,
401+  .columns:first-child {
402+    margin-left: 0;
403+  }
404+  .one.column,
405+  .one.columns {
406+    width: 4.66666666667%;
407+  }
408+  .two.columns {
409+    width: 13.3333333333%;
410+  }
411+  .three.columns {
412+    width: 22%;
413+  }
414+  .four.columns {
415+    width: 30.6666666667%;
416+  }
417+  .five.columns {
418+    width: 39.3333333333%;
419+  }
420+  .six.columns {
421+    width: 48%;
422+  }
423+  .seven.columns {
424+    width: 56.6666666667%;
425+  }
426+  .eight.columns {
427+    width: 65.3333333333%;
428+  }
429+  .nine.columns {
430+    width: 74.0%;
431+  }
432+  .ten.columns {
433+    width: 82.6666666667%;
434+  }
435+  .eleven.columns {
436+    width: 91.3333333333%;
437+  }
438+  .twelve.columns {
439+    width: 100%;
440+    margin-left: 0;
441+  }
442+  .one-third.column {
443+    width: 30.6666666667%;
444+  }
445+  .two-thirds.column {
446+    width: 65.3333333333%;
447+  }
448+  .one-half.column {
449+    width: 48%;
450+  }
451+  /* Offsets */
452+  .offset-by-one.column,
453+  .offset-by-one.columns {
454+    margin-left: 8.66666666667%;
455+  }
456+  .offset-by-two.column,
457+  .offset-by-two.columns {
458+    margin-left: 17.3333333333%;
459+  }
460+  .offset-by-three.column,
461+  .offset-by-three.columns {
462+    margin-left: 26%;
463+  }
464+  .offset-by-four.column,
465+  .offset-by-four.columns {
466+    margin-left: 34.6666666667%;
467+  }
468+  .offset-by-five.column,
469+  .offset-by-five.columns {
470+    margin-left: 43.3333333333%;
471+  }
472+  .offset-by-six.column,
473+  .offset-by-six.columns {
474+    margin-left: 52%;
475+  }
476+  .offset-by-seven.column,
477+  .offset-by-seven.columns {
478+    margin-left: 60.6666666667%;
479+  }
480+  .offset-by-eight.column,
481+  .offset-by-eight.columns {
482+    margin-left: 69.3333333333%;
483+  }
484+  .offset-by-nine.column,
485+  .offset-by-nine.columns {
486+    margin-left: 78.0%;
487+  }
488+  .offset-by-ten.column,
489+  .offset-by-ten.columns {
490+    margin-left: 86.6666666667%;
491+  }
492+  .offset-by-eleven.column,
493+  .offset-by-eleven.columns {
494+    margin-left: 95.3333333333%;
495+  }
496+  .offset-by-one-third.column,
497+  .offset-by-one-third.columns {
498+    margin-left: 34.6666666667%;
499+  }
500+  .offset-by-two-thirds.column,
501+  .offset-by-two-thirds.columns {
502+    margin-left: 69.3333333333%;
503+  }
504+  .offset-by-one-half.column,
505+  .offset-by-one-half.columns {
506+    margin-left: 52%;
507+  }
508+}
509+
510+
511+/* Base Styles
512+–––––––––––––––––––––––––––––––––––––––––––––––––– */
513+
514+
515+/* NOTE
516+html is set to 62.5% so that all the REM measurements throughout Skeleton
517+are based on 10px sizing. So basically 1.5rem = 15px :) */
518+
519+html {
520+  font-size: 62.5%;
521+}
522+
523+body {
524+  font-size: 1.5em;
525+  /* currently ems cause chrome bug misinterpreting rems on body element */
526+  line-height: 1.6;
527+  font-weight: 400;
528+  font-family: Helvetica, Arial, sans-serif;
529+  color: #222;
530+}
531+
532+
533+/* Typography
534+–––––––––––––––––––––––––––––––––––––––––––––––––– */
535+
536+h1,
537+h2,
538+h3,
539+h4,
540+h5,
541+h6 {
542+  margin-top: 0;
543+  margin-bottom: 2rem;
544+  font-weight: 300;
545+}
546+
547+h1 {
548+  font-size: 4.0rem;
549+  line-height: 1.2;
550+  letter-spacing: -.1rem;
551+}
552+
553+h2 {
554+  font-size: 3.6rem;
555+  line-height: 1.25;
556+  letter-spacing: -.1rem;
557+}
558+
559+h3 {
560+  font-size: 3.0rem;
561+  line-height: 1.3;
562+  letter-spacing: -.1rem;
563+}
564+
565+h4 {
566+  font-size: 2.4rem;
567+  line-height: 1.35;
568+  letter-spacing: -.08rem;
569+}
570+
571+h5 {
572+  font-size: 1.8rem;
573+  line-height: 1.5;
574+  letter-spacing: -.05rem;
575+}
576+
577+h6 {
578+  font-size: 1.5rem;
579+  line-height: 1.6;
580+  letter-spacing: 0;
581+}
582+
583+
584+/* Larger than phablet */
585+
586+@media (min-width: 550px) {
587+  h1 {
588+    font-size: 5.0rem;
589+  }
590+  h2 {
591+    font-size: 4.2rem;
592+  }
593+  h3 {
594+    font-size: 3.6rem;
595+  }
596+  h4 {
597+    font-size: 3.0rem;
598+  }
599+  h5 {
600+    font-size: 2.4rem;
601+  }
602+  h6 {
603+    font-size: 1.5rem;
604+  }
605+}
606+
607+p {
608+  margin-top: 0;
609+}
610+
611+
612+/* Links
613+–––––––––––––––––––––––––––––––––––––––––––––––––– */
614+
615+a {
616+  color: #1EAEDB;
617+}
618+
619+a:hover {
620+  color: #0FA0CE;
621+}
622+
623+
624+/* Buttons
625+–––––––––––––––––––––––––––––––––––––––––––––––––– */
626+
627+.button,
628+button,
629+input[type="submit"],
630+input[type="reset"],
631+input[type="button"] {
632+  display: inline-block;
633+  height: 38px;
634+  padding: 0 30px;
635+  color: #555;
636+  text-align: center;
637+  font-size: 11px;
638+  font-weight: 600;
639+  line-height: 38px;
640+  letter-spacing: .1rem;
641+  text-transform: uppercase;
642+  text-decoration: none;
643+  white-space: nowrap;
644+  background-color: transparent;
645+  border-radius: 4px;
646+  border: 1px solid #bbb;
647+  cursor: pointer;
648+  box-sizing: border-box;
649+}
650+
651+.button:hover,
652+button:hover,
653+input[type="submit"]:hover,
654+input[type="reset"]:hover,
655+input[type="button"]:hover,
656+.button:focus,
657+button:focus,
658+input[type="submit"]:focus,
659+input[type="reset"]:focus,
660+input[type="button"]:focus {
661+  color: #333;
662+  border-color: #888;
663+  outline: 0;
664+}
665+
666+.button.button-primary,
667+button.button-primary,
668+input[type="submit"].button-primary,
669+input[type="reset"].button-primary,
670+input[type="button"].button-primary {
671+  color: #FFF;
672+  background-color: #2196F3;
673+  border-color: #2196F3;
674+}
675+
676+.button.button-primary:hover,
677+button.button-primary:hover,
678+input[type="submit"].button-primary:hover,
679+input[type="reset"].button-primary:hover,
680+input[type="button"].button-primary:hover,
681+.button.button-primary:focus,
682+button.button-primary:focus,
683+input[type="submit"].button-primary:focus,
684+input[type="reset"].button-primary:focus,
685+input[type="button"].button-primary:focus {
686+  color: #FFF;
687+  background-color: #1E88E5;
688+  border-color: #1E88E5;
689+}
690+
691+
692+/* Forms
693+–––––––––––––––––––––––––––––––––––––––––––––––––– */
694+
695+input[type="email"],
696+input[type="number"],
697+input[type="search"],
698+input[type="text"],
699+input[type="tel"],
700+input[type="url"],
701+input[type="password"],
702+textarea,
703+select {
704+  height: 38px;
705+  padding: 6px 10px;
706+  /* The 6px vertically centers text on FF, ignored by Webkit */
707+  background-color: #fff;
708+  border: 1px solid #D1D1D1;
709+  border-radius: 4px;
710+  box-shadow: none;
711+  box-sizing: border-box;
712+}
713+
714+
715+/* Removes awkward default styles on some inputs for iOS */
716+
717+input[type="email"],
718+input[type="number"],
719+input[type="search"],
720+input[type="text"],
721+input[type="tel"],
722+input[type="url"],
723+input[type="password"],
724+textarea {
725+  -webkit-appearance: none;
726+  -moz-appearance: none;
727+  appearance: none;
728+}
729+
730+textarea {
731+  min-height: 65px;
732+  padding-top: 6px;
733+  padding-bottom: 6px;
734+}
735+
736+input[type="email"]:focus,
737+input[type="number"]:focus,
738+input[type="search"]:focus,
739+input[type="text"]:focus,
740+input[type="tel"]:focus,
741+input[type="url"]:focus,
742+input[type="password"]:focus,
743+textarea:focus,
744+select:focus {
745+  border: 1px solid #33C3F0;
746+  outline: 0;
747+}
748+
749+label,
750+legend {
751+  display: block;
752+  margin-bottom: .5rem;
753+  font-weight: 600;
754+}
755+
756+fieldset {
757+  padding: 0;
758+  border-width: 0;
759+}
760+
761+input[type="checkbox"],
762+input[type="radio"] {
763+  display: inline;
764+}
765+
766+label > .label-body {
767+  display: inline-block;
768+  margin-left: .5rem;
769+  font-weight: normal;
770+}
771+
772+
773+/* Lists
774+–––––––––––––––––––––––––––––––––––––––––––––––––– */
775+
776+ul {
777+  list-style: circle inside;
778+}
779+
780+ol {
781+  list-style: decimal inside;
782+}
783+
784+ol,
785+ul {
786+  padding-left: 0;
787+  margin-top: 0;
788+}
789+
790+ul ul,
791+ul ol,
792+ol ol,
793+ol ul {
794+  margin: 1.5rem 0 1.5rem 3rem;
795+  font-size: 90%;
796+}
797+
798+li {
799+  margin-bottom: 1rem;
800+}
801+
802+
803+/* Code
804+–––––––––––––––––––––––––––––––––––––––––––––––––– */
805+
806+code {
807+  padding: .2rem .5rem;
808+  margin: 0 .2rem;
809+  font-size: 90%;
810+  white-space: nowrap;
811+  background: #F1F1F1;
812+  border: 1px solid #E1E1E1;
813+  border-radius: 4px;
814+}
815+
816+pre > code {
817+  display: block;
818+  padding: 1rem 1.5rem;
819+  white-space: pre;
820+}
821+
822+
823+/* Tables
824+–––––––––––––––––––––––––––––––––––––––––––––––––– */
825+
826+th,
827+td {
828+  padding: 12px 15px;
829+  text-align: left;
830+  border-bottom: 1px solid #E1E1E1;
831+}
832+
833+th:first-child,
834+td:first-child {
835+  padding-left: 0;
836+}
837+
838+th:last-child,
839+td:last-child {
840+  padding-right: 0;
841+}
842+
843+
844+/* Spacing
845+–––––––––––––––––––––––––––––––––––––––––––––––––– */
846+
847+button,
848+.button {
849+  margin-bottom: 1rem;
850+}
851+
852+input,
853+textarea,
854+select,
855+fieldset {
856+  margin-bottom: 1.5rem;
857+}
858+
859+pre,
860+blockquote,
861+dl,
862+figure,
863+table,
864+p,
865+ul,
866+ol,
867+form {
868+  margin-bottom: 2.5rem;
869+}
870+
871+
872+/* Utilities
873+–––––––––––––––––––––––––––––––––––––––––––––––––– */
874+
875+.u-full-width {
876+  width: 100%;
877+  box-sizing: border-box;
878+}
879+
880+.u-max-full-width {
881+  max-width: 100%;
882+  box-sizing: border-box;
883+}
884+
885+.u-pull-right {
886+  float: right;
887+}
888+
889+.u-pull-left {
890+  float: left;
891+}
892+
893+
894+/* Misc
895+–––––––––––––––––––––––––––––––––––––––––––––––––– */
896+
897+hr {
898+  margin-top: 3rem;
899+  margin-bottom: 3.5rem;
900+  border-width: 0;
901+  border-top: 1px solid #E1E1E1;
902+}
903+
904+
905+/* Clearing
906+–––––––––––––––––––––––––––––––––––––––––––––––––– */
907+
908+
909+/* Self Clearing Goodness */
910+
911+.container:after,
912+.row:after,
913+.u-cf {
914+  content: "";
915+  display: table;
916+  clear: both;
917+}
918+
919+
920+/* Media Queries
921+–––––––––––––––––––––––––––––––––––––––––––––––––– */
922+
923+
924+/*
925+Note: The best way to structure the use of media queries is to create the queries
926+near the relevant code. For example, if you wanted to change the styles for buttons
927+on small devices, paste the mobile query code up in the buttons section and style it
928+there.
929+*/
930+
931+
932+/* Larger than mobile */
933+
934+@media (min-width: 400px) {}
935+
936+
937+/* Larger than phablet (also point when grid becomes active) */
938+
939+@media (min-width: 550px) {}
940+
941+
942+/* Larger than tablet */
943+
944+@media (min-width: 750px) {}
945+
946+
947+/* Larger than desktop */
948+
949+@media (min-width: 1000px) {}
950+
951+
952+/* Larger than Desktop HD */
953+
954+@media (min-width: 1200px) {}