diff --git a/lyra/web/static/index.html b/lyra/web/static/index.html
index 918f5a6..a0adb37 100644
--- a/lyra/web/static/index.html
+++ b/lyra/web/static/index.html
@@ -41,9 +41,7 @@
Actions
-
-
@@ -116,6 +114,14 @@
+
+
+
@@ -523,6 +529,8 @@
// iOS pans the visual viewport when the keyboard opens; follow its top
// edge so the pinned #chat sits exactly in the visible area.
root.setProperty("--app-offset", off + "px");
+ // Keyboard open ⇒ hide the bottom tab bar so the input pins to the keyboard.
+ document.body.classList.toggle("kb", (window.innerHeight - h) > 150);
}
// Re-measure across the keyboard animation: iOS reports a stale (too-short)
// height mid-animation, so sample a few times until it settles.
@@ -568,6 +576,7 @@
hamburgerMenu.addEventListener("click", toggleMobileMenu);
mobileMenuOverlay.addEventListener("click", closeMobileMenu);
+ document.getElementById("moreTab").addEventListener("click", toggleMobileMenu);
// Sync mobile menu controls with desktop
const mobileMode = document.getElementById("mobileMode");
@@ -1011,15 +1020,9 @@
document.getElementById("mobileFullLogBtn").addEventListener("click", () => {
closeMobileMenu(); window.location.href = "/logs";
});
- document.getElementById("mobileMindBtn").addEventListener("click", () => {
- closeMobileMenu(); window.location.href = "/self";
- });
document.getElementById("mobileJournalBtn").addEventListener("click", () => {
closeMobileMenu(); window.location.href = "/journal";
});
- document.getElementById("mobileHandsBtn").addEventListener("click", () => {
- closeMobileMenu(); window.location.href = "/hands";
- });
// Connect to the global live log on page load.
connectThinkingStream();
diff --git a/lyra/web/static/style.css b/lyra/web/static/style.css
index 535a758..9c85713 100644
--- a/lyra/web/static/style.css
+++ b/lyra/web/static/style.css
@@ -677,6 +677,9 @@ select:hover {
/* Wordmark + status dot — shown only in the mobile header (media query below) */
.brand, .brand-dot { display: none; }
+/* Bottom tab bar — mobile only (shown in the media query) */
+#tabbar { display: none; }
+
/* Hamburger Menu */
.hamburger-menu {
display: none;
@@ -784,21 +787,26 @@ select:hover {
@media screen and (max-width: 768px) {
body {
padding: 0;
+ background: var(--bg-elev); /* matches the tab bar so any strip below #chat is seamless */
}
#chat {
position: fixed;
- top: 0;
- left: 0;
- right: 0;
+ top: 0; left: 0; right: 0;
width: 100%;
- /* Height follows the *visible* viewport (above the keyboard); offsetTop
- shift is applied via JS transform so it tracks iOS's viewport panning. */
- height: var(--app-height, 100dvh);
- transform: translateY(var(--app-offset, 0px));
+ height: 100dvh; /* the *visible* viewport (excludes the home-indicator zone);
+ overrides the base 95vh. Body bg matches the bar below it. */
+ background: var(--bg-dark);
border-radius: 0;
border: none;
}
+ /* Only while the keyboard is open do we follow the *visible* viewport: release
+ the bottom anchor and size from the top by the measured visible height. */
+ body.kb #chat {
+ bottom: auto;
+ height: var(--app-height, 100dvh);
+ transform: translateY(var(--app-offset, 0px));
+ }
/* Show hamburger, hide desktop header controls */
.hamburger-menu {
@@ -857,14 +865,51 @@ select:hover {
font-size: 0.85rem;
}
- /* Input area - bigger touch targets */
+ /* Input area - bigger touch targets. The tab bar owns the bottom safe-area
+ inset now (the input is no longer the bottom-most element). */
#input {
padding: 12px;
- padding-bottom: calc(12px + env(safe-area-inset-bottom));
padding-left: calc(12px + env(safe-area-inset-left));
padding-right: calc(12px + env(safe-area-inset-right));
}
+ /* Bottom tab bar */
+ #tabbar {
+ display: flex;
+ flex: none; /* never let it be compressed/clipped by the flex column */
+ border-top: 1px solid var(--border);
+ background: var(--bg-elev);
+ padding-bottom: 6px; /* 100dvh already excludes the home-indicator zone */
+ padding-left: env(safe-area-inset-left);
+ padding-right: env(safe-area-inset-right);
+ }
+ #tabbar .tab {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 3px;
+ padding: 7px 0 5px;
+ background: none;
+ border: none;
+ border-radius: 0;
+ color: var(--text-fade);
+ font-family: var(--font-console);
+ text-decoration: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+ #tabbar .tab:hover { background: none; }
+ #tabbar .tab:active { background: var(--accent-soft); }
+ #tabbar .tab .ti { font-size: 1.3rem; line-height: 1; filter: grayscale(.45); }
+ #tabbar .tab .tl { font-size: .64rem; letter-spacing: .3px; }
+ #tabbar .tab.active { color: var(--accent); }
+ #tabbar .tab.active .ti { filter: none; }
+ body.kb #tabbar { display: none; } /* keyboard open ⇒ hide so input pins to keyboard */
+
+ /* The "More" tab is the menu trigger now — retire the hamburger. */
+ .hamburger-menu { display: none !important; }
+
#userInput {
font-size: 16px; /* Prevents zoom on iOS */
padding: 12px;