diff --git a/special-pages/pages/new-tab/app/InlineError.js b/special-pages/pages/new-tab/app/InlineError.js
new file mode 100644
index 000000000..66ce9dee3
--- /dev/null
+++ b/special-pages/pages/new-tab/app/InlineError.js
@@ -0,0 +1,29 @@
+import { h } from 'preact';
+import { ErrorBoundary } from '../../../shared/components/ErrorBoundary.js';
+import { useMessaging } from './types.js';
+
+/**
+ * @param {object} props
+ * @param {import("preact").ComponentChild} props.children
+ * @param {string} props.named
+ * @param {(message: string) => import("preact").ComponentChild} [props.fallback]
+ */
+export function InlineError({ children, named, fallback }) {
+    const messaging = useMessaging();
+    /**
+     * @param {any} error
+     * @param {string} id
+     */
+    const didCatch = (error, id) => {
+        const message = error?.message || error?.error || 'unknown';
+        const composed = `Customizer section '${id}' threw an exception: ` + message;
+        messaging.reportPageException({ message: composed });
+    };
+    const inlineMessage = 'A problem occurred with this feature. DuckDuckGo was notified';
+    const fallbackElement = fallback?.(inlineMessage) || <p>{inlineMessage}</p>;
+    return (
+        <ErrorBoundary didCatch={(error) => didCatch(error, named)} fallback={fallbackElement}>
+            {children}
+        </ErrorBoundary>
+    );
+}
diff --git a/special-pages/pages/new-tab/app/components/App.js b/special-pages/pages/new-tab/app/components/App.js
index 57e59e217..ea8052250 100644
--- a/special-pages/pages/new-tab/app/components/App.js
+++ b/special-pages/pages/new-tab/app/components/App.js
@@ -75,9 +75,7 @@ export function App() {
                         data-browser-panel
                     >
                         <div class={styles.asideContent}>
-                            <div class={styles.asideContentInner}>
-                                <CustomizerDrawer displayChildren={displayChildren} />
-                            </div>
+                            <CustomizerDrawer displayChildren={displayChildren} />
                         </div>
                     </aside>
                 )}
