On an Android standalone PWA the composer's bottom control row was rendering about one line below the visible viewport, behind the system gesture bar, so those controls were unreachable. The theme already pads the titlebar with env(safe-area-inset-top/left/right), but the viewport meta never opted into viewport-fit=cover, so every safe-area inset collapsed to 0 and the bottom edge had no reservation. Add viewport-fit=cover to the viewport meta (base patch) so the insets carry real values, and reserve env(safe-area-inset-bottom) at the bottom of the composer (brand.css), additive with the app's existing --keyboard-bottom-inset and absorbed by the flex-1 message scroller so total height stays within the viewport. Inert on desktop (env() resolves to 0). Dockerfile verifies the meta patch landed; brand/dockerfile contracts covered by tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
264 lines
7.7 KiB
CSS
264 lines
7.7 KiB
CSS
/* Restrained Hermes/Atlas identity layered after the pinned upstream theme. */
|
|
:root {
|
|
--accent: #187f8b;
|
|
--accent-hover: #126a74;
|
|
--accent-bg: rgba(24, 127, 139, 0.09);
|
|
--accent-bg-strong: rgba(24, 127, 139, 0.17);
|
|
--accent-text: #126f7a;
|
|
--blue: #187f9f;
|
|
--gold: #9a661f;
|
|
--focus-ring: rgba(24, 127, 139, 0.38);
|
|
--focus-glow: rgba(24, 127, 139, 0.12);
|
|
--hermes-violet: #7065a8;
|
|
--hermes-grid: rgba(24, 127, 139, 0.035);
|
|
}
|
|
|
|
:root.dark {
|
|
color-scheme: dark;
|
|
--bg: #070a12;
|
|
--sidebar: #0d1420;
|
|
--surface: #111b29;
|
|
--surface-subtle: rgba(116, 202, 214, 0.035);
|
|
--surface-subtle-hover: rgba(116, 202, 214, 0.075);
|
|
--border: #203044;
|
|
--border2: rgba(174, 218, 224, 0.18);
|
|
--border-subtle: rgba(174, 218, 224, 0.08);
|
|
--border-muted: rgba(174, 218, 224, 0.13);
|
|
--text: #e8f1f4;
|
|
--strong: #f8fcfd;
|
|
--muted: #91a5b3;
|
|
--em: #c5d2d8;
|
|
--accent: #48cfcc;
|
|
--accent-hover: #75dedb;
|
|
--accent-bg: rgba(72, 207, 204, 0.09);
|
|
--accent-bg-strong: rgba(72, 207, 204, 0.17);
|
|
--accent-text: #6bd8d4;
|
|
--blue: #4ca4cd;
|
|
--gold: #f0b66b;
|
|
--code-bg: #09111c;
|
|
--code-inline-bg: rgba(4, 10, 17, 0.72);
|
|
--code-text: #b9e5e4;
|
|
--pre-text: #dce8ec;
|
|
--input-bg: rgba(193, 229, 233, 0.045);
|
|
--hover-bg: rgba(193, 229, 233, 0.07);
|
|
--topbar-bg: rgba(9, 14, 24, 0.96);
|
|
--main-bg: rgba(7, 10, 18, 0.72);
|
|
--focus-ring: rgba(72, 207, 204, 0.38);
|
|
--focus-glow: rgba(72, 207, 204, 0.12);
|
|
--hermes-violet: #9d8ee0;
|
|
--hermes-violet-muted: rgba(157, 142, 224, 0.12);
|
|
--hermes-grid: rgba(72, 207, 204, 0.025);
|
|
--error: #f08b79;
|
|
--success: #65c9a6;
|
|
--warning: #f0b66b;
|
|
--info: #69b9dc;
|
|
}
|
|
|
|
:root.dark body {
|
|
background:
|
|
radial-gradient(circle at 78% 8%, rgba(72, 164, 205, 0.085), transparent 31rem),
|
|
radial-gradient(circle at 28% 100%, rgba(157, 142, 224, 0.045), transparent 34rem),
|
|
linear-gradient(145deg, #070a12, #080d17 55%, #071017);
|
|
}
|
|
|
|
:root.dark .app-titlebar,
|
|
:root.dark .rail,
|
|
:root.dark .sidebar,
|
|
:root.dark .rightpanel,
|
|
:root.dark .topbar,
|
|
:root.dark .composer-wrap {
|
|
border-color: var(--border);
|
|
background-color: rgba(13, 20, 32, 0.94);
|
|
}
|
|
|
|
/* A quiet workstation canvas: enough technical texture to feel intentional,
|
|
but low-contrast enough to remain behind the work. */
|
|
:root.dark .main-view,
|
|
:root.dark .messages {
|
|
background-color: transparent;
|
|
}
|
|
|
|
:root.dark .messages-shell {
|
|
background-image:
|
|
linear-gradient(var(--hermes-grid) 1px, transparent 1px),
|
|
linear-gradient(90deg, var(--hermes-grid) 1px, transparent 1px),
|
|
radial-gradient(circle at 76% 22%, rgba(76, 164, 205, 0.055), transparent 30rem),
|
|
radial-gradient(circle at 22% 78%, var(--hermes-violet-muted), transparent 35rem);
|
|
background-size: 32px 32px, 32px 32px, auto, auto;
|
|
background-repeat: repeat, repeat, no-repeat, no-repeat;
|
|
}
|
|
|
|
/* The transcript is an accelerated scroll surface. Painting gradients on it
|
|
produces stale compositor tiles on Chromium as the scroll offset changes,
|
|
which looks like a dark rectangle moving along the right edge. Keep the
|
|
scroll surface transparent and paint the texture on its stationary shell. */
|
|
:root.dark .messages {
|
|
background-image: none;
|
|
}
|
|
|
|
:root.dark .rail {
|
|
background-image: linear-gradient(180deg, rgba(72, 207, 204, 0.035), transparent 38%, rgba(157, 142, 224, 0.03));
|
|
}
|
|
|
|
:root.dark .sidebar {
|
|
box-shadow: inset -1px 0 rgba(72, 207, 204, 0.035);
|
|
}
|
|
|
|
:root.dark .session-item.active {
|
|
border-color: rgba(72, 207, 204, 0.17);
|
|
background: linear-gradient(90deg, rgba(72, 207, 204, 0.11), rgba(157, 142, 224, 0.045));
|
|
box-shadow: inset 2px 0 rgba(72, 207, 204, 0.52);
|
|
}
|
|
|
|
.app-titlebar-icon img {
|
|
display: block;
|
|
width: 22px;
|
|
height: 22px;
|
|
border: 1px solid rgba(72, 207, 204, 0.24);
|
|
border-radius: 7px;
|
|
box-shadow: 0 0 0 2px rgba(72, 207, 204, 0.05);
|
|
}
|
|
|
|
.app-titlebar-title {
|
|
letter-spacing: 0.025em;
|
|
}
|
|
|
|
/* The established Hermes-agent character is the new-chat persona. */
|
|
.empty-logo {
|
|
isolation: isolate;
|
|
min-width: 126px;
|
|
min-height: 126px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.empty-logo::before {
|
|
width: 224px;
|
|
height: 224px;
|
|
background:
|
|
radial-gradient(circle, rgba(72, 207, 204, 0.15), rgba(76, 164, 205, 0.07) 42%, rgba(112, 101, 168, 0.035) 58%, transparent 72%);
|
|
}
|
|
|
|
.empty-logo::after {
|
|
content: "";
|
|
position: absolute;
|
|
z-index: 1;
|
|
inset: -7px;
|
|
border: 1px solid rgba(24, 127, 139, 0.2);
|
|
border-radius: 29px;
|
|
background: linear-gradient(145deg, rgba(255, 255, 255, 0.12), transparent 38%, rgba(112, 101, 168, 0.08));
|
|
box-shadow: 0 16px 42px rgba(18, 55, 65, 0.12);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.empty-logo .hermes-agent-portrait {
|
|
position: relative;
|
|
z-index: 2;
|
|
display: block;
|
|
width: 112px;
|
|
height: 112px;
|
|
object-fit: cover;
|
|
border: 1px solid rgba(24, 127, 139, 0.28);
|
|
border-radius: 22px;
|
|
box-shadow: 0 10px 30px rgba(18, 55, 65, 0.18);
|
|
}
|
|
|
|
:root.dark .empty-logo::after {
|
|
border-color: rgba(72, 207, 204, 0.28);
|
|
background: linear-gradient(145deg, rgba(72, 207, 204, 0.1), transparent 40%, rgba(157, 142, 224, 0.09));
|
|
box-shadow: 0 0 0 1px rgba(72, 207, 204, 0.035), 0 18px 48px rgba(0, 0, 0, 0.36), 0 0 34px rgba(76, 164, 205, 0.07);
|
|
}
|
|
|
|
:root.dark .empty-logo .hermes-agent-portrait {
|
|
border-color: rgba(72, 207, 204, 0.32);
|
|
box-shadow: 0 12px 34px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
:root.dark .empty-state h2 {
|
|
color: var(--strong);
|
|
letter-spacing: -0.015em;
|
|
text-shadow: 0 0 24px rgba(72, 207, 204, 0.08);
|
|
}
|
|
|
|
:root.dark .empty-state p {
|
|
color: #9cafba;
|
|
}
|
|
|
|
:root.dark .suggestion {
|
|
border-color: rgba(174, 218, 224, 0.15);
|
|
background: linear-gradient(105deg, rgba(17, 27, 41, 0.88), rgba(13, 20, 32, 0.74));
|
|
box-shadow: inset 2px 0 rgba(72, 207, 204, 0.12), 0 8px 22px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
:root.dark .suggestion:hover,
|
|
:root.dark .suggestion:focus-visible {
|
|
color: var(--strong);
|
|
border-color: rgba(72, 207, 204, 0.4);
|
|
background: linear-gradient(105deg, rgba(72, 207, 204, 0.11), rgba(157, 142, 224, 0.065));
|
|
}
|
|
|
|
:root.dark .suggestion:focus-visible {
|
|
outline: 2px solid var(--focus-ring);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
:root.dark .composer-box:focus-within {
|
|
border-color: rgba(72, 207, 204, 0.66);
|
|
box-shadow: 0 0 0 2px var(--focus-glow), 0 10px 34px rgba(0, 0, 0, 0.28), 0 0 28px rgba(157, 142, 224, 0.04);
|
|
}
|
|
|
|
/* Keep the conversation instrument inside the same cyan/blue/gold family. */
|
|
:root.dark .voice-mode-bar {
|
|
--voice-accent: 72, 207, 204;
|
|
--voice-accent-secondary: 76, 164, 205;
|
|
border-bottom-color: rgba(174, 218, 224, 0.11);
|
|
background:
|
|
radial-gradient(circle at 50% 38%, rgba(var(--voice-accent), 0.085), transparent 47%),
|
|
linear-gradient(180deg, rgba(17, 27, 41, 0.8), rgba(7, 10, 18, 0.35));
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
:root.dark body {
|
|
background: #070a12;
|
|
}
|
|
|
|
.app-titlebar-icon img,
|
|
:root.dark .composer-box:focus-within,
|
|
.suggestion {
|
|
transition: none !important;
|
|
}
|
|
|
|
.suggestion:hover,
|
|
.suggestion:focus-visible {
|
|
transform: none !important;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.empty-logo {
|
|
min-width: 104px;
|
|
min-height: 104px;
|
|
}
|
|
|
|
.empty-logo::before {
|
|
width: 176px;
|
|
height: 176px;
|
|
}
|
|
|
|
.empty-logo .hermes-agent-portrait {
|
|
width: 92px;
|
|
height: 92px;
|
|
border-radius: 19px;
|
|
}
|
|
}
|
|
|
|
/* Reserve the device safe-area (home indicator / gesture bar) below the
|
|
composer so its control row is never hidden behind it. Requires
|
|
viewport-fit=cover on the viewport meta (set in hermes-webui-base-patch.py);
|
|
env() resolves to 0 on devices without an inset, so this is inert on desktop.
|
|
Additive with the keyboard inset the app already manages via
|
|
--keyboard-bottom-inset, and absorbed by the flex-1 message scroller so the
|
|
total column height stays within the viewport (body is height:100dvh). */
|
|
.composer-wrap {
|
|
padding-bottom: calc(16px + var(--keyboard-bottom-inset, 0px) + env(safe-area-inset-bottom, 0px)) !important;
|
|
}
|