diff --git a/css/panopoly-modal.css b/css/panopoly-modal.css
index 11bebd7..201f333 100644
--- a/css/panopoly-modal.css
+++ b/css/panopoly-modal.css
@@ -197,7 +197,7 @@
 }
 
 #modal-content fieldset.widget-preview-single {
-  margin-top: 0em;
+  margin-top: 10px;
   margin-bottom: 1em;
 }
 
diff --git a/panopoly-magic.js b/panopoly-magic.js
index f52f96b..6fcb463 100644
--- a/panopoly-magic.js
+++ b/panopoly-magic.js
@@ -27,20 +27,29 @@
    */
   Drupal.behaviors.panopolyMagicAutosubmit = {
     attach: function (context, settings) {
+      // Replaces click with mousedown for submit so both normal and ajax work.
+      $('.ctools-auto-submit-click', context)
+      // Exclude the 'Style' type form because then you have to press the
+      // "Next" button multiple times.
+      // @todo: Should we include the places this works rather than excluding?
+      .filter(function () { return $(this).closest('form').attr('id').indexOf('panels-edit-style-type-form') !== 0; })
+      .click(function(event) {
+        if ($(this).hasClass('ajax-processed')) {
+          event.stopImmediatePropagation();
+          $(this).trigger('mousedown');
+          return false;
+        }
+      });
+
       // 'this' references the form element
       function triggerSubmit (e) {
-        var $this = $(this);
-        if (!$this.hasClass('ctools-ajaxing')) {
-          $this.addClass('ctools-ajaxing');
+        var $this = $(this), preview_widget = $('.widget-preview', context);
+        if (!preview_widget.hasClass('panopoly-magic-loading')) {
+          preview_widget.addClass('panopoly-magic-loading');
           $this.find('.ctools-auto-submit-click').click();
         }
       }
 
-      function triggerDisable (e) {
-        var $this = $(this);
-        $this.find(':submit:not(.ctools-auto-submit-click)').val(Drupal.t('Updating...')).addClass('form-disabled').attr('disabled', 'disabled');
-      }
-
       // e.keyCode: key
       var discardKeyCode = [
         16, // shift
@@ -60,26 +69,26 @@
         27  // esc
       ];
 
-      // Disable save button and update label on auto submit. 
-      $(':submit.ctools-auto-submit-click', context)
-      .once('ctools-auto-submit')
-      .bind('click', function (e) {
-        triggerDisable.call(e.target.form);
-      });
-
       // Special handling for link field widgets. This ensures content which is ahah'd in still properly autosubmits.
       $('.field-widget-link-field input:text', context).addClass('panopoly-textfield-autosubmit').addClass('ctools-auto-submit-exclude');
 
-      // Handle title fields.
+      // Handle text fields and textareas.
       var timer;
-      $('.panopoly-textfield-autosubmit', context)
+      $('.panopoly-textfield-autosubmit, .panopoly-textarea-autosubmit', context)
       .once('ctools-auto-submit')
-      .bind('keyup keydown blur', function (e) {
+      .bind('keyup blur', function (e) {
         var $element;
         $element = $('.widget-preview .pane-title', context);
 
+        clearTimeout(timer);
+
+        // Filter out discarded keys.
+        if (e.type !== 'blur' && $.inArray(e.keyCode, discardKeyCode) > 0) {
+          return;
+        }
+
         // Special handling for title elements.
-        if (($element.length || !$.inArray(e.keycode, discardKeyCode)) && $(e.target).parent().hasClass('form-item-title')) {
+        if ($element.length && $(e.target).parent('.form-item-title,.form-item-widget-title').length) {
 
           // If all text was removed, remove the existing title markup from the dom.
           if (!$(e.target).val().length) {
@@ -96,13 +105,11 @@
         } 
         // Automatically submit the field on blur. This won't happen if title markup is already present.
         else if (e.type == 'blur') {
-          clearTimeout(timer);
           triggerSubmit.call(e.target.form)
         }
         // If all else fails, just trigger a timer to submit the form a second after the last activity.
         else {
-          clearTimeout(timer);
-          timer = setTimeout(function () { triggerSubmit.call(e.target.form); }, 1000)
+          timer = setTimeout(function () { triggerSubmit.call(e.target.form); }, 1000);
         }
       });
   
@@ -113,22 +120,6 @@
         triggerSubmit.call(e.target.form);
       });
 
-      // Handle textarea fields.
-      $('.panopoly-textarea-autosubmit', context)
-      .once('ctools-auto-submit')
-      .bind('keyup keydown blur', function (e) {
-        // On blur just autosubmit the form.
-        if (e.type == 'blur') {
-          clearTimeout(timer);
-          triggerSubmit.call(e.target.form)
-        }
-        // Otherwise set a 1 second delay on anything that's a valid keypress.
-        else {
-          clearTimeout(timer);
-          timer = setTimeout(function () { triggerSubmit.call(e.target.form); }, 1000)
-        }
-      });
-
       // Prevent ctools auto-submit from firing when changing text formats.
       $(':input.filter-list').addClass('ctools-auto-submit-exclude');
 
diff --git a/panopoly_magic.module b/panopoly_magic.module
index 70966f9..4bc84a3 100644
--- a/panopoly_magic.module
+++ b/panopoly_magic.module
@@ -1,5 +1,4 @@
 <?php
-
 include_once('panopoly_magic.features.inc');
 
 /**
@@ -32,6 +31,56 @@ function panopoly_magic_init() {
 }
 
 /**
+ * Implements hook_menu()
+ */
+function panopoly_magic_menu() {
+  $items = array();
+  $items['system/panopoly-magic'] = array(
+    'title' => 'AHAH callback',
+    'page callback' => 'panopoly_magic_ajax_form_callback',
+    'delivery callback' => 'ajax_deliver',
+    'access callback' => TRUE,
+    'theme callback' => 'ajax_base_page_theme',
+    'type' => MENU_CALLBACK,
+    'file path' => 'includes',
+    'file' => 'form.inc',
+  );
+  return $items;
+}
+
+/**
+ * Replaces system/ajax for pane configuration preview callback to work with
+ * ctools multi step form.
+ */
+function panopoly_magic_ajax_form_callback() {
+  list($form, $form_state) = ajax_get_form();
+  ctools_include('content');
+  ctools_include('modal');
+
+  if (isset($form_state['no_redirect']) && !isset($form_state['form_info'])) {
+    $form_state += array(
+      're_render' => FALSE,
+      'no_redirect' => !empty($form_state['ajax']),
+     );
+     $form = drupal_build_form($form_state['build_info']['form_id'], $form_state);
+  }
+  else {
+    // These ensure the class files/form definations are included.
+    $content_type = ctools_get_content_type($form_state['pane']->type);
+    $subtype = ctools_content_get_subtype($content_type, $form_state['subtype_name']);
+
+    $form = ctools_content_form($form_state['op'], $form_state['form_info'], $form_state, $form_state['plugin'], $form_state['subtype_name'], $form_state['conf'], $form_state['step']);
+  }
+
+  if (!empty($form_state['triggering_element'])) {
+    $callback = $form_state['triggering_element']['#ajax']['callback'];
+  }
+  if (!empty($callback) && function_exists($callback)) {
+    return $callback($form, $form_state);
+  }
+}
+
+/**
  * Implements hook_apps_app_info()
  */
 function panopoly_magic_apps_app_info() {
@@ -176,7 +225,7 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
       $style = (isset($pane->style['style'])) ? panels_get_style($pane->style['style']) : NULL;
     }
     elseif ($form_id == 'panels_edit_style_type_form') {
-      $style = ($form_state['rebuild'] && $form_id == 'panels_edit_style_type_form') ? panels_get_style($form_state['values']['style']) : panels_get_style($form_state['style']);
+      $style = ($form_state['rebuild'] && $form_id == 'panels_edit_style_type_form') ? panels_get_style($form_state['input']['style']) : panels_get_style($form_state['style']);
     }
     elseif ($form_id == 'panels_edit_style_settings_form') {
       $style = panels_get_style($form_state['style']);
@@ -269,21 +318,21 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
         unset($configuration['image_link']);
       }
     }
-    $content = (empty($preview_subtype)) ? ctools_content_render($pane->type, $pane->subtype, $configuration, $keywords, $args, $context) : ctools_content_render($pane->type, $preview_subtype, $configuration, $keywords, $args, $context);
-
-    // Create the preview fieldset
-    if ($form_id == 'fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form' || is_object($content)) {
 
-      // Create the fieldset with appropriate content
-      $form['widget_preview'] = array(
-        '#type' => 'fieldset',
-        '#title' => t('Preview'),
-        '#attributes' => array('class' => array('widget-preview', 'widget-preview-single')),
-        '#collapsible' => FALSE,
-        '#weight' => -100,
-      );
-      $form['widget_preview']['preview'] = array(
-        '#markup' => (!empty($style['render pane'])) ? theme($style['render pane'], array('content' => $content, 'pane' => $pane, 'display' => $display, 'style' => $style, 'settings' => $pane->style['settings'])) : theme('panels_pane', array('content' => $content, 'pane' => $pane, 'display' => $display)),
+    // only render preview for Panes and not Regions
+    if (isset($pane)) {
+      // Creates preview outside of form itself to fix various bugs, like form
+      // inside form and double rendering.
+      $form['#post_render'][] = 'panopoly_magic_form_post_render_preview';
+      $form['#panopoly_magic_preview_info'] = array(
+        'preview_subtype' => isset($preview_subtype) ? $preview_subtype : NULL,
+        'pane' => $pane,
+        'configuration' =>  $configuration,
+        'keywords' => $keywords,
+        'args' => $args,
+        'context' => $context,
+        'style' => $style,
+        'display' => $display,
       );
 
       // Remove the clearfix for preview floating
@@ -294,7 +343,6 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
       $preview_attributes = array(
         'class' => array(
           'widget-preview-button',
-          'ctools-use-ajax',
         ),
       );
 
@@ -308,17 +356,23 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
       $form['buttons']['preview'] = array(
         '#type' => 'button',
         '#value' => t('Update Preview'),
-        '#wizard type' => 'next',
         '#attributes' => $preview_attributes,
+        '#ajax' => array(
+          'callback' => 'panopoly_magic_ajax_update_preview',
+          'wrapper' => 'panopoly-form-widget-preview',
+          'path' => 'system/panopoly-magic',
+        ),
       );
 
       // Autosubmit the form
       ctools_add_js('auto-submit');
       $form['#attributes']['class'][] = 'ctools-auto-submit-full-form';
     }
+    // Convert any other #ajax enabled buttons to use system/panopoly-magic.
+    _panopoly_magic_add_path_to_ajax($form);
   }
   
-   /**
+  /**
    * Globally improve the buttons for the Chaos Tools Content Type Settings Forms
    */
   if (strpos($form_id, 'content_type_edit_form') || $form_id == 'ctools_entity_field_content_type_formatter_options') {
@@ -326,7 +380,7 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
     if (!empty($form['buttons']['return'])) {
       $form['buttons']['return']['#value'] = t('Save');
     }
-    $form['buttons']['#weight'] = (!empty($form['widget_preview']['#weight'])) ? ($form['widget_preview']['#weight'] +1 ): -99;
+    $form['buttons']['#weight'] = -99;
   }
 
   /**
@@ -382,6 +436,57 @@ function panopoly_magic_form_alter(&$form, &$form_state, $form_id) {
 }
 
 /**
+ * Content panes should not use default system/ajax. Use our own for now.
+ */
+function _panopoly_magic_add_path_to_ajax($element) {
+  if (!empty($element['#ajax']) && !isset($element['#ajax']['path'])) {
+    $element['#ajax']['path'] = 'system/panopoly-magic';
+  }
+  foreach (element_children($element) as $key) {
+    _panopoly_magic_add_path_to_ajax($element[$key]);
+  }
+}
+
+/**
+ * Ajax callback that just returns the rendered preview.
+ */
+function panopoly_magic_ajax_update_preview($form, $form_state) {
+  if (isset($form_state['values']) && isset($form['#panopoly_magic_preview_info']['configuration'])) {
+    $form['#panopoly_magic_preview_info']['configuration'] = $form_state['values'] + $form['#panopoly_magic_preview_info']['configuration'];
+  }
+  return panopoly_magic_form_post_render_preview('', $form);
+}
+
+/**
+ * Add the preview to the form output.
+ *
+ * It is done here so the form is fully processed.
+ */
+function panopoly_magic_form_post_render_preview($output, $form) {
+  extract($form['#panopoly_magic_preview_info']);
+  $content = (empty($preview_subtype)) ? ctools_content_render($pane->type, $pane->subtype, $configuration, $keywords, $args, $context) : ctools_content_render($pane->type, $preview_subtype, $configuration, $keywords, $args, $context);
+
+  // Create the fieldset with appropriate content
+  $preview = array(
+    '#type' => 'fieldset',
+    '#title' => 'Preview',
+    '#attributes' => array(
+      'id' => 'panopoly-form-widget-preview',
+      'class' => array('widget-preview', 'widget-preview-single'),
+    ),
+    '#collapsible' => FALSE,
+    '#weight' => -100,
+  );
+  if (!empty($content)) {
+    $preview['preview']['#markup'] = (!empty($style['render pane'])) ? theme($style['render pane'], array('content' => $content, 'pane' => $pane, 'display' => $display, 'style' => $style, 'settings' => $pane->style['settings'])) : theme('panels_pane', array('content' => $content, 'pane' => $pane, 'display' => $display));
+  }
+  else {
+    $preview['preview']['#markup'] = t('[no preview]');
+  }
+  return drupal_render($preview) . $output;
+}
+
+/**
  * Recursively parse form elements to add special autosubmit handling on a per field-type basis.
  */
 function panopoly_magic_autosubmit_configure(&$element) {
@@ -709,7 +814,6 @@ function panopoly_magic_form_views_content_views_panes_content_type_edit_form_al
 
   // Add a custom submit handler to our preview and submit option
   $form['#submit'][] = 'panopoly_magic_views_content_type_modal_submit';
-  $form['buttons']['preview']['#submit'][] = 'panopoly_magic_views_content_type_modal_submit';
 }
 
 /**