diff --git a/special-pages/pages/new-tab/app/components/App.module.css b/special-pages/pages/new-tab/app/components/App.module.css
index 0c16b26f3..2e15c2dd9 100644
--- a/special-pages/pages/new-tab/app/components/App.module.css
+++ b/special-pages/pages/new-tab/app/components/App.module.css
@@ -89,6 +89,11 @@ body:has([data-reset-layout="true"]) .tube {
     .layout[data-animating="true"] & {
         overflow: hidden;
     }
+
+    .layout[data-animating="false"] &[aria-hidden=true] {
+        visibility: hidden;
+        opacity: 0;
+    }
 }
 
 .asideContent {
@@ -96,11 +101,6 @@ body:has([data-reset-layout="true"]) .tube {
     width: var(--ntp-drawer-width);
 }
 
-.asideContentInner {
-    padding: 1rem;
-    padding-right: calc(1rem - var(--ntp-drawer-scroll-width));
-}
-
 .asideScroller {
     &::-webkit-scrollbar {
         width: var(--ntp-drawer-scroll-width);
diff --git a/special-pages/pages/new-tab/app/components/BackgroundProvider.js b/special-pages/pages/new-tab/app/components/BackgroundProvider.js
index 6c8fc4ca0..920dab70b 100644
--- a/special-pages/pages/new-tab/app/components/BackgroundProvider.js
+++ b/special-pages/pages/new-tab/app/components/BackgroundProvider.js
@@ -1,7 +1,8 @@
 import { Fragment, h } from 'preact';
+import cn from 'classnames';
 import styles from './BackgroundReceiver.module.css';
 import { values } from '../customizer/values.js';
-import { useContext } from 'preact/hooks';
+import { useContext, useState } from 'preact/hooks';
 import { CustomizerContext } from '../customizer/CustomizerProvider.js';
 import { detectThemeFromHex } from '../customizer/utils.js';
 
@@ -92,20 +93,9 @@ export function BackgroundConsumer({ browser }) {
             const gradient = values.gradients[background.value];
             return (
                 <Fragment key="gradient">
+                    <ImageCrossFade src={gradient.path}></ImageCrossFade>
                     <div
-                        class={styles.root}
-                        data-animate="false"
-                        data-testid="BackgroundConsumer"
-                        style={{
-                            backgroundColor: gradient.fallback,
-                            backgroundImage: `url(${gradient.path})`,
-                            backgroundSize: 'cover',
-                            backgroundRepeat: 'no-repeat',
-                        }}
-                    />
-                    <div
-                        class={styles.root}
-                        data-animate="false"
+                        className={styles.root}
                         style={{
                             backgroundImage: `url(gradients/grain.png)`,
                             backgroundRepeat: 'repeat',
@@ -118,19 +108,7 @@ export function BackgroundConsumer({ browser }) {
         }
         case 'userImage': {
             const img = background.value;
-            return (
-                <div
-                    class={styles.root}
-                    data-animate="true"
-                    data-testid="BackgroundConsumer"
-                    style={{
-                        backgroundImage: `url(${img.src})`,
-                        backgroundSize: 'cover',
-                        backgroundRepeat: 'no-repeat',
-                        backgroundPosition: 'center center',
-                    }}
-                ></div>
-            );
+            return <ImageCrossFade src={img.src} />;
         }
         default: {
             console.warn('Unreachable!');
@@ -138,3 +116,52 @@ export function BackgroundConsumer({ browser }) {
         }
     }
 }
+
+/**
+ * @param {object} props
+ * @param {string} props.src
+ */
+function ImageCrossFade({ src }) {
+    /**
+     * Proxy the image source, so that we can keep the old
+     * image around whilst the new one is loading.
+     */
+    const [stable, setStable] = useState(src);
+    /**
+     * Trigger the animation:
+     *
+     * NOTE: this animation is deliberately NOT done purely with CSS-triggered state.
+     * Whilst debugging in WebKit, I found the technique below to be 100% reliable
+     * in terms of fading a new image over the top of an existing one.
+     *
+     * If you find a better way, please test in webkit-based browsers
+     */
+    return (
+        <Fragment>
+            <img src={stable} class={styles.root} style={{ display: src === stable ? 'none' : 'block' }} />
+            <img
+                src={src}
+                class={cn(styles.root, styles.over)}
+                onLoad={(e) => {
+                    const elem = /** @type {HTMLImageElement} */ (e.target);
+
+                    // HACK: This is what I needed to force, to get 100% predictability. 🤷
+                    elem.style.opacity = '0';
+
+                    const anim = elem.animate([{ opacity: '0' }, { opacity: '1' }], {
+                        duration: 250,
+                        iterations: 1,
+                        easing: 'ease-in-out',
+                        fill: 'both',
+                    });
+
+                    // when the fade completes, we want to reset the stable `src`.
+                    // This allows the image underneath to be updated but also allows us to un-mount the fader on top.
+                    anim.onfinish = () => {
+                        setStable(src);
+                    };
+                }}
+            />
+        </Fragment>
+    );
+}
diff --git a/special-pages/pages/new-tab/app/components/BackgroundReceiver.module.css b/special-pages/pages/new-tab/app/components/BackgroundReceiver.module.css
index 13b48b58e..9060ff8b1 100644
--- a/special-pages/pages/new-tab/app/components/BackgroundReceiver.module.css
+++ b/special-pages/pages/new-tab/app/components/BackgroundReceiver.module.css
@@ -4,12 +4,12 @@
     inset: 0;
     width: 100vw;
     height: 100vh;
+    object-fit: cover;
     pointer-events: none;
 
     &[data-animate="true"] {
-        transition: all .3s ease-in-out;
+        transition: background .25s ease-in-out;
     }
-
     &[data-background-kind="default"][data-theme=dark] {
         background: var(--default-dark-bg);
     }
@@ -17,3 +17,10 @@
         background: var(--default-light-bg);
     }
 }
+
+.under {
+    opacity: 1;
+}
+.over {
+    opacity: 0;
+}
diff --git a/special-pages/pages/new-tab/app/components/Components.jsx b/special-pages/pages/new-tab/app/components/Components.jsx
index af93222f3..053f2fb10 100644
--- a/special-pages/pages/new-tab/app/components/Components.jsx
+++ b/special-pages/pages/new-tab/app/components/Components.jsx
@@ -4,6 +4,7 @@ import { mainExamples, otherExamples } from './Examples.jsx';
 import { useThemes } from '../customizer/themes.js';
 import { useSignal } from '@preact/signals';
 import { BackgroundConsumer } from './BackgroundProvider.js';
+import { CustomizerThemesContext } from '../customizer/CustomizerProvider.js';
 const url = new URL(window.location.href);
 
 const list = {
@@ -32,18 +33,20 @@ export function Components() {
     const { main, browser } = useThemes(dataSignal);
 
     return (
-        <div class={styles.main} data-main-scroller data-theme={main}>
-            <BackgroundConsumer browser={browser} />
-            <div data-content-tube class={styles.contentTube}>
-                {isolated && <Isolated entries={filtered} e2e={e2e} />}
-                {!isolated && (
-                    <Fragment>
-                        <DebugBar id={ids[0]} ids={ids} entries={entries} />
-                        <Stage entries={/** @type {any} */ (filtered)} />
-                    </Fragment>
-                )}
+        <CustomizerThemesContext.Provider value={{ main, browser }}>
+            <div class={styles.main} data-main-scroller data-theme={main}>
+                <BackgroundConsumer browser={browser} />
+                <div data-content-tube class={styles.contentTube}>
+                    {isolated && <Isolated entries={filtered} e2e={e2e} />}
+                    {!isolated && (
+                        <Fragment>
+                            <DebugBar id={ids[0]} ids={ids} entries={entries} />
+                            <Stage entries={/** @type {any} */ (filtered)} />
+                        </Fragment>
+                    )}
+                </div>
             </div>
-        </div>
+        </CustomizerThemesContext.Provider>
     );
 }
 
diff --git a/special-pages/pages/new-tab/app/components/DismissButton.jsx b/special-pages/pages/new-tab/app/components/DismissButton.jsx
index 6c746cd84..81b10259a 100644
--- a/special-pages/pages/new-tab/app/components/DismissButton.jsx
+++ b/special-pages/pages/new-tab/app/components/DismissButton.jsx
@@ -8,12 +8,13 @@ import styles from './DismissButton.module.css';
  * @param {object} props
  * @param {string} [props.className]
  * @param {() => void} [props.onClick]
+ * @param {import("preact").ComponentProps<"button"> & Record<string, string>} [props.buttonProps]
  */
-export function DismissButton({ className, onClick }) {
+export function DismissButton({ className, onClick, buttonProps = {} }) {
     const { t } = useTypedTranslation();
 
     return (
-        <button class={cn(styles.btn, className)} onClick={onClick} aria-label={t('ntp_dismiss')} data-testid="dismissBtn">
+        <button class={cn(styles.btn, className)} onClick={onClick} aria-label={t('ntp_dismiss')} data-testid="dismissBtn" {...buttonProps}>
             <Cross />
         </button>
     );
diff --git a/special-pages/pages/new-tab/app/components/DismissButton.module.css b/special-pages/pages/new-tab/app/components/DismissButton.module.css
index 4cb07aed2..dcadd9078 100644
--- a/special-pages/pages/new-tab/app/components/DismissButton.module.css
+++ b/special-pages/pages/new-tab/app/components/DismissButton.module.css
@@ -12,6 +12,13 @@
     border-radius: 50%;
     transition: all .3s;
 
+    svg {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translateX(-50%) translateY(-50%);
+    }
+
     &:hover {
         background-color: var(--color-black-at-9);
         cursor: pointer;
diff --git a/special-pages/pages/new-tab/app/customizer/components/BackgroundSection.js b/special-pages/pages/new-tab/app/customizer/components/BackgroundSection.js
index badce3333..6c2d34e83 100644
--- a/special-pages/pages/new-tab/app/customizer/components/BackgroundSection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/BackgroundSection.js
@@ -42,40 +42,33 @@ export function BackgroundSection({ data, onNav, onUpload, select }) {
     }
 
     return (
-        <div class={styles.section}>
-            <h3 class={styles.sectionTitle}>Background</h3>
-            <ul class={cn(styles.sectionBody, styles.bgList)} role="radiogroup">
-                <li class={styles.bgListItem}>
-                    <DefaultPanel
-                        checked={data.value.background.kind === 'default'}
-                        onClick={() => select({ background: { kind: 'default' } })}
-                    />
-                </li>
-                <li class={styles.bgListItem}>
-                    <ColorPanel
-                        checked={data.value.background.kind === 'color' || data.value.background.kind === 'hex'}
-                        color={displayColor}
-                        onClick={() => onNav('color')}
-                    />
-                </li>
-                <li class={styles.bgListItem}>
-                    <GradientPanel
-                        checked={data.value.background.kind === 'gradient'}
-                        gradient={gradient}
-                        onClick={() => onNav('gradient')}
-                    />
-                </li>
-                <li class={styles.bgListItem}>
-                    <BackgroundImagePanel
-                        checked={data.value.background.kind === 'userImage'}
-                        onClick={() => onNav('image')}
-                        data={data}
-                        upload={onUpload}
-                        browserTheme={browser}
-                    />
-                </li>
-            </ul>
-        </div>
+        <ul class={cn(styles.bgList)} role="radiogroup">
+            <li class={styles.bgListItem}>
+                <DefaultPanel
+                    checked={data.value.background.kind === 'default'}
+                    onClick={() => select({ background: { kind: 'default' } })}
+                />
+            </li>
+            <li class={styles.bgListItem}>
+                <ColorPanel
+                    checked={data.value.background.kind === 'color' || data.value.background.kind === 'hex'}
+                    color={displayColor}
+                    onClick={() => onNav('color')}
+                />
+            </li>
+            <li class={styles.bgListItem}>
+                <GradientPanel checked={data.value.background.kind === 'gradient'} gradient={gradient} onClick={() => onNav('gradient')} />
+            </li>
+            <li class={styles.bgListItem}>
+                <BackgroundImagePanel
+                    checked={data.value.background.kind === 'userImage'}
+                    onClick={() => onNav('image')}
+                    data={data}
+                    upload={onUpload}
+                    browserTheme={browser}
+                />
+            </li>
+        </ul>
     );
 }
 
diff --git a/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.js b/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.js
index 52b61ec5d..8f4c09b25 100644
--- a/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.js
@@ -1,4 +1,4 @@
-import styles from './CustomizerDrawerInner.module.css';
+import styles from './BrowserThemeSection.module.css';
 import cn from 'classnames';
 import { h } from 'preact';
 import { useComputed } from '@preact/signals';
@@ -11,49 +11,46 @@ import { useComputed } from '@preact/signals';
 export function BrowserThemeSection(props) {
     const current = useComputed(() => props.data.value.theme);
     return (
-        <div class={styles.section}>
-            <h3 class={styles.sectionTitle}>Browser Theme</h3>
-            <ul class={cn(styles.sectionBody, styles.themeList)}>
-                <li class={styles.themeItem}>
-                    <button
-                        class={styles.themeButton}
-                        role="radio"
-                        type="button"
-                        aria-checked={current.value === 'light'}
-                        tabindex={0}
-                        onClick={() => props.setTheme({ theme: 'light' })}
-                    >
-                        <span class="sr-only">Select light theme</span>
-                    </button>
-                    Light
-                </li>
-                <li class={styles.themeItem}>
-                    <button
-                        class={styles.themeButton}
-                        role="radio"
-                        type="button"
-                        aria-checked={current.value === 'dark'}
-                        tabindex={0}
-                        onClick={() => props.setTheme({ theme: 'dark' })}
-                    >
-                        <span className="sr-only">Select dark theme</span>
-                    </button>
-                    Dark
-                </li>
-                <li class={styles.themeItem}>
-                    <button
-                        class={styles.themeButton}
-                        role="radio"
-                        type="button"
-                        aria-checked={current.value === 'system'}
-                        tabindex={0}
-                        onClick={() => props.setTheme({ theme: 'system' })}
-                    >
-                        <span className="sr-only">Select system theme</span>
-                    </button>
-                    System
-                </li>
-            </ul>
-        </div>
+        <ul class={styles.themeList}>
+            <li class={styles.themeItem}>
+                <button
+                    class={cn(styles.themeButton, styles.themeButtonLight)}
+                    role="radio"
+                    type="button"
+                    aria-checked={current.value === 'light'}
+                    tabindex={0}
+                    onClick={() => props.setTheme({ theme: 'light' })}
+                >
+                    <span class="sr-only">Select light theme</span>
+                </button>
+                Light
+            </li>
+            <li class={styles.themeItem}>
+                <button
+                    class={cn(styles.themeButton, styles.themeButtonDark)}
+                    role="radio"
+                    type="button"
+                    aria-checked={current.value === 'dark'}
+                    tabindex={0}
+                    onClick={() => props.setTheme({ theme: 'dark' })}
+                >
+                    <span class="sr-only">Select dark theme</span>
+                </button>
+                Dark
+            </li>
+            <li class={styles.themeItem}>
+                <button
+                    class={cn(styles.themeButton, styles.themeButtonSystem)}
+                    role="radio"
+                    type="button"
+                    aria-checked={current.value === 'system'}
+                    tabindex={0}
+                    onClick={() => props.setTheme({ theme: 'system' })}
+                >
+                    <span class="sr-only">Select system theme</span>
+                </button>
+                System
+            </li>
+        </ul>
     );
 }
diff --git a/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.module.css b/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.module.css
new file mode 100644
index 000000000..9a9e69ec7
--- /dev/null
+++ b/special-pages/pages/new-tab/app/customizer/components/BrowserThemeSection.module.css
@@ -0,0 +1,72 @@
+.themeList {
+    display: flex;
+    gap: 18px;
+    --chip-size: 42px;
+    --chip-size-half: calc(var(--chip-size) / 2);
+}
+.themeItem {
+    display: grid;
+    justify-items: center;
+    grid-row-gap: 4px;
+}
+.themeButton {
+    display: block;
+    width: var(--chip-size);
+    height: var(--chip-size);
+    border-radius: 50%;
+
+    &[aria-checked="true"] {
+        outline: 2px solid var(--ntp-color-primary);
+        outline-offset: 2px;
+    }
+
+    &:focus-visible {
+        outline: 2px solid var(--ntp-focus-outline-color);
+        outline-offset: 2px;
+    }
+
+    &:active {
+        opacity: .9;
+    }
+}
+
+.themeButtonLight {
+    border: 1px solid var(--color-black-at-12);
+    background: white;
+}
+
+.themeButtonDark {
+    border: 1px solid var(--color-white-at-9);
+    background: var(--color-gray-80);
+}
+
+.themeButtonSystem {
+    position: relative;
+    border: none;
+    background: transparent;
+    width: var(--chip-size);
+    height: var(--chip-size);
+    display: flex;
+    padding: 0;
+    margin: 0;
+
+    &:before {
+        content: " ";
+        display: block;
+        width: var(--chip-size-half);
+        height: var(--chip-size);
+        border-radius: 0 var(--chip-size-half) var(--chip-size-half) 0;
+        transform: rotate(180deg);
+        border: 1px solid var(--color-black-at-12);
+        background: white;
+    }
+    &:after {
+        content: " ";
+        display: block;
+        width: var(--chip-size-half);
+        height: var(--chip-size);
+        border-radius: 0 var(--chip-size-half) var(--chip-size-half) 0;
+        border: 1px solid var(--color-white-at-9);
+        background: var(--color-gray-80);
+    }
+}
\ No newline at end of file
diff --git a/special-pages/pages/new-tab/app/customizer/components/ColorSelection.js b/special-pages/pages/new-tab/app/customizer/components/ColorSelection.js
index 0f40712eb..36614cf1a 100644
--- a/special-pages/pages/new-tab/app/customizer/components/ColorSelection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/ColorSelection.js
@@ -6,6 +6,7 @@ import styles from './CustomizerDrawerInner.module.css';
 import { BackChevron, Picker } from '../../components/Icons.js';
 import { useComputed } from '@preact/signals';
 import { detectThemeFromHex } from '../utils.js';
+import { InlineError } from '../../InlineError.js';
 
 /**
  * @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem, CustomizerData, PredefinedColor, BackgroundData } from '../../../types/new-tab.js'
@@ -18,29 +19,17 @@ import { detectThemeFromHex } from '../utils.js';
  * @param {() => void} props.back
  */
 export function ColorSelection({ data, select, back }) {
-    console.log('    RENDER:ColorSelection?');
-
     function onClick(event) {
         let target = /** @type {HTMLElement|null} */ (event.target);
-        while (target && target !== event.currentTarget) {
-            if (target.getAttribute('role') === 'radio') {
-                event.preventDefault();
-                event.stopImmediatePropagation();
-                if (target.getAttribute('aria-checked') === 'false') {
-                    if (target.dataset.key) {
-                        const value = /** @type {PredefinedColor} */ (target.dataset.key);
-                        select({ background: { kind: 'color', value } });
-                    } else {
-                        console.warn('missing dataset.key');
-                    }
-                } else {
-                    console.log('ignoring click on selected color');
-                }
-                break;
-            } else {
-                target = target.parentElement;
-            }
+        const selector = `[role="radio"][aria-checked="false"][data-value]`;
+        if (!target?.matches(selector)) {
+            target = /** @type {HTMLElement|null} */ (target?.closest(selector));
         }
+        if (!target) return;
+        const value = /** @type {PredefinedColor} */ (target.dataset.value);
+        // todo: report exception?
+        if (!(value in values.colors)) return console.warn('could not select color', value);
+        select({ background: { kind: 'color', value } });
     }
 
     return (
@@ -50,10 +39,12 @@ export function ColorSelection({ data, select, back }) {
                 Solid Colors
             </button>
             <div class={styles.sectionBody}>
-                <div class={cn(styles.bgList)} role="radiogroup" onClick={onClick}>
-                    <PickerPanel data={data} select={select} />
-                    <ColorGrid data={data} />
-                </div>
+                <InlineError named={'ColorGrid'}>
+                    <div class={cn(styles.bgList)} role="radiogroup" onClick={onClick}>
+                        <PickerPanel data={data} select={select} />
+                        <ColorGrid data={data} />
+                    </div>
+                </InlineError>
             </div>
         </div>
     );
@@ -79,7 +70,7 @@ function ColorGrid({ data }) {
                             style={{ background: entry.hex }}
                             role="radio"
                             aria-checked={key === selected.value}
-                            data-key={key}
+                            data-value={key}
                         >
                             <span class="sr-only">Select {key}</span>
                         </button>
@@ -140,7 +131,7 @@ function PickerPanel({ data, select }) {
                     }
                 }}
             />
-            <span class={cn(styles.colorInputIcon, styles.dynamicIconColor)} data-color-mode={modeSelected}>
+            <span class={cn(styles.colorInputIcon, styles.dynamicPickerIconColor)} data-color-mode={modeSelected}>
                 <Picker />
             </span>
         </div>
diff --git a/special-pages/pages/new-tab/app/customizer/components/Customizer.examples.js b/special-pages/pages/new-tab/app/customizer/components/Customizer.examples.js
index 418ff72c1..06e727266 100644
--- a/special-pages/pages/new-tab/app/customizer/components/Customizer.examples.js
+++ b/special-pages/pages/new-tab/app/customizer/components/Customizer.examples.js
@@ -1,7 +1,7 @@
-import { h, Fragment } from 'preact';
+import { h } from 'preact';
 import { noop } from '../../utils.js';
 import { CustomizerButton } from './Customizer.js';
-import { VisibilityMenu } from './VisibilityMenu.js';
+import { EmbeddedVisibilityMenu, VisibilityMenu } from './VisibilityMenu.js';
 import { BackgroundSection } from './BackgroundSection.js';
 import { ColorSelection } from './ColorSelection.js';
 import { GradientSelection } from './GradientSelection.js';
@@ -64,13 +64,32 @@ export const customizerExamples = {
     },
     'customizer-menu': {
         factory: () => (
-            <Fragment>
-                <div>
-                    <CustomizerButton isOpen={true} />
-                </div>
+            <MaxContent>
+                <CustomizerButton isOpen={true} />
+                <br />
+                <VisibilityMenu
+                    rows={[
+                        {
+                            id: 'favorites',
+                            title: 'Favorites',
+                            icon: 'star',
+                            toggle: noop('toggle favorites'),
+                            visibility: 'hidden',
+                            index: 0,
+                        },
+                        {
+                            id: 'privacyStats',
+                            title: 'Privacy Stats',
+                            icon: 'shield',
+                            toggle: noop('toggle favorites'),
+                            visibility: 'visible',
+                            index: 1,
+                        },
+                    ]}
+                />
                 <br />
-                <MaxContent>
-                    <VisibilityMenu
+                <div style="width: 206px; border: 1px dotted black">
+                    <EmbeddedVisibilityMenu
                         rows={[
                             {
                                 id: 'favorites',
@@ -90,8 +109,8 @@ export const customizerExamples = {
                             },
                         ]}
                     />
-                </MaxContent>
-            </Fragment>
+                </div>
+            </MaxContent>
         ),
     },
 };
diff --git a/special-pages/pages/new-tab/app/customizer/components/Customizer.js b/special-pages/pages/new-tab/app/customizer/components/Customizer.js
index 1abddb9f9..42626b7e8 100644
--- a/special-pages/pages/new-tab/app/customizer/components/Customizer.js
+++ b/special-pages/pages/new-tab/app/customizer/components/Customizer.js
@@ -46,7 +46,7 @@ export function Customizer() {
             <CustomizerButton buttonId={BUTTON_ID} menuId={MENU_ID} toggleMenu={toggleMenu} buttonRef={buttonRef} isOpen={isOpen} />
             <div id={MENU_ID} class={cn(styles.dropdownMenu, { [styles.show]: isOpen })} aria-labelledby={BUTTON_ID}>
                 <VisibilityMenuPopover>
-                    <VisibilityMenu rows={rowData} variant={'popover'} />
+                    <VisibilityMenu rows={rowData} />
                 </VisibilityMenuPopover>
             </div>
         </div>
diff --git a/special-pages/pages/new-tab/app/customizer/components/Customizer.module.css b/special-pages/pages/new-tab/app/customizer/components/Customizer.module.css
index b7b4c0f5a..e4f64cfc3 100644
--- a/special-pages/pages/new-tab/app/customizer/components/Customizer.module.css
+++ b/special-pages/pages/new-tab/app/customizer/components/Customizer.module.css
@@ -21,8 +21,8 @@
 
 /** todo: is this a re-usable button, yet? */
 .customizeButton {
-    background-color: transparent;
-    border: 1px solid var(--color-black-at-9);
+    background-color: var(--ntp-surface-background-color);
+    border: 1px solid var(--ntp-surface-border-color);
     border-radius: var(--border-radius-sm);
     padding: var(--sp-2) var(--sp-3);
     cursor: pointer;
diff --git a/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.js b/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.js
index c981cecc7..d7ea04479 100644
--- a/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.js
+++ b/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.js
@@ -1,4 +1,5 @@
-import { h } from 'preact';
+import { Fragment, h } from 'preact';
+import cn from 'classnames';
 import styles from './CustomizerDrawerInner.module.css';
 import { useDrawerControls } from '../../components/Drawer.js';
 import { BackgroundSection } from './BackgroundSection.js';
@@ -6,8 +7,12 @@ import { BrowserThemeSection } from './BrowserThemeSection.js';
 import { VisibilityMenuSection } from './VisibilityMenuSection.js';
 import { ColorSelection } from './ColorSelection.js';
 import { GradientSelection } from './GradientSelection.js';
-import { useSignal } from '@preact/signals';
+import { batch, useSignal } from '@preact/signals';
 import { ImageSelection } from './ImageSelection.js';
+import { BorderedSection, CustomizerSection } from './CustomizerSection.js';
+import { SettingsLink } from './SettingsLink.js';
+import { DismissButton } from '../../components/DismissButton.jsx';
+import { InlineError } from '../../InlineError.js';
 
 /**
  * @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem, CustomizerData, BackgroundData } from '../../../types/new-tab.js'
@@ -23,27 +28,98 @@ import { ImageSelection } from './ImageSelection.js';
  */
 export function CustomizerDrawerInner({ data, select, onUpload, setTheme, deleteImage }) {
     const { close } = useDrawerControls();
-    const state = useSignal('home');
-    function onNav(nav) {
-        state.value = nav;
-    }
-    function back() {
-        state.value = 'home';
-    }
     return (
         <div class={styles.root}>
-            <header class={styles.header}>
+            <header class={cn(styles.header, styles.internal)}>
                 <h2>Customize</h2>
-                <button onClick={close}>Close</button>
+                <DismissButton
+                    onClick={close}
+                    className={styles.closeBtn}
+                    buttonProps={{
+                        'aria-label': 'Close',
+                    }}
+                />
             </header>
-            {state.value === 'home' && <BackgroundSection data={data} onNav={onNav} onUpload={onUpload} select={select} />}
-            {state.value === 'home' && <BrowserThemeSection data={data} setTheme={setTheme} />}
-            {state.value === 'home' && <VisibilityMenuSection />}
-            {state.value === 'color' && <ColorSelection data={data} select={select} back={back} />}
-            {state.value === 'gradient' && <GradientSelection data={data} select={select} back={back} />}
-            {state.value === 'image' && (
-                <ImageSelection data={data} select={select} back={back} onUpload={onUpload} deleteImage={deleteImage} />
-            )}
+            <InlineError
+                named="Customizer Drawer"
+                fallback={(message) => (
+                    <div class={styles.internal}>
+                        <p>{message}</p>
+                    </div>
+                )}
+            >
+                <TwoCol
+                    left={({ push }) => (
+                        <div class={styles.sections}>
+                            <CustomizerSection title={'Background'}>
+                                <BackgroundSection data={data} onNav={push} onUpload={onUpload} select={select} />
+                            </CustomizerSection>
+                            <CustomizerSection title={'Browser Theme'}>
+                                <BrowserThemeSection data={data} setTheme={setTheme} />
+                            </CustomizerSection>
+                            <CustomizerSection title={'Sections'}>
+                                <VisibilityMenuSection />
+                            </CustomizerSection>
+                            <BorderedSection>
+                                <SettingsLink />
+                            </BorderedSection>
+                        </div>
+                    )}
+                    right={({ id, pop }) => (
+                        <Fragment>
+                            {id === 'color' && <ColorSelection data={data} select={select} back={pop} />}
+                            {id === 'gradient' && <GradientSelection data={data} select={select} back={pop} />}
+                            {id === 'image' && (
+                                <ImageSelection data={data} select={select} back={pop} onUpload={onUpload} deleteImage={deleteImage} />
+                            )}
+                        </Fragment>
+                    )}
+                />
+            </InlineError>
+        </div>
+    );
+}
+
+/**
+ * @param {object} props
+ * @param {(args: {push: (id: string) => void}) => import('preact').ComponentChild} props.left
+ * @param {(args: {id: string, pop: () => void}) => import('preact').ComponentChild} props.right
+ */
+function TwoCol({ left, right }) {
+    const visibleScreen = useSignal('home');
+    const renderedScreen = useSignal('home');
+    const col1 = useSignal(true);
+
+    /**
+     * @param {string} id
+     */
+    function push(id) {
+        visibleScreen.value = id;
+        requestAnimationFrame(() => {
+            renderedScreen.value = id;
+        });
+    }
+
+    function pop() {
+        batch(() => {
+            col1.value = true;
+            visibleScreen.value = 'home';
+        });
+    }
+
+    function transitionEnded() {
+        if (visibleScreen.value !== 'home') {
+            col1.value = false;
+        }
+        renderedScreen.value = visibleScreen.value;
+    }
+
+    return (
+        <div class={styles.colwrap}>
+            <div class={styles.cols} data-sub={visibleScreen} onTransitionEnd={transitionEnded}>
+                <div class={cn(styles.col, styles.col1)}>{col1.value && left({ push })}</div>
+                <div class={cn(styles.col, styles.col2)}>{renderedScreen.value !== 'home' && right({ id: renderedScreen.value, pop })}</div>
+            </div>
         </div>
     );
 }
diff --git a/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.module.css b/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.module.css
index 2a0b116c3..cd58513d8 100644
--- a/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.module.css
+++ b/special-pages/pages/new-tab/app/customizer/components/CustomizerDrawerInner.module.css
@@ -3,20 +3,48 @@
     animation-fill-mode: forwards;
     animation-timing-function: ease-in-out;
     animation-duration: .1s;
-    padding-block: 1rem;
-    display: grid;
-    grid-auto-rows: max-content;
-    grid-row-gap: var(--sp-4);
     font-size: var(--small-label-font-size);
     line-height: var(--small-label-line-height);
     font-weight: var(--small-label-font-weight);
 }
 
+.sections {
+    display: grid;
+    grid-auto-rows: max-content;
+    grid-row-gap: var(--sp-9);
+}
+
 .header {
     display: flex;
     justify-content: space-between;
+    align-items: center;
 }
 
+.internal {
+    padding-top: 1rem;
+    padding-left: 1rem;
+    padding-right: calc(1rem - var(--ntp-drawer-scroll-width));
+}
+
+.closeBtn {
+    width: 24px;
+    height: 24px;
+    position: relative;
+
+    color: var(--color-black-at-84);
+    background-color: var(--color-black-at-9);
+    &:hover {
+        background-color: var(--color-black-at-18);
+    }
+
+    [data-theme="dark"] & {
+        color: var(--color-white-at-84);
+        background-color: var(--color-white-at-9);
+        &:hover {
+            background-color: var(--color-white-at-18);
+        }
+    }
+}
 .backBtn {
     background: none;
     border: none;
@@ -41,6 +69,10 @@
 .section {
     width: 100%;
 }
+.borderedSection {
+    border-top: 1px solid var(--ntp-surface-border-color);
+    padding-top: calc(18 * var(--px-in-rem))
+}
 .sectionBody {
     margin-top: 16px;
 }
@@ -55,9 +87,10 @@
     grid-template-rows: max-content max-content;
     grid-gap: 12px;
 }
+
 .bgListItem {
     display: grid;
-    grid-row-gap: 6px;
+    grid-row-gap: 4px;
     white-space: nowrap;
     position: relative;
 
@@ -69,39 +102,50 @@
 }
 .bgPanel {
     display: grid;
-    aspect-ratio: 16/10;
+    aspect-ratio: 16/10.6;
     border-radius: 4px;
     align-items: center;
     justify-content: center;
     border: none;
     outline: none;
+    box-shadow: 0 0 0 1px var(--ntp-surface-border-color) inset;
 
     &[aria-checked="true"] {
-        outline: 3px solid var(--ntp-color-primary);
+        outline: 2px solid var(--ntp-color-primary);
         outline-offset: 2px;
     }
     &:focus-visible {
-        outline: 3px solid var(--ntp-focus-outline-color);
+        outline: 2px solid var(--ntp-focus-outline-color);
         outline-offset: 2px;
     }
+
     &:active {
         opacity: .9;
     }
 }
 
 .bgPanelEmpty {
-    border: 1px solid var(--ntp-surface-border-color);
     background-color: rgba(0, 0, 0, 0.03);
     [data-theme=dark] & {
         background-color: rgba(255, 255, 255, 0.06);
     }
 }
 
-.bgPanelOutlined {
-    border: 1px solid var(--ntp-surface-border-color);
-    background-color: transparent;
-}
 .dynamicIconColor {
+    &[data-color-mode="light"] {
+        color: black;
+        svg path {
+            fill-opacity: 0.6;
+        }
+    }
+    &[data-color-mode="dark"] {
+        color: white;
+        svg path {
+            fill-opacity: 0.6;
+        }
+    }
+}
+.dynamicPickerIconColor {
     &[data-color-mode="light"] {
         color: black;
         svg path {
@@ -124,34 +168,6 @@
         pointer-events: none;
     }
 }
-.themeList {
-
-    display: flex;
-    gap: 18px;
-}
-.themeItem {
-    display: grid;
-    justify-items: center;
-    grid-row-gap: 6px;
-}
-.themeButton {
-    display: block;
-    width: 42px;
-    height: 42px;
-    border-radius: 50%;
-    border: 1px solid #0000001F;
-    &[aria-checked="true"] {
-        outline: 3px solid var(--ntp-color-primary);
-        outline-offset: 2px;
-    }
-    &:focus-visible {
-        outline: 3px solid var(--ntp-focus-outline-color);
-        outline-offset: 2px;
-    }
-    &:active {
-        opacity: .9;
-    }
-}
 
 @keyframes fade-in {
     0% {
@@ -170,4 +186,77 @@
     position: absolute;
     top: 4px;
     right: 4px;
+
+    &[data-color-mode="light"] {
+        background-color: var(--color-black-at-60);
+        color: white;
+        &:hover {
+            background-color: black;
+        }
+    }
+
+    &[data-color-mode="dark"] {
+        background-color: var(--color-white-at-60);
+        color: black;
+        &:hover {
+            background-color: white;
+        }
+    }
+
+    &:focus-visible {
+        opacity: 1;
+    }
 }
+
+.colwrap {
+    width: 100%;
+    overflow: hidden;
+}
+.cols {
+    display: flex;
+    transition: transform .3s ease-in-out;
+    > * {
+        flex-shrink: 0;
+        width: 100%;
+    }
+    &:not([data-sub=home]) {
+        transform: translateX(-100%);
+        .col1 {
+            opacity: 0;
+            visibility: hidden;
+        }
+    }
+    &[data-sub=home] {
+        .col2 {
+            opacity: 0;
+            visibility: hidden;
+        }
+    }
+}
+.col {
+    padding: 1rem;
+    padding-right: calc(1rem - var(--ntp-drawer-scroll-width));
+    padding-top: 24px;
+    opacity: 1;
+    visibility: visible;
+    transition: opacity .3s linear, visibility .3s linear;
+}
+
+.col1 {}
+.col2 {}
+
+.settingsLink {
+    display: flex;
+    font-size: 13px;
+    justify-content: space-between;
+    align-items: center;
+    text-decoration: none;
+    color: var(--ntp-color-primary);
+
+    &:focus {
+        outline: none;
+    }
+    &:focus-visible {
+        text-decoration: underline;
+    }
+}
\ No newline at end of file
diff --git a/special-pages/pages/new-tab/app/customizer/components/CustomizerSection.js b/special-pages/pages/new-tab/app/customizer/components/CustomizerSection.js
new file mode 100644
index 000000000..4daf6a217
--- /dev/null
+++ b/special-pages/pages/new-tab/app/customizer/components/CustomizerSection.js
@@ -0,0 +1,30 @@
+import styles from './CustomizerDrawerInner.module.css';
+import { Fragment, h } from 'preact';
+import cn from 'classnames';
+
+/**
+ * @param {object} props
+ * @param {import("preact").ComponentChild | null} props.title
+ * @param {import("preact").ComponentChild} props.children
+ */
+export function CustomizerSection({ title, children }) {
+    return (
+        <div className={styles.section}>
+            {title === null && children}
+            {title !== null && (
+                <Fragment>
+                    <h3 className={styles.sectionTitle}>{title}</h3>
+                    <div className={styles.sectionBody}>{children}</div>
+                </Fragment>
+            )}
+        </div>
+    );
+}
+
+/**
+ * @param {object} props
+ * @param {import("preact").ComponentChild} props.children
+ */
+export function BorderedSection({ children }) {
+    return <div class={cn(styles.section, styles.borderedSection)}>{children}</div>;
+}
diff --git a/special-pages/pages/new-tab/app/customizer/components/GradientSelection.js b/special-pages/pages/new-tab/app/customizer/components/GradientSelection.js
index 410e26c7c..8e2d65aae 100644
--- a/special-pages/pages/new-tab/app/customizer/components/GradientSelection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/GradientSelection.js
@@ -5,6 +5,7 @@ import { values } from '../values.js';
 import styles from './CustomizerDrawerInner.module.css';
 import { useComputed } from '@preact/signals';
 import { BackChevron } from '../../components/Icons.js';
+import { InlineError } from '../../InlineError.js';
 
 /**
  * @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem, BackgroundData, CustomizerData, PredefinedGradient } from '../../../types/new-tab.js'
@@ -21,25 +22,15 @@ export function GradientSelection({ data, select, back }) {
 
     function onClick(event) {
         let target = /** @type {HTMLElement|null} */ (event.target);
-        while (target && target !== event.currentTarget) {
-            if (target.getAttribute('role') === 'radio') {
-                event.preventDefault();
-                event.stopImmediatePropagation();
-                if (target.getAttribute('aria-checked') === 'false') {
-                    if (target.dataset.key) {
-                        const value = /** @type {PredefinedGradient} */ (target.dataset.key);
-                        select({ background: { kind: 'gradient', value } });
-                    } else {
-                        console.warn('missing dataset.key');
-                    }
-                } else {
-                    console.log('ignoring click on selected color');
-                }
-                break;
-            } else {
-                target = target.parentElement;
-            }
+        const selector = `[role="radio"][aria-checked="false"][data-value]`;
+        if (!target?.matches(selector)) {
+            target = /** @type {HTMLElement|null} */ (target?.closest(selector));
         }
+        if (!target) return;
+        const value = /** @type {PredefinedGradient} */ (target.dataset.value);
+        // todo: report exception?
+        if (!(value in values.gradients)) return console.warn('could not select gradient', value);
+        select({ background: { kind: 'gradient', value } });
     }
 
     return (
@@ -49,7 +40,9 @@ export function GradientSelection({ data, select, back }) {
                 Gradients
             </button>
             <div className={styles.sectionBody} onClick={onClick}>
-                <GradientGrid data={data} />
+                <InlineError named={'GradientSelection'}>
+                    <GradientGrid data={data} />
+                </InlineError>
             </div>
         </div>
     );
@@ -73,8 +66,9 @@ function GradientGrid({ data }) {
                             tabIndex={0}
                             role="radio"
                             aria-checked={key === selected.value}
-                            data-key={key}
+                            data-value={key}
                             style={{
+                                backgroundColor: entry.fallback,
                                 backgroundImage: `url(${entry.path})`,
                                 backgroundSize: 'cover',
                                 backgroundRepeat: 'no-repeat',
diff --git a/special-pages/pages/new-tab/app/customizer/components/ImageSelection.js b/special-pages/pages/new-tab/app/customizer/components/ImageSelection.js
index dd8fe8ede..b3924de66 100644
--- a/special-pages/pages/new-tab/app/customizer/components/ImageSelection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/ImageSelection.js
@@ -4,9 +4,16 @@ import cn from 'classnames';
 import styles from './CustomizerDrawerInner.module.css';
 import { useComputed } from '@preact/signals';
 import { DismissButton } from '../../components/DismissButton.jsx';
-import { BackChevron } from '../../components/Icons.js';
+import { BackChevron, PlusIcon } from '../../components/Icons.js';
+import { useContext } from 'preact/hooks';
+import { CustomizerThemesContext } from '../CustomizerProvider.js';
+import { InlineError } from '../../InlineError.js';
+import { useTypedTranslationWith } from '../../types.js';
 
 /**
+ * @import enStrings from '../strings.json';
+ * @import ntpStrings from '../../strings.json';
+ * @typedef {enStrings & ntpStrings} strings
  * @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem, CustomizerData, BackgroundData, PredefinedGradient } from '../../../types/new-tab.js'
  */
 
@@ -19,32 +26,18 @@ import { BackChevron } from '../../components/Icons.js';
  * @param {(id: string) => void} props.deleteImage
  */
 export function ImageSelection({ data, select, back, onUpload, deleteImage }) {
-    // const gradient = values.gradients.gradient02;
-
+    const { t } = useTypedTranslationWith(/** @type {strings} */ ({}));
     function onClick(event) {
         let target = /** @type {HTMLElement|null} */ (event.target);
-        while (target && target !== event.currentTarget) {
-            if (target.getAttribute('role') === 'radio') {
-                event.preventDefault();
-                event.stopImmediatePropagation();
-                if (target.getAttribute('aria-checked') === 'false') {
-                    if (target.dataset.key) {
-                        const value = /** @type {string} */ (target.dataset.key);
-                        const match = data.value.userImages.find((i) => i.id === value);
-                        if (match) {
-                            select({ background: { kind: 'userImage', value: match } });
-                        }
-                    } else {
-                        console.warn('missing dataset.key');
-                    }
-                } else {
-                    console.log('ignoring click on selected color');
-                }
-                break;
-            } else {
-                target = target.parentElement;
-            }
+        const selector = `[role="radio"][aria-checked="false"][data-id]`;
+        if (!target?.matches(selector)) {
+            target = /** @type {HTMLElement|null} */ (target?.closest(selector));
         }
+        if (!target) return;
+        const value = /** @type {string} */ (target.dataset.id);
+        const match = data.value.userImages.find((i) => i.id === value);
+        if (!match) return console.warn('could not find matching image', value);
+        select({ background: { kind: 'userImage', value: match } });
     }
 
     return (
@@ -54,7 +47,12 @@ export function ImageSelection({ data, select, back, onUpload, deleteImage }) {
                 My Backgrounds
             </button>
             <div className={styles.sectionBody} onClick={onClick}>
-                <ImageGrid data={data} deleteImage={deleteImage} onUpload={onUpload} />
+                <InlineError named={'Image Selection'}>
+                    <ImageGrid data={data} deleteImage={deleteImage} onUpload={onUpload} />
+                </InlineError>
+            </div>
+            <div className={styles.sectionBody}>
+                <p>{t('customizer_image_privacy')}</p>
             </div>
         </div>
     );
@@ -67,13 +65,22 @@ export function ImageSelection({ data, select, back, onUpload, deleteImage }) {
  * @param {() => void} props.onUpload
  */
 function ImageGrid({ data, deleteImage, onUpload }) {
+    const { browser } = useContext(CustomizerThemesContext);
     const selected = useComputed(() => data.value.background.kind === 'userImage' && data.value.background.value.id);
     const entries = useComputed(() => {
         return data.value.userImages;
     });
+    const max = 8;
+    const diff = max - entries.value.length;
+    const placeholders = new Array(diff).fill(null);
+
     return (
         <ul className={cn(styles.bgList)}>
-            {entries.value.map((entry) => {
+            {entries.value.map((entry, index) => {
+                // eslint-disable-next-line no-labels,no-unused-labels
+                $INTEGRATION: (() => {
+                    if (entry.id === '__will_throw__') throw new Error('Simulated error');
+                })();
                 return (
                     <li className={styles.bgListItem} key={entry.id}>
                         <button
@@ -82,22 +89,41 @@ function ImageGrid({ data, deleteImage, onUpload }) {
                             tabIndex={0}
                             role="radio"
                             aria-checked={entry.id === selected.value}
-                            data-key={entry.id}
+                            data-id={entry.id}
                             style={{
                                 backgroundImage: `url(${entry.thumb})`,
                                 backgroundSize: 'cover',
                                 backgroundRepeat: 'no-repeat',
                             }}
-                        ></button>
-                        <DismissButton className={styles.deleteBtn} onClick={() => deleteImage(entry.id)} />
+                        >
+                            <span class="sr-only">Select image {index + 1}</span>
+                        </button>
+                        <DismissButton
+                            className={styles.deleteBtn}
+                            onClick={() => deleteImage(entry.id)}
+                            buttonProps={{
+                                'data-color-mode': String(entry.colorScheme),
+                                'aria-label': `Delete image ${index + 1}`,
+                            }}
+                        />
+                    </li>
+                );
+            })}
+            {placeholders.map((_, index) => {
+                return (
+                    <li className={styles.bgListItem} key={`placeholder-${diff}-${index}`}>
+                        <button
+                            type="button"
+                            onClick={onUpload}
+                            class={cn(styles.bgPanel, styles.bgPanelEmpty, styles.dynamicIconColor)}
+                            data-color-mode={browser}
+                        >
+                            <PlusIcon />
+                            <span class="sr-only">Add Background</span>
+                        </button>
                     </li>
                 );
             })}
-            <li className={styles.bgListItem}>
-                <button type="button" onClick={onUpload}>
-                    Add image
-                </button>
-            </li>
         </ul>
     );
 }
diff --git a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.js b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.js
index 8a7c374aa..51230a244 100644
--- a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.js
+++ b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.js
@@ -1,10 +1,13 @@
 import { h } from 'preact';
 import cn from 'classnames';
-import { useId } from 'preact/hooks';
+import { useId, useContext } from 'preact/hooks';
 
 import { DuckFoot, Shield } from '../../components/Icons.js';
 import styles from './VisibilityMenu.module.css';
 import { useTypedTranslation } from '../../types.js';
+import { Switch } from '../../../../../shared/components/Switch/Switch.js';
+import { usePlatformName } from '../../settings.provider.js';
+import { CustomizerThemesContext } from '../CustomizerProvider.js';
 
 /**
  * @import { Widgets, WidgetConfigItem } from '../../../types/new-tab.js'
@@ -17,13 +20,12 @@ import { useTypedTranslation } from '../../types.js';
  *
  * @param {object} props
  * @param {VisibilityRowData[]} props.rows
- * @param {'popover' | 'embedded'} [props.variant]
  */
-export function VisibilityMenu({ rows, variant = 'popover' }) {
+export function VisibilityMenu({ rows }) {
     const MENU_ID = useId();
 
     return (
-        <ul className={cn(styles.list, variant === 'embedded' && styles.embedded)}>
+        <ul className={cn(styles.list)}>
             {rows.map((row) => {
                 return (
                     <li key={row.id}>
@@ -61,6 +63,42 @@ export function VisibilityMenu({ rows, variant = 'popover' }) {
     );
 }
 
+/**
+ * @param {object} props
+ * @param {VisibilityRowData[]} props.rows
+ */
+export function EmbeddedVisibilityMenu({ rows }) {
+    const platformName = usePlatformName();
+    const { browser } = useContext(CustomizerThemesContext);
+    return (
+        <ul className={cn(styles.list, styles.embedded)}>
+            {rows.map((row) => {
+                return (
+                    <li key={row.id}>
+                        <div class={cn(styles.menuItemLabel, styles.menuItemLabelEmbedded)}>
+                            <span className={styles.svg}>
+                                {row.icon === 'shield' && <DuckFoot />}
+                                {row.icon === 'star' && <Shield />}
+                            </span>
+                            <span>{row.title ?? row.id}</span>
+                            <Switch
+                                theme={browser.value}
+                                platformName={platformName}
+                                checked={row.visibility === 'visible'}
+                                size="medium"
+                                onChecked={() => row.toggle?.(row.id)}
+                                onUnchecked={() => row.toggle?.(row.id)}
+                                ariaLabel={`Toggle ${row.title}`}
+                                pending={false}
+                            />
+                        </div>
+                    </li>
+                );
+            })}
+        </ul>
+    );
+}
+
 export function Heading() {
     const { t } = useTypedTranslation();
     return <h2 className="sr-only">{t('widgets_visibility_menu_title')}</h2>;
diff --git a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.module.css b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.module.css
index df78c99b9..dfaabd03b 100644
--- a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.module.css
+++ b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenu.module.css
@@ -25,6 +25,7 @@
 
 .embedded {
     font-size: var(--small-label-font-size);
+    gap: 12px;
 }
 
 .menuItemLabel {
@@ -37,8 +38,22 @@
     > * {
         min-width: 0;
     }
+
+    label {
+        margin-left: auto;
+    }
 }
 
+.menuItemLabelEmbedded {
+    white-space: normal;
+    gap: 6px;
+    height: auto;
+    > * {
+        min-width: auto;
+    }
+}
+
+
 .svg {
     flex-shrink: 0;
     width: 1rem;
diff --git a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenuSection.js b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenuSection.js
index 79e88fed7..ab0543386 100644
--- a/special-pages/pages/new-tab/app/customizer/components/VisibilityMenuSection.js
+++ b/special-pages/pages/new-tab/app/customizer/components/VisibilityMenuSection.js
@@ -1,7 +1,6 @@
 import { useLayoutEffect, useState } from 'preact/hooks';
 import { Customizer, getItems } from './Customizer.js';
-import styles from './CustomizerDrawerInner.module.css';
-import { VisibilityMenu } from './VisibilityMenu.js';
+import { EmbeddedVisibilityMenu } from './VisibilityMenu.js';
 import { h } from 'preact';
 
 export function VisibilityMenuSection() {
@@ -13,17 +12,12 @@ export function VisibilityMenuSection() {
         function handler() {
             setRowData(getItems());
         }
+
         window.addEventListener(Customizer.UPDATE_EVENT, handler);
         return () => {
             window.removeEventListener(Customizer.UPDATE_EVENT, handler);
         };
     }, []);
-    return (
-        <div class={styles.section}>
-            <h3 class={styles.sectionTitle}>Sections</h3>
-            <div class={styles.sectionBody}>
-                <VisibilityMenu rows={rowData} variant={'embedded'} />
-            </div>
-        </div>
-    );
+
+    return <EmbeddedVisibilityMenu rows={rowData} />;
 }
diff --git a/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.page.js b/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.page.js
index 0bde3dbc1..31640556f 100644
--- a/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.page.js
+++ b/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.page.js
@@ -34,8 +34,7 @@ export class CustomizerPage {
         const { page } = this.ntp;
         await page.locator('aside').getByRole('button', { name: 'Close' }).click();
         await expect(page.locator('aside')).toHaveAttribute('aria-hidden', 'true');
-        // todo: This will be added in a follow up
-        // await expect(page.locator('aside')).toHaveCSS('visibility', 'hidden');
+        await expect(page.locator('aside')).toHaveCSS('visibility', 'hidden');
     }
 
     async opensSettings() {
diff --git a/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.spec.js b/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.spec.js
index f498d2651..f901e3792 100644
--- a/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.spec.js
+++ b/special-pages/pages/new-tab/app/customizer/integration-tests/customizer.spec.js
@@ -203,7 +203,7 @@ test.describe('newtab customizer', () => {
         await cp.hasEmptyImagesPanel();
         await cp.acceptsImagesUpdate();
     });
-    test.skip('loads without images, and handles root-level exceptions', async ({ page }, workerInfo) => {
+    test('loads without images, and handles root-level exceptions', async ({ page }, workerInfo) => {
         const ntp = NewtabPage.create(page, workerInfo);
         const cp = new CustomizerPage(ntp);
         await ntp.reducedMotion();
@@ -213,7 +213,7 @@ test.describe('newtab customizer', () => {
         await cp.acceptsBadImagesUpdate();
         await cp.closesCustomizer();
     });
-    test.skip('loads with images, and handles nested exceptions', async ({ page }, workerInfo) => {
+    test('loads with images, and handles nested exceptions', async ({ page }, workerInfo) => {
         const ntp = NewtabPage.create(page, workerInfo);
         const cp = new CustomizerPage(ntp);
         await ntp.reducedMotion();
@@ -230,7 +230,7 @@ test.describe('newtab customizer', () => {
         await cp.opensCustomizer();
         await cp.uploadsFirstImage();
     });
-    test.skip('trigger additional file uploads', async ({ page }, workerInfo) => {
+    test('trigger additional file uploads', async ({ page }, workerInfo) => {
         const ntp = NewtabPage.create(page, workerInfo);
         const cp = new CustomizerPage(ntp);
         await ntp.reducedMotion();
@@ -249,7 +249,7 @@ test.describe('newtab customizer', () => {
         await cp.setsDarkTheme();
         await cp.darkThemeIsSelected();
     });
-    test.skip('opening settings', async ({ page }, workerInfo) => {
+    test('opening settings', async ({ page }, workerInfo) => {
         const ntp = NewtabPage.create(page, workerInfo);
         const cp = new CustomizerPage(ntp);
         await ntp.reducedMotion();
diff --git a/special-pages/pages/new-tab/app/customizer/mocks.js b/special-pages/pages/new-tab/app/customizer/mocks.js
index 4100965ee..77793df1e 100644
--- a/special-pages/pages/new-tab/app/customizer/mocks.js
+++ b/special-pages/pages/new-tab/app/customizer/mocks.js
@@ -139,6 +139,12 @@ export function customizerData() {
 
     if (url.searchParams.has('userImages')) {
         customizer.userImages = [values.userImages['01'], values.userImages['02'], values.userImages['03']];
+        if (url.searchParams.get('willThrowPageException') === 'userImages') {
+            customizer.userImages[0] = {
+                ...customizer.userImages[0],
+                id: '__will_throw__',
+            };
+        }
     }
     if (url.searchParams.has('userColor')) {
         const hex = `#` + url.searchParams.get('userColor');
diff --git a/special-pages/pages/new-tab/app/customizer/strings.json b/special-pages/pages/new-tab/app/customizer/strings.json
new file mode 100644
index 000000000..5338d1acc
--- /dev/null
+++ b/special-pages/pages/new-tab/app/customizer/strings.json
@@ -0,0 +1,6 @@
+{
+  "customizer_image_privacy": {
+    "title": "Images are stored on your device so DuckDuckGo can’t see or access them.",
+    "note": "Shown near a button that allows a user to upload an image to be used as a background."
+  }
+}
diff --git a/special-pages/pages/new-tab/app/favorites/components/Favorites.js b/special-pages/pages/new-tab/app/favorites/components/Favorites.js
index e2e820459..76f1105a1 100644
--- a/special-pages/pages/new-tab/app/favorites/components/Favorites.js
+++ b/special-pages/pages/new-tab/app/favorites/components/Favorites.js
@@ -10,6 +10,8 @@ import { usePlatformName } from '../../settings.provider.js';
 import { useDropzoneSafeArea } from '../../dropzone.js';
 import { TileRow } from './TileRow.js';
 import { FavoritesContext } from './FavoritesProvider.js';
+import { CustomizerContext } from '../../customizer/CustomizerProvider.js';
+import { useComputed } from '@preact/signals';
 
 /**
  * @typedef {import('../../../types/new-tab.js').Expansion} Expansion
@@ -46,9 +48,15 @@ export function Favorites({ gridRef, favorites, expansion, toggle, openContextMe
     const hiddenCount = expansion === 'collapsed' ? favorites.length - ROW_CAPACITY : 0;
     const rowHeight = ITEM_HEIGHT + ROW_GAP;
     const canToggleExpansion = favorites.length >= ROW_CAPACITY;
+    const { data } = useContext(CustomizerContext);
+    const kind = useComputed(() => data.value.background.kind);
 
     return (
-        <div class={cn(styles.root, !canToggleExpansion && styles.noExpansionBtn)} data-testid="FavoritesConfigured">
+        <div
+            class={cn(styles.root, !canToggleExpansion && styles.noExpansionBtn)}
+            data-testid="FavoritesConfigured"
+            data-background-kind={kind}
+        >
             <VirtualizedGridRows
                 WIDGET_ID={WIDGET_ID}
                 favorites={favorites}
diff --git a/special-pages/pages/new-tab/app/favorites/components/Tile.js b/special-pages/pages/new-tab/app/favorites/components/Tile.js
index a603c470b..516b1f8ae 100644
--- a/special-pages/pages/new-tab/app/favorites/components/Tile.js
+++ b/special-pages/pages/new-tab/app/favorites/components/Tile.js
@@ -139,7 +139,7 @@ function PlusIconWrapper({ onClick }) {
     const { state, ref } = useItemState(`PLACEHOLDER-URL-${id}`, `PLACEHOLDER-ID-${id}`);
     return (
         <div class={styles.item} ref={ref} data-edge={'closestEdge' in state && state.closestEdge}>
-            <button class={cn(styles.icon, styles.placeholder, styles.plus)} aria-labelledby={id} onClick={onClick}>
+            <button class={cn(styles.icon, styles.plus, styles.draggable)} aria-labelledby={id} onClick={onClick}>
                 <PlusIcon />
             </button>
             <div class={styles.text} id={id}>
diff --git a/special-pages/pages/new-tab/app/favorites/components/Tile.module.css b/special-pages/pages/new-tab/app/favorites/components/Tile.module.css
index 4b343b0db..8856ce9c6 100644
--- a/special-pages/pages/new-tab/app/favorites/components/Tile.module.css
+++ b/special-pages/pages/new-tab/app/favorites/components/Tile.module.css
@@ -37,22 +37,41 @@
 }
 
 .draggable {
-    background-color: var(--color-black-at-3);
+    backdrop-filter: blur(48px);
+    background: var(--ntp-surface-background-color);
+    box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.12), 0px 0px 3px 0px rgba(0, 0, 0, 0.16);
 
     &:hover {
-        background-color: var(--color-black-at-9);
+        background-color: var(--color-white-at-18);
     }
 
-    &:active {
-        transform: scale(0.95);
+    [data-theme="dark"] & {
+        &:hover {
+            background: rgba(0, 0, 0, 0.24);
+        }
     }
 
-    [data-theme=dark] & {
-        background-color: var(--color-white-at-6);
+    [data-background-kind="userImage"] & {
+        border: 1px solid var(--color-black-at-9);
+        background: var(--color-white-at-30);
+
         &:hover {
-            background-color: var(--color-white-at-12);
+            background: var(--color-white-at-18);
         }
     }
+
+    [data-theme="dark"] [data-background-kind="userImage"] & {
+        border: 1px solid var(--color-white-at-9);
+        background: var(--color-black-at-18);
+
+        &:hover {
+            background: rgba(0, 0, 0, 0.24);
+        }
+    }
+
+    &:active {
+        transform: scale(0.95);
+    }
 }
 
 .favicon {
@@ -95,25 +114,13 @@
 
 .plus {
     outline: none;
-    border-style: solid;
+    border: none;
     color: var(--color-black-at-90);
 
     [data-theme=dark] & {
         color: var(--color-white-at-85);
     }
 
-    &:hover {
-        background: var(--color-black-at-3);
-
-        [data-theme=dark] & {
-            background: var(--color-white-at-9);
-        }
-    }
-
-    &:active {
-        transform: scale(0.95);
-    }
-
     &:focus-visible {
         box-shadow: var(--focus-ring);
     }
diff --git a/special-pages/pages/new-tab/app/index.js b/special-pages/pages/new-tab/app/index.js
index 8ef1a1f89..b2cfe9bcc 100644
--- a/special-pages/pages/new-tab/app/index.js
+++ b/special-pages/pages/new-tab/app/index.js
@@ -164,7 +164,7 @@ async function resolveEntryPoints(widgets, didCatch) {
     try {
         const loaders = widgets.map((widget) => {
             return (
-                widgetEntryPoint(widget.id)
+                widgetEntryPoint(widget.id, didCatch)
                     // eslint-disable-next-line promise/prefer-await-to-then
                     .then((mod) => [widget.id, mod])
             );
diff --git a/special-pages/pages/new-tab/app/styles/ntp-theme.css b/special-pages/pages/new-tab/app/styles/ntp-theme.css
index 256b50066..62b362278 100644
--- a/special-pages/pages/new-tab/app/styles/ntp-theme.css
+++ b/special-pages/pages/new-tab/app/styles/ntp-theme.css
@@ -2,7 +2,7 @@
     --default-light-bg: var(--color-gray-0);
     --default-dark-bg: var(--color-gray-85);
     --ntp-gap: 2rem;
-    --ntp-drawer-width: calc(236 * var(--px-in-rem));
+    --ntp-drawer-width: calc(224 * var(--px-in-rem));
     --ntp-drawer-scroll-width: 12px;
     --ntp-combined-width: calc(var(--ntp-drawer-width) + var(--ntp-drawer-scroll-width));
 
@@ -24,7 +24,7 @@
     /* label small */
     --small-label-font-size: 11px;
     --small-label-font-weight: 400;
-    --small-label-line-height: 11px;
+    --small-label-line-height: 14px;
 
     --border-radius-lg: 12px;
     --border-radius-md: 8px;
@@ -52,7 +52,7 @@
     --ntp-background-color: var(--default-dark-bg);
     --ntp-surface-background-color: var(--color-black-at-18);
     --ntp-surfaces-panel-background-color: #222222;
-    --ntp-surface-border-color: var(--color-white-at-9);
+    --ntp-surface-border-color: var(--color-white-at-12);
     --ntp-text-normal: var(--color-white-at-84);
     --ntp-text-muted: var(--color-white-at-60);
     --ntp-color-primary: var(--color-blue-30);
diff --git a/special-pages/pages/new-tab/app/widget-list/WidgetList.js b/special-pages/pages/new-tab/app/widget-list/WidgetList.js
index 52c59dd24..fdcd34fa2 100644
--- a/special-pages/pages/new-tab/app/widget-list/WidgetList.js
+++ b/special-pages/pages/new-tab/app/widget-list/WidgetList.js
@@ -13,16 +13,17 @@ import { Centered } from '../components/Layout.js';
 function placeholderWidget(id) {
     return {
         factory: () => {
-            return <p>Entry point for {id} was not found. This is a bug.</p>;
+            return null;
         },
     };
 }
 
 /**
  * @param {string} id
+ * @param {(e: {message:string}) => void} didCatch
  * @return {Promise<{factory: () => import("preact").ComponentChild}>}
  */
-export async function widgetEntryPoint(id) {
+export async function widgetEntryPoint(id, didCatch) {
     try {
         const mod = await import(`../entry-points/${id}.js`);
         if (typeof mod.factory !== 'function') {
@@ -32,6 +33,7 @@ export async function widgetEntryPoint(id) {
         return mod;
     } catch (e) {
         console.error(e);
+        didCatch(e);
         return placeholderWidget(id);
     }
 }
diff --git a/special-pages/pages/new-tab/public/locales/en/new-tab.json b/special-pages/pages/new-tab/public/locales/en/new-tab.json
index ceb6f2022..d762d1507 100644
--- a/special-pages/pages/new-tab/public/locales/en/new-tab.json
+++ b/special-pages/pages/new-tab/public/locales/en/new-tab.json
@@ -200,5 +200,9 @@
   "favorites_add": {
     "title": "Add Favorite",
     "note": "A button that allows a user to add a new 'favorite' bookmark to their existing list"
+  },
+  "customizer_image_privacy": {
+    "title": "Images are stored on your device so DuckDuckGo can’t see or access them.",
+    "note": "Shown near a button that allows a user to upload an image to be used as a background."
   }
 }
\ No newline at end of file
diff --git a/special-pages/shared/components/Switch/Switch.js b/special-pages/shared/components/Switch/Switch.js
index 2297963e2..c170f9fe1 100644
--- a/special-pages/shared/components/Switch/Switch.js
+++ b/special-pages/shared/components/Switch/Switch.js
@@ -10,10 +10,11 @@ import styles from './Switch.module.css';
  * @param {"light" | "dark"} props.theme
  * @param {boolean} props.pending - Indicates if the switch is in a pending state.
  * @param {boolean} [props.checked=false] - Indicates if the switch is checked.
+ * @param {'small' | 'medium' | 'large'} [props.size] - Which size?
  * @param {Function} props.onChecked - Callback function to be called when the switch is checked.
  * @param {Function} props.onUnchecked - Callback function to be called when the switch is unchecked.
  */
-export function Switch({ checked = false, platformName, theme, ...props }) {
+export function Switch({ checked = false, platformName, size, theme, ...props }) {
     const { onChecked, onUnchecked, ariaLabel, pending } = props;
     function change(e) {
         if (e.target.checked === true) {
@@ -23,7 +24,7 @@ export function Switch({ checked = false, platformName, theme, ...props }) {
         }
     }
     return (
-        <label class={styles.label} data-platform-name={platformName} data-theme={theme}>
+        <label class={styles.label} data-platform-name={platformName} data-theme={theme} data-size={size}>
             <input
                 disabled={pending}
                 type="checkbox"
diff --git a/special-pages/shared/components/Switch/Switch.module.css b/special-pages/shared/components/Switch/Switch.module.css
index 9fc8f2c1d..dbe89c980 100644
--- a/special-pages/shared/components/Switch/Switch.module.css
+++ b/special-pages/shared/components/Switch/Switch.module.css
@@ -47,6 +47,11 @@
         --track-border-radius: 10px;
     }
 
+    &[data-platform-name="macos"][data-size="medium"] {
+        --switch-width: 32px;
+        --switch-height: 18px;
+        --switch-handle-size: 16px;
+    }
     &[data-platform-name="macos"][data-theme="dark"] {
         --track-bg-color: var(--color-white-at-9);
         --switch-handle-color-checked: #CECECE;