webui: Cleanup css and fix layout

- Fixes #13
diff --git a/webui/src/sketch-app-shell.css b/webui/src/sketch-app-shell.css
index 57c96df..31a7676 100644
--- a/webui/src/sketch-app-shell.css
+++ b/webui/src/sketch-app-shell.css
@@ -1,22 +1,9 @@
 html,
 body {
   height: 100%;
-  overflow-y: auto;
 }
 
 body {
-  font-family:
-    system-ui,
-    -apple-system,
-    BlinkMacSystemFont,
-    "Segoe UI",
-    Roboto,
-    sans-serif;
   margin: 0;
-  padding: 0;
-  color: #333;
-  line-height: 1.4;
-  overflow-x: hidden; /* Prevent horizontal scrolling */
   display: flex;
-  flex-direction: column;
 }
diff --git a/webui/src/web-components/sketch-app-shell.ts b/webui/src/web-components/sketch-app-shell.ts
index 61b14a8..c22f9d9 100644
--- a/webui/src/web-components/sketch-app-shell.ts
+++ b/webui/src/web-components/sketch-app-shell.ts
@@ -1,17 +1,19 @@
 import { css, html, LitElement } from "lit";
 import { customElement, property, state } from "lit/decorators.js";
-import { DataManager, ConnectionStatus } from "../data";
-import { State, AgentMessage } from "../types";
-import "./sketch-container-status";
-import "./sketch-view-mode-select";
-import "./sketch-network-status";
-import "./sketch-timeline";
-import "./sketch-chat-input";
-import "./sketch-diff-view";
-import "./sketch-charts";
-import "./sketch-terminal";
-import { SketchDiffView } from "./sketch-diff-view";
+import { ConnectionStatus, DataManager } from "../data";
+import { AgentMessage, State } from "../types";
 import { aggregateAgentMessages } from "./aggregateAgentMessages";
+import "./sketch-charts";
+import "./sketch-chat-input";
+import "./sketch-container-status";
+import "./sketch-diff-view";
+import { SketchDiffView } from "./sketch-diff-view";
+import "./sketch-network-status";
+import "./sketch-terminal";
+import "./sketch-timeline";
+import "./sketch-view-mode-select";
+
+import { createRef, ref } from "lit/directives/ref.js";
 
 type ViewMode = "chat" | "diff" | "charts" | "terminal";
 
@@ -41,31 +43,53 @@
         sans-serif;
       color: #333;
       line-height: 1.4;
-      min-height: 100vh;
+      height: 100vh;
       width: 100%;
       position: relative;
       overflow-x: hidden;
+      display: flex;
+      flex-direction: column;
     }
 
     /* Top banner with combined elements */
-    .top-banner {
+    #top-banner {
       display: flex;
+      align-self: flex-start;
       justify-content: space-between;
       align-items: center;
       padding: 5px 20px;
       margin-bottom: 0;
       border-bottom: 1px solid #eee;
       gap: 10px;
-      position: fixed;
       top: 0;
       left: 0;
       right: 0;
       background: white;
-      z-index: 100;
       box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
       max-width: 100%;
     }
 
+    /* View mode container styles - mirroring timeline.css structure */
+    #view-container {
+      align-self: stretch;
+      overflow-y: auto;
+      flex: 1;
+    }
+
+    #view-container-inner {
+      max-width: 1200px;
+      margin: 0 auto;
+      position: relative;
+      padding-bottom: 10px;
+      padding-top: 10px;
+    }
+
+    #chat-input {
+      align-self: flex-end;
+      width: 100%;
+      box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
+    }
+
     .banner-title {
       font-size: 18px;
       font-weight: 600;
@@ -88,19 +112,8 @@
       text-overflow: ellipsis;
     }
 
-    /* View mode container styles - mirroring timeline.css structure */
-    .view-container {
-      max-width: 1200px;
-      margin: 0 auto;
-      margin-top: 65px; /* Space for the top banner */
-      margin-bottom: 90px; /* Increased space for the chat input */
-      position: relative;
-      padding-bottom: 15px; /* Additional padding to prevent clipping */
-      padding-top: 15px; /* Add padding at top to prevent content touching the header */
-    }
-
     /* Allow the container to expand to full width in diff mode */
-    .view-container.diff-active {
+    #view-container.diff-active {
       max-width: 100%;
     }
 
@@ -348,7 +361,7 @@
     // Wait for DOM update to complete
     this.updateComplete.then(() => {
       // Update active view
-      const viewContainer = this.shadowRoot?.querySelector(".view-container");
+      const viewContainer = this.shadowRoot?.querySelector("#view-container");
       const chatView = this.shadowRoot?.querySelector(".chat-view");
       const diffView = this.shadowRoot?.querySelector(".diff-view");
       const chartView = this.shadowRoot?.querySelector(".chart-view");
@@ -490,9 +503,11 @@
     }
   }
 
