diff --git a/core/lib/Drupal/Core/Booze/DrunkController.php b/core/lib/Drupal/Core/Booze/DrunkController.php
new file mode 100644
index 0000000..3e53325
--- /dev/null
+++ b/core/lib/Drupal/Core/Booze/DrunkController.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Booze\DrunkController.
+ */
+
+namespace Drupal\Core\Booze;
+
+use SplObjectStorage;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+use Drupal\Core\Config\Entity\Display;
+
+/**
+ * Default controller for handling text/html responses.
+ *
+ * It's called DrunkController in order to absolutely ensure we rename it later.
+ *
+ * ...right?
+ */
+class DrunkController implements ContainerAwareInterface {
+
+  /**
+   * A SPLOS containing rendered block output, keyed by the block instance that
+   * produced it.
+   *
+   * @var \SplObjectStorage
+   */
+  public $renderedBlocks;
+
+  /**
+   * An associative array of rendered regions.
+   * @var array
+   */
+  public $renderedRegions;
+
+  /**
+   * The injection container for this object.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * The Response object that is built up over the course of the request and
+   * eventually returned from this controller.
+   *
+   * @var \Symfony\Component\HttpFoundation\Response
+   */
+  protected $response;
+
+  /**
+   * The display configuration object containing layout and block information,
+   * used by this controller to assemble all page elements.
+   *
+   * @var \Drupal\Core\Config\Entity\Display
+   */
+  protected $display;
+
+  public function __construct() {
+    $this->renderedBlocks = new SplObjectStorage();
+  }
+
+  /**
+   * Injects the service container used by this object.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The service container this object should use.
+   */
+  public function setContainer(ContainerInterface $container = NULL) {
+    $this->container = $container;
+  }
+
+  /**
+   * Controller method for normal rendering of blocks and layouts-driven HTML
+   * page.
+   *
+   * @todo the output may need more or less assembling, depending on how
+   * 'compiled' of a template we have in the display. We either can handle it
+   * below here via a delegator pattern, or with sibling classes.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   * @param \Drupal\Core\Config\Entity\Display        $_display
+   *   A configuration object containing the layout instance and set of
+   *   block instances that have been configured to be used for the current
+   *   route.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   */
+  public function respond(Request $request, Display $_display) {
+    $this->display = $_display;
+
+    // Create a Response object right away that we can easily decorate as we go.
+    $this->response = new Response();
+
+    $this->renderBlocks($request);
+    $this->renderLayout($request);
+    // @todo handle out-of-band stuff, e.g. css/js
+
+    $content = $this->display->getLayoutPluginInstance()->renderLayout($this->renderedRegions);
+    $this->response->setContent($content);
+
+    return $this->response;
+  }
+
+  /**
+   * Renders all blocks contained in the current display object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   */
+  protected function renderBlocks(Request $request) {
+    $attributes = $request->attributes;
+    // Strip off stuff that can't go through to subrequests.
+    $attributes->remove('system_path');
+    $attributes->remove('_content');
+
+    $blocks = array(
+      'info' => array(),
+      'instances' => array(),
+    );
+
+    foreach ($this->display->getAllBlockInfo() as $name => $info) {
+      $instance = block_manager()->getInstance(array('config' => $name));
+      // @todo block/pane styles - we're keeping them, right?
+      // @todo still need very much to handle context injection. yikes.
+      // @todo handle out-of-band attached stuff from blocks, e.g. css/js
+      if ($info['method'] === 'direct') {
+        // this is the direct rendering approach.
+        $this->renderedBlocks[$instance] = $instance->render();
+      }
+      elseif ($info['method'] === 'subrequest') {
+        // this routes rendering through a subrequest.
+        // @todo When we have a Generator, we can replace the forward() call with
+        // a render() call, which would handle ESI and hInclude as well.  That will
+        // require an _internal route.  For examples, see:
+        // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
+        // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
+        $response = $this->container->get('http_kernel')->forward(array($instance, 'render'), $attributes, $request->query->all());
+        $this->renderedBlocks[$instance] = $response->getContent();
+      }
+    }
+  }
+
+  /**
+   * Renders all rendered blocks into their respective layout regions, using
+   * the layout attached to the current display object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   */
+  protected function renderLayout(Request $request) {
+    $layout = $this->display->getLayoutPluginInstance();
+    // @todo need to add this method to the layout plugin interface.
+    // @todo this is another place we could do block placement renegotiation by region role (@see Display)
+    foreach ($layout->getRegions() as $region => $info) {
+      // @todo region styles, if we do them, would go in around here
+      $to_render = array();
+      foreach ($this->display->getBlocksByRegion($region) as $block) {
+        $to_render[] = $this->renderedBlocks[$block];
+      }
+
+      $this->renderedRegions[$region] = $layout->renderRegion($region);
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Entity/Display.php b/core/lib/Drupal/Core/Config/Entity/Display.php
new file mode 100644
index 0000000..2180734
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Entity/Display.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Booze\Display.
+ */
+
+namespace Drupal\Core\Config\Entity;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Configuration encapsulator that provides all the data needed by block-driven
+ * controllers to render a page.
+ */
+class Display extends ConfigEntityBase {
+
+  /**
+   * The ID (config name) identifying a specific display object.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable label of this display.
+   * @todo we probably don't need/want to have this.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The UUID identifying a specific display object.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The layout plugin instance being used to serve this page.
+   *
+   * @var Drupal\layout\Plugin\LayoutInterface
+   */
+  protected $layoutPlugin;
+
+  /**
+   * The name of the layout plugin to use.
+   *
+   * @var string
+   */
+  protected $layout;
+
+  public function __construct(array $values, $entity_type) {
+    $this->layout = $values['layout'];
+    // @todo oh so much, but really, blocks for now.
+    parent::__construct($values, 'booze_display');
+  }
+
+  /**
+   * Sets the controller that should be used to serve the main page content.
+   *
+   * "Main page content" is analogous to the callback specified in
+   * 'page callback' in Drupal 6 and 7. In a blocks-driven world, such direct
+   * callbacks run a bit against the grain. However, the simplicity of
+   * implementing and understanding it is so beneficial to developer experience
+   * that we provide this 'main' content option, which simulates the old
+   * behavior by injecting a virtualized 'main' block that passes through
+   * to a specified callback.
+   *
+   * We normalize the main content block here in the configuration object so
+   * that various controllers consuming the display need not repeatedly
+   * implement this special handling for the main callback.
+   *
+   * @param Callable $callback
+   *   Any form of callable. @todo PHP 5.4 allows type hinting as Callable.
+   * @param array $args
+   *   An array of arguments to pass to the callable.
+   */
+  public function setMainContent($callback, array $args = array()) {
+    // @todo need to wrap the callback up in a passthru block
+    // @todo consider throwing an exception if this display instance doesn't appear to support main content
+  }
+
+
+  /**
+   * Returns the config info about all blocks on this display.
+   *
+   * There are two levels of configuration that are being captured here: the
+   * configuration for the block itself (i.e., config generated by a user saving
+   * the block's edit form), and configuration for how the particular block
+   * instance behaves in *this* display. The former is typically its own config
+   * object, and only a reference to that config key is stored directly on this
+   * object. The most important examples of the latter are the region in which
+   * the block is placed, and its weighting within the region.
+   *
+   * @todo we need to explore a *lot* more just how freestanding we make blocks.
+   *
+   * @return array
+   *   An array of block info, keyed on each block's config name.
+   *
+   * @todo implement batch-loading logic to try to minimize discrete queries.
+   */
+  public function getAllBlockInfo() {
+
+  }
+
+  /**
+   * Returns the layout plugin to be used with this display.
+   *
+   * @return Drupal\layout\Plugin\LayoutInterface
+   */
+  public function getLayoutPluginInstance() {
+    if ($this->layoutPlugin === NULL) {
+      // @todo if we're doing any magic resolution/hotswapping of layouts, this
+      // is a place it could happen.
+      $this->layoutPlugin = layout_manager()->createInstance($this->layout);
+    }
+
+    return $this->layoutPlugin;
+  }
+}
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index ed15dd5..d5a74e3 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -90,6 +90,7 @@ public function build(ContainerBuilder $container) {
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RouteProcessorSubscriber());
+    $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\DrunkControllerSubscriber());
     $container->set('content_negotiation', $content_negotation);
     $dispatcher->addSubscriber(\Drupal\Core\ExceptionController::getExceptionListener($container));
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
index 4f1dc75..230e715 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -27,6 +27,13 @@ class AccessSubscriber implements EventSubscriberInterface {
    *   The Event to process.
    */
   public function onKernelRequestAccessCheck(GetResponseEvent $event) {
+    $legacy = $event->getRequest()->attributes->get('_legacy');
+    // Only check this if we're not in legacy mode. While technically redundant
+    // with the isset() check below, this makes it more clear that this subscriber
+    // deals only with legacy router items.
+    if (!isset($legacy)) {
+      return;
+    }
 
     $router_item = $event->getRequest()->attributes->get('drupal_menu_item');
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php
new file mode 100644
index 0000000..bbd5be0
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/DrunkControllerSubscriber.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\DrunkControllerSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+use Drupal\Core\Config\Entity\Display;
+
+/**
+ * Controller injector for text/html responses.
+ */
+class DrunkControllerSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Attaches a controller for blocks-driven HTML rendering, if appropriate.
+   *
+   * This determines if the Request and Route are appropriate candidates for
+   * passing through block-driven rendering, and if so, attaches a controller
+   * to do so.
+   *
+   * This is more or less the default case; we only don't take over if Drupal
+   * is doing things like form processing, returning JSON or other things that
+   * (generally) do not involve composing HTML.
+   *
+   */
+  public function onDrunkenKernelController(FilterControllerEvent $event) {
+    $request = $event->getRequest();
+    // @todo implement a separate flag and remove this hardmapping
+    if ('\Drupal\Core\Booze\DrunkController::respond' !== $request->attributes->get('_controller')) {
+      return;
+    }
+
+    $route = $request->attributes->get('_route');
+
+    if (!empty($route)) {
+      $display = entity_load('booze_display', $route);
+    }
+
+    if (empty($display)) {
+      // @todo put a real, meaningful exception here. and/or figure out fallback behavior.
+      // throw new \Exception('no display');
+      // $display = new Display(array('id' => 'new'));
+      return;
+    }
+
+    $display->setMainContent($request->attributes->get('_content'));
+
+    $request->attributes->set('_display', $display);
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::CONTROLLER][] = array('onDrunkenKernelController', 100);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
index 7009eff..f40ada3 100644
--- a/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
@@ -36,18 +36,18 @@ class LegacyControllerSubscriber implements EventSubscriberInterface {
    *   The Event to process.
    */
   public function onKernelControllerLegacy(FilterControllerEvent $event) {
+    $legacy = $event->getRequest()->attributes->get('_legacy');
+    // Only monkey with the controller if we're handling a legacy route.
+    if (!isset($legacy)) {
+      return;
+    }
+
     $request = $event->getRequest();
     $router_item = $request->attributes->get('drupal_menu_item');
     $controller = $event->getController();
 
     // This BC logic applies only to functions. Otherwise, skip it.
     if (is_string($controller) && function_exists($controller)) {
-      // Flag this as a legacy request.  We need to use this for subrequest
-      // handling so that we can treat older page callbacks and new routes
-      // differently.
-      // @todo Remove this line as soon as possible.
-      $request->attributes->set('_legacy', TRUE);
-
       $new_controller = function() use ($router_item) {
         return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
       };
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
index 61c7788..e628271 100644
--- a/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
@@ -27,12 +27,15 @@ class RouteProcessorSubscriber implements EventSubscriberInterface {
    *
    * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   Event that is created to create a response for a request.
+   *
+   * @todo this logic is very simplistic right now - we need more discussion
+   *  around how best to make this determination.
    */
   public function onRequestSetController(GetResponseEvent $event) {
     $request = $event->getRequest();
 
     if (!$request->attributes->has('_controller') && $request->attributes->has('_content')) {
-      $request->attributes->set('_controller', '\Drupal\Core\HtmlPageController::content');
+      $request->attributes->set('_controller', '\Drupal\Core\Booze\DrunkController::respond');
     }
   }
 
diff --git a/core/lib/Drupal/Core/LegacyUrlMatcher.php b/core/lib/Drupal/Core/LegacyUrlMatcher.php
index 48987a9..a8d4c20 100644
--- a/core/lib/Drupal/Core/LegacyUrlMatcher.php
+++ b/core/lib/Drupal/Core/LegacyUrlMatcher.php
@@ -111,6 +111,12 @@ public function matchRequest(Request $request) {
         require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
       }
 
+      // Flag this as a legacy request.  We need to use this for subrequest
+      // handling so that we can treat older page callbacks and new routes
+      // differently.
+      // @todo Remove this line as soon as possible.
+      $request->attributes->set('_legacy', TRUE);
+
       return $ret;
     }
 
diff --git a/core/modules/layout/layout.info b/core/modules/layout/layout.info
new file mode 100644
index 0000000..187bfea
--- /dev/null
+++ b/core/modules/layout/layout.info
@@ -0,0 +1,5 @@
+name = Layout
+description = Makes it possible to swap different page layouts.
+package = Core
+version = VERSION
+core = 8.x
diff --git a/core/modules/layout/layout.module b/core/modules/layout/layout.module
new file mode 100644
index 0000000..7d82798
--- /dev/null
+++ b/core/modules/layout/layout.module
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Manages page layouts for content presentation.
+ */
+
+/**
+ * Get the layout plugin manager instance.
+ *
+ * @return Drupal\layout\Plugin\Type\LayoutManager
+ *   The layout plugin manager instance.
+ */
+function layout_manager() {
+  return drupal_container()->get('plugin.manager.layout');
+}
+
+/**
+ * Implements hook_theme().
+ *
+ * Expose all layouts as theme items, so themes can override layout markup.
+ */
+function layout_theme($existing, $type, $theme, $path) {
+  $items = array();
+  foreach (layout_manager()->getDefinitions() as $name => $layout) {
+    $items[$layout['theme']] = array(
+      'variables' => array('content' => NULL),
+      'path' => $layout['path'],
+      'template' => $layout['template'],
+    );
+  }
+  return $items;
+}
diff --git a/core/modules/layout/layouts/static/onecol/onecol.tpl.php b/core/modules/layout/layouts/static/onecol/onecol.tpl.php
new file mode 100644
index 0000000..74f5c30
--- /dev/null
+++ b/core/modules/layout/layouts/static/onecol/onecol.tpl.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @file
+ * Template for a one column layout.
+ *
+ * This template provides a very simple "one column" display layout.
+ *
+ * Variables:
+ * - $content: An array of content, each item in the array is keyed to one
+ *   region of the layout. This layout supports the following sections:
+ *   $content['middle']: The only region in the layout.
+ */
+?>
+<div class="layout-display layout-onecol clearfix">
+  <div class="layout-region layout-col">
+    <div class="inside"><?php print $content['middle']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/layouts/static/onecol/onecol.yml b/core/modules/layout/layouts/static/onecol/onecol.yml
new file mode 100644
index 0000000..d401573
--- /dev/null
+++ b/core/modules/layout/layouts/static/onecol/onecol.yml
@@ -0,0 +1,5 @@
+title: Single column
+category: Columns: 1
+template: onecol
+regions:
+  middle: 'Middle column'
diff --git a/core/modules/layout/layouts/static/twocol/twocol.css b/core/modules/layout/layouts/static/twocol/twocol.css
new file mode 100644
index 0000000..9654752
--- /dev/null
+++ b/core/modules/layout/layouts/static/twocol/twocol.css
@@ -0,0 +1,17 @@
+.layout-twocol .layout-col-left {
+  float: left;
+  width: 50%;
+}
+
+.layout-twocol .layout-col-left .inside {
+  margin-right: .5em;
+}
+
+.layout-twocol .layout-col-right {
+  float: right;
+  width: 50%;
+}
+
+.layout-twocol .layout-col-right .inside {
+  margin-left: .5em;
+}
diff --git a/core/modules/layout/layouts/static/twocol/twocol.tpl.php b/core/modules/layout/layouts/static/twocol/twocol.tpl.php
new file mode 100644
index 0000000..158426a
--- /dev/null
+++ b/core/modules/layout/layouts/static/twocol/twocol.tpl.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Template for a 2 column layout.
+ *
+ * This template provides a two column display layout, with each column equal in
+ * width.
+ *
+ * Variables:
+ * - $content: An array of content, each item in the array is keyed to one
+ *   region of the layout. This layout supports the following sections:
+ *   - $content['left']: Content in the left column.
+ *   - $content['right']: Content in the right column.
+ */
+?>
+<div class="layout-display layout-twocol clearfix">
+  <div class="layout-region layout-col-left">
+    <div class="inside"><?php print $content['left']; ?></div>
+  </div>
+
+  <div class="layout-region layout-col-right">
+    <div class="inside"><?php print $content['right']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/layouts/static/twocol/twocol.yml b/core/modules/layout/layouts/static/twocol/twocol.yml
new file mode 100644
index 0000000..5e19696
--- /dev/null
+++ b/core/modules/layout/layouts/static/twocol/twocol.yml
@@ -0,0 +1,8 @@
+title: Two column
+category: Columns: 2
+template: twocol
+stylesheets:
+  - twocol.css
+regions:
+  left: 'Left side'
+  right: 'Right side'
diff --git a/core/modules/layout/lib/Drupal/layout/LayoutBundle.php b/core/modules/layout/lib/Drupal/layout/LayoutBundle.php
new file mode 100644
index 0000000..59b8513
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/LayoutBundle.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\LayoutBundle.
+ */
+
+namespace Drupal\Layout;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Layout dependency injection container.
+ */
+class LayoutBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Register the LayoutManager class with the dependency injection container.
+    $container->register('plugin.manager.layout', 'Drupal\layout\Plugin\Type\LayoutManager');
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
new file mode 100644
index 0000000..c585295
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Derivative\Layout.
+ */
+
+namespace Drupal\layout\Plugin\Derivative;
+
+use DirectoryIterator;
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Layout plugin derivative definition.
+ */
+class Layout implements DerivativeInterface {
+
+  /**
+   * List of derivatives.
+   *
+   * Associative array keyed by 'provider__layoutname' where provider is the
+   * module or theme name and layoutname is the .yml filename, such as
+   * 'bartik__page' or 'layout__onecol'. The values of the array are associative
+   * arrays themselves with metadata about the layout such as 'template', 'css',
+   * 'admin css' and so on.
+   *
+   * @var array
+   */
+  protected $derivatives = array();
+
+  /**
+   * Layout derivative type.
+   *
+   * Defines the subdirectory under ./layout where layout metadata is loooked
+   * for. Overriding implementations should change this to look for other
+   * types in a different subdirectory.
+   *
+   * @var string
+   */
+  protected $type = 'static';
+
+  /**
+   * 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) {
+    $available_layout_providers = array();
+
+    // Add all modules as possible layout providers.
+    foreach (module_list() as $module) {
+      $available_layout_providers[$module] = array(
+        'type' => 'module',
+        'provider' => $module,
+        'dir' => drupal_get_path('module', $module),
+      );
+    }
+
+    // Add all themes as possible layout providers.
+    foreach (list_themes() as $theme_id => $theme) {
+      $available_layout_providers[$theme_id] = array(
+        'type' => 'theme',
+        'provider' => $theme->name,
+        'dir' => drupal_get_path('theme', $theme->name),
+      );
+    }
+
+    foreach ($available_layout_providers as $provider) {
+      // Looks for layouts in the 'layout' directory under the module/theme.
+      // There could be subdirectories under there with one layout defined
+      // in each.
+      $dir = $provider['dir'] . DIRECTORY_SEPARATOR . 'layouts' . DIRECTORY_SEPARATOR . $this->type;
+      if (file_exists($dir)) {
+        $this->iterateDirectories($dir, $provider);
+      }
+    }
+    return $this->derivatives;
+  }
+
+  /**
+   * Finds layout definitions by looking for layout metadata.
+   */
+  protected function iterateDirectories($dir, $provider) {
+    $directories = new DirectoryIterator($dir);
+    foreach ($directories as $fileinfo) {
+      if ($fileinfo->isDir() && !$fileinfo->isDot()) {
+        // Keep discovering in subdirectories to arbitrary depth.
+        $this->iterateDirectories($fileinfo->getPathname(), $provider);
+      }
+      elseif ($fileinfo->isFile() && pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'yml') {
+        // Declarative layout definitions are defined with a .yml file in a
+        // layout subdirectory. This provides all information about the layout
+        // such as layout markup template and CSS and JavaScript files to use.
+        $directory = new FileStorage($fileinfo->getPath());
+        $key = $provider['provider'] . '__' .  $fileinfo->getBasename('.yml');
+        $this->derivatives[$key] = $directory->read($fileinfo->getBasename('.yml'));
+        $this->derivatives[$key]['theme'] = $key;
+        $this->derivatives[$key]['path'] = $fileinfo->getPath();
+        // If the layout author didn't specify a template name, assume the same
+        // name as the yml file.
+        if (!isset($this->derivatives[$key]['template'])) {
+          $this->derivatives[$key]['template'] = $fileinfo->getBasename('.yml');
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
new file mode 100644
index 0000000..5b874f6
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\LayoutInterface.
+ */
+
+namespace Drupal\layout\Plugin;
+
+/**
+ * Defines the shared interface for all layout plugins.
+ */
+interface LayoutInterface {
+
+  /**
+   * Returns a list of regions.
+   *
+   * @return array
+   *   An array of region machine names.
+   */
+  public function getRegions();
+
+  /**
+   * Renders layout and returns the rendered markup.
+   *
+   * @return string
+   *   Rendered HTML output from the layout.
+   */
+  public function renderLayout();
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
new file mode 100644
index 0000000..e58ad4b
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Type\LayoutManager.
+ */
+
+namespace Drupal\layout\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Component\Plugin\Factory\ReflectionFactory;
+
+/**
+ * Layout plugin manager.
+ */
+class LayoutManager extends PluginManagerBase {
+
+  protected $defaults = array(
+    'class' => 'Drupal\layout\Plugin\layout\layout\StaticLayout',
+  );
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::__construct().
+   */
+  public function __construct() {
+    // Create layout plugin derivatives from declaratively defined layouts.
+    $this->discovery = new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('layout', 'layout'));
+    $this->factory = new ReflectionFactory($this);
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
new file mode 100644
index 0000000..e300dc3
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\layout\layout\StaticLayout.
+ */
+
+namespace Drupal\layout\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 = "static_layout",
+ *  derivative = "Drupal\layout\Plugin\Derivative\Layout"
+ * )
+ */
+class StaticLayout 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::getRegions().
+   */
+  public function getRegions() {
+    $definition = $this->getDefinition();
+    return $definition['regions'];
+  }
+
+  /**
+   * Returns the list of CSS files associated with this layout.
+   */
+  public function getStylesheetFiles() {
+    $definition = $this->getDefinition();
+    return $definition['stylesheets'];
+  }
+
+  /**
+   * Returns the list of administrative CSS files associated with this layout.
+   */
+  public function getAdminStylesheetFiles() {
+    $definition = $this->getDefinition();
+    // Fall back on regular CSS for the admin page if admin CSS not provided.
+    return isset($definition['admin stylesheets']) ? $definition['admin stylesheets'] : $definition['stylesheets'];
+  }
+
+  /**
+   * Returns the list of JS files associated with this layout.
+   */
+  public function getScriptFiles() {
+    $definition = $this->getDefinition();
+    if (isset($definition['scripts']))  {
+      return $definition['scripts'];
+    }
+    return array();
+  }
+
+  /**
+   * Returns the list of administrative JS files associated with this layout.
+   */
+  public function getAdminScriptFiles() {
+    $definition = $this->getDefinition();
+    $files = array();
+    if (isset($definition['admin scripts'])) {
+      $files = $definition['admin scripts'];
+    }
+    elseif (isset($definition['scripts'])) {
+      // Fall back on regular JS for the admin page if admin JS not provided.
+      $files = $definition['scripts'];
+    }
+    return $files;
+  }
+
+  /**
+   * Implements Drupal\layout\Plugin\LayoutInterface::renderLayout().
+   */
+  public function renderLayout($admin = FALSE) {
+    $definition = $this->getDefinition();
+
+    // Assemble a render array with the regions and attached CSS/JS.
+    $build = array(
+      '#theme' => $definition['theme'],
+      '#content' => array(),
+    );
+
+    // Render all regions needed for this layout.
+    foreach ($this->getRegions() as $region => $title) {
+      // @todo This is just stub code to fill in regions with stuff for now.
+      // When blocks are related to layouts and not themes, we can make this
+      // really be filled in with blocks.
+      $build['#content'][$region] = '<h3>' . $title . '</h3>';
+    }
+
+    // Fill in attached CSS and JS files based on metadata.
+    if (!$admin) {
+      $build['#attached'] = array(
+        'css' => $this->getStylesheetFiles(),
+        'js' => $this->getScriptFiles(),
+      );
+    }
+    else {
+      $build['#attached'] = array(
+        'css' => $this->getAdminStylesheetFiles(),
+        'js' => $this->getAdminScriptFiles(),
+      );
+    }
+
+    // Include the path of the definition in all CSS and JS files.
+    foreach (array('css', 'js') as $type) {
+      foreach ($build['#attached'][$type] as &$filename) {
+        $filename = $definition['path'] . '/' . $filename;
+      }
+    }
+
+    return drupal_render($build);
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
new file mode 100644
index 0000000..1068373
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Tests\LayoutDerivativesTest.
+ */
+
+namespace Drupal\layout\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests the layout system derivatives.
+ */
+class LayoutDerivativesTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('layout', 'layout_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Layout derivatives',
+      'description' => 'Tests layout derivatives discovery.',
+      'group' => 'Layout',
+    );
+  }
+
+  /**
+   * Tests for module/theme layout derivatives.
+   */
+  function testDerivatives() {
+    $manager = drupal_container()->get('plugin.manager.layout');
+
+    $definitions = $manager->getDefinitions();
+    $this->assertTrue(is_array($definitions), 'Definitions found.');
+    $this->assertTrue(count($definitions) == 3, 'Three definitions available.');
+    $this->assertTrue(isset($definitions['static_layout:layout__onecol']), 'One column layout found.');
+    $this->assertTrue(isset($definitions['static_layout:layout__twocol']), 'Two column layout found.');
+    $this->assertTrue(isset($definitions['static_layout:layout_test_theme__page']), 'Test theme page layout found.');
+
+    // Get a two column layout instance. This is defined under the layout
+    // module.
+    $layout = $manager->createInstance('static_layout:layout__twocol', array());
+    // Verify the expected regions are properly available.
+    $regions = $layout->getRegions();
+    $this->assertTrue(is_array($regions), 'Regions array present.');
+    $this->assertTrue(count($regions) == 2, 'Two regions defined.');
+    $this->assertTrue(isset($regions['left']), 'Left region found.');
+    $this->assertTrue(isset($regions['right']), 'Right region found.');
+
+    // Render the layout and look at whether expected region names and classes
+    // were in the output.
+    $render = $layout->renderLayout();
+    $this->drupalSetContent($render);
+    $this->assertText('Left side');
+    $this->assertText('Right side');
+    $this->assertRaw('class="layout-display layout-twocol');
+
+    // Get the page layout defined by the layout test theme.
+    $layout = $manager->createInstance('static_layout:layout_test_theme__page', array());
+    // Verify the expected regions are properly available.
+    $regions = $layout->getRegions();
+    $this->assertTrue(is_array($regions), 'Regions array present.');
+    $this->assertTrue(count($regions) == 15, '15 regions defined.');
+    $this->assertTrue(isset($regions['footer']), 'Footer region found.');
+    $this->assertTrue(isset($regions['triptych_middle']), 'Triptych middle region found.');
+
+    // Render the layout and look at whether expected region names and classes
+    // were in the output.
+    $render = $layout->renderLayout();
+    $this->drupalSetContent($render);
+    $this->assertText('Triptych middle');
+    $this->assertText('Footer');
+    $this->assertRaw('id="triptych-wrapper"');
+  }
+
+  /**
+   * Test layout functionality as applies to pages.
+   */
+  function testPageLayout() {
+    // The layout-test page uses the layout_test_theme page layout.
+    $this->drupalGet('layout-test');
+    $this->assertText('Triptych middle');
+    $this->assertText('Footer');
+    $this->assertRaw('id="triptych-wrapper"');
+
+    // Ensure the CSS was added.
+    $this->assertRaw('@import url("' . url('', array('absolute' => TRUE)) . drupal_get_path('theme', 'layout_test_theme') . '/layouts/static/page/page.css');
+  }
+}
diff --git a/core/modules/layout/tests/layout_test.info b/core/modules/layout/tests/layout_test.info
new file mode 100644
index 0000000..7b054a9
--- /dev/null
+++ b/core/modules/layout/tests/layout_test.info
@@ -0,0 +1,6 @@
+name = Layout test
+description = Helps with testing layouts.
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/layout/tests/layout_test.module b/core/modules/layout/tests/layout_test.module
new file mode 100644
index 0000000..9dce5b9
--- /dev/null
+++ b/core/modules/layout/tests/layout_test.module
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Layout testing module.
+ */
+
+/**
+ * Implementation of hook_menu().
+ */
+function layout_test_menu() {
+  $items['layout-test'] = array(
+    'title' => 'Layout test',
+    'page callback' => 'layout_test_page',
+    'access callback' => TRUE,
+  );
+  return $items;
+}
+
+/**
+ * Page callback for layout testing.
+ */
+function layout_test_page() {
+  // Hack to enable and apply the theme to this page and manually invoke its
+  // layout plugin and render it.
+  global $theme;
+  $theme = 'layout_test_theme';
+  theme_enable(array($theme));
+  $layout = layout_manager()->createInstance('static_layout:layout_test_theme__page');
+  return $layout->renderLayout();
+}
+
+/**
+ * Implements hook_system_theme_info().
+ */
+function layout_test_system_theme_info() {
+  $themes['layout_test_theme'] = drupal_get_path('module', 'layout_test') . '/themes/layout_test_theme/layout_test_theme.info';
+  return $themes;
+}
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info b/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info
new file mode 100644
index 0000000..84bcff0
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info
@@ -0,0 +1,4 @@
+name = Layout test theme
+description = Theme for testing the layout system
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.css b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.css
new file mode 100644
index 0000000..a624d66
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.css
@@ -0,0 +1 @@
+/* Page CSS */
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.yml b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.yml
new file mode 100644
index 0000000..e44950a
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/page.yml
@@ -0,0 +1,21 @@
+title: Layout test page
+category: Other
+template: test-layout
+stylesheets:
+  - page.css
+regions:
+  header: 'Header'
+  help: 'Help'
+  highlighted: 'Highlighted'
+  featured: 'Featured'
+  content: 'Content'
+  sidebar_first: 'Sidebar first'
+  sidebar_second: 'Sidebar second'
+  triptych_first: 'Triptych first'
+  triptych_middle: 'Triptych middle'
+  triptych_last: 'Triptych last'
+  footer_firstcolumn: 'Footer first column'
+  footer_secondcolumn: 'Footer second column'
+  footer_thirdcolumn: 'Footer third column'
+  footer_fourthcolumn: 'Footer fourth column'
+  footer: 'Footer'
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/test-layout.tpl.php b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/test-layout.tpl.php
new file mode 100644
index 0000000..3a5db91
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/page/test-layout.tpl.php
@@ -0,0 +1,62 @@
+<div id="page-wrapper"><div id="page">
+
+  <div id="header"><div class="section clearfix">
+    <?php print $content['header']; ?>
+  </div></div> <!-- /.section, /#header -->
+
+  <?php if ($content['featured']): ?>
+    <div id="featured"><div class="section clearfix">
+      <?php print $content['featured']; ?>
+    </div></div> <!-- /.section, /#featured -->
+  <?php endif; ?>
+
+  <div id="main-wrapper" class="clearfix"><div id="main" class="clearfix">
+
+    <div id="content" class="column"><div class="section">
+      <?php if ($content['highlighted']): ?><div id="highlighted"><?php print $content['highlighted']; ?></div><?php endif; ?>
+      <?php print $content['help']; ?>
+      <?php print $content['content']; ?>
+    </div></div> <!-- /.section, /#content -->
+
+    <?php if ($content['sidebar_first']): ?>
+      <div id="sidebar-first" class="column sidebar"><div class="section">
+        <?php print $content['sidebar_first']; ?>
+      </div></div> <!-- /.section, /#sidebar-first -->
+    <?php endif; ?>
+
+    <?php if ($content['sidebar_second']): ?>
+      <div id="sidebar-second" class="column sidebar"><div class="section">
+        <?php print $content['sidebar_second']; ?>
+      </div></div> <!-- /.section, /#sidebar-second -->
+    <?php endif; ?>
+
+  </div></div> <!-- /#main, /#main-wrapper -->
+
+  <?php if ($content['triptych_first'] || $content['triptych_middle'] || $content['triptych_last']): ?>
+    <div id="triptych-wrapper"><div id="triptych" class="clearfix">
+      <?php print $content['triptych_first']; ?>
+      <?php print $content['triptych_middle']; ?>
+      <?php print $content['triptych_last']; ?>
+    </div></div> <!-- /#triptych, /#triptych-wrapper -->
+  <?php endif; ?>
+
+  <div id="footer-wrapper"><div class="section">
+
+    <?php if ($content['footer_firstcolumn'] || $content['footer_secondcolumn'] || $content['footer_thirdcolumn'] || $content['footer_fourthcolumn']): ?>
+      <div id="footer-columns" class="clearfix">
+        <?php print $content['footer_firstcolumn']; ?>
+        <?php print $content['footer_secondcolumn']; ?>
+        <?php print $content['footer_thirdcolumn']; ?>
+        <?php print $content['footer_fourthcolumn']; ?>
+      </div> <!-- /#footer-columns -->
+    <?php endif; ?>
+
+    <?php if ($content['footer']): ?>
+      <div id="footer" class="clearfix">
+        <?php print $content['footer']; ?>
+      </div> <!-- /#footer -->
+    <?php endif; ?>
+
+  </div></div> <!-- /.section, /#footer-wrapper -->
+
+</div></div> <!-- /#page, /#page-wrapper -->
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 34e36fe..63911ec 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -4182,6 +4182,29 @@ function system_archiver_info() {
 }
 
 /**
+ * Implements hook_entity_info().
+ *
+ * Provides the core Display object, which contains all the info necessary for
+ * block-driven pages.
+ */
+function system_entity_info() {
+  return array(
+    'booze_display' => array(
+      'label' => t('Display'),
+      'entity class' => 'Drupal\Core\Config\Entity\Display',
+      'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+      'config prefix' => 'booze.display',
+      'fieldable' => FALSE,
+      'entity keys' => array(
+        'id' => 'id',
+        'label' => 'label', // @todo may not need this if internal-only (?)
+        'uuid' => 'uuid',
+      ),
+    )
+  );
+}
+
+/**
  * Returns HTML for a confirmation form.
  *
  * By default this does not alter the appearance of a form at all,
