diff --git a/core/modules/rlayout/config/grid.six_column_fluid_tablet.yml b/core/modules/rlayout/config/grid.six_column_fluid_tablet.yml
new file mode 100644
index 0000000..744496e
--- /dev/null
+++ b/core/modules/rlayout/config/grid.six_column_fluid_tablet.yml
@@ -0,0 +1,12 @@
+id: six_column_fluid_tablet
+label: Six column fluid
+type: equal_column
+options:
+  type: 'fluid'
+  width: 100
+  columns: 6
+  padding_width: 1.5
+  gutter_width: 2
+  breakpoints:
+    - 'module.rlayout.tablet'
+
diff --git a/core/modules/rlayout/config/grid.three_column_fluid_smartphone.yml b/core/modules/rlayout/config/grid.three_column_fluid_smartphone.yml
new file mode 100644
index 0000000..a5077bf
--- /dev/null
+++ b/core/modules/rlayout/config/grid.three_column_fluid_smartphone.yml
@@ -0,0 +1,12 @@
+id: three_column_fluid_smartphone
+label: Three column fluid
+type: equal_column
+options:
+  type: 'fluid'
+  width: 100
+  columns: 3
+  padding_width: 1.5
+  gutter_width: 2
+  breakpoints:
+    - 'module.rlayout.smartphone'
+
diff --git a/core/modules/rlayout/config/grid.twelve_column_fluid_standard.yml b/core/modules/rlayout/config/grid.twelve_column_fluid_standard.yml
new file mode 100644
index 0000000..ef98884
--- /dev/null
+++ b/core/modules/rlayout/config/grid.twelve_column_fluid_standard.yml
@@ -0,0 +1,12 @@
+id: twelve_column_fluid_standard
+label: Twelve column fluid
+type: equal_column
+options:
+  type: 'fluid'
+  width: 100
+  columns: 12
+  padding_width: 1.5
+  gutter_width: 2
+  breakpoints:
+    - 'module.rlayout.standard'
+
diff --git a/core/modules/rlayout/config/rlayout.breakpoints.yml b/core/modules/rlayout/config/rlayout.breakpoints.yml
new file mode 100644
index 0000000..d8f354c
--- /dev/null
+++ b/core/modules/rlayout/config/rlayout.breakpoints.yml
@@ -0,0 +1,3 @@
+smartphone: '(min-width: 0px)'
+tablet: 'all and (min-width: 321px) and (max-width: 760px)'
+standard: 'all and (min-width: 761px)'
diff --git a/core/modules/rlayout/config/rlayoutset.default.yml b/core/modules/rlayout/config/rlayoutset.default.yml
new file mode 100644
index 0000000..292e361
--- /dev/null
+++ b/core/modules/rlayout/config/rlayoutset.default.yml
@@ -0,0 +1,19 @@
+id: default
+label: Default layout
+regions:
+  - header_a
+  - header_b
+  - header_c
+  - subheader_a
+  - subheader_b
+  - subheader_c
+  - navigation
+  - title
+  - body
+  - sidebar_a
+  - sidebar_b
+  - sidebar_c
+  - footer_a
+  - footer_b
+  - footer_c
+overrides:
diff --git a/core/modules/rlayout/designer/app/libs/Grid/Grid.js b/core/modules/rlayout/designer/app/libs/Grid/Grid.js
new file mode 100644
index 0000000..ff7b42a
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/Grid/Grid.js
@@ -0,0 +1,25 @@
+(function (RLD, $) {
+
+  RLD['Grid'] = (function () {
+
+    var plugin = 'Grid';
+    
+    function Grid() {
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    Grid.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    Grid.prototype.setup = function () {
+      
+    };
+    
+    return Grid;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/app/libs/GridList/GridList.js b/core/modules/rlayout/designer/app/libs/GridList/GridList.js
new file mode 100644
index 0000000..2f039d8
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/GridList/GridList.js
@@ -0,0 +1,58 @@
+(function (RLD, $) {
+  // Temp location.
+  RLD['GridList'] = (function () {
+
+    var plugin = 'GridList';
+
+    function GridList() {
+      this.items = [];
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    GridList.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    GridList.prototype.setup = function () {
+      // Process list items.
+      if ('grids' in this) {
+        this.processList(this.grids);
+        delete this.grids;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] The list has no items at setup.');
+      }
+    };
+    /**
+     *
+     */
+    GridList.prototype.build = function () {
+      return this.$editor;
+    };
+    /**
+     *
+     */
+    GridList.prototype.processList = function (items) {
+      var i;
+      for (i = 0; i < items.length; i++) {
+        this.items.push(new RLD.Grid({
+          'machine_name': items[i].machine_name,
+          'columns': items[i].columns,
+          'classes': items[i].classes
+        }));
+      }
+    };
+    /**
+     *
+     */
+    GridList.prototype.update = function (type, list) {
+      this.items = type;
+    };
+
+    return GridList;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/LayoutList/LayoutList.js b/core/modules/rlayout/designer/app/libs/LayoutList/LayoutList.js
new file mode 100644
index 0000000..fa81b04
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/LayoutList/LayoutList.js
@@ -0,0 +1,72 @@
+(function (RLD, $) {
+  // Temp location.
+  RLD['LayoutList'] = (function () {
+
+    var plugin = 'LayoutList';
+
+    function LayoutList() {
+      this.items = [];
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    LayoutList.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    LayoutList.prototype.setup = function () {
+      // Process list items.
+      if ('layouts' in this) {
+        this.processList(this.layouts);
+        delete this.layouts;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] The list has no items at setup.');
+      }
+    };
+    /**
+     *
+     */
+    LayoutList.prototype.processList = function (items) {
+      // The broadcaster just pipes events through.
+      var handlers = {};
+      var newSet = [];
+      var i, layoutStep, listener, fn;
+      // Create obects for each composite.
+      for (i = 0; i < items.length; i++) {
+        // Save the layout elements into a unit.
+        layoutStep = new RLD.LayoutStep({
+          'regionList': items[i].regionList,
+          'step': items[i].step,
+          'grid': items[i].grid
+        });
+        // Pust the layoutStep into the list.
+        this.items.push(layoutStep);
+        newSet.push(layoutStep);
+        
+      }
+      // Register pass-through topics.
+      this.transferSubscriptions(this.items);
+      // Return the items that were added.
+      return newSet;
+    };
+    /**
+     *
+     */
+    LayoutList.prototype.addItem = function (layout) {
+      var items = this.processList([layout]);
+      return items;
+    }
+    /**
+     *
+     */
+    LayoutList.prototype.update = function (type, list) {
+      this.items = list;
+    };
+
+    return LayoutList;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/LayoutManager/LayoutManager.js b/core/modules/rlayout/designer/app/libs/LayoutManager/LayoutManager.js
new file mode 100644
index 0000000..b7e3456
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/LayoutManager/LayoutManager.js
@@ -0,0 +1,426 @@
+(function (RLD, $) {
+  /**
+   * LayoutManager editor provides functionality to display, add and remove
+   * layout representations across arbitrary, user-defined breakpoint limits.
+   */
+  RLD['LayoutManager'] = (function build() {
+
+    var plugin = 'LayoutManager';
+
+    function LayoutManager() {
+      // Ui components.
+      this.options = {
+        'ui': {
+          'class-layout': 'rld-stepmanager',
+          'class-layout-tabs': 'rld-steps',
+          'class-layout-content': 'rld-layouts'
+        }
+      };
+      this.$editor = $();
+      this.$stepSelector = $();
+      this.$steps = $();
+      this.$layouts = $();
+      this.activeLayoutStep;
+      // Setup
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    LayoutManager.prototype = new RLD.InitClass();
+    /**
+     * Integrate instantiation options.
+     */
+    LayoutManager.prototype.setup = function () {
+      var fn, steps;
+      // Instantiate classes.
+      this.stepManager = new RLD.StepManager();
+      this.layoutList = new RLD.LayoutList();
+      // Define topics that will pass-through.
+      this.topic('regionOrderUpdated');
+      this.topic('layoutSaved');
+      this.topic('regionResized');
+      this.topic('regionResizing');
+      this.topic('regionResizeStarted');
+      // Transfer pass-through subscriptions.
+      this.transferSubscriptions([
+        this.stepManager,
+        this.regionList,
+        this.layoutList
+      ]);
+      // Register for events on the stepManager.
+      fn = $.proxy(this.switchStep, this);
+      this.stepManager.topic('stepActivated').subscribe(fn);
+      // Register for events on the regionList.
+      fn = $.proxy(this.insertRegion, this);
+      this.regionList.topic('regionAdded').subscribe(fn);
+      fn = $.proxy(this.removeRegion, this);
+      this.regionList.topic('regionRemoved').subscribe(fn);
+      // Register for events on the layoutList
+      fn = $.proxy(this.requestRegionRemove, this);
+      this.layoutList.topic('regionRemoved').subscribe(fn);
+      // Assemble the editor managers and containers.
+      this.$stepSelector = $('<div>', {
+        'class': this.ui['class-layout']
+      });
+      this.$steps = $('<ul>', {
+        'class': this.ui['class-layout-tabs']
+      });
+      this.$layouts = $('<div>', {
+        'class': this.ui['class-layout-content']
+      });
+      // Register Layouts into the layoutList
+      // For every step we'll register a layout.
+      steps = this.stepList.info('items');
+      // Create obects for each composite.
+      for (i = 0; i < steps.length; i++) {
+        // Save the composition elements into a unit.
+        this.registerLayoutStep(steps[i]);
+      }
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.build = function () {
+      // Assemble the editor fraemwork.
+      this.$editor = $('<div>', {
+        'class': 'rld-layout-manager'
+      })
+      .append(
+        this.$stepSelector
+        .append(
+          this.stepManager.build(this.$steps)
+        )
+      )
+      .append(
+        this.$layouts
+        .append(
+          $('<div>', {
+            'class': 'rld-screen clearfix',
+          })
+        )
+      );
+      /*this.$editor
+      .delegate('button.save', 'click.ResponsiveLayoutDesigner', {'type': 'save'}, this.update); */
+      return this.$editor;
+    };
+    /**
+     * A layout is a set of regions, in the context of a step, laid out on a grid.
+     */
+    LayoutManager.prototype.registerLayoutStep = function (step) {
+      // Add the LayoutSteps to the LayoutList.
+      this.layoutList.addItem({
+        'step': step,
+        'regionList': this.regionList,
+        'grid': this.gridList.getItem(step.grid.info('machine_name'))
+      });
+      // Add the Step to the StepManager.
+      this.stepManager.addItem(step);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.switchStep = function (event, step) {
+      var args = arguments;
+      var id = this.stepManager.info('activeStep').info('breakpoint');
+      var $screen = this.$layouts.find('.rld-screen');
+      var $layout = $('<div>', {
+        'class': 'rld-layout'
+      });
+      var layout = this.getActiveLayout();
+      var i, grid, gridColumns, gridClasses;
+      // Clear out the current screen.
+      $screen.children('.rld-layout').hide(0, function () {
+        $(this).remove();
+      });
+      grid = layout.info('grid');
+      gridColumns = grid.info('columns');
+      gridClasses = grid.info('classes') || [];
+      if (gridClasses.length > 0) {
+        $screen.addClass();
+      }
+      $screen.animate({
+        width: layout.step.info('size')
+      });
+      // Append the frame to the screen.
+      $screen
+      .append(
+        $layout
+        .empty()
+        .addClass('rld-container-' + gridColumns)
+        .append(this.buildAddRegionButton('top'))
+        .append(this.buildGridUnderlay(gridColumns))
+        .append(layout.build())
+        .append(this.buildAddRegionButton('bottom'))
+      );
+
+      this.topic('stepActivated').publish(step);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.getActiveLayout = function () {
+      var activeStep = this.stepManager.info('activeStep');
+      var layout;
+      for (i = 0; i < this.layoutList.info('items').length; i++) {
+        layout = this.layoutList.info('items')[i];
+        if (layout.step.info('machine_name') === activeStep.info('machine_name')) {
+          return layout;
+        }
+      }
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.buildGridUnderlay = function (columns, height) {
+      var $overlay = $('<div>', {
+        'class': 'rld-grid-underlay clearfix'
+      });
+      var cols = Number(columns);
+      var fn;
+      while (cols) {
+        $overlay.append(
+          $('<div>', {
+            'class': 'rld-span_1 rld-col rld-grid-col'
+          })
+        );
+        cols -= 1;
+      }
+      return $overlay;
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.buildAddRegionButton = function (location) {
+      var handler = $.proxy(this.addRegionHandler, this);
+      var $controls = $('<div>', {
+        'class': 'rld-layoutstep-controls' + ' ' + location
+      })
+      .append(
+        $('<button>', {
+          'text': 'Add region to ' + location
+        })
+        .bind('click', {'location': location}, handler)
+      );
+      return $controls;
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.addRegionHandler = function (event) {
+      event.preventDefault();
+      var regionList = this.regionList;
+      this.candidateRegionName = '';
+      this.candidateRegionMachineName = '';
+      // Dialog pieces.
+      var $input = $('<input>', {
+        'type': 'text'
+      });
+      var $machineName = $('<div>', {
+        'text': '',
+        'class': 'rld-description'
+      });
+      var $availableRegionSelectbox = $('<select>', {
+        'name': 'available-region-select'
+      })
+      .append(
+        $('<option>', {
+          'text': 'No selection',
+          'value': 'null',
+          'selected': 'selected'
+        })
+      );
+      // Populate the available regions select box with a list of regions.
+      var availableRegions = this.availableRegionList.info('items');
+      var i;
+      for (i = 0; i < availableRegions.length; i++) {
+        $('<option>', {
+          'text': availableRegions[i].label || 'No label',
+          'value': availableRegions[i].machine_name
+        })
+        .appendTo($availableRegionSelectbox);
+      }
+      // Machine name checking callback.
+      var machineNameCheck = $.proxy(this.regionList.guaranteeMachineName, this.regionList);
+      // Machine name writing callback.
+      var machineNamePrint = $.proxy(function machineNamePrintProxy(checker, $input, $display, event) {
+        var candidate = this.candidateRegionName = $input.val() || '';
+        var machine_name = candidate.replace(/\s+/g, '_').toLowerCase();
+        // Confine the machine name to 24 characters.
+        if (machine_name.length > 24) {
+          machine_name = machine_name.slice(0, 24);
+        }
+        this.candidateRegionMachineName = machine_name;
+        var isUnique = checker(machine_name);
+        if (isUnique) {
+          $display
+          .text(machine_name)
+          .css({
+            'color': 'black'
+          });
+        }
+        else {
+          $display
+          .text(machine_name)
+          .css({
+            'color': 'red'
+          });
+        }
+      }, this, machineNameCheck, $input, $machineName);
+      // Create and insert the dialog.
+      var $dialog = $('<div>', {
+        'class': 'rld-dialog'
+      });
+      if (availableRegions.length > 0) {
+        $dialog
+        .append($('<label>', {
+          'text': 'Existing region'
+        }))
+        .append($availableRegionSelectbox);
+      };
+      $dialog
+      .append($('<label>', {
+        'text': 'New region'
+      }))
+      .append($input)
+      .append($machineName)
+      .on({
+        'keydown': RLD.Utils.keyManager,
+        'keyup': machineNamePrint
+      },
+      'input',
+      {
+        'callback': machineNamePrint,
+        'pattern': /^[_]*[A-Za-z0-9\_\-\+\s]*$/
+      })
+      .dialog({
+        'title': 'Add a region',
+        'resizable': true,
+        'modal': true
+      });
+      // This is a touch circular, but we need the callback to have the context of the application,
+      // not the dialog box.
+      // Create the dialog callbacks.
+      var buttons = {};
+      var saveCallback = $.Callbacks();
+      var save = $.proxy(this.requestRegionAdd, this, event.data.location, $dialog);
+      // This here is expected to be the div#dialog, which it will be
+      // when the cancel function is called by the dialog.
+      var cancel = function () {
+        $(this).dialog('destroy');
+      };
+      saveCallback.add(save);
+      saveCallback.add(cancel);
+      buttons['Save'] = saveCallback.fire;
+      buttons['Cancel'] = cancel;
+      $dialog.dialog('option', 'buttons', buttons);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.requestRegionAdd = function (location, $dialog, event) {
+      // If an available region is selected, add it.
+      var region;
+      var $selectedAvailableRegion = $dialog.find('[name="available-region-select"]').find('option').filter(':selected');
+      if ($selectedAvailableRegion.length > 0 && $selectedAvailableRegion.val() !== 'null') {
+        var region = this.availableRegionList.getItem($selectedAvailableRegion.val());
+        this.regionList.insertItem({
+          'machine_name': region.machine_name,
+          'label': region.label
+        }, location);
+        // Remove the region from the list of available regions.
+        this.availableRegionList.removeItem(region);
+        return; // This isn't the best interaction. It's just stub code for now.
+      }
+      // this.availableRegionList.removeItem(region.machine_name);
+      // If a new region is named, add it.
+      this.regionList.insertItem({
+        'machine_name': this.candidateRegionMachineName,
+        'label': this.candidateRegionName
+      }, location);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.insertRegion = function (event, updatedRegionList, newRegionItems, location) {
+      this.getActiveLayout().insertRows(newRegionItems, location);
+      // Publish the regionAdded topic.
+      this.topic('regionAdded').publish(event, this);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.requestRegionRemove = function (event, layoutStep, region) {
+      // Remove the item from the regionList.
+      this.regionList.removeItem(region);
+      // Add the region to the list of available regions.
+      this.availableRegionList.addItem(region);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.removeRegion = function (event, region) {
+      var $region = region.info('$editor');
+      var $prev = $region.prev('.rld-region');
+      var $next = $region.next('.rld-region');
+      var span = region.info('span');
+      var activeStep = this.stepManager.info('activeStep');
+      var layoutManager = this;
+      var layout = this.getActiveLayout();
+      var passiveRegion, replacementRegion;
+      // If region has no siblings, hide row. Otherwise, hide region.
+      if ($prev.length === 0 && $next.length === 0) {
+        $region.closest('.rld-row').slideUp(function () {
+          $(this).remove();
+        });
+      }
+      else {
+        $region.slideUp(function () {
+          $(this).remove();
+          if ($prev.length > 0) {
+            passiveRegion = $prev.data('RLD/Region');
+          }
+          else if ($next.length > 0) {
+            passiveRegion = $next.data('RLD/Region');
+          }
+          if (passiveRegion !== undefined) {
+            span = passiveRegion.alterSpan(span, true);
+            // Save any changes to regions.
+            // This doesn't belong here at all, but it's what we've got for the moment.
+            var r;
+            var regionList = layout.regionList.info('items');
+            for (r in regionList) {
+              if (regionList.hasOwnProperty(r)) {
+                // If the region already has an override, update it.
+                if ('span' in regionList[r] && regionList[r].span > 0) {
+                  var item = layout.step.regionList.getItem(regionList[r].info('machine_name'))
+                  if (item) {
+                    item.alterColumns(regionList[r].span);
+                  }
+                  // If the region doesn't have an override yet, create one. This can't be a reference to the
+                  // canonical regionList regions, it needs to be a new object.
+                  else {
+                    var temp = regionList[r].snapshot();
+                    temp.columns = temp.span;
+                    layout.step.regionList.addItem(temp);
+                  }
+                }
+              }
+            }
+          }
+        });
+      }
+      layoutManager.topic('regionRemoved').publish(event, layoutManager);
+    };
+    /**
+     *
+     */
+    LayoutManager.prototype.hideRegion = function (event) {
+      this.topic('regionHidden').publish(event, this);
+    };
+
+    return LayoutManager;
+
+  }());
+
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/LayoutPreviewer/LayoutPreviewer.js b/core/modules/rlayout/designer/app/libs/LayoutPreviewer/LayoutPreviewer.js
new file mode 100644
index 0000000..7fe1454
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/LayoutPreviewer/LayoutPreviewer.js
@@ -0,0 +1,220 @@
+(function (RLD, $) {
+  /**
+   * LayoutPreviewer editor provides functionality to display, add and remove
+   * layout representations across arbitrary, user-defined breakpoint limits.
+   */
+  RLD['LayoutPreviewer'] = (function build() {
+
+    var plugin = 'LayoutPreviewer';
+    
+    function LayoutPreviewer() {
+      // Ui components.
+      this.options = {
+        'ui': {
+          'class-layout': 'rld-stepmanager',
+          'class-layout-tabs': 'rld-steps'
+        }
+      };
+      this.$editor = $();
+      this.$stepSelector = $();
+      this.activeLayoutStep;
+      // Setup
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    LayoutPreviewer.prototype = new RLD.InitClass();
+    /**
+     * Integrate instantiation options.
+     */
+    LayoutPreviewer.prototype.setup = function () {
+      var fn, steps;
+      // Instantiate classes.
+      this.stepManager = new RLD.StepManager();
+      this.layoutList = new RLD.LayoutList();
+      // Define topics that will pass-through.
+      // -- none.
+      // Transfer pass-through subscriptions.
+      this.transferSubscriptions([
+        this.stepManager,
+        this.layoutList
+      ]);
+      // Register for events on the stepManager.
+      fn = $.proxy(this.switchStep, this);
+      this.stepManager.topic('stepActivated').subscribe(fn);
+      // Assemble the editor managers and containers.
+      this.$stepSelector = $('<div>', {
+        'class': this.ui['class-layout']
+      });
+      this.$steps = $('<ul>', {
+        'class': this.ui['class-layout-tabs']
+      });
+      this.$layouts = $('<div>', {
+        'class': this.ui['class-layout-content']
+      });
+      // Register Layouts into the layoutList
+      // For every step we'll register a layout.
+      steps = this.stepList.info('items');
+      // Create obects for each composite.
+      for (i = 0; i < steps.length; i++) {
+        // Save the composition elements into a unit.
+        this.registerLayoutStep(steps[i]);
+      }
+      // Register a DOM ready handler.
+      $(document).on('ready', $.proxy(this.injectPreviewDOM, this));
+    };
+    /**
+     *
+     */
+    LayoutPreviewer.prototype.build = function () {
+      // Assemble the editor fraemwork.
+      this.$editor = $('<div>', {
+        'class': 'rld-layout-previewer'
+      })
+      .append(
+        this.$stepSelector
+        .append(
+          this.stepManager.build(this.$steps)
+        )
+      );
+      /*this.$editor
+      .delegate('button.save', 'click.ResponsiveLayoutDesigner', {'type': 'save'}, this.update); */
+      return this.$editor;
+    };
+    /**
+     *
+     */
+    LayoutPreviewer.prototype.switchStep = function (event, step) {
+      var width, frame, $frame, fn;
+      var $editor = this.$editor;
+      var grid = this.gridList.getItem(step.grid['machine_name']);
+      var $frame = $('.rld-previewer');
+      if ($frame.length === 0) {
+        var preview = $('<div>', {}).load( window.location.href, function (data, status, jqXHR) {
+          var $html = $(this);
+          var $head = $html.find('meta, link, title, style, script');
+          var $body = $html.find('#page-wrapper');
+          $head = $('<div>', {
+            'html': $head
+          });
+          $body = $('<div>', {
+            'html': $body
+          });
+          frame = document.createElement('iframe');
+          // Hard-coded offset. This is bad bad bad. 
+          frame.height = document.documentElement.clientHeight - 120;
+          frame.className = 'rld-modal rld-previewer';
+          document.body.appendChild(frame);
+          var content = '<!DOCTYPE html><html><head>' + $head.html() + '</head><body>' + $body.html() + '</body></html>';
+          
+          frame.contentWindow.document.open('text/html', 'replace');
+          frame.contentWindow.document.write(content);
+          frame.contentWindow.document.close();
+          var $frame = $('.rld-previewer');
+          width = Number(step.info('size'));
+          $frame.animate({
+            width: width,
+            left: (document.documentElement.clientWidth - width ) / 2
+          });
+        });
+        $('.rld-previewer');
+        $('<div>', {
+          'class': 'rld-modal rld-modal-screen'
+        })
+        .css({
+          'display': 'none'
+        })
+        .appendTo('body')
+        .fadeIn();
+        $('<div>', {
+          'class': 'rld-modal rld-modal-close',
+          'html': $('<a>', {
+            'href': '#',
+            'text': 'Close'
+          })
+          .on({
+            'click': function (event) {
+              // The following functions are acting globally on the page. This is bad bad bad.
+              // They should only apply with the application or the editor.
+              $('.rld-steps .rld-active').removeClass('rld-active');
+              $('.rld-modal').fadeOut(function () {
+                $(this).remove();
+              });
+            }
+          })
+        })
+        .prependTo($editor);
+      }
+      width = Number(step.info('size'));
+      $frame.animate({
+        width: width,
+        left: (document.documentElement.clientWidth - width ) / 2
+      });
+      this.topic('stepActivated').publish(step);
+    };
+    /**
+     *
+     */
+    LayoutPreviewer.prototype.injectPreviewDOM = function (event) {
+      var steps = this.stepList.info('items');
+      var grids = this.gridList.info('items')
+      var breakUpPayload = {};
+      var step;
+      for (step in steps) {
+        if (steps.hasOwnProperty(step)) {
+          // breakUpPayload[step.machine_name] = $.proxy(this.switchStep, this, step, );
+        }
+      }
+    };
+    /**
+     *
+     */
+    LayoutPreviewer.prototype.getActiveLayout = function () {
+      var activeStep = this.stepManager.info('activeStep');
+      var layout;
+      for (i = 0; i < this.layoutList.info('items').length; i++) {
+        layout = this.layoutList.info('items')[i];
+        if (layout.step.info('machine_name') === activeStep.info('machine_name')) {
+          return layout;
+        }
+      }
+    };
+    /**
+     *
+     */
+    LayoutPreviewer.prototype.buildGridUnderlay = function (columns, height) {
+      var $overlay = $('<div>', {
+        'class': 'rld-grid-underlay clearfix'
+      });
+      var cols = Number(columns);
+      var fn;
+      while (cols) {
+        $overlay.append(
+          $('<div>', {
+            'class': 'rld-span_1 rld-col rld-grid-col'
+          })
+        );
+        cols -= 1;
+      }
+      return $overlay;
+    };
+    /**
+     * A layout is a set of regions, in the context of a step, laid out on a grid.
+     */
+    LayoutPreviewer.prototype.registerLayoutStep = function (step) {
+      // Add the LayoutSteps to the LayoutList.
+      this.layoutList.addItem({
+        'step': step,
+        'regionList': this.regionList,
+        'grid': this.gridList.getItem(step.grid.info('machine_name'))
+      });
+      // Add the Step to the StepManager.
+      this.stepManager.addItem(step);
+    };
+
+    return LayoutPreviewer;
+    
+  }());
+
+}(ResponsiveLayoutDesigner, jQuery));
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/app/libs/LayoutStep/LayoutStep.js b/core/modules/rlayout/designer/app/libs/LayoutStep/LayoutStep.js
new file mode 100644
index 0000000..ff2a252
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/LayoutStep/LayoutStep.js
@@ -0,0 +1,484 @@
+(function (RLD, $) {
+  
+  RLD['LayoutStep'] = (function () {
+
+    var plugin = 'LayoutStep';
+
+    // Layout Class
+    function LayoutStep() {
+      this.deltaColumns = 0;
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    LayoutStep.prototype = new RLD.InitClass();
+    
+    LayoutStep.prototype.setup = function () {
+      // Define topics that will pass-through.
+      this.topic('regionOrderUpdated');
+      this.topic('regionAdded');
+      this.topic('regionRemoved');
+      this.topic('regionResized');
+      this.topic('regionResizing');
+      this.topic('regionResizeStarted');
+    };
+    
+    LayoutStep.prototype.build = function (options, items) {
+      this.$editor = $('<div>', {});
+      var regions = items || this.regionList.info('items');
+      this.$editor.append(this.buildRows(regions).contents());
+      // Bind behaviors.
+      fn = $.proxy(this.sortRows, this);
+      this.$editor.sortable({
+        // Make a placeholder visible when dragging.
+        placeholder: "ui-state-highlight",
+        // When the dragging and dropping is done, save updated region
+        // list in our local list.
+        deactivate: fn
+      });
+      // Return the $editor as a jQuery object.
+      return this.$editor;
+    };
+    
+    LayoutStep.prototype.sortRows = function (event, data) {
+      var regionList = [];
+      var i;
+      // Get the region objects in their new order.
+      var $regions = data.sender.find('.rld-region');
+      for (i = 0; i < $regions.length; i++) {
+        regionList.push($($regions[i]).data('RLD/Region'));
+      }
+      this.regionList.update(regionList);
+      // 
+      this.topic('regionOrderUpdated').publish(this);
+    };
+    LayoutStep.prototype.modifyRegionBuild = function ($region) {
+      var region = $region.data('RLD/Region');
+      var fn;
+      // Add splittrs to the regions.
+      $region
+      .prepend(
+        $('<div>', {
+          'class': 'rld-splitter rld-splitter-left'
+        })
+        .data('RLD/Region/Splitter-side', 'left')
+      )
+      .append(
+        $('<div>', {
+          'class': 'rld-splitter rld-splitter-right'
+        })
+        .data('RLD/Region/Splitter-side', 'right')
+      )
+      .append(
+        $('<a>', {
+          'class': 'rld-region-close',
+          'href': '#',
+          'text': 'X',
+          'title': 'Close',
+        })
+      );
+      // Region resize.
+      fn = $.proxy(this.startRegionResize, this);
+      $region
+      .on({
+        'mousedown.ResponsiveLayoutDesigner': fn
+      }, '.rld-splitter', {'region': region});
+      // Region remove.
+      $region.on({
+        'click.ResponsiveLayoutDesigner': this.removeRegion
+      },'.rld-region-close', {'manager': this});
+      // Return the editor as a DOM fragment.
+      return $region
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.startRegionResize = function (event) {
+      this.$editor.sortable('disable');
+      event.stopImmediatePropagation();
+      var data = event.data;
+      var region = data.region;
+      var $region = region.info('$editor');
+      var $splitter = $(event.target);
+      // @todo eventually the row should be stored in state, not structure.
+      var $row = $region.closest('.rld-row');
+      var i, widthOffset, originalColumn;
+      // Mark the region as active.
+      region.info('active', true);
+      // Mark the splitter active.
+      $splitter.addClass('splitter-active');
+      // Since the resize function will be called on mousemove, we don't want
+      // to calculate the state of the row's region more than once. So we
+      // pass this information into the handlers.
+      // Determine if the splitter is on the left or right side of region.
+      data.$row = $row;
+      data.side = $splitter.data('RLD/Region/Splitter-side');
+      data.width = $region.outerWidth(true);
+      // Find all the regions/placeholders in this row.
+      data.units = $row.find('.rld-unit').map(function (index, element) {
+        return $(this).data('RLD/Region');
+      });
+      // Calculate the column size so regions can be snapped to grid columns.
+      data.totalColumns = Number(this.grid.info('columns'));
+      data.frame = Math.floor(Number(this.step.info('size')) / data.totalColumns);
+      // Store the X origin of the original click.
+      data.originX = event.pageX;
+      // Get the column the resize started in.
+      widthOffset = (data.side === 'right') ? data.width : 0;
+      originalColumn = Math.floor(($region.position().left + widthOffset) / data.frame);
+      data.originColumn = (data.side === 'left') ? ++originalColumn : originalColumn;
+      // Calculate the left and right bounds for the resizing.
+      data.rightMaxTraversal = data.totalColumns - data.originColumn;
+      data.leftMaxTraversal = (data.originColumn -1) * -1;
+      // Add behaviors.
+      fn = $.proxy(this.resizeRegion, this);
+      $(document).bind('mousemove.regionResize', data, fn);
+      fn = $.proxy(this.finishRegionResize, this);
+      $(document).bind('mouseup.regionResize', data, fn);
+      // Call listeners.
+      this.topic('regionResizeStarted').publish(this);
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.resizeRegion = function (event) {
+      event.stopImmediatePropagation();
+      var data = event.data;
+      var region = data.region;
+      var bypass= false;
+      // Calculate the number of grid columns the mouse has traversed.
+      var columnsTraversed = Math.floor((event.pageX - data.originX) / data.frame);
+      // We want regions resized from the right to resize on the trailing
+      // edge of the column, not the leading edge.
+      if (columnsTraversed < 0 ) {
+        columnsTraversed += 1;
+      }
+      // Get the difference between the distance we know we've covered in previous loops,
+      // and where the mouse is in this loop.
+      var traversedChunk = columnsTraversed - this.deltaColumns;
+      // This is the amount that the region might be changed by.
+      var proposedDelta = this.deltaColumns + traversedChunk;
+      // Check to see if the region needs to be sized up to the edge.
+      if ((proposedDelta === data.leftMaxTraversal || proposedDelta === data.rightMaxTraversal) && proposedDelta !== 0) {
+        bypass = true;
+      }
+      // Check to see if we are totally off the screen.
+      if (proposedDelta <= data.leftMaxTraversal || proposedDelta >= data.rightMaxTraversal) {
+        proposedDelta = (proposedDelta > 0) ? data.rightMaxTraversal : (proposedDelta < 0) ? data.leftMaxTraversal : proposedDelta;
+      }
+      // Only resize the region if the frame changes.
+      if (bypass || columnsTraversed !== this.deltaColumns) {
+        this.deltaColumns = proposedDelta;
+        // Get an object of two regions: the one to be expanded and the one to be contracted.
+        var affectedRegions = this.getAffectedRegions(region, data, traversedChunk);
+        // Resize the affected regions by the amount traversed chunk of columns.
+        if (affectedRegions.right) {
+          affectedRegions.right.alterSpan((traversedChunk * -1), true);
+        }
+        if (affectedRegions.left) {
+          affectedRegions.left.alterSpan(traversedChunk, true);
+        }
+      }
+      // this.topic('regionResizing').publish(this);
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.finishRegionResize = function (event) {
+      this.$editor.sortable('enable');
+      event.stopImmediatePropagation();
+      var layout = this;
+      var data = event.data;
+      var region = data.region;
+      var $region = region.info('$editor');
+      // Perform a final resize.
+      this.resizeRegion.apply(this, arguments);
+      // Move the next available region up to the placeholder.
+      var placeholder = this.getActivePlaceholder(data);
+      if (placeholder) {
+        var layout = this; // hack, hack, hack...
+        var $placeholder = placeholder.info('$editor');
+        var $nextRow = data.$row.next('.rld-row');
+        var $candidateRegion = $nextRow.find('.rld-region:first');
+        if ($candidateRegion.length > 0) {
+          var size = placeholder.info('span');
+          $candidateRegion.animate({
+            width: 0
+          });
+          $candidateRegion.queue(function (next) {
+            var $shiftedRegion = $candidateRegion.detach();
+            var shiftedRegion = $shiftedRegion.data('RLD/Region');
+            $placeholder.animate({
+              width: 0
+            });
+            $placeholder.queue(function (next) {
+              var $this = $(this);
+              $this.data('RLD/Region').alterSpan(0);
+              $this.removeAttr('style');
+              next();
+            });
+            $placeholder.queue(function (next) {
+              $shiftedRegion[(data.side === 'left') ? 'insertAfter' : 'insertBefore']($placeholder);
+              $shiftedRegion.animate({
+                'width': size * data.frame
+              });
+              $shiftedRegion.queue(function (next) {
+                var $this = $(this);
+                $this.data('RLD/Region').alterSpan(size);
+                $this.removeAttr('style');
+                next();
+              });
+              $shiftedRegion.queue(function (next) {
+                var $regions = $nextRow.find('.rld-region');
+                if ($regions.length === 0) {
+                  $nextRow.slideUp(function () {$(this).remove()});
+                }
+                if ($regions.length === 1) {}
+                if ($regions.length > 1) {}
+                // Save any changes to regions.
+                // This doesn't belong here at all, but it's what we've got for the moment.
+                var r;
+                var regionList = layout.regionList.info('items');
+                for (r in regionList) {
+                  if (regionList.hasOwnProperty(r)) {
+                    // If the region already has an override, update it.
+                    if ('span' in regionList[r] && regionList[r].span > 0 && regionList[r].span < data.totalColumns) {
+                      var item = layout.step.regionList.getItem(regionList[r].info('machine_name'))
+                      if (item) {
+                        item.alterColumns(regionList[r].span);
+                      }
+                      // If the region doesn't have an override yet, create one. This can't be a reference to the
+                      // canonical regionList regions, it needs to be a new object.
+                      else {
+                        var temp = regionList[r].snapshot();
+                        temp.columns = temp.span;
+                        layout.step.regionList.addItem(temp);
+                      }
+                    }
+                  }
+                }
+                // Call listeners for this event.
+                layout.topic('regionResized').publish(layout);
+                next();
+              });
+              next();
+            });
+            next();
+          });
+        }
+      }      
+      // Clean up state.
+      region.info('active', null);
+      this.deltaColumns = 0;
+      $region.find('.splitter').removeClass('splitter-active');
+      $(document).unbind('.regionResize');
+    }
+    /**
+     *
+     */
+    LayoutStep.prototype.removeRegion = function (event) {
+      event.preventDefault();
+      var $region = $(this).closest('.rld-region');
+      var region = $region.data('RLD/Region');
+      event.data.manager.topic('regionRemoved').publish(event, event.data.manager, region);
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.getAffectedRegions = function (region, data, traversedChunk) {
+      var units = data.units;
+      var activeSide = (data.side === 'left') ? 'right' : 'left';
+      var candidateSide = (data.side === 'left') ? 'left' : 'right';
+      var regions = {};
+      // Expanding and contracting checks for the active and candidate regions.
+      var isActiveContracting = ((activeSide === 'left' && traversedChunk < 0) || (activeSide === 'right' && traversedChunk > 0));
+      var isActiveExpanding = ((activeSide === 'left' && traversedChunk > 0) || (activeSide === 'right' && traversedChunk < 0));
+      var isCandidateContracting = ((candidateSide === 'left' && traversedChunk < 0) || (candidateSide === 'right' && traversedChunk > 0));
+      var isCandidateExpanding = ((candidateSide === 'left' && traversedChunk > 0) || (candidateSide === 'right' && traversedChunk < 0));
+      var i, index, candidate;
+      // Assume nothing is changing.
+      regions[activeSide] = null;
+      regions[candidateSide] = null;
+      // Don't allow the active region to contract smaller than one column or expand more than the total number of columns.
+      if ((region.columns === 1 && isActiveContracting) || ((region.columns === data.totalColumns) && isActiveExpanding)) {
+        return regions;
+      }
+      // If the active region can be altered, then determine which unit will be the passive unit.
+      // This is a zero-sum game. Someone has to make room or take room.
+      // Get the index of the active region from the units.
+      index = this.getActiveRegionIndex(units);
+      // Try candidate units until one can be manipulated.
+      for (i = (data.side === 'left') ? (index - 1) : (index + 1); i >= 0 && i < units.length; (data.side === 'left') ? i-- : i++) {
+        // The try-catch is here to make sure we don't access an index of units
+        // that doesn't exist and blow up the application.
+        try {
+          candidate = units[i];
+          // If the candidate is a placeholder, just use it.
+          if (candidate.type === 'placeholder') {
+            regions[candidateSide] = candidate;
+            break;
+          }
+          // Don't allow the candidate region to contract smaller than one column or expand more than the total number of columns.
+          if ((candidate.columns === 1 && isCandidateContracting) || ((candidate.columns === data.totalColumns) && isCandidateExpanding)) {
+            return regions;
+          }
+          // The candidate can be manipulated.
+          regions[candidateSide] = candidate;
+          break;
+        }
+        catch (error) {
+          regions[candidateSide] = null;
+        }
+      }
+      // The region can be resized. We should have a candidate as well.
+      regions[activeSide] = region;
+      return regions;
+    }
+    /**
+     *
+     */
+    LayoutStep.prototype.getActivePlaceholder = function (data) {
+      var units = data.units;
+      var activeRegionIndex = this.getActiveRegionIndex(units);
+      var placeHolderIndex = (activeRegionIndex === 1 && data.side === 'left') ? 0 : (units.length - 1);
+      var placeholder = units[placeHolderIndex];
+      if (placeholder.type === 'placeholder') {
+        if (units[placeHolderIndex].span > 0) {
+          return units[placeHolderIndex];
+        }
+      }
+      return null;
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.getActiveRegion = function (units) {
+      return units[this.getActiveRegionIndex(units)];
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.getActiveRegionIndex = function (units) {
+      var i;
+      for (i = 0; i < units.length; i++) {
+        if ('active' in units[i] && units[i].active) {
+          return i;
+        }
+      }
+      return null;
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.buildRows = function (regions, options) {
+      var $container = $('<div>', {});
+      var step = this.step;
+      var grid = this.grid;
+      var count = 0;
+      // The size of a region may be overridden in this step.
+      var regionOverrides = step.info('regionList').info('items');
+      var $row;
+      var i, k, fn, region, $region, span;
+      // Build rows and regions.
+      for (i = 0; i < regions.length; i++) {
+        var override = undefined;
+        var classes = ['rld-col rld-unit'];
+        // Start a new row if the spans in the previous row are sufficient or exceed the allotment.
+        if ((count === 0) || (count >= grid.columns)) {
+          // Append a placeholder to the end of a row.
+          if (count >= grid.columns) {
+            $row.append(
+              new RLD.Region({
+                'type': 'placeholder'
+              })
+              .build({
+                'classes': classes
+              })
+            );
+          }
+          // Create a new row.
+          $row = $('<div>', {
+            'class': 'rld-row clearfix'
+          })
+          // Append a placeholder to the start of the row.
+          .append(
+            new RLD.Region({
+              'type': 'placeholder'
+            })
+            .build({
+              'classes': classes
+            })
+          )
+          // Append the row to the editor.
+          .appendTo($container);
+          // Restart the row span count.
+          count = 0;
+        }
+        region = regions[i];
+        // If this step has region overrides, get the override that matches this region, if any.
+        if (regionOverrides.length > 0) {
+          for (k = 0; k < regionOverrides.length; k++) {         
+            if (region.info('machine_name') === regionOverrides[k]['machine_name']) {
+              override = regionOverrides[k];
+              break;
+            }
+          }
+        }
+        // If an override for this region exists, use it.
+        if (override !== undefined) {
+          span = override.columns;
+          count += override.columns;
+        }
+        // Otherwise the region is assumed to be full width.
+        else {
+          span = grid.columns;
+          count = grid.columns;
+        }
+        // Build the region.
+        $region = regions[i].build({
+          'classes': classes
+        });
+        // Alter its span.
+        $region
+        .data('RLD/Region')
+        // Get the Region object and update its span.
+        .alterSpan(span)
+        // Append it to the row.
+        $row.append(
+          this.modifyRegionBuild($region)
+        );
+        // Append a placeholder to the end of a row if this is the last item processed.
+        if (i === (regions.length - 1)) {
+          $row.append(
+            new RLD.Region({
+              'type': 'placeholder'
+            })
+            .build({
+              'classes': classes
+            })
+          );
+        }
+      }
+      return $container;
+    };
+    /**
+     *
+     */
+    LayoutStep.prototype.insertRows = function (items, location) {
+      var $editor = this.$editor;
+      // Get a well-formed region, ready to insert into a layout.
+      var $rows = this.buildRows(items).contents();
+      // Insert the wrapped region into the editor.
+      $rows.hide()
+      [(location !== undefined && location === 'top') ? 'prependTo' : 'appendTo']($editor);
+      // Reveal the wrapped regions in a pretty way.
+      $editor
+      .find($rows)
+      .slideDown(500);
+    };
+    return LayoutStep;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/Region/Region.js b/core/modules/rlayout/designer/app/libs/Region/Region.js
new file mode 100644
index 0000000..c419c0a
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/Region/Region.js
@@ -0,0 +1,101 @@
+(function (RLD, $) {
+
+  RLD['Region'] = (function () {
+
+    var plugin = 'Region';
+    /**
+     *
+     */
+    function Region() {
+      this.$editor = $('<div>', {});
+      this.type = 'region';
+      this.columns = 0; // The number of columns this region consumes in a step.
+      this.span = 0; // A temporary column consumption count for rendering a view.
+      this.columnClass = 'rld-span_';
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    Region.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    Region.prototype.setup = function () {
+      this.span = this.columns;
+    };
+    /**
+     *
+     */
+    Region.prototype.build = function (options) {
+      // @todo this classes stuff needs to be generalized.
+      var classes = [];
+      var style = {};
+      classes.push('rld-' + this.type);
+      var fn;
+      if (options && 'classes' in options && 'length' in options.classes && options.classes.length > 0) {
+        classes = classes.concat(options.classes).join(' ');
+      }
+      if (options && 'style' in options && typeof options.style === 'object') {
+        style = $.extend(style, options.style);
+      }
+      
+      this.$editor = $('<div>', {
+        'id': ('label' in this) ? 'rld-region-' + this.label.split(' ').join('_') : '',
+        'class': classes,
+        'html': $('<div>', {
+          'class': 'rld-inner',
+          'html': $('<p>', {
+            'text': this.label
+          })
+        })
+      })
+      .css(style);
+      // Save a reference to the model object to data().
+      this.$editor
+      .data('RLD/Region', this);
+    
+      return this.$editor;
+    };
+    /**
+     *
+     */
+    Region.prototype.alterColumns = function (columns, isRelative) {
+      if (isRelative) {
+        this.columns += Number(columns);
+      }
+      else {
+        this.columns = Number(columns);
+      }
+      if (this.columns < 0) {
+        this.columns = 0;
+      }
+      // Change the view to match the number of columns.
+      this.alterSpan(this.columns);
+      // Return the view.
+      return this.$editor;
+    };
+    /**
+     *
+     */
+    Region.prototype.alterSpan = function (span, isRelative) {
+      if (isRelative) {
+        this.span += Number(span);
+      }
+      else {
+        this.span = Number(span);
+      }
+      if (this.span < 0) {
+        this.span = 0;
+      }
+      var span = this.columnClass + this.span;
+      this.$editor.supplantClass(this.columnClass, span);
+      // Return the new span.
+      return span;
+    };
+  
+    return Region;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/RegionList/RegionList.js b/core/modules/rlayout/designer/app/libs/RegionList/RegionList.js
new file mode 100644
index 0000000..798a09c
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/RegionList/RegionList.js
@@ -0,0 +1,119 @@
+(function (RLD, $) {
+  // Temp location.
+  RLD['RegionList'] = (function () {
+
+    var plugin = 'RegionList';
+
+    function RegionList() {
+      this.items = [];
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     *
+     * Options passed into the constructor are assigned as
+     * properties of the instance.
+     */
+    RegionList.prototype = new RLD.InitClass();
+    /**
+     * Called by the InitClass prototype.
+     */
+    RegionList.prototype.setup = function () {
+      // Format the regions.
+      if ('regions' in this) {
+        this.processList(this.regions);
+        delete this.regions;
+      }
+      else {
+        this.log('[RLD | RegionList] The RegionList instance has no Regions at setup.');
+      }
+    };
+    /**
+     *
+     */
+    RegionList.prototype.processList = function (items, location) {
+      var newSet = [];
+      var i, item, region;
+      for (i = 0; i < items.length; i++) {
+        item = items[i];
+        // Check if this item is already an item.
+        if ('init' in item && typeof item['init'] === 'function') {
+          region = item;
+        }
+        else {
+          region = new RLD.Region({
+            'label': ('label' in item) ? item['label'] : 'No label',
+            'machine_name': ('machine_name' in item) ? item['machine_name'] : 'no_machine_name',
+            'classes': ('classes' in item) ? item['classes'] : [],
+            'columns': ('columns' in item) ? item['columns'] : null
+          });
+        };
+        // Add the new region to the list.
+        this.items[(location !== undefined && location === 'top') ? 'unshift' : 'push'](region);
+        newSet.push(region);
+      }
+      // Transfer pass-through subscriptions.
+      this.transferSubscriptions(this.items);
+      // Return the items that were added.
+      return newSet;
+    };
+    /**
+     * @todo, this method needs better argument type handling. It could
+     * be either an array or an object.
+     *
+     * @todo this should be a private method.
+     */
+    RegionList.prototype.addItem = function (items, location) {
+      if (typeof items === 'object' && !('length' in items)) {
+        var items = [items];
+      }
+      return this.processList(items, location)
+    };
+    /**
+     * This is public method, an interface for this.addItem().
+     *
+     * @todo, this method needs better argument type handling. It could
+     * be either an array or an object.
+     */
+    RegionList.prototype.insertItem = function (item, location) {
+      var newItems = this.addItem(item, location);
+      this.topic('regionAdded').publish(this.items, newItems, location);
+    };
+    /**
+     *
+     */
+    RegionList.prototype.removeItem = function (region) {
+      var items = this.items;
+      var i;
+      for (i = 0; i < items.length; i++) {
+        if (items[i].machine_name === region.machine_name) {
+          this.items.splice(i, 1);
+        }
+      }
+      this.topic('regionRemoved').publish(region);
+    };
+    /**
+     *
+     */
+    RegionList.prototype.update = function (type, list) {
+      this.items = type;
+    };
+    /**
+     *
+     */
+    RegionList.prototype.guaranteeMachineName = function (name) {
+      var regions = this.items;
+      var i;
+      for (i = 0; i < regions.length; i++) {
+        if (regions[i].machine_name === name) {
+          return false;
+        }
+      }
+      return true;
+    };
+
+    return RegionList;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/Step/Step.js b/core/modules/rlayout/designer/app/libs/Step/Step.js
new file mode 100644
index 0000000..d6688bd
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/Step/Step.js
@@ -0,0 +1,54 @@
+(function (RLD, $) {
+  // Temp location.
+  RLD['Step'] = (function () {
+
+    var plugin = 'Step';
+    
+    function Step() {
+      this.options = {
+        'breakpoint': '0'
+      };
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    Step.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    Step.prototype.setup = function () {
+      this.topic('stepRegionOverridden');
+    }
+    /**
+     *
+     */
+    Step.prototype.supplantRegion = function (region) {
+      var items = this.regionList.info('items');
+      var i, index;
+      for (i = 0; i < items.length; i++) {
+        if (items[i].machine_name === region.machine_name) {
+          index = i;
+          break;
+        }
+      }
+      if (index !== undefined) {
+        this.regionList.info('items').splice(index, 1, region);
+      }
+      else {
+        this.regionList.addItem(region);
+      }
+      this.topic('stepRegionOverridden').publish(region); 
+    };
+    /**
+     *
+     */
+    Step.prototype.removeRegion = function (region) {
+      
+    };
+
+    return Step;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/app/libs/StepList/StepList.js b/core/modules/rlayout/designer/app/libs/StepList/StepList.js
new file mode 100644
index 0000000..2fb9449
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/StepList/StepList.js
@@ -0,0 +1,90 @@
+(function (RLD, $) {
+  // Temp location.
+  RLD['StepList'] = (function () {
+
+    var plugin = 'StepList';
+
+    function StepList() {
+      this.items = [];
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    StepList.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    StepList.prototype.setup = function () {
+      // Process list items.
+      if ('steps' in this) {
+        this.processList(this.steps);
+        delete this.steps;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] The list has no items at setup.');
+      }
+      // Calculate the step sizes.
+      this.processStepSizes();
+      
+    };
+    /**
+     *
+     */
+     StepList.prototype.processList = function (items) {
+      var i;
+      for (i = 0; i < items.length; i++) {
+        this.items.push(new RLD.Step({
+          'label': items[i].label,
+          'machine_name': items[i].machine_name,
+          'breakpoint': items[i].breakpoint,
+          'regionList': new RLD.RegionList({
+            'regions': items[i].regions
+          }),
+          'grid': new RLD.Grid({
+            'machine_name': items[i].grid
+          })
+        }));
+      }
+    };
+    /**
+     *
+     */
+    StepList.prototype.update = function (type, list) {
+      this.stepItems = type;
+      this.topic('stepOrderUpdated').publish(this);
+    };
+    /**
+     * @Todo, the steps will need to be sorted by breakpoint
+     * before the sizes can be calculated.
+     */
+    StepList.prototype.processStepSizes = function () {
+      var i;
+      if ('items' in this) {
+        var i, k, step, breakMin, breakMax, size;
+        for (i = 0; i < this.items.length; i++) {
+          step = this.items[i];
+          breakMin = Number(step.info('breakpoint')) || 0;
+          if (this.items[i + 1] !== undefined) {
+            breakMax = Number(this.items[i + 1].info('breakpoint'));
+            size = breakMax - breakMin;
+            // Add all of the previous breakpoint sizes to this step size.
+            if (i > 0) {
+              size += this.items[i - 1].info('size');
+            }
+          }
+          else {
+            // The largest step should be at least 600. If it exceeds 600, it needs
+            // an uppder bound, so we add 100 to its breakpoint size.
+            size = (breakMin < 600) ? 600 : breakMin + 250;
+          }
+          this.items[i].info('size', size);
+        }
+      }
+    };
+
+    return StepList;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
diff --git a/core/modules/rlayout/designer/app/libs/StepManager/StepManager.js b/core/modules/rlayout/designer/app/libs/StepManager/StepManager.js
new file mode 100644
index 0000000..16ec5af
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/StepManager/StepManager.js
@@ -0,0 +1,88 @@
+(function (RLD, $) {
+
+  RLD['StepManager'] = (function () {
+
+    var plugin = 'StepManager';
+
+    function StepManager() {
+      this.steps = [];
+      this.activeStep;
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the InitClass Object.
+     */
+    StepManager.prototype = new RLD.InitClass();
+    /**
+     *
+     */
+    StepManager.prototype.setup = function (options) {
+      // UI objects.
+      this.$stepContainer = $('<div>', {});
+      this.activeStep = this.activeStep || this.steps[0];
+    };
+    /**
+     *
+     */
+    StepManager.prototype.build = function ($stepContainer) {
+      this.$editor = $('<div>', {});
+      var handler, i, step, breakpoint, size, label, id;
+      this.$stepContainer = ($stepContainer.length > 0) ? $stepContainer : this.$stepContainer;
+      // Clear the UI.
+      this.$stepContainer.children().remove();
+      // Build the list of steps.
+      for (i = 0; i < this.steps.length; i++) {
+        step = this.steps[i];
+        breakpoint = step.info('breakpoint');
+        size = step.info('size');
+        label = step.info('label');
+        id = 'breakpoint-' + breakpoint;
+        this.$stepContainer
+        .append(
+          $('<li>', {
+            'class': 'rld-tab'
+          })
+          .append(
+            $('<a>', {
+              'class': 'rld-link',
+              'href': '#' + id,
+              'text': size + 'px'
+            })
+            .data('RLD/Step', step)
+          )
+        );
+      }
+      // Attach behaviors.
+      this.$stepContainer.delegate('a', 'click.RLD.StepManager', {'manager': this}, this.activateStep);
+      // Attach the steps and layouts to the $editor and return it.
+      return this.$editor
+      .append(this.$stepContainer);
+    };
+    /**
+     *
+     */
+    StepManager.prototype.addItem = function (step) {
+      this.steps.push(step);
+    };
+    /**
+     *
+     */
+    StepManager.prototype.activateStep = function (event) {
+      event.preventDefault();
+      var $stepLink = $(this);
+      var manager = event.data.manager;
+      var step = $stepLink.data('RLD/Step');
+      if (step === undefined) {
+        step = manager.steps[0];
+      }
+      manager.activeStep = step;
+      manager.info('$editor').find('a').removeClass('rld-active');
+      $stepLink.addClass('rld-active');
+      manager.topic('stepActivated').publish(event, step);
+    };
+    
+    return StepManager;
+    
+  }());
+}(ResponsiveLayoutDesigner, jQuery));
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/app/libs/Utils/Utils.js b/core/modules/rlayout/designer/app/libs/Utils/Utils.js
new file mode 100644
index 0000000..5dde78b
--- /dev/null
+++ b/core/modules/rlayout/designer/app/libs/Utils/Utils.js
@@ -0,0 +1,84 @@
+(function (RLD, $) {
+
+  function mapKeyToChar(isShifted, keyCode) {
+    if (keyCode === 27
+      || keyCode === 8
+      || keyCode === 9
+      || keyCode === 20
+      || keyCode === 16
+      || keyCode === 17
+      || keyCode === 91
+      || keyCode === 13
+      || keyCode === 92
+      || keyCode === 18) {
+      return false;
+    }
+    if (typeof isShifted != "boolean" || typeof keyCode != "number") {
+      return false;
+    }
+    var charMap = [];
+    charMap[192] = "~";
+    charMap[49] = "!";
+    charMap[50] = "@";
+    charMap[51] = "#";
+    charMap[52] = "$";
+    charMap[53] = "%";
+    charMap[54] = "^";
+    charMap[55] = "&";
+    charMap[56] = "*";
+    charMap[57] = "(";
+    charMap[48] = ")";
+    charMap[109] = "_";
+    charMap[107] = "+";
+    charMap[219] = "{";
+    charMap[221] = "}";
+    charMap[220] = "|";
+    charMap[59] = ":";
+    charMap[222] = "\"";
+    charMap[188] = "<";
+    charMap[190] = ">";
+    charMap[191] = "?";
+    charMap[32] = " ";
+    var character = "";
+    if (isShifted) {
+      if (keyCode >= 65 && keyCode <= 90) {
+        character = String.fromCharCode(keyCode);
+      }
+      else {
+        character = charMap[keyCode];
+      }
+    }
+    else {
+      if (keyCode >= 65 && keyCode <= 90) {
+        character = String.fromCharCode(keyCode).toLowerCase();
+      }
+      else {
+        character = String.fromCharCode(keyCode);
+      }
+    }
+    return character;
+  }
+
+  RLD['Utils'] = {};
+  /**
+   * Keymanager
+   */
+  RLD['Utils'].keyManager = function (event) {
+    var pattern = event.data.pattern;
+    var callback = event.data.callback;
+      // Get the key.
+    var key = mapKeyToChar(event.shiftKey, event.keyCode);
+    if (key && typeof pattern === 'object' && 'exec' in pattern && typeof callback === 'function') {
+      // Invoke callback if the key is allowed or the delete key is pressed.
+      if (pattern.exec(key) || $.inArray(key, [8]) > -1) {
+        event.key = key;
+        callback.apply(this, arguments);
+      }
+      else {
+        console.log(event.keyCode);
+        // The key(s) are illegal, do nothing.
+        event.preventDefault();
+      } 
+    }
+  };
+}(ResponsiveLayoutDesigner, jQuery));
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/app/main.js b/core/modules/rlayout/designer/app/main.js
new file mode 100644
index 0000000..ed88af4
--- /dev/null
+++ b/core/modules/rlayout/designer/app/main.js
@@ -0,0 +1,314 @@
+(function (window, $) {
+  var RLD = (function(){
+    /**
+     * Extend jQuery to smooth out version differences.
+     */
+    $.Event.prototype.getDelegator = function () {
+      if ('delegateTarget' in this) {
+        return this.delegateTarget;
+      }
+      if ('liveFired' in this) {
+        return this.liveFired;
+      }
+      return null;
+    };
+    // Identify jQuery events from other objects.
+    $.Event.prototype.__marker = 'jQueryEvent';
+    /**
+     * Create the InitClass object that all other objects will extend.
+     */
+    var InitClass = (function () {
+
+      var plugin = 'InitClass';
+
+      function InitClass() {
+        // Don't set anything in here, or all objects will inherit these values.
+      }
+      /**
+       * Safe logging function.
+       */
+      InitClass.prototype.log = function (message, type) {
+        if ('console' in window) {
+          var type = type || 'log';
+          if (type in console) {
+            console[type](message);
+          }
+        }
+      };
+      /**
+       *
+       */
+      InitClass.prototype.init = function (opts) {
+        // Create a topics property for pub/sub event handling.
+        this.topics = {};
+        // Process the options for this instance.
+        var prop;
+        var options = ('options' in this) ? this.options : {};
+        options = $.extend({}, options, opts);
+        for (prop in options) {
+          if (options.hasOwnProperty(prop)) {
+            this[prop] = options[prop];
+          }
+        }
+        // Delete the options.
+        if ('options' in this) {
+          delete this.options;
+        }
+        // Call the object's setup method.
+        this.setup.apply(this);
+      };
+      /**
+       *
+       */
+      InitClass.prototype.setup = function () {};
+      /**
+       *
+       */
+      InitClass.prototype.info = function (property, value) {              
+        if (value !== undefined) {
+          this[property] = value;
+          return;
+        }
+        if (property in this) {
+          return this[property];
+        }
+        return;
+      };
+      /**
+       *
+       */
+      InitClass.prototype.build = function (options) {
+        return this.$editor;
+      };
+      /**
+       *
+       */
+      InitClass.prototype.addItem = function (item) {
+        this.items.push(item);
+      };
+      /**
+       *
+       */
+      InitClass.prototype.getItem = function (index) {
+        var i;
+        for (i = 0; i < this.items.length; i++) {
+          for (property in this.items[i]) {
+            if ('machine_name' in this.items[i] && this.items[i]['machine_name'] === index) {
+                return this.items[i];
+            }
+          }
+        }
+        this.log('[RLD | ' + plugin + '] Item not found in this set.', 'info');
+        return null;
+      };
+      /**
+       *
+       */
+      InitClass.prototype.snapshot = function (index) {
+        var snapshot = {};
+        var prop;
+        for (prop in this) {
+          if (this.hasOwnProperty(prop)) {
+            snapshot[prop] = this[prop];
+          }
+        }
+        return snapshot;
+      };
+      /**
+       *
+       */
+      InitClass.prototype.topic = function(id) {
+        var callbacks;
+        var topic = id && this.topics[id];
+        if (!topic) {
+          callbacks = jQuery.Callbacks('unique');
+          topic = {
+            publish: function () {
+              // Create a jQuery Event for consistency and shift it into the arguments.
+              if (!(arguments.length > 0 && typeof arguments[0] === 'object' && '__marker' in arguments[0] && arguments[0].__marker === 'jQueryEvent')) {
+                var e = $.Event(id);
+                // Unshift in the original target for reference if this event bubbles.
+                e.type = id;
+                Array.prototype.unshift.call(arguments, e); 
+              }
+              callbacks.fireWith(this, arguments);
+            },
+            subscribe: callbacks.add,
+            unsubscribe: callbacks.remove
+          };
+          if (id) {
+            this.topics[id] = topic;
+          }
+        }
+        return topic;
+      };
+      /**
+       *
+       */
+      InitClass.prototype.transferSubscriptions = function (subscribers) {
+        var subs = subscribers;
+        var topics = this.topics;
+        var i, k, top, id, sub;
+        if (!('length' in subscribers && subscribers.length > 0)) {
+          subs = [subscribers];
+        }
+        for (i = 0; i < subs.length; i++) {
+          sub = subs[i];
+          if ('topic' in sub) {
+            for (top in topics) {
+              if (topics.hasOwnProperty(top)) {
+                sub.topic(top).subscribe(this.topic(top).publish); 
+              }
+            }
+          }
+        }
+      };
+      
+      return InitClass;
+    }());
+
+    /**
+     * The ResponsiveLayoutDesigner is a facade for a set of sub-systems that manage
+     * the configuration of a responsive layout through a browser.
+     */
+    function ResponsiveLayoutDesigner() {
+      var options = {};
+      var plugin = 'ResponsiveLayoutDesigner';
+      this.steps = {};
+      this.regions = {};
+      this.grids = {};
+      // Initialize the object.
+      this.init.apply(this, arguments);
+    }
+    /**
+     * Extend the RLD with the InitClass.
+     */
+    ResponsiveLayoutDesigner.prototype = new InitClass();
+    /**
+     * Provide the InitClass for all other Classes to extend.
+     */
+    ResponsiveLayoutDesigner.InitClass = InitClass;
+    /**
+     * Implement the init() interface.
+     */
+    ResponsiveLayoutDesigner.prototype.setup = function () {
+      // Merge in user options.
+      var regionList, availableRegionList, stepList, gridList;
+      // Create the application root node.
+      this.$editor = $('<div>', {
+        'class': 'rld-application'
+      });
+      // Instansiate the LayoutManager.
+      // this.regions is a simple object. The RegionList provides methods to
+      // manipulate this simple set.
+      regionList = new RLD.RegionList();
+      availableRegionList = new RLD.RegionList();
+      if ('regions' in this) {
+        if ('active' in this.regions) {
+          regionList.addItem(this.regions.active);
+        }
+        if ('available' in this.regions) {
+          availableRegionList.addItem(this.regions.available);
+        }
+        delete this.regions;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] No regions provided.');
+      }
+      if ('steps' in this) {
+        stepList = new RLD.StepList({
+          'steps': this.steps
+        });
+        delete this.steps;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] No steps provided.');
+      }
+      if ('grids' in this) {
+        gridList = new RLD.GridList({
+          'grids': this.grids
+        });
+        delete this.grids;
+      }
+      else {
+        this.log('[RLD | ' + plugin + '] No grids provided.');
+      }
+      // Create a layout manager.
+      this.layoutManager = new RLD.LayoutManager({
+        'stepList': stepList,
+        'regionList': regionList,
+        'availableRegionList': availableRegionList,
+        'gridList': gridList
+      });
+      // Create a layoutPreviewer.
+      this.layoutPreviewer = new RLD.LayoutPreviewer({
+        'stepList': stepList,
+        'gridList': gridList
+      });
+      // Define topics that will pass-through.
+      this.topic('stepActivated');
+      this.transferSubscriptions(this.layoutPreviewer);
+      this.topic('regionOrderUpdated');
+      this.topic('layoutSaved');
+      this.topic('regionAdded');
+      this.topic('regionRemoved');
+      this.topic('regionResized');
+      this.topic('regionResizing');
+      this.topic('regionResizeStarted');
+      // Transfer pass-through subscriptions.
+      this.transferSubscriptions(this.layoutManager);
+    };
+    /**
+     * Generate a view of the class instance.
+     *
+     * Returns a DOM fragment.
+     */
+    ResponsiveLayoutDesigner.prototype.build = function () {
+      // Build the layoutManager and attach it to the $editor.
+      this.layoutManager.build().appendTo(this.$editor);
+      // Activate the first step.
+      var firstStep = this.layoutManager.stepList.info('items')[0];
+      var simpleClick = $.Event('click');
+      simpleClick.data = {'manager': this.layoutManager.stepManager};
+      this.layoutManager.stepManager.activateStep.call(this.layoutManager.stepManager.info('$editor').find('a:first').get(0), simpleClick);
+      return this.$editor;
+    };
+
+    ResponsiveLayoutDesigner.prototype.snapshot = function () {
+      return this.layoutManager;
+    };
+
+    return ResponsiveLayoutDesigner;
+    
+  }());
+  
+  // Expose ResponsiveLayoutDesigner to the global object
+  return (window.ResponsiveLayoutDesigner = RLD);
+}(window, jQuery));
+/**
+ * supplantClass() jQuery plugin
+ *
+ * Adds the replacement class string to each element.
+ *
+ * If a class or classes contain the needle, they are removed from the element.
+ */
+(function ($) {
+	// Add the plugin as a property of the jQuery fn object.
+	$.fn['supplantClass'] = function (needle, replacement) {
+	  return this.each(function (index, element) {
+      var $this = $(this);
+      var cl = [];
+      // Get an array of classes the excludes any that contain the needle.
+      var classes = ($this.attr('class') || '').split(' ');
+      for (var i = 0; i < classes.length; i++) {
+        if (classes[i].indexOf(needle) === -1) {
+          cl.push(classes[i]);
+        }
+      }
+      // Push the replacement in all cases.
+      $.merge(cl, replacement.split(' '));
+      // Create a string and assign it to the object.
+      $this.removeAttr('class').addClass(cl.join(' '));
+    });
+	};
+}(jQuery));
diff --git a/core/modules/rlayout/designer/assets/css/application.css b/core/modules/rlayout/designer/assets/css/application.css
new file mode 100644
index 0000000..d821310
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/css/application.css
@@ -0,0 +1,311 @@
+/* @group This styling needs to be removed eventually. It's presentation and it doesn't belong here. */
+.rld-steps a {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+/* @end */
+.clearfix::after {
+  clear: both;
+  content: ' ';
+  display: block;
+  height: 0;
+  visibility: hidden;
+  width: 100%;
+}
+.rld-screen {
+  margin: 0 auto;
+  max-width: 1024px;
+  text-align: center;
+}
+.rld-iframe {
+  margin: 0 auto;
+}
+
+/* @group Layout */
+
+/* @group StepManager */
+.rld-layout-manager,
+.rld-layout-previewer {
+  text-align: center;
+}
+.rld-layout-manager .rld-stepmanager,
+.rld-layout-previewer .rld-stepmanager {
+  margin: 0 auto;
+}
+.rld-steps {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+.rld-steps .rld-tab {
+  display: inline-block;
+  margin-left: 1em;
+  margin-right: 1em;
+  position: relative;
+  vertical-align: middle;
+}
+.rld-steps .rld-tab::before,
+.rld-steps .rld-tab::after {
+  background-attachment: scroll;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  content: ' ';
+  display: block;
+  position: absolute;
+}
+.rld-steps .rld-tab::before {
+  background-color: #7d7d7d;
+  height: 1px;
+  left: -22px;
+  top: 1.2em;
+  width: 20px;
+}
+.rld-steps .rld-tab:first-child::before {  
+  background-color: transparent;
+  background-image: url("../images/small-screen.png");
+  background-position: left center;
+  height: 22px;
+  left: -28px;
+  top: 6px;
+  width: 25px;
+}
+.rld-steps .rld-tab:last-child::after {
+  background-image: url("../images/large-screen.png");
+  background-position: right center;
+  height: 22px;
+  right: -39px;
+  top: 6px;
+  width: 35px;
+}
+.rld-steps .rld-tab .rld-link {
+  -moz-border-radius: 4px;
+  -webkit-border-radius: 4px;
+  border-radius: 4px;
+  display: inline-block;
+  line-height: 1.8em;
+  padding: 5px;
+}
+.rld-layout-previewer .rld-tab .rld-link {
+  color: #ccc;
+}
+.rld-layout-manager .rld-tab .rld-link {
+  color: #484848;
+}
+.rld-link.rld-active::after {
+  border-style: solid;
+  border-width: 6px 4px;
+  bottom: 2px;
+  content: ' ';
+  display: block;
+  height: 0px;
+  left: 40%;
+  position: absolute;
+  width: 0px;
+}
+.rld-layout-previewer .rld-link.rld-active::after {
+  border-color: transparent transparent #ccc transparent;
+}
+.rld-layout-manager .rld-link.rld-active::after {
+  border-color: transparent transparent #484848 transparent;
+}
+.rld-layouts {
+  margin-top: 0.5em;
+  padding: 1em;
+}
+
+/* @end */
+
+/* @end */
+
+/* @group Region and row styles */
+
+.rld-layouts {
+  position: relative;
+}
+.rld-layout {
+  position: relative;
+}
+.rld-row {
+  -webkit-user-select: none;
+  cursor: move;
+}
+.rld-placeholder,
+.rld-region {
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  float: left;
+  margin-bottom: 15px;
+  position: relative;
+}
+.rld-region {
+  width: 100%; /* splitter def: 960px */
+}
+.rld-region > .rld-inner {
+  background-color: #555;
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+  border-radius: 5px;
+  color: white;
+  height: 4em;
+  margin: 0 2px;
+  padding: 0 1em;
+}
+.rld-placeholder {
+  background-color: white;
+  background-color: rgba(255,255,255,0.6667);
+  color: #ccc;
+  height: 4em;
+  outline: 1px dashed #31cbff;
+  outline-offset: -5px;
+  overflow: hidden;
+  width: 0;
+}
+.rld-region .rld-region-close {
+  position: absolute;
+  right: 14px;
+  top: 7px;
+  background-color: #cdcdcd;
+  border-radius: 10px;
+  color: #484848;
+  padding: 4px;
+  cursor: default;
+  font-size: 0.8em;
+  line-height: 0.8em;
+  text-decoration: none;
+}
+.rld-col.rld-region {
+  padding: 0;
+}
+.rld-grid-underlay {
+  bottom: 24px;
+  height: auto;
+  margin: 0.25em 0;
+  min-height: 20px;
+  position: absolute;
+  top: 24px;
+  width: 100%;
+}
+.rld-grid-underlay + * {
+  margin-top: 24px;
+}
+.rld-grid-col {
+  background-color: #E0E3E8;
+  border-color: white;
+  border-style: solid;
+  border-width: 0 2px;
+  height: 100%;
+  min-height: 1em;
+  outline: 1px solid #B8B8B8;
+  outline-offset: -3px;
+}
+.rld-layoutstep-controls {
+  margin-bottom: 5px;
+}
+.rld-splitter {
+  background-attachment: scroll;
+  background-image: url('../images/grippie.png');
+  background-position: center center;
+  background-repeat: no-repeat;
+  bottom: 0;
+	cursor: e-resize;	/* in case col-resize isn't supported */
+	cursor: col-resize;
+	height: 100%;
+  position: absolute;
+  top: 0;
+  width: 12px;
+  z-index: 1;
+}
+.rld-splitter:hover {
+  background-color: rgba(0, 0, 0, 0.3333);
+}
+.rld-splitter-left {
+  background-position: left center;
+  left: 3px;
+  -moz-border-radius: 5px 0 0 5px;
+  -webkit-border-radius: 5px 0 0 5px;
+  border-radius: 5px 0 0 5px;
+}
+.rld-splitter-right {
+  background-position: right center;
+  right: 3px;
+  -moz-border-radius: 0 5px 5px 0;
+  -webkit-border-radius: 0 5px 5px 0;
+  border-radius: 0 5px 5px 0;
+}
+.rld-splitter-active {
+  background: url('../images/grippie-active.png') 0 -3px;
+}
+
+/* @end */
+
+/* @grid */
+
+/* @group Sortable */
+
+.ui-sortable .ui-state-highlight {
+  height: 50px;
+  padding: 10px;
+  margin: 1em 0;
+}
+.ui-sortable .region .drag-icon {
+  font-size: 2em;
+  margin-right: 1em;
+}
+.ui-sortable .region .remove-icon {
+  position: absolute;
+  right: 5px;
+  top: 20px;
+  background-color: #cdcdcd;
+  border-radius: 10px;
+  color: #484848;
+  padding: 4px;
+  cursor: default;
+}
+
+/* @end */
+
+/* @end */
+
+/* @group Previewer */
+
+.rld-previewer {
+  border: 1px solid #484848;
+  position: absolute;
+  top: 100px;
+}
+@media (max-width:600px) {
+  .rld-layout-previewer {
+    display: none;    
+  }
+}
+.rld-modal-screen {
+  background-color: white;
+  bottom: 0;
+  height: 100%;
+  opacity: 0.9;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.rld-modal-close {
+  position: absolute;
+  margin-top: 5px;
+  right: 10px;
+}
+.rld-modal-close a {
+  background-color: #59a;
+  border: 1px solid #3F3F3F;
+  border-radius: 7px;
+  box-sizing: border-box;
+  color: white;
+  display: inline-block;
+  letter-spacing: 1.5pt;
+  padding: 0.08333em 0.3333em;
+}
+.rld-modal-close a:hover {
+  background-color: #666666;
+  color: white;
+  text-decoration: none;
+}
+
+/* @end */
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/css/grid.css b/core/modules/rlayout/designer/assets/css/grid.css
new file mode 100644
index 0000000..18c93b2
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/css/grid.css
@@ -0,0 +1,117 @@
+/* Generic column setup */
+.rld-col {
+  -webkit-box-sizing:border-box;
+  -moz-box-sizing:border-box;
+  box-sizing:border-box;
+  float:left;
+}
+
+
+
+/* 1. 3 Column Grid 0px - 349px 
+----------------------------------------------------------------------------- */
+
+.rld-container-3 .rld-span_1 {
+  width:33.3333333333%;
+}
+.rld-container-3 .rld-span_2 {
+  width:66.6666666667%;
+}
+.rld-container-3 .rld-span_3 {
+  width:100%;
+}
+
+/* 2. 6 Column Grid 350px - 759px 
+----------------------------------------------------------------------------- */
+
+.rld-container-6 .rld-span_1 {
+  width:16.6666666667%;
+}
+.rld-container-6 .rld-span_2 {
+  width:33.3333333333%;
+}
+.rld-container-6 .rld-span_3 {
+  width:50%;
+}
+.rld-container-6 .rld-span_4 {
+  width:66.6666666667%;
+}
+.rld-container-6 .rld-span_5 {
+  width:83.3333333333%;
+}
+.rld-container-6 .rld-span_6 {
+  width:100%;
+}
+
+/* 3. 10 Column Grid 760px - 959px 
+----------------------------------------------------------------------------- */
+.rld-container-10 .rld-span_1 {
+  width:10%;
+}
+.rld-container-10 .rld-span_2 {
+  width:20%;
+}
+.rld-container-10 .rld-span_3 {
+  width:30%;
+}
+.rld-container-10 .rld-span_4 {
+  width:40%;
+}
+.rld-container-10 .rld-span_5 {
+  width:50%;
+}
+.rld-container-10 .rld-span_6 {
+  width:60%;
+}
+.rld-container-10 .rld-span_7 {
+  width:70%;
+}
+.rld-container-10 .rld-span_8 {
+  width:80%;
+}
+.rld-container-10 .rld-span_9 {
+  width:90%;
+}
+.rld-container-10 .rld-span_10 {
+  width:100%;
+}
+
+/* 4. 12 Column Grid 960px - Infinity 
+----------------------------------------------------------------------------- */
+
+.rld-container-12 .rld-span_1 {
+  width:8.3333333333%;
+}
+.rld-container-12 .rld-span_2 {
+  width:16.6666666667%;
+}
+.rld-container-12 .rld-span_3 {
+  width:25%;
+}
+.rld-container-12 .rld-span_4 {
+  width:33.3333333333%;
+}
+.rld-container-12 .rld-span_5 {
+  width:41.6666666667%;
+}
+.rld-container-12 .rld-span_6 {
+  width:50%;
+}
+.rld-container-12 .rld-span_7 {
+  width:58.3333333333%;
+}
+.rld-container-12 .rld-span_8 {
+  width:66.6666666667%;
+}
+.rld-container-12 .rld-span_9 {
+  width:75%;
+}
+.rld-container-12 .rld-span_10 {
+  width:83.3333333333%;
+}
+.rld-container-12 .rld-span_11 {
+  width:91.6666666667%;
+}
+.rld-container-12 .rld-span_12 {
+  width:100%;
+}
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/images/grippie-active.png b/core/modules/rlayout/designer/assets/images/grippie-active.png
new file mode 100644
index 0000000..5f5e75d
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/images/grippie-active.png
@@ -0,0 +1,14 @@
+PNG
+
+   IHDR      =   eF%   	pHYs    ~  
+OiCCPPhotoshop ICC profile  xڝSgTS=BKKoR RB&*!	J!QEEȠQ,
+!{kּ>H3Q5B.@
+$p d!s# ~<<+" x M0B\t8K @zB @F&S  `cb P- `' { [!  eD h; VE X0 fK9 - 0IWfH    0Q) { `##x  FW<+*  x<$9E[-qWW.(I+6aa@.y24  x6_-"bbϫp@  t~,/;m%h^uf@ Wp~<<EJB[aW}g_Wl~<$2]GLϒ	bG"IbX*QqD2"B)%d,>5 j>{-]cK'Xt  o(hw?G% fIq  ^D$.Tʳ?  D*A,`6B$BB
+dr`)B(Ͱ*`/@4Qhp.U=pa(	Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F; 2G1Q=C7Fdt1r=6Ыhڏ>C03l0.B8,	c˱"VcϱwE	6wB aAHXLXNH $4	7	Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![
+b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGwǈg(gwLӋT071oUX**|
+J&*/TުUUT^S}FU3S	ԖUPSSg;goT?~YYLOCQ_ cx,!ku5&|v*=9C3J3WRf?qtN	(~))4L1e\kXHQG6EYAJ'\'GgSSݧ
+M=:.kDwn^Loy}/TmGX$<5qo</QC]@Caaᄑ<FFi\$mmƣ&&!&KMMRM);L;L֙͢5=12כ߷`ZxZ,eIZYnZ9YXUZ]F%ֻNNgðɶۮm}agbgŮ}}=Z~sr:V:ޚΜ?}/gX3)iSGggs󈋉K.>.ȽJtq]zۯ6iܟ4)Y3sCQ?0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz %gA[z|!?:eAAA!h쐭!ΑiP~aa~'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl{/]py.,:@LN8A*%w%
+yg"/6шC\*NH*Mz쑼5y$3,幄'LLݛ:v m2=:1qB!Mggfvˬen/kY-
+BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9<qy
++V<*mOW~&zMk^ʂkU
+}]OX/Yߵa>(xoʿܔĹdff-[nڴVE/(ۻC<e;?TTTT6ݵan{4[>ɾUUMfeI?m]Nmq#׹=TR+Gw-6U#pDy	:v{vg/jBFS[b[O>zG4<YyJTiӓgό}~.`ۢ{cjotE;;\tWW:_mt<Oǻ\kz{f7y՞9=ݽzo~r'˻w'O_@AC݇?[jwGCˆ8>99?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-    cHRM  z%        u0  `  :  o_F   (IDATxb3 LX0    L>    IENDB`
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/images/grippie.png b/core/modules/rlayout/designer/assets/images/grippie.png
new file mode 100644
index 0000000..bf78930
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/images/grippie.png
@@ -0,0 +1,14 @@
+PNG
+
+   IHDR      =   eF%   	pHYs    ~  
+OiCCPPhotoshop ICC profile  xڝSgTS=BKKoR RB&*!	J!QEEȠQ,
+!{kּ>H3Q5B.@
+$p d!s# ~<<+" x M0B\t8K @zB @F&S  `cb P- `' { [!  eD h; VE X0 fK9 - 0IWfH    0Q) { `##x  FW<+*  x<$9E[-qWW.(I+6aa@.y24  x6_-"bbϫp@  t~,/;m%h^uf@ Wp~<<EJB[aW}g_Wl~<$2]GLϒ	bG"IbX*QqD2"B)%d,>5 j>{-]cK'Xt  o(hw?G% fIq  ^D$.Tʳ?  D*A,`6B$BB
+dr`)B(Ͱ*`/@4Qhp.U=pa(	Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F; 2G1Q=C7Fdt1r=6Ыhڏ>C03l0.B8,	c˱"VcϱwE	6wB aAHXLXNH $4	7	Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![
+b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGwǈg(gwLӋT071oUX**|
+J&*/TުUUT^S}FU3S	ԖUPSSg;goT?~YYLOCQ_ cx,!ku5&|v*=9C3J3WRf?qtN	(~))4L1e\kXHQG6EYAJ'\'GgSSݧ
+M=:.kDwn^Loy}/TmGX$<5qo</QC]@Caaᄑ<FFi\$mmƣ&&!&KMMRM);L;L֙͢5=12כ߷`ZxZ,eIZYnZ9YXUZ]F%ֻNNgðɶۮm}agbgŮ}}=Z~sr:V:ޚΜ?}/gX3)iSGggs󈋉K.>.ȽJtq]zۯ6iܟ4)Y3sCQ?0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz %gA[z|!?:eAAA!h쐭!ΑiP~aa~'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl{/]py.,:@LN8A*%w%
+yg"/6шC\*NH*Mz쑼5y$3,幄'LLݛ:v m2=:1qB!Mggfvˬen/kY-
+BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9<qy
++V<*mOW~&zMk^ʂkU
+}]OX/Yߵa>(xoʿܔĹdff-[nڴVE/(ۻC<e;?TTTT6ݵan{4[>ɾUUMfeI?m]Nmq#׹=TR+Gw-6U#pDy	:v{vg/jBFS[b[O>zG4<YyJTiӓgό}~.`ۢ{cjotE;;\tWW:_mt<Oǻ\kz{f7y՞9=ݽzo~r'˻w'O_@AC݇?[jwGCˆ8>99?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-    cHRM  z%        u0  `  :  o_F   (IDATxbؽ{3 LX0    UKCZ|    IENDB`
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/images/large-screen.png b/core/modules/rlayout/designer/assets/images/large-screen.png
new file mode 100644
index 0000000..035312c
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/images/large-screen.png
@@ -0,0 +1,5 @@
+PNG
+
+   IHDR         xw   sBIT|d   	pHYs    ~   tEXtCreation Time 8/18/12%A=   tEXtSoftware Adobe Fireworks CS6輲   IDAT8핱0E_d؀`2ݕ&P^dl 	L9`9Tu|DU+ e8=3`
+)EK0M#dE~%j#ϸNCCJYeSq ; ˀyvW
+xnTulAD!/DU3HYFME]NfWB]zy\K U.Mr    IENDB`
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/images/small-screen.png b/core/modules/rlayout/designer/assets/images/small-screen.png
new file mode 100644
index 0000000..d8888c0
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/images/small-screen.png
@@ -0,0 +1,4 @@
+PNG
+
+   IHDR         V2/   sBIT|d   	pHYs    ~   tEXtCreation Time 8/18/12%A=   tEXtSoftware Adobe Fireworks CS6輲   IDAT8퓽0F=#$#lFHwmFH	la FyɒLg#a 7
+yt03$r"_a)^gX2%Cśw8K>$͎R2'~n@vRMeu    IENDB`
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/js/plugins/breakup/MIT-LICENSE.txt b/core/modules/rlayout/designer/assets/js/plugins/breakup/MIT-LICENSE.txt
new file mode 100644
index 0000000..a5ab045
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/js/plugins/breakup/MIT-LICENSE.txt
@@ -0,0 +1,7 @@
+Copyright (c) 2012 Jesse Renee Beach
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/js/plugins/breakup/README.md b/core/modules/rlayout/designer/assets/js/plugins/breakup/README.md
new file mode 100644
index 0000000..8acf76b
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/js/plugins/breakup/README.md
@@ -0,0 +1,54 @@
+BreakUp Plugin
+=======
+
+A jQuery plugin that fires custom events when configured breakpoints have been breached.
+
+
+Summary
+=======
+
+Register callbacks with custom breakpoint events to support responsive development in browser. This plugin makes it easier to trigger javascript responsive step behaviors without writing custom breakpoint checking code.
+
+
+API
+=======
+
+$.Breakup(breakpoints [, options,] selector [, arguments]) returns a BreakUp object.
+
+**breakpoints** is an object whose properties are breakpoint minimum values, and whose values are functions. These functions will be called when a breakpoint is crossed. The 'this' context of the callback will be the set of selected elements. The first argument of the callback will be the *breakChanged* event. Additional arguments passed to the BreakUp function (see below) will be provided to the callback function.
+
+```javascript
+{
+  'default': small,
+  '450': narrow,
+  '600': tablet,
+  '750': desktop,
+  '900': large
+}
+```
+
+**options** (optional) is an object with the following possible values
+
+<table>
+  <thead>
+    <tr>
+      <th>Property</th>
+      <th>Value</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>namespace</td>
+      <td>A string representing a namespace for the breakChanged event.</td>
+    </tr>
+  </tbody>
+</table>
+
+**selector** is either a jQuery object of elements, a string selector that can be passed to the jQuery function or the window or document objects. BreakUp will not work without elements to act on. 
+
+**arguments** is any number of argument of any type. They will all be passed to the breakpoint's callback function.
+
+Examples
+=======
+
+See http://jessebeach.github.com/breakup/ for a live example.
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/js/plugins/breakup/grunt.js b/core/modules/rlayout/designer/assets/js/plugins/breakup/grunt.js
new file mode 100644
index 0000000..4f4828b
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/js/plugins/breakup/grunt.js
@@ -0,0 +1,10 @@
+module.exports = function(grunt) {
+  grunt.initConfig({
+    min: {
+      dist: {
+        src: ['jquery.breakup.js'],
+        dest: 'jquery.breakup.min.js'
+      }
+    }
+  });
+};
\ No newline at end of file
diff --git a/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.js b/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.js
new file mode 100644
index 0000000..018ae40
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.js
@@ -0,0 +1,257 @@
+/*jslint bitwise: true, eqeqeq: true, immed: true, newcap: true, nomen: false,
+ onevar: false, plusplus: false, regexp: true, undef: true, white: false, indent: 2
+ browser: true */
+
+/*global window: true define: true jQuery: true */
+
+/**
+ * A jQuery plugin.
+ *
+ * Register arbitrary functions to be fired against jQuery objects when
+ * specified break points are entered.
+ *
+ * Author: Jesse Renee Beach
+ * Author URI: http://qemist.us
+ * Author Twitter: @jessebeach
+ * Author Github: https://github.com/jessebeach
+ *
+ */
+
+// The plugin factory function.
+(function ($) {
+  var pluginName = 'BreakUp';
+  var $ = jQuery;
+  // plugin defaults
+  var defaults = {
+    'breakpoints': {
+      '0': $.noop
+    },
+    'options': {
+      'namespace': pluginName
+    }
+  };
+  // Add the plugin as a property of the jQuery fn object.
+  $[pluginName] = (function () {
+    // Functions for manipulating the arguments variable.
+    var shift = Array.prototype.shift;
+    var unshift = Array.prototype.unshift;
+    var slice = Array.prototype.slice;
+    var splice = Array.prototype.splice;
+    /**
+     * Build a new BreakUp object.
+     */
+    function BreakUp() {
+      this.currentBreak = undefined;
+      this.breakPoints = {};
+      this.updated = false;
+      this.namespace = pluginName;
+      this.$elements = $();
+      // Initialize the plugin instance.
+      this.initialize.apply(this, arguments);
+    }
+     
+    BreakUp.prototype.initialize = function (breakpoints, options, selector) {
+      var bp, opts, fn;
+      // Determine if breakpoints were passed in. If not, return, there's nothing to do.
+      if (typeof breakpoints === 'string' || (typeof breakpoints === 'object' && 'jquery' in breakpoints)) {
+        this.log('[' + pluginName + '] No breakpoints were provided for BreakUp to act on.', 'info');
+        return;
+      }
+      // Merge user options with default options.
+      bp = $.extend({}, defaults.breakpoints, breakpoints);
+      // Strip the breakpoints from the arguments list.
+      shift.call(arguments);
+      // Unshift the merged breakpoints back into the arguments.
+      unshift.call(arguments, bp);
+      // Options is a jquery object or selector
+      if ((typeof options === 'object' && 'jquery' in options) || typeof options === 'string') {
+        // Slip an undefined into the arguments where the options should be.
+        splice.call(arguments, 1, 0, undefined);
+      }
+      // Process the options.
+      if (options) {
+        opts = $.extend({}, defaults.options, options);
+        this.namespace = opts.namespace;
+        // More options to come...
+      }
+      // A selector is necessary to create a context. It cannot be empty. First check
+      // for a string selector.
+      if (typeof selector === 'string' || selector === window || selector === document) {
+        this.$elements = $(selector);
+        // If the selector was provided as something other than a jQuery obejct,
+        // we need to replace the corresponding argument with the jQuery selector version.
+        splice.call(arguments, 2, 1, this.$elements);
+      }
+      // Then check for a jQuery object. 
+      else if (typeof selector === 'object' && 'jquery' in selector) {
+        this.$elements = selector;
+      }
+      // If the selector matched nothing, error out.
+      if (this.$elements.length === 0) {
+        this.log('[' + pluginName + '] Neither a jQuery object nor a valid selector were provided for BreakUp to act on.', 'info');
+        return;
+      }
+      // The arguments should only contain the breakpoints, the context elements,
+      // and additional arguments for the callbacks from this point on.
+      // The options are needed only for initialization, so we remove them.
+      splice.call(arguments, 1, 1);
+      this.registerBreakPoints.apply(this, arguments);
+      // Register a handler on the window resize and load events.
+      fn = this.buildProxy(this.breakCheck, this);
+      $(window).bind('resize' + '.' + this.namespace, fn);
+      $(window).bind('load' + '.' + this.namespace, fn);
+    };
+    /**
+     * Given an object of breakpoint properties and functions associated with those properties,
+     * store them internally for reference later.
+     */
+    BreakUp.prototype.registerBreakPoints = function (breakpoints) {
+      var bps = breakpoints;
+      var br;
+      var index;
+      if (typeof bps === 'object') {
+        // Remove the breakpoints from the arguments array.
+        shift.call(arguments);
+        // Loop through the breakpoints.
+        for (br in bps) {
+          if (bps.hasOwnProperty(br)) {
+            if (isNaN(br) && br !== 'default') {
+              this.log('[' + pluginName + '] The breakpoint property name \"' + br + '\" is not valid. The property must convert to a number or be the word \"default\".', 'info');
+              continue;
+            }
+            if (typeof bps[br] === 'function') {
+              // Represent the default breakpoint as zero internally.
+              index = (br === 'default') ? '0': br;
+              // Unshift the function into the arguments.
+              unshift.call(arguments, bps[br]);
+              var args = slice.call(arguments);
+              // Build a proxy from the function and store it.
+              this.breakPoints[index] = this.buildProxy.apply(this, arguments);
+              // Shift the function off the arguments.
+              shift.call(arguments);
+            }
+            else {
+              this.log('[' + pluginName + '] ' + bps[br] + ', for the breakpoint ' + br + ' is not a function.', 'info');
+            }
+          }
+        }
+      }
+    };
+    /**
+     * Gets the current breakpoint.
+     *
+     * @return (String) candidate: the number-as-a-string index in the list of breakPoints
+     * of the current break point as determined by the screen size.
+     */
+    BreakUp.prototype.getBreakPoint = function () {
+      var br;
+      var candidate;
+      var screen = this.getScreenWidth();
+      for (br in this.breakPoints) {
+        if (this.breakPoints.hasOwnProperty(br)) {
+          if (Number(br) <= screen && (Number(br) > Number(candidate) || Number(br) === 0)) {
+            candidate = br;
+          }
+        }
+      }
+      return candidate;
+    };
+    /**
+     * Returns the stored set of breakpoints and their functions.
+     */
+    BreakUp.prototype.listBreakPoints = function () {
+      return this.breakPoints;
+    };
+     /**
+     * Get the current break point and call the function associated with it.
+     */
+    BreakUp.prototype.breakChangeHandler = function (event) {
+      // updated will be set to false when a new breakpoint is encountered.
+      if (!this.updated) {
+        var callback = this.getBreakPointHandler();
+        if (typeof callback === 'function') {
+          // Pass the event object through to the callback.
+          callback(event);
+          this.updated = true;
+          return;
+        }
+        else {
+          this.log('[' + pluginName + '] The handler for the current breakpoint is not a function.', 'info');
+        }
+      }
+    };
+    /**
+     * Get the function associated with a stored breakpoint.
+     */
+    BreakUp.prototype.getBreakPointHandler = function () {
+      return this.breakPoints[this.getBreakPoint()];
+    };
+    /**
+     * Check to see if the screen is in a new breakpoint. Also
+     * called on page load.
+     */
+    BreakUp.prototype.breakCheck = function (event) {
+      var br = this.getBreakPoint();
+      if (this.currentBreak !== br) {
+        // Note the current breakpoint.
+        this.currentBreak = br;
+        this.updated = false;
+        // And do something.
+        this.breakChangeHandler(event);
+      }
+    };
+    
+    /**
+     * Returns a function with 'this' set as the context object.
+     *
+     * All additional arguments are passed through to the returned function.
+     */
+    BreakUp.prototype.buildProxy = function (fn, context) {
+      var f = fn;
+      var c = context;
+      // Pull the top two args -- fn and context -- off the arguments array.
+      for (var i = 0; i < 2; i++) {
+        Array.prototype.shift.call(arguments);
+      }
+      // Get a local reference to the arguments.
+      var args = Array.prototype.slice.call(arguments);
+      return function () {
+        // Push the callers arguments into the arguments list.
+        // This will most likely be an $.Event object.
+        for (var i = 0; i < arguments.length; i++) {
+          args.unshift(arguments[i]);
+        }
+        f.apply(c, args);
+      };
+    };
+    /**
+    * Get the screen width.
+    */
+    BreakUp.prototype.getScreenWidth = function () {
+      return window.innerWidth || document.documentElement.offsetWidth || document.documentElement.clientWidth;
+    };
+    /**
+     *
+     */
+    BreakUp.prototype.setNameSpace = function (ns) {
+      this.namespace = ns;
+    };
+    /**
+     * 
+     */
+    BreakUp.prototype.getNameSpace = function () {
+      return this.namespace;
+    };
+  	/**
+     * Simple console logging utility that won't blow up in older browsers.
+     */
+    BreakUp.prototype.log = function (message, type) {
+      if ('console' in window) {
+        var t = type || 'log';
+        console[t](message);
+      }
+    };
+    // Return a new BreakUp object.
+    return BreakUp;
+  }());
+}(jQuery));
diff --git a/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.min.js b/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.min.js
new file mode 100644
index 0000000..4dea445
--- /dev/null
+++ b/core/modules/rlayout/designer/assets/js/plugins/breakup/jquery.breakup.min.js
@@ -0,0 +1 @@
+(function(a){if(typeof define=="function"&&define.amd)define(["jquery"],a);else{if(window.jQuery===undefined)return typeof window.console=="object"&&typeof window.console.log=="function"&&console.log('The plugin "BreakUp" failed to run because jQuery is not present.'),null;a()}})(function(){var a="BreakUp",b=jQuery,c={breakpoints:{0:b.noop},options:{namespace:a}};b[a]=function(){function h(){this.currentBreak=undefined,this.breakPoints={},this.updated=!1,this.namespace=a,this.$elements=b(),this.initialize.apply(this,arguments)}var d=Array.prototype.shift,e=Array.prototype.unshift,f=Array.prototype.slice,g=Array.prototype.splice;return h.prototype.initialize=function(f,h,i){var j,k,l;if(typeof f=="string"||typeof f=="object"&&"jquery"in f){this.log("["+a+"] No breakpoints were provided for BreakUp to act on.","info");return}j=b.extend({},c.breakpoints,f),d.call(arguments),e.call(arguments,j),(typeof h=="object"&&"jquery"in h||typeof h=="string")&&g.call(arguments,1,0,undefined),h&&(k=b.extend({},c.options,h),this.namespace=k.namespace),typeof i=="string"||i===window||i===document?(this.$elements=b(i),g.call(arguments,2,1,this.$elements)):typeof i=="object"&&"jquery"in i&&(this.$elements=i);if(this.$elements.length===0){this.log("["+a+"] Neither a jQuery object nor a valid selector were provided for BreakUp to act on.","info");return}g.call(arguments,1,1),this.registerBreakPoints.apply(this,arguments),l=this.buildProxy(this.breakCheck,this),b(window).bind("resize."+this.namespace,l),b(window).bind("load."+this.namespace,l)},h.prototype.registerBreakPoints=function(b){var c=b,g,h;if(typeof c=="object"){d.call(arguments);for(g in c)if(c.hasOwnProperty(g)){if(isNaN(g)&&g!=="default"){this.log("["+a+'] The breakpoint property name "'+g+'" is not valid. The property must convert to a number or be the word "default".',"info");continue}if(typeof c[g]=="function"){h=g==="default"?"0":g,e.call(arguments,c[g]);var i=f.call(arguments);this.breakPoints[h]=this.buildProxy.apply(this,arguments),d.call(arguments)}else this.log("["+a+"] "+c[g]+", for the breakpoint "+g+" is not a function.","info")}}},h.prototype.getBreakPoint=function(){var a,b,c=this.getScreenWidth();for(a in this.breakPoints)this.breakPoints.hasOwnProperty(a)&&Number(a)<=c&&(Number(a)>Number(b)||Number(a)===0)&&(b=a);return b},h.prototype.listBreakPoints=function(){return this.breakPoints},h.prototype.breakChangeHandler=function(b){if(!this.updated){var c=this.getBreakPointHandler();if(typeof c=="function"){c(b),this.updated=!0;return}this.log("["+a+"] The handler for the current breakpoint is not a function.","info")}},h.prototype.getBreakPointHandler=function(){return this.breakPoints[this.getBreakPoint()]},h.prototype.breakCheck=function(a){var b=this.getBreakPoint();this.currentBreak!==b&&(this.currentBreak=b,this.updated=!1,this.breakChangeHandler(a))},h.prototype.buildProxy=function(a,b){var c=a,d=b;for(var e=0;e<2;e++)Array.prototype.shift.call(arguments);var f=Array.prototype.slice.call(arguments);return function(){for(var a=0;a<arguments.length;a++)f.unshift(arguments[a]);c.apply(d,f)}},h.prototype.getScreenWidth=function(){return window.innerWidth||document.documentElement.offsetWidth||document.documentElement.clientWidth},h.prototype.setNameSpace=function(a){this.namespace=a},h.prototype.getNameSpace=function(){return this.namespace},h.prototype.log=function(a,b){if("console"in window){var c=b||"log";console[c](a)}},h}()});
\ No newline at end of file
diff --git a/core/modules/rlayout/lib/Drupal/rlayout/Plugin/Derivative/Layout.php b/core/modules/rlayout/lib/Drupal/rlayout/Plugin/Derivative/Layout.php
new file mode 100644
index 0000000..15ebee1
--- /dev/null
+++ b/core/modules/rlayout/lib/Drupal/rlayout/Plugin/Derivative/Layout.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\rlayout\Plugin\Derivative\Layout.
+ */
+
+namespace Drupal\rlayout\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+/**
+ * Layout plugin derivative definition.
+ */
+class Layout implements DerivativeInterface {
+
+  /**
+   * List of derivatives.
+   *
+   * Associative array keyed by responsive layout machine name. The values of
+   * the array are associative arrays themselves with metadata about the
+   * layout such as the order of regions and breakpoint overrides.
+   *
+   * @var array
+   */
+  protected $derivatives = array();
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    // Use module_invoke() because plugins are active even if the module is not
+    // enabled.
+    $this->derivatives = array();
+    $layouts = module_invoke('rlayout', 'load_all');
+    if (!empty($layouts)) {
+      foreach ($layouts as $key => $layout) {
+        $this->derivatives[$key] = array(
+          'id' => $layout->id(),
+          'title' => $layout->label(),
+          'available regions' => $layout->regions,
+          'region overrides' => $layout->overrides,
+          'class' => 'Drupal\rlayout\Plugin\layout\layout\ResponsiveLayout',
+        );
+      }
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/rlayout/lib/Drupal/rlayout/Plugin/layout/layout/ResponsiveLayout.php b/core/modules/rlayout/lib/Drupal/rlayout/Plugin/layout/layout/ResponsiveLayout.php
new file mode 100644
index 0000000..bd71bc8
--- /dev/null
+++ b/core/modules/rlayout/lib/Drupal/rlayout/Plugin/layout/layout/ResponsiveLayout.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\rlayout\Plugin\layout\layout\ResponsiveLayout.
+ */
+
+namespace Drupal\rlayout\Plugin\layout\layout;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * @Plugin(
+ *  id = "responsive_layout",
+ *  derivative = "Drupal\rlayout\Plugin\Derivative\Layout"
+ * )
+ */
+class ResponsiveLayout extends PluginBase implements LayoutInterface {
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginBase::__construct().
+   */
+  public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
+    // Get definition by discovering the declarative information.
+    $definition = $discovery->getDefinition($plugin_id);
+    foreach ($definition['regions'] as $region => $title) {
+      if (!isset($configuration['regions'][$region])) {
+        $configuration['regions'][$region] = array();
+      }
+    }
+    parent::__construct($configuration, $plugin_id, $discovery);
+  }
+
+  /**
+   * Implements Drupal\layout\Plugin\LayoutInterface::renderLayout().
+   */
+  public function renderLayout($admin = FALSE) {
+    $definition = $this->getDefinition();
+    return '@todo Temporary';
+  }
+}
diff --git a/core/modules/rlayout/lib/Drupal/rlayout/RLayout.php b/core/modules/rlayout/lib/Drupal/rlayout/RLayout.php
new file mode 100644
index 0000000..51415db
--- /dev/null
+++ b/core/modules/rlayout/lib/Drupal/rlayout/RLayout.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\rlayout\RLayout.
+ */
+
+namespace Drupal\rlayout;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the layout entity.
+ */
+class RLayout extends ConfigEntityBase {
+
+  /**
+   * The layout ID (machine name).
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The layout UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The layout label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * List of regions used in this layout.
+   *
+   * @var array
+   */
+  public $regions;
+
+  /**
+   * Region width overrides for different breakpoints.
+   *
+   * @var array
+   */
+  public $overrides;
+
+}
diff --git a/core/modules/rlayout/lib/Drupal/rlayout/RLayoutFormController.php b/core/modules/rlayout/lib/Drupal/rlayout/RLayoutFormController.php
new file mode 100644
index 0000000..d109b7d
--- /dev/null
+++ b/core/modules/rlayout/lib/Drupal/rlayout/RLayoutFormController.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\rlayout\RLayoutFormController.
+ */
+
+namespace Drupal\rlayout;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+use Drupal\region\Region;
+
+/**
+ * Form controller for the layout edit/add forms.
+ */
+class RLayoutFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::prepareEntity().
+   *
+   * Prepares the layout object filling in a few default values.
+   */
+  protected function prepareEntity(EntityInterface $layout) {
+    if (empty($layout->regions)) {
+      if ($default = rlayout_load('default')) {
+        // Attempt to clone the default layout if available.
+        $layout->regions = $default->regions;
+        $layout->overrides = $default->overrides;
+      }
+      else {
+        // If the default cannot be cloned, set some defaults.
+        $layout->regions = array();
+        $default_regions = region_load_all();
+        foreach ($default_regions as $region) {
+          $layout->regions[] = $region->id();
+        }
+        $layout->overrides = array();
+      }
+    }
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $layout) {
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $layout->label(),
+      '#description' => t("Example: 'Front page', 'Section page'."),
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $layout->id(),
+      '#machine_name' => array(
+        'exists' => 'rlayout_load',
+        'source' => array('label'),
+      ),
+      '#disabled' => !$layout->isNew(),
+    );
+
+    $layoutdata = array();
+    $default_regions = region_load_all();
+    foreach ($layout->regions as $id) {
+      $layoutdata['regions'][] = array(
+        'id' => $id,
+        'label' => $default_regions[$id]->label(),
+      );
+    }
+    $layoutdata['overrides'] = (array) $layout->overrides;
+
+    $form['layout_regions'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Region and bunnypoint configuration'),
+      '#default_value' => drupal_json_encode($layoutdata),
+      '#suffix' => '<div id="responsive-layout-designer"></div>',
+    );
+
+    $form['#attached'] = array(
+      'library' => array(
+        array('system', 'jquery.ui.dialog'),
+        array('system', 'jquery.ui.sortable'),
+        array('rlayout', 'rlayout-designer'),
+        array('rlayout', 'rlayout-admin'),
+      ),
+      'js' => array(
+        array(
+          'data' => array(
+            'responsiveLayout' => array(
+              'layout' => $layout,
+              'defaultRegions' => region_load_all(),
+              'defaultBreakpoints' => rlayout_breakpoints_load_all(),
+              'defaultGrids' => entity_load_multiple('grid'),
+            ),
+          ),
+          'type' => 'setting',
+        ),
+      ),
+      'css' => array(
+        array(
+          // Embed the grid css inline for now. Yeah, I know this is evil.
+          // It is just a prototype for now, ok? I know it is evil. Yes.
+          'data' => rlayout_breakpoint_get_css(FALSE),
+          'type' => 'inline',
+        ),
+      ),
+    );
+
+  // JSON2 is required for stringifying JavaScript data structures in older browsers.
+  /*$name = 'json2';
+  if (!libraries_detect($name)) {
+    watchdog('responsive', 'The JSON-js library is recommended for this module to function properly. Some older browsers do not provide the JSON function natively. Please visit !url to obtain this library.',
+      array(
+        '!url' => l('JSON-js (Github)', 'https://github.com/douglascrockford/JSON-js',
+          array(
+            'absolute' => TRUE,
+            'external' => TRUE
+          )
+        )
+      ),
+      WATCHDOG_NOTICE
+    );
+  }
+  else {
+    libraries_load($name);
+  }*/
+
+    return parent::form($form, $form_state, $layout);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::actions().
+   */
+  protected function actions(array $form, array &$form_state) {
+    // Only includes a Save action for the entity, no direct Delete button.
+    return array(
+      'submit' => array(
+        '#value' => t('Save'),
+        '#validate' => array(
+          array($this, 'validate'),
+        ),
+        '#submit' => array(
+          array($this, 'submit'),
+          array($this, 'save'),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $layout = $this->getEntity($form_state);
+
+    $default_regions = region_load_all();
+    $new_layout_settings = drupal_json_decode($form_state['values']['layout_regions']);
+
+    if (!empty($new_layout_settings)) {
+      $layout->regions = array();
+      foreach ($new_layout_settings['regions'] as $region) {
+        $layout->regions[] = $region['id'];
+
+        /*/ Save region in common regions list in case it is new.
+        if (!isset($default_regions[$region['id']])) {
+          $region = (object) array(
+            'id' => $region['id'],
+            'label' => $region['label'],
+          );
+          region_save($region);
+        }*/
+      }
+      $layout->overrides = $new_layout_settings['overrides'];
+    }
+    $layout->save();
+
+    watchdog('layout', 'Layout @label saved.', array('@label' => $layout->label()), WATCHDOG_NOTICE);
+    drupal_set_message(t('Layout %label saved.', array('%label' => $layout->label())));
+
+    $form_state['redirect'] = 'admin/structure/layouts';
+  }
+
+}
diff --git a/core/modules/rlayout/rlayout-admin.css b/core/modules/rlayout/rlayout-admin.css
new file mode 100644
index 0000000..2d239c3
--- /dev/null
+++ b/core/modules/rlayout/rlayout-admin.css
@@ -0,0 +1,23 @@
+/* @group Base layout editor */
+
+.form-item-layout-regions {
+  display: none;
+}
+
+.rld-inner p {
+  margin: 0;
+}
+
+/* @end */
+
+/* @group ResponsiveLayoutDesigner */
+
+.rld-layouts {
+  border: 1px solid #d0d0d1;
+}
+
+.rld-controls button {
+  display: none;
+}
+
+/* @end */
diff --git a/core/modules/rlayout/rlayout-admin.js b/core/modules/rlayout/rlayout-admin.js
new file mode 100644
index 0000000..44066a7
--- /dev/null
+++ b/core/modules/rlayout/rlayout-admin.js
@@ -0,0 +1,149 @@
+(function ($, ResponsiveLayoutDesigner, JSON) {
+
+/**
+ * Safe logging function.
+ */
+function log (message, type) {
+  if ('console' in window) {
+    var type = type || 'log';
+    if (type in console) {
+      console[type](message);
+    }
+  }
+}
+
+Drupal.responsiveLayout = Drupal.responsiveLayout || {};
+
+Drupal.behaviors.responsiveLayoutAdmin = {
+  attach: function(context) {
+    // Initialize responsive layout editor.
+    Drupal.responsiveLayout.init();
+  }
+}
+
+/**
+ * Initialize responsive layout editor.
+ */
+Drupal.responsiveLayout.init = function() {
+  // Initialize region list and per-breakpoint columns.
+  var regionList = [];
+  var regionNames = {};
+  var layoutConfig = JSON.parse($('#edit-layout-regions').val());
+  for (var regionIndex in layoutConfig.regions) {
+    regionList.push({
+      'machine_name': layoutConfig.regions[regionIndex].id,
+      'label': layoutConfig.regions[regionIndex].label,
+    });
+    regionNames[layoutConfig.regions[regionIndex].id] = '';
+  }
+
+  // More regions that are available for this layout to use (but not currently
+  // in use).
+  var availableRegionList = [];
+  for (var existingRegionName in Drupal.settings.responsiveLayout.defaultRegions) {
+    if (!(existingRegionName in regionNames)) {
+      availableRegionList.push({
+        'machine_name': existingRegionName,
+        'label': Drupal.settings.responsiveLayout.defaultRegions[existingRegionName].label
+      });
+    }
+  }
+
+  // Build a list of grids for the editor.
+  var gridList = [];
+  for (var gridIndex in Drupal.settings.responsiveLayout.defaultGrids) {
+    gridList.push({
+      'machine_name': Drupal.settings.responsiveLayout.defaultGrids[gridIndex].id,
+      'columns': Drupal.settings.responsiveLayout.defaultGrids[gridIndex].options.columns,
+      'classes': ['rld-container-' + Drupal.settings.responsiveLayout.defaultGrids[gridIndex].id],
+    });
+  }
+
+  // Build a list of breakpoints for the editor.
+  var breakpointList = [];
+  for (var breakpointIndex in Drupal.settings.responsiveLayout.defaultBreakpoints) {
+    var overrideList = [];
+    var name = Drupal.settings.responsiveLayout.defaultBreakpoints[breakpointIndex].id;
+    for (var overrideIndex in layoutConfig.overrides[name]) {
+      overrideList.push({
+        'machine_name': layoutConfig.overrides[name][overrideIndex].id,
+        'columns': layoutConfig.overrides[name][overrideIndex].columns,
+      });
+    }
+    breakpointList.push({
+      'label': Drupal.settings.responsiveLayout.defaultBreakpoints[breakpointIndex].label,
+      'machine_name': name,
+      // @todo: make sure that em/px based breakpoints work alike (also on server side).
+      'breakpoint': Drupal.settings.responsiveLayout.defaultBreakpoints[breakpointIndex].width,
+      'grid': Drupal.settings.responsiveLayout.defaultBreakpoints[breakpointIndex].grid,
+      'regions': overrideList,
+    });
+  }
+
+  // Instantiate a layout designer.
+  this.editor = new ResponsiveLayoutDesigner({
+    'regions': {
+      'active': regionList,
+      'available': availableRegionList
+    },
+    'steps': breakpointList,
+    'grids': gridList,
+  });
+
+  // var save = $.proxy(this.save, this);
+
+  // Register event listeners. Just update our representation of the layout
+  // for any event for now.
+  this.editor.topic('regionOrderUpdated').subscribe(Drupal.responsiveLayout.recordState);
+  this.editor.topic('regionAdded').subscribe(Drupal.responsiveLayout.recordState);
+  this.editor.topic('regionRemoved').subscribe(Drupal.responsiveLayout.recordState);
+  this.editor.topic('regionHidden').subscribe(Drupal.responsiveLayout.recordState);
+  this.editor.topic('regionResized').subscribe(Drupal.responsiveLayout.recordState);
+
+  // Insert the editor in the DOM.
+  this.editor.build().appendTo('#responsive-layout-designer');
+
+  // Save a reference to the editor to the DOM for development.
+  window.RLDEditor = this.editor;
+}
+
+/**
+ * Respond to app updates.
+ *
+ * This is generic so we don't have to have a callback for each event we'd like
+ * to track during prototyping.
+ */
+Drupal.responsiveLayout.recordState = function(event) {
+
+  var layoutSettings = {'regions' : [], 'overrides': {}};
+  // Get a dump of the state of the application.
+  var layoutManager = Drupal.responsiveLayout.editor.snapshot();
+  var regionList = layoutManager.info('regionList');
+  var regions = regionList.info('items');
+  for (var i = 0; i < regions.length; i++) {
+    layoutSettings.regions.push({'id': regions[i].info('machine_name'), 'label': regions[i].info('label')});
+  }
+
+  var stepList = layoutManager.info('stepList');
+  var steps = stepList.info('items');
+  for (var i = 0; i < steps.length; i++) {
+    layoutSettings.overrides[steps[i].machine_name] = [];
+    if (steps[i].regionList.items.length) {
+      for (var r = 0; r < steps[i].regionList.items.length; r++) {
+        layoutSettings.overrides[steps[i].machine_name].push({
+          'id': steps[i].regionList.items[r].machine_name,
+          'columns': steps[i].regionList.items[r].columns
+        });
+      }
+    }
+  }
+
+  // The value of the textarea is saved to the server when the whole layout is
+  // saved. We do not have live AJAX communication because the interaction is
+  // built with rapid changes in mind (ordering, adding new regions, resizing),
+  // and we don't have a live preview needed given the useful builder view
+  // itself.
+  $('#edit-layout-regions').val(JSON.stringify(layoutSettings));
+}
+
+})(jQuery, ResponsiveLayoutDesigner, window.JSON);
diff --git a/core/modules/rlayout/rlayout.admin.inc b/core/modules/rlayout/rlayout.admin.inc
new file mode 100644
index 0000000..e82ce56
--- /dev/null
+++ b/core/modules/rlayout/rlayout.admin.inc
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Administration functions to maintain a set of layouts.
+ */
+
+use Drupal\rlayout\RLayout;
+
+/**
+ * Page callback: Presents list of layouts.
+ *
+ * @see layout_menu()
+ */
+function rlayout_page_list() {
+  $controller = entity_list_controller('rlayout');
+  return $controller->render();
+}
+
+/**
+ * Page callback: Presents the layout editing form.
+ *
+ * @see layout_menu()
+ */
+function rlayout_page_edit(RLayout $layout) {
+  drupal_set_title(t('<em>Edit layout</em> @label', array('@label' => $layout->label())), PASS_THROUGH);
+  return entity_get_form($layout);
+}
+
+/**
+ * Page callback: Provides the new layout addition form.
+ *
+ * @see layout_menu()
+ */
+function rlayout_page_add() {
+  $layout = entity_create('rlayout', array());
+  return entity_get_form($layout);
+}
+
+/**
+ * Page callback: Form constructor for layout deletion confirmation form.
+ *
+ * @see layout_menu()
+ */
+function rlayout_delete_confirm($form, &$form_state, RLayout $layout) {
+  // Always provide entity id in the same form key as in the entity edit form.
+  $form['id'] = array('#type' => 'value', '#value' => $layout->id());
+  $form_state['layout'] = $layout;
+  return confirm_form($form,
+    t('Are you sure you want to remove the layout %title?', array('%title' => $layout->label())),
+    'admin/structure/layouts',
+    t('This action cannot be undone.'),
+    t('Delete'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Form submission handler for layout_delete_confirm().
+ */
+function rlayout_delete_confirm_submit($form, &$form_state) {
+  $layout = $form_state['layout'];
+  $layout->delete();
+  drupal_set_message(t('Layout %label has been deleted.', array('%label' => $layout->label())));
+  watchdog('layout', 'Layout %label has been deleted.', array('%label' => $layout->label()), WATCHDOG_NOTICE);
+  $form_state['redirect'] = 'admin/structure/layouts';
+}
diff --git a/core/modules/rlayout/rlayout.info b/core/modules/rlayout/rlayout.info
new file mode 100644
index 0000000..a1aa1f7
--- /dev/null
+++ b/core/modules/rlayout/rlayout.info
@@ -0,0 +1,12 @@
+name = Responsive layouts
+description = Responsive layout editor.
+package = Core
+version = VERSION
+core = 8.x
+dependencies[] = config
+configure = admin/structure/layouts
+
+dependencies[] = layout
+dependencies[] = breakpoint
+dependencies[] = region
+dependencies[] = grid
diff --git a/core/modules/rlayout/rlayout.module b/core/modules/rlayout/rlayout.module
new file mode 100644
index 0000000..9d2a7b4
--- /dev/null
+++ b/core/modules/rlayout/rlayout.module
@@ -0,0 +1,252 @@
+<?php
+
+/**
+ * @file
+ * Responsive layout builder tool for Panels.
+ */
+
+use Drupal\rlayout\RLayout;
+
+/**
+ * Implements hook_menu().
+ */
+function rlayout_menu() {
+  $items = array();
+  $items['admin/structure/layouts'] = array(
+    'title' => 'Layouts',
+    'description' => 'Manage list of layouts.',
+    'page callback' => 'rlayout_page_list',
+    'access callback' => 'user_access',
+    'access arguments' => array('administer layouts'),
+    'file' => 'rlayout.admin.inc',
+  );
+  $items['admin/structure/layouts/add'] = array(
+    'title' => 'Add layout',
+    'page callback' => 'rlayout_page_add',
+    'access callback' => 'user_access',
+    'access arguments' => array('administer layouts'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'rlayout.admin.inc',
+  );
+  $items['admin/structure/layouts/manage/%rlayout'] = array(
+    'title' => 'Edit layout',
+    'page callback' => 'rlayout_page_edit',
+    'page arguments' => array(4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer layouts'),
+    'type' => MENU_CALLBACK,
+    'file' => 'rlayout.admin.inc',
+  );
+  $items['admin/structure/layouts/manage/%rlayout/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/structure/layouts/manage/%rlayout/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rlayout_delete_confirm', 4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer layouts'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'rlayout.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_permission().
+ */
+function rlayout_permission() {
+  return array(
+    'administer layouts' => array(
+      'title' => t('Administer responsive layouts'),
+      'description' => t('Administer backend settings for responsive layouts.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function rlayout_entity_info() {
+  $types['rlayout'] = array(
+    'label' => 'Responsive layout',
+    'entity class' => 'Drupal\rlayout\RLayout',
+    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+    'form controller class' => array(
+      'default' => 'Drupal\rlayout\RLayoutFormController',
+    ),
+    'list controller class' => 'Drupal\Core\Config\Entity\ConfigEntityListController',
+    'list path' => 'admin/structure/layouts',
+    'uri callback' => 'rlayout_uri',
+    'config prefix' => 'rlayoutset',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+  );
+  return $types;
+}
+
+/**
+ * Entity URI callback.
+ *
+ * @param Drupal\rlayout\RLayout $layout
+ *   Layout configuration entity instance.
+ *
+ * @return array
+ *   Entity URI information.
+ */
+function rlayout_uri(RLayout $layout) {
+  return array(
+    'path' => 'admin/structure/layouts/manage/' . $layout->id(),
+  );
+}
+
+/**
+ * Load one layout object by its identifier.
+ *
+ * @return Drupal\rlayout\RLayout
+ *   Layout configuration entity instance.
+ */
+function rlayout_load($id) {
+  return entity_load('rlayout', $id);
+}
+
+/**
+ * Load all layout objects.
+ *
+ * @return array
+ *   List of Drupal\rlayout\RLayout instances keyed by id.
+ */
+function rlayout_load_all() {
+  return entity_load_multiple('rlayout');
+}
+
+/**
+ * Helper function to return processed breakpoint information for layouts.
+ *
+ * @todo
+ *   Currently tied to one breakpoint group in rlayout, but it should be
+ *   made independent and configurable.
+ */
+function rlayout_breakpoints_load_all() {
+  $grid_breakpoints = entity_load('breakpoint_group', 'module.rlayout.rlayout');
+  $breakpoint_info = array();
+  foreach ($grid_breakpoints->breakpoints as $key => $breakpoint) {
+    // Only include this breakpoint in the output if we found a grid for it.
+    // Other type of breakpoints are not useful for us, since we cannot display
+    // an editing interface without a grid for each breakpoint.
+    if ($grid = rlayout_breakpoint_find_grid($key)) {
+      // @todo This considers em and px based widths the same number. This is due
+      // to the JS responsive layout designer not being able to take qualified
+      // widths. It can only take numbers. Should be fixed there first and then
+      // here.
+      $low_width = 0;
+      if (preg_match('!min-width: (\d+)[ep]!', $breakpoint->mediaQuery, $found)) {
+        $low_width = $found[1];
+      }
+      $breakpoint_info[$key] = (object) array(
+        'id' => $key,
+        'label' => $breakpoint->label,
+        'width' => $low_width,
+        'grid' => $grid,
+        'mediaQuery' => $breakpoint->mediaQuery,
+      );
+    }
+  }
+  return $breakpoint_info;
+}
+
+/**
+ * Find the (first) grid matching this breakpoint.
+ */
+function rlayout_breakpoint_find_grid($breakpoint_key) {
+  $all_grids = entity_load_multiple('grid');
+  foreach ($all_grids as $grid) {
+    if (!empty($grid->options['breakpoints']) && in_array($breakpoint_key, $grid->options['breakpoints'])) {
+      return $grid->id();
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Implements hook_library_info().
+ */
+function rlayout_library_info() {
+  $path = drupal_get_path('module', 'rlayout');
+  $rld_path = $path . '/designer';
+
+  $libraries['rlayout-designer'] = array(
+    'title' => 'Responsive layout designer',
+    'version' => '0.1',
+    'js' => array(
+      $rld_path . '/assets/js/plugins/breakup/jquery.breakup.js' => array(),
+      $rld_path . '/app/main.js' => array(),
+      $rld_path . '/app/libs/Utils/Utils.js' => array(),
+      $rld_path . '/app/libs/LayoutManager/LayoutManager.js' => array(),
+      $rld_path . '/app/libs/LayoutPreviewer/LayoutPreviewer.js' => array(),
+      $rld_path . '/app/libs/LayoutList/LayoutList.js' => array(),
+      $rld_path . '/app/libs/LayoutStep/LayoutStep.js' => array(),
+      $rld_path . '/app/libs/StepManager/StepManager.js' => array(),
+      $rld_path . '/app/libs/StepList/StepList.js' => array(),
+      $rld_path . '/app/libs/Step/Step.js' => array(),
+      $rld_path . '/app/libs/RegionList/RegionList.js' => array(),
+      $rld_path . '/app/libs/Region/Region.js' => array(),
+      $rld_path . '/app/libs/GridList/GridList.js' => array(),
+      $rld_path . '/app/libs/Grid/Grid.js' => array(),
+    ),
+    'css' => array(
+      $rld_path . '/assets/css/application.css' => array(),
+      $rld_path . '/assets/css/grid.css' => array(),
+    ),
+  );
+
+  $libraries['rlayout-admin'] = array(
+    'title' => 'Layout admin interface',
+    'version' => '0.1',
+    'js' => array(
+      $path . '/rlayout-admin.js' => array(),
+    ),
+    'css' => array(
+      $path . '/rlayout-admin.css' => array(),
+    ),
+  );
+  return $libraries;
+}
+
+/**
+ * Build CSS for the breakpoints with media queries.
+ *
+ * @param boolean $include_media_queries
+ *   Whether generate one flat CSS without media queries (useful for
+ *   administration), or wrap breakpoints with media queries (for frontend).
+ *
+ * @todo
+ *   Figure out a good way to avoid equal max/min-weights in subsequent
+ *   breakpoints if that is a problem.
+ */
+function rlayout_breakpoint_get_css($include_media_queries = TRUE) {
+  $breakpoints = rlayout_breakpoints_load_all();
+
+  $breakpoint_css = array();
+  foreach ($breakpoints as $name => $breakpoint) {
+    $grid = entity_load('grid', $breakpoint->grid);
+    if ($include_media_queries) {
+      $breakpoint_css[] =
+        '@media ' . $breakpoint->mediaQuery . ' {';
+      // Get grid CSS from gridbuilder and apply some extra indentation.
+      $breakpoint_css[] = '  ' . str_replace("\n", "\n  ", $grid->getGridCss('.panel-responsive', '.rld-span-' . $name . '_'));
+      $breakpoint_css[] = "\n}";
+    }
+    else {
+      $breakpoint_css[] = $grid->getGridCss(NULL, NULL, TRUE);
+    }
+  }
+  $css = join("\n", $breakpoint_css);
+
+  return $css;
+}