+  private scrollContainerRef = createRef<HTMLElement>();
+
   render() {
     return html`
-      <div class="top-banner">
+      <div id="top-banner">
         <div class="title-container">
           <h1 class="banner-title">sketch</h1>
           <h2 id="chatTitle" class="chat-title">${this.title}</h2>
@@ -522,36 +537,43 @@
         </div>
       </div>
 
-      <div class="view-container">
-        <div class="chat-view ${this.viewMode === "chat" ? "view-active" : ""}">
-          <sketch-timeline
-            .messages=${this.messages}
-            .scrollContainer=${this}
-          ></sketch-timeline>
-        </div>
-
-        <div class="diff-view ${this.viewMode === "diff" ? "view-active" : ""}">
-          <sketch-diff-view
-            .commitHash=${this.currentCommitHash}
-          ></sketch-diff-view>
-        </div>
-
-        <div
-          class="chart-view ${this.viewMode === "charts" ? "view-active" : ""}"
-        >
-          <sketch-charts .messages=${this.messages}></sketch-charts>
-        </div>
-
-        <div
-          class="terminal-view ${this.viewMode === "terminal"
-            ? "view-active"
-            : ""}"
-        >
-          <sketch-terminal></sketch-terminal>
+      <div id="view-container" ${ref(this.scrollContainerRef)}>
+        <div id="view-container-inner">
+          <div
+            class="chat-view ${this.viewMode === "chat" ? "view-active" : ""}"
+          >
+            <sketch-timeline
+              .messages=${this.messages}
+              .scrollContainer=${this.scrollContainerRef}
+            ></sketch-timeline>
+          </div>
+          <div
+            class="diff-view ${this.viewMode === "diff" ? "view-active" : ""}"
+          >
+            <sketch-diff-view
+              .commitHash=${this.currentCommitHash}
+            ></sketch-diff-view>
+          </div>
+          <div
+            class="chart-view ${this.viewMode === "charts"
+              ? "view-active"
+              : ""}"
+          >
+            <sketch-charts .messages=${this.messages}></sketch-charts>
+          </div>
+          <div
+            class="terminal-view ${this.viewMode === "terminal"
+              ? "view-active"
+              : ""}"
+          >
+            <sketch-terminal></sketch-terminal>
+          </div>
         </div>
       </div>
 
-      <sketch-chat-input @send-chat="${this._sendChat}"></sketch-chat-input>
+      <div id="chat-input">
+        <sketch-chat-input @send-chat="${this._sendChat}"></sketch-chat-input>
+      </div>
     `;
   }
 
diff --git a/webui/src/web-components/sketch-chat-input.ts b/webui/src/web-components/sketch-chat-input.ts
index 74e462f..257c583 100644
--- a/webui/src/web-components/sketch-chat-input.ts
+++ b/webui/src/web-components/sketch-chat-input.ts
@@ -13,14 +13,9 @@
   static styles = css`
     /* Chat styles - exactly matching timeline.css */
     .chat-container {
-      position: fixed;
-      bottom: 0;
-      left: 0;
       width: 100%;
       background: #f0f0f0;
       padding: 15px;
-      box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
-      z-index: 1000;
       min-height: 40px; /* Ensure minimum height */
     }
 
diff --git a/webui/src/web-components/sketch-timeline.ts b/webui/src/web-components/sketch-timeline.ts
index e2c8ee7..15df383 100644
--- a/webui/src/web-components/sketch-timeline.ts
+++ b/webui/src/web-components/sketch-timeline.ts
@@ -4,6 +4,7 @@
 import { customElement, property, state } from "lit/decorators.js";
 import { AgentMessage } from "../types";
 import "./sketch-timeline-message";
+import { Ref } from "lit/directives/ref";
 
 @customElement("sketch-timeline")
 export class SketchTimeline extends LitElement {
@@ -15,7 +16,7 @@
   private scrollingState: "pinToLatest" | "floating" = "pinToLatest";
 
   @property({ attribute: false })
-  scrollContainer: HTMLElement;
+  scrollContainer: Ref<HTMLElement>;
 
   static styles = css`
     /* Hide views initially to prevent flash of content */
@@ -100,8 +101,8 @@
    * Scroll to the bottom of the timeline
    */
   private scrollToBottom(): void {
-    this.scrollContainer?.scrollTo({
-      top: this.scrollContainer?.scrollHeight,
+    this.scrollContainer.value?.scrollTo({
+      top: this.scrollContainer.value?.scrollHeight,
       behavior: "smooth",
     });
   }
@@ -117,7 +118,10 @@
       }
     }
     if (changedProperties.has("scrollContainer")) {
-      this.scrollContainer?.addEventListener("scroll", this._handleScroll);
+      this.scrollContainer.value?.addEventListener(
+        "scroll",
+        this._handleScroll,
+      );
     }
   }
 
@@ -140,9 +144,9 @@
   private _handleScroll(event) {
     const isAtBottom =
       Math.abs(
-        this.scrollContainer.scrollHeight -
-          this.scrollContainer.clientHeight -
-          this.scrollContainer.scrollTop,
+        this.scrollContainer.value.scrollHeight -
+          this.scrollContainer.value.clientHeight -
+          this.scrollContainer.value.scrollTop,
       ) <= 1;
     if (isAtBottom) {
       this.scrollingState = "pinToLatest";
@@ -161,7 +165,8 @@
       "showCommitDiff",
       this._handleShowCommitDiff as EventListener,
     );
-    this.scrollContainer?.addEventListener("scroll", this._handleScroll);
+
+    this.scrollContainer.value?.addEventListener("scroll", this._handleScroll);
   }
 
   // See https://lit.dev/docs/components/lifecycle/
@@ -174,7 +179,10 @@
       this._handleShowCommitDiff as EventListener,
     );
 
-    this.scrollContainer?.removeEventListener("scroll", this._handleScroll);
+    this.scrollContainer.value?.removeEventListener(
+      "scroll",
+      this._handleScroll,
+    );
   }
 
   // messageKey uniquely identifes a AgentMessage based on its ID and tool_calls, so