atlas-iac/dockerfiles/hermes-webui-brand.css
jenkins ffa477585d hermes(webui): fix mobile composer hidden behind the gesture bar
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
2026-08-24 23:32:27 -03:00

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;
}